Isoclock 保姆级解读:从零看懂一套 LA-ICP-MS U-Pb 定年流程
面向第一次接触 U-Pb 定年、或者第一次打开这份源码的人 —— 每一节都先讲人话,再指给你看代码在哪一行
图解版 · 56 张图 · 行号对应重构后的 3676 行 Isoclock2.0.py · 图里所有数字由脚本实算,未手写
目录 · 写在最前面1 第 1 章 先弄懂在测什么:U-Pb 定年的三条腿2 第 2 章 分水岭:为什么 Isoclock 不是给锆石写的3 第 3 章 普通铅:把这件事彻底说透4 第 4 章 从原始文件到年龄:每一步在干什么5 第 5 章 怎么让别人接受你的结果6 第 6 章 局限与可以改进的方向7 第 7 章 代码地图:新手该从哪读起8 第 8 章 术语速查表· 附录 参考文献
面向第一次接触 U-Pb 定年、或者第一次打开这份源码的人。
每一段都尽量做到:先说人话讲原理,再指给你看代码在哪一行,最后说这个参数为什么这么设。
写在最前面
这份文档不是官方手册的重写,也不是 API 文档。它的来历是:有人把 Isoclock2.0.py 从头到尾读了一遍,把里面每一处"为什么"整理出来,写成一条从不懂到懂的路径。
所以先说清楚三件事:
它不能代替原始手册 。软件的操作说明在仓库里的两份 PDF 里,本文只做补充和解释。行号对应的是本仓库当前版本 (重构后的 3676 行 Isoclock2.0.py)。上游原版行号不同,如果你用的是原版,按函数名搜索即可。本文对科学逻辑保持原样 。代码里有些写法是故意保留的(哪怕看起来别扭),因为它们会影响已经发表的结果。凡是这种地方,我都会明确标注"原样保留",并说明为什么不能顺手改。
阅读建议:
这份文档怎么用:五类读者,五条入口 不必从头读到尾 —— 先找到自己属于哪一类。 完全的新手 第 1 章 → 第 5 章,顺着读 先把"在测什么"和"每一步在干嘛"过一遍 只想知道"普通铅"是什么 直接跳第 3 章 全文最核心的一章,讲到透为止 只想知道每步在干嘛 跳第 4 章 6 步流水线,跟着一个样品的数据走 想让审稿人接受结果 跳第 5 章 三问四招 + 一份可以直接抄的清单 想知道这软件哪儿不行 跳第 6 章 数值缺陷 / 方法局限 / 工程局限 三条铁律(先说清楚,免得误会) 1 它不能代替官方手册 —— 操作说明在仓库里的两份 PDF 里,本文只做补充和解释。 2 行号对应本仓库重构后的 3676 行 Isoclock2.0.py;用上游原版请按函数名搜索。 3 科学逻辑保持原样 —— 有些看起来别扭的写法是故意保留的,改了会影响已发表的结果。 图 1 这份文档的五条入口。也可以当作一张"我该从哪读起"的自查表 —— 先定位自己,再决定读多少。
完全的新手:从第 1 章顺着读到第 5 章。 只想知道"普通铅到底是什么":直接跳第 3 章。 只想知道"这套流程每一步在干嘛":跳第 4 章。 想知道"我的结果怎么让审稿人接受":跳第 5 章。 想知道"这软件哪儿不行":跳第 6 章。
第 1 章 先弄懂在测什么:U-Pb 定年的三条腿
这一章只讲最基础的三件事:我们到底在测什么、一个比值凭什么能变成年龄、以及为什么一次测量能给四个年龄。这三件事是后面所有内容的底座。
第 1 章要搭起来的东西:一个可以被解释的年龄 四个小节各负责一根柱子,缺一根都站不住。 一个可以被解释的年龄 1 在测什么 锆石与低 U/Pb 矿物的 比值差异很大,先分清 自己属于哪一类。 §1.1 §1.3 2 比值凭什么是年龄 两条曲线相除,初始量 U₀ 消失 —— 所以只需要比值, 不需要绝对量。 §1.2 3 四个年龄互相校验 ²⁰⁷Pb/²⁰⁶Pb 对铅丢失不敏感, 所以它能用来判断协和度。 §1.4 一句话:一个比值 + 一个常数 = 一个年龄;不同比值给出不同的年龄,差异本身就是判据。 图 2 第 1 章的骨架:三根柱子撑起同一个结论。一个比值加一个常数就是一个年龄,而不同比值给出的年龄之间的差异,就是后面所有判断的依据。
1.1 三个计时器
自然界的铀和钍会慢慢变成铅,这个"变"的速度(衰变常数)是物理常数,不随温度、压力改变。这就是铀铅定年能用的根本原因。
母体 子体 衰变常数 λ(/年) 半衰期 ²³⁸U ²⁰⁶Pb 1.55125 × 10⁻¹⁰ 4.468 Ga ²³⁵U ²⁰⁷Pb 9.8485 × 10⁻¹⁰ 0.704 Ga ²³²Th ²⁰⁸Pb 4.9475 × 10⁻¹¹ 14.01 Ga
三条衰变链:一次激光打点,同时开了三个独立的计时器 每个计时器给出一个年龄;它们本该一致 —— 不一致的地方就是这门学问的全部内容。 母体 衰变常数 λ 与半衰期 子体 能算出的年龄 ²³⁸U λ₂₃₈ = 1.55125×10⁻¹⁰ /年 半衰期 4.468 Ga ²⁰⁶Pb ²⁰⁶Pb/²³⁸U 年龄 ²³⁵U λ₂₃₅ = 9.8485×10⁻¹⁰ /年 半衰期 0.704 Ga ²⁰⁷Pb ²⁰⁷Pb/²³⁵U 年龄 ²³²Th λ₂₃₂ = 4.9475×10⁻¹¹ /年 半衰期 14.01 Ga ²⁰⁸Pb ²⁰⁸Pb/²³²Th 年龄 把半衰期画成条:三个计时器的"快慢"差了一个数量级 ²³⁸U 4.468 Ga ²³⁵U 0.704 Ga ²³²Th 14.010 Ga ²³⁵U 半衰期只有 ²³⁸U 的 1/6.3:它衰变快,老样品里 ²⁰⁷Pb 增长更快。两条链速度不同,才能互相校验。 图 3 三条衰变链就是三个独立的计时器。它们本该给出同一个年龄 —— 不一致的地方,才是后面七章要讲的全部内容。
这三个数字在代码里到处都是,但写法有点"分裂",第一次看很容易被绕晕,提前说清楚:
第 470 行写的是 0.000000000155125,单位是 每一年 (1.55125 × 10⁻¹⁰)。 第 116-118 行(SK2model)写的是 0.155125,单位是 每百万年 (1.55125 × 10⁻¹⁰ × 10⁶)。 两者是同一个常数,只差 10⁶。看到 0.155125 就知道年龄以 Ma 为单位,看到 0.000000000155125 就知道年龄以年为单位 —— 仅此而已,不是笔误。
同一个衰变常数,两种单位 —— 看数字就知道代码此刻用的是什么 这不是笔误,是"年龄单位"不同导致的。搞混了,年龄会差 100 万倍。 0.155125 单位:每百万年(/Ma) 用在 SK2model(第 116-118 行) 0.000000000155125 单位:每年(/yr) 用在 Age_Calculate_average(第 470 行) × 10⁶ × 10⁻⁶ 同 数量级尺(log₁₀) 10^-12 10^-9 10^-6 10^-3 1 0.000000000155125 0.155125 读者须知 看到 0.155125 就知道"年龄以 Ma 为单位",看到 1.55125×10⁻¹⁰ 就知道"年龄以年为单位"。两个值永远同时出现在代码的不同模块里。 顺带说 ²³⁸U/²³⁵U = 137.818 也几乎出现在每个比值换算里:因为它俩是同一元素的同位素,你测不出绝对量,只能测比值。 图 4 同一个衰变常数的两种单位写法。看到哪个数,就知道代码此刻把年龄当成 Ma 还是当年。搞混了,年龄会差 100 万倍。
第四个数字是 ²³⁸U/²³⁵U = 137.818 (现代值)。它出现在几乎所有比值换算里:因为 ²³⁵U 和 ²³⁸U 是同一个元素的同位素,你测不出它们的绝对量,只能测它们的比值,而这个比值假设是常数。
1.2 年龄公式:为什么长成 ln(r+1)/λ 这个样子
假设矿物结晶那一刻,里面只有母体 U,一点子体 Pb 都没有(后面会看到,这个假设往往是错的,这就是整篇文章的主题)。过了时间 t 之后:
母体剩余:U = U₀ · e^(−λt)
子体生成:Pb* = U₀ · (1 − e^(−λt))
两式相除,U₀ 消掉,得到比值 :
Pb*/U = e^(λt) − 1
反解出 t:
t = ln(Pb*/U + 1) / λ
这就是代码里那个反复出现的模式(第 470 行为例):
log(abs(date_all[2][i]*f206_238+1))/(0.000000000155125)/1000000
# └─ 校正后 206Pb/238U ──┘ └── λ₂₃₈(每年)──┘ └ 换成 Ma ┘
理解了三件事,这一行就通了:
+1 是因为 e^(λt) − 1 反解时要把 −1 移过去;除法用的是 λ 而不是别的,因为指数反函数是对数; 最后 /1000000 是把"年"换成"百万年"。
注意 abs() 。这个绝对值函数体现了一种"不抛错"的工程态度:如果某个点的比值算出来是负数(信号太差、背景扣过头),数学上取对数会报错,程序直接崩。加了绝对值,程序能跑完,但结果是没有物理意义的垃圾值 。这是这套代码的一个通病:宁可给你一个假数字,也不肯中断。后面第 6 章会回到这一点。
年龄公式为什么长成 ln(r+1)/λ —— 四步推到头 1 t = 0 时 U = U₀ Pb* = 0 结晶那一刻的假设:晶格里一点铅都没有 2 t 之后,母体 U = U₀ · e^(−λt) 母体按指数衰减,λ 就是图的斜率 3 t 之后,子体 Pb* = U₀ · (1 − e^(−λt)) 生成量 = 初始量 − 剩下的母体 4 相除(关键) Pb* / U = e^(λt) − 1 U₀ 被消掉 —— 所以定年只需要比值 5 反解出的年龄 t = ln(Pb* / U + 1) / λ 指数反函数是对数;+1 是把 −1 移过去 把两条曲线画出来,就看懂"U₀ 被消掉"是什么意思了 0 0.2 0.5 0.8 1 0 0.5 1 时间 t(任意单位) 占 U₀ 的比例 U = U₀·e^(−λt) Pb* = U₀·(1 − e^(−λt)) 两条曲线都带 U₀。相除 → U₀ 消失 → 你只需要知道比值,不需要知道 U 的绝对量 代码第 470 行 log(abs(date_all[2][i]*f206_238+1))/(0.000000000155125)/1000000 就是最后那一行:+1、取对数、除 λ、换成 Ma。 图 5 年龄公式的四步推导。关键在于第三步:两条曲线都带 U₀,相除之后 U₀ 消失 —— 所以定年只需要比值,不需要绝对量。
同一条公式换了母体就得到另外三个年龄:
206Pb/238U 年龄: log(r+1) / λ₂₃₈ # 第 470 行
207Pb/235U 年龄: log(r+1) / λ₂₃₅ # 第 472 行
208Pb/232Th 年龄: log(r+1) / λ₂₃₂ # 第 474 行
1.3 为什么单次测量能出四个年龄
一次激光打点,你同时得到 ²⁰⁶Pb、²⁰⁷Pb、²⁰⁸Pb、²³²Th、²³⁸U 五个信号,于是能算三对"母-子"关系和一堆比值。理论上,如果这个点从结晶到现在一直是封闭体系:
²⁰⁶Pb*/²³⁸U 给出的年龄 ²⁰⁷Pb*/²³⁵U 给出的年龄 ²⁰⁷Pb*/²⁰⁶Pb 给出的年龄
三者必须一致。 把它们画在一张图上(横轴 ²⁰⁷Pb/²³⁵U,纵轴 ²⁰⁶Pb/²³⁸U),一致的点全部落在一条曲线上 —— 这条曲线叫协和线(concordia) 。
这两根轴不能反。这是 Wetherill 图的正式约定(Isoplot 的 "Normal" 图、IsoplotR 的 type=1 都是这个朝向);反过来把 ²³⁸U/²⁰⁶Pb 放在横轴的那个是 Tera-Wasserburg 图,是另一个东西。轴一反,曲线就从"凹"变成"凸",数据落在线的哪一侧也跟着反 —— 这是最容易画错、也最难自己发现的一处。
不一致的两种情况,含义完全不同:
偏离方向 常见原因 后果 沿一条指向原点 的弦排开(落在协和线下方 ) 放射成因铅丢了(后期变质、流体、退晶质化) 叫"不协和",需要做铅丢失 模型 沿一条近水平 的混合线向右漂(落在协和线右下方 ) 混进了普通铅 叫"普通铅污染",需要做普通铅校正
这两件事长得像,但处理方式完全相反 。第二章会看到,Isoclock 存在的全部理由,就是第二种情况。
协和线:一条把"两个时钟是否一致"画出来的曲线 横轴 ²⁰⁷Pb*/²³⁵U、纵轴 ²⁰⁶Pb*/²³⁸U —— Wetherill 图的正式朝向,与 Isoplot / IsoplotR 的 "Normal" 协和图一致。 0 1 2 3 4 5 6 0 0.1 0.2 0.3 0.4 ²⁰⁷Pb* / ²³⁵U (横轴) ²⁰⁶Pb* / ²³⁸U (纵轴) 1000 1500 1900 协和线 concordia 刻度:200 / 500 / 1000 / 1500 / 1900 Ma 1 2 1 铅丢失:数据沿一条两端都交于协和线的"弦"排开,表观 ²⁰⁶Pb/²³⁸U 年龄被压小 —— 教科书说的"在协和线下方"指的是这个。 2 普通铅混入:数据沿一条近水平的混合线向右漂,同样落到协和线下方。两种机制的位移方向完全不同,这才是判别依据(图按真实坐标绘制)。 图 6 协和线把"两个时钟是否一致"画了出来。铅丢失的数据沿一条指向原点的弦排开,普通铅混入的数据沿一条近水平的混合线向右漂 —— 两条线的方向完全不同,这才是判别依据。
1.4 ²⁰⁷Pb/²⁰⁶Pb 年龄为什么特殊
前面三个年龄里,后一个特别值得单独说,因为它在代码里是一个没有解析解 的迭代函数。
Age76Pb()(第 122 行)要解决的问题是:已知测到的 ²⁰⁷Pb/²⁰⁶Pb 比值,反推年龄。
而理论关系是:
²⁰⁷Pb*/²⁰⁶Pb = (1/137.818) · (e^(λ₂₃₅·t) − 1) / (e^(λ₂₃₈·t) − 1)
(对应第 130 行的 Rap。)
这个式子里 t 同时出现在分子分母的指数上,没办法用代数解出来 ,只能数值求解。代码用的是"割线法"(secant method):
while abs(Rap76 - Rap) > 0.00005 and N < 10: # 第 133 行
delta = Rap76 - Rap
if Rap < Rap76:
Tmin = Tm # 猜小了,把下界提上来
else:
Tmax = Tm # 猜大了,把上界压下去
Rapi = (t=Tmin 时算出的比值) # 第 138 行
Raps = (t=Tmax 时算出的比值) # 第 139 行
Tm = Tmin + (Tmax - Tmin) * (Rap76 - Rapi) / (Raps - Rapi)
Age76Pb = Tm
N = N + 1
逻辑本身没问题。但这里有一个真实的缺陷 ,我必须在第 6 章单独讲:循环条件是 abs(Rap76 - Rap) > 0.00005,而 Rap 是在进入循环之前 算好的、循环里从来没有更新过 。所以这个条件一旦第一次为真,就永远为真 —— 循环实际固定跑 10 次就退出。
²⁰⁷Pb/²⁰⁶Pb 年龄为什么没有公式解:割线法的十次逼近 t 同时出现在分子和分母的指数上,代数上解不出来,只能一遍遍试。 0 1000 2000 3000 4000 0 0.1 0.2 0.3 假设的年龄 t(Ma) 算出的 ²⁰⁷Pb*/²⁰⁶Pb R(t) ↑ 继续上升,超出纵轴(t > 3.75 Ga) 实测值 R* 真值 2100 Ma 代码返回 2135 Ma 割线法怎么走 ① 先猜区间中点 2278 Ma;② 算出此时 R 是偏大还是偏小;③ 据此把区间的一头收进来; ④ 在收缩后的区间上画割线,割线与水平线的交点作为下一次猜测。反复十次。 区间从 [0.001, 4556] 开始 —— 这是代码里写死的初始范围,横跨地球年龄。 好消息:这个迭代对 1 Ga 以下的样品几乎无误差(实测 1000 Ma 处仅 +0.03 Ma)。坏消息在第 6 章。 图 7 ²⁰⁷Pb/²⁰⁶Pb 年龄没有解析解,只能迭代。水平线是实测值,曲线是"假设年龄 → 理论比值"的关系,每次割线交点让猜测更靠近真值。
这不是致命的(10 次割线法对多数年龄已经够收敛),但它让"收敛判据"变成了一句空话。1.5–2.2 Ga 的老年龄会因此偏高最多约 35 Ma(实测在 2.1 Ga 处是 +34.77 Ma)。这一条已经写进仓库 README 的 Known issues,第 6 章有完整的实测数据。
这个细节值得记住:代码里最危险的不是报错,是看起来在收敛其实没有。 如果作者当初把 Rap 的更新放进循环,这个 bug 就不会存在。
那行收敛判据为什么等于没写:一个变量没跟着更新 循环条件是 abs(R* − R) > 0.00005,而 R 在进循环之前算了一次,循环体内再没动过。 代码实际做的事 Rap = R(Tm) while |R* − Rap| > 5e−5 and N < 10: 更新 Tmin / Tmax Tm = 割线交点 N += 1 Rap 在这个循环里 从未被重新计算 → 条件值恒定 → 要么一次都不跑, 要么稳稳跑满 10 次 本来想做的事 while N < 10: 更新 Tmin / Tmax Tm = 割线交点 Rap = R(Tm) ← 关键一行 N += 1 收敛到 5e−5 就提前退出 → 迭代次数由精度决定, 而不是由常数 10 决定 但输出会变 → 必须作者确认 结果:迭代次数被写死成 10。对多数年龄够用,但在 1.5–2.2 Ga 区间偏差可达 +34.77 Ma。 注意判据里还有 delta = R* − Rap 这个赋值 —— 它算完也没被用。 图 8 左边是代码实际做的事,右边是它本来想做的事。差别只有一行:Rap 有没有在循环里重新计算。这一行决定了"收敛判据"是不是一句空话。
第 2 章 分水岭:为什么 Isoclock 不是给锆石写的
这一章回答你问的最关键的那个问题。
同一批数据,两条完全不同的路线 分岔点不是矿物名字,而是 ²⁰⁶Pb/²⁰⁴Pb 有多大。 同一批 LA-ICP-MS 数据 实测 ²⁰⁶Pb/²⁰⁴Pb 有多大? > 1000 典型锆石 处理重点:铅丢失,不是普通铅 · 普通铅占比 < 1%,扣也行、不扣也行 · 主要工作是处理不协和(铅丢失) · ²⁰⁷Pb/²⁰⁶Pb 当"体检指标"用 · 界面选择:Standard not Corr. + None < 200 磷灰石 / 黑钨矿 处理重点:普通铅是主要矛盾 · 普通铅占比可达 10–90% · 必须在计算第一步就扣掉 · 四条路选一,还要用 TW 图自证 · ²⁰⁴Pb 通道质量决定选哪条路 误判的代价:把锆石的流程套到磷灰石上,会把 3% 的误差当成 0.1% 报出去。 图 9 整份文档的分水岭。分岔点不是矿物名字,而是 ²⁰⁶Pb/²⁰⁴Pb —— 这一个数字决定了你的数据里有多少铅根本不是年龄信息。
2.1 先看锆石过的是什么日子
锆石(ZrSiO₄)在 U-Pb 定年里是"天选之子",原因有两个:
锆石为什么是"天选之子":两个原因,一个结果 原因一:晶格排斥铅 锆石晶格是为 Zr⁴⁺ 和 Si⁴⁺ 准备的。 Pb²⁺ 半径 ≈ 1.19 Å,而 Zr⁴⁺ ≈ 0.72 Å(六配位); 电价也是 2+ 对 4+ —— 尺寸和电荷都不对。 所以结晶时抓到的"初始铅"极少。 原因二:富含铀 锆石里的 U 常在几十到几千 ppm 的量级。 铀多 → 自己衰变长出来的放射成因铅多。 两个原因叠加,结果是下面这个数字。 ²⁰⁶Pb/²⁰⁴Pb 常常 > 1000 也就是说:测到的铅里 99.9% 以上是铀衰变自己长出来的 于是锆石的常规流程变成这样: ① 一般不做普通铅校正 ② 主要工作是处理铅丢失 ③ ²⁰⁷Pb/²⁰⁶Pb 当"体检指标" 注意:界面里的 Zircon(91500) 按钮指的是"用 91500 当标样", 不是"这块样品是锆石、要走特殊流程" —— 这两件事别混。 图 10 两个原因叠加出一个结果。理解这张图,就理解为什么锆石流程里"不扣普通铅"是一个合理选择,而不是偷懒。
第一,它排斥铅。 锆石的晶格是给 Zr⁴⁺ 和 Si⁴⁺ 准备的,Pb²⁺ 半径太大、电荷不对,几乎进不去。所以锆石结晶时抓到的"初始铅"极少。
第二,它富含铀。 锆石里的 U 常是几十到几千 ppm 的量级。
两个因素叠加,结果就是:锆石的 ²⁰⁶Pb/²⁰⁴Pb 常常大于 1000 ,也就是说,你测到的铅里,99.9% 以上都是铀衰变自己长出来的,外面混进来的那点普通铅可以忽略。
所以锆石的常规处理流程是:
不做普通铅校正 (或者只在极少数情况下做);主要工作是处理铅丢失 导致的"不协和"; ²⁰⁷Pb/²⁰⁶Pb 比值在锆石里主要当成一个"体检指标",用来看协和度。
如果你拿锆石数据跑 Isoclock,正确的选择是:计算模式选 "Standard not Corr.",样品普通铅校正选 "None" 。界面里那个 Zircon(91500) 按钮指的是用 91500 当标样 ,不是"这块样品是锆石所以要走特殊流程",这两件事别混。
2.2 换一批矿物,情况就反过来了
Isoclock 面向的是磷灰石、榍石、黑钨矿、锡石这一类矿物。它们的共同特征,恰恰和锆石相反:
锆石 磷灰石 榍石 黑钨矿 / 锡石 典型 U 含量 几十–几千 ppm < 50 ppm 几十–几百 ppm 低,变化大 晶格对 Pb 的排斥 极强 弱 中等 弱 ²⁰⁶Pb/²⁰⁴Pb 常 > 1000 常 < 200 几十–几百 常常很低 普通铅占比 < 1% 可达 10–90% 常 10–50% 常很高 主要矛盾 铅丢失 普通铅 普通铅 普通铅 + 基体
为什么不是"矿物不同",而是"普通铅占比不同" 同一台仪器、同样的测量精度:²⁰⁶Pb/²⁰⁴Pb 越低,测到的铅里"不是这块矿物的"就越多。 锆石 91500 1000 榍石 300 磷灰石 MAD-NEW 150 黑钨矿 / 锡石 45 20 50 100 200 500 1000 2000 实测 ²⁰⁶Pb/²⁰⁴Pb(对数轴) 普通铅占 ²⁰⁶Pb 的比例 < 1% 10–50% 可达 10–90% 常很高 同一份 ²⁰⁴Pb 通道的测量误差(假设 5%),传到年龄上差多少 0 1 2 3 4 年龄的相对误差(%) ← 曲线向左继续急剧上升:R < 42 时误差已超出纵轴上限 锆石 榍石 磷灰石 黑钨矿 1000 100 实测 ²⁰⁶Pb/²⁰⁴Pb 图 11 分水岭不在"矿物种类",而在 ²⁰⁶Pb/²⁰⁴Pb 有多大。这个数字决定了你的数据里有多少铅根本不是这块矿物的年龄信息。
2.3 用"误差传递"把这个困难量化
这是理解整件事最有用的一把尺子。
普通铅的 ²⁰⁶Pb/²⁰⁴Pb 大约是 18.7(第 3 章会给出来历)。假设你的样品实测 ²⁰⁶Pb/²⁰⁴Pb = R,那么普通铅在总 ²⁰⁶Pb 里的占比就是:
f_c = 18.7 / R
而你要做的那个减法,是拿"普通铅的量"去减。这份量本身的相对误差会原封不动地传到放射成因铅上,并且被放大 :
放射成因 ²⁰⁶Pb 的相对误差 = σ_204 × f_c / (1 − f_c)
└─ 204 通道的相对误差
样品 ²⁰⁶Pb/²⁰⁴Pb 普通铅占比 f_c 误差传递因子 f_c/(1−f_c) 若 204 通道有 5% 相对误差 1000(典型锆石) 1.9% 0.02 → 年龄误差 0.10% 200 9.3% 0.10 → 0.52% 100 18.7% 0.23 → 1.15% 50 37.4% 0.60 → 2.99%
两行对照着看,结论就出来了:
锆石 (R ≈ 1000):就算 ²⁰⁴Pb 通道有 5% 的误差,传到年龄上也只剩 0.1%。所以普通铅在锆石里是"扣也行、不扣也行"。黑钨矿 (R ≈ 50):同样 5% 的 ²⁰⁴Pb 误差,传到年龄上是 3% 。如果这块矿物是 200 Ma,那就是额外的 ±6 Ma —— 而这还只是 ²⁰⁴Pb 一个通道带来的。
这就是为什么低 U/Pb 矿物的数据,误差条天生就比锆石宽。不是测得更差,而是年龄信息被普通铅稀释了。
把"普通铅占比"和"误差被放大多少"画在一起 普通铅的 ²⁰⁶Pb/²⁰⁴Pb 约 18.7。样品实测值为 R 时,普通铅占 f = 18.7/R,传递因子就是 f/(1−f)。 0 0.2 0.4 0.6 0.8 1 f 或 f/(1−f) ← 红线继续上升,已出框 f = 18.7/R 普通铅占比 f/(1−f) 误差传递因子 锆石 ≥1000 榍石 300 磷灰石 150 黑钨矿 48 误差传递因子 f/(1−f):R = 48 时是 0.64 —— ²⁰⁴Pb 通道的相对误差有六成会原样传到年龄上 读法:两条曲线在 R 大时都贴近 0(锆石:普通铅无所谓),在 R 小时一起翘起来(低 U/Pb 矿物:普通铅是主要矛盾)。 这就是"低 U/Pb 矿物的误差条天生更宽"的全部原因 —— 年龄信息被稀释了,不是测得更差。 图 12 两条曲线:普通铅占比 f = 18.7/R,与误差传递因子 f/(1−f)。R 大时两者都贴近 0,R 小时一起翘起来 —— 这就是"低 U/Pb 矿物的误差条天生更宽"的全部原因。
2.4 一句话总结这个差异
在锆石里,普通铅是一个可以事后再处理的"小麻烦";在低 U/Pb 矿物里,它是一个必须在计算第一步就解决的"主要矛盾"。
从"我测的是什么矿物"到"该选哪条校正路径" 这张图对应界面上的两组按钮:左边管标样,右边管样品。别把它们搞混。 这批样品的 ²⁰⁶Pb/²⁰⁴Pb 大致多少? (用 Mean_Cps.csv 里 206 与 204 的净信号相除即可) > 200:放射成因铅占绝对优势 锆石一类。计算模式选 Standard not Corr.,样品校正选 None —— 不做普通铅校正 20–200:普通铅已不可忽略 但 ²⁰⁴Pb 通道信号尚可 < 20:普通铅占大头 ²⁰⁴Pb 信号弱,可能测不准 优先:204Pb 或 Cal 204Pb 最直接、假设最少。但先确认仪器导出时 做过 Hg 校正(否则 ²⁰⁴Pb 被高估) 优先:207Pb 或 208Pb 不依赖 ²⁰⁴Pb 通道,避开它信号弱的 软肋;代价是要假设普通铅比值 无论选哪条:标样侧的 Standard Corr. / Standard not Corr. 取决于标样本身含不含普通铅 MAD-NEW 磷灰石含 → Standard Corr.;91500 锆石不含 → Standard not Corr. 最后的保险:三条路径各跑一遍(第 5 章敏感性检验),结果一致才写进论文。 图 13 从"我测的是什么矿物"到"该选哪条校正路径"的决策链。界面上那两组按钮分别管标样和样品,这张图把它们的分工摆在了一起。
这句话解释了 Isoclock 为什么长成现在这样:
为什么它要提供四种普通铅校正路径 (第 3 章)—— 因为换一种矿物,能用的通道就不一样; 为什么它有一个 "Standard Corr. / Standard not Corr." 开关 —— 因为是否需要对标样本身 也做普通铅校正,取决于标样含不含普通铅(MAD-NEW 磷灰石含,91500 锆石不含); 为什么输出表里专门留出 Tera-Wasserburg 图的两列数据 (见第 5 章)—— 因为对这类矿物,最能让审稿人信服的证据不是"我扣了普通铅",而是"我用等时线图证明我扣对了"。
2.5 还有一件比普通铅更根本的事
这一点必须说,因为它比技术细节重要:
普通铅校正是"数据整理",不是"地质论证"。
如果你测的矿物在结晶之后经历过后期流体改造、重结晶、或者多期生长的混合,那么它的 U-Pb 体系可能已经被重置或者部分重置。这时候,无论你选哪条校正路径、参数调得多精细,你算出来的"年龄"都可能没有对应的地质事件 —— 它只是一个数学上的平均值。
所以拿到一个漂亮年龄时,先问自己三个问题:
算出一个漂亮年龄之后,先过三道闸 普通铅校正是"数据整理",不是"地质论证"。 算出一个漂亮的年龄 关键不是"过不过", 而是每一问的答案都要 写进论文。审稿人问的 其实就是这三问。 1 这个点和同一块样品其他点一致吗? 不一致 → 可能有二期生长、混合或重置 三条都留痕 2 有独立证据证明它是一期形成的吗? 结构观察、共生关系、其他定年方法 三条都留痕 3 换一种校正方法,年龄会变多少? 第 5.5 节的敏感性检验就是干这个的 三条都留痕 可以写进结论 —— 但三条假设都要写在明面上 图 14 普通铅校正只是数据整理。这三道闸决定了算出来的那个数能不能被称作"年龄"。第 5.5 节的敏感性检验,做的就是第三问。
这个点和同一块样品其他点一致吗? 这个矿物有独立的证据(结构观察、共生关系、其他定年方法)证明它是一期形成的吗? 如果我把校正方法换一种,年龄会变多少?
第 5 章会给出具体的操作方法。
第 3 章 普通铅:把这件事彻底说透
这一章是全文最核心的部分。
第 3 章:普通铅,三个问题一次说透 这一章是全文最核心的部分 —— 慢一点读没关系。 ① 是什么 矿物结晶时从周围环境里 "抓"进来的铅 —— 它不含 这块矿物的年龄信息。 ²⁰⁴Pb 是它唯一的指纹, 可它偏偏特别难测。 §3.1 – §3.3 ② 怎么扣 四条路径共用同一个思路: 只是"比例尺"不同,解的是 同一个混合比例 f。 S&K 两阶段模型负责给出 普通铅本身长什么样。 §3.4 – §3.5 ③ 参数从哪来 用标样年龄反解出普通铅 参数;默认值有具体来历; 模型铅只是平均猜测。 弄清来历,才知道什么时候 不该用默认值。 §3.6 – §3.8 读完这一章,你应该能回答一句话:我的数据里,到底有多少铅不是"我的矿物"的年龄信息? 图 15 第 3 章的三个问题:它是什么、怎么扣、参数从哪来。读完这一章,你应该能一句话回答"我的数据里有多少铅不是我的矿物的"。
3.1 普通铅到底是什么
矿物结晶的那一刻,它的晶格会从周围环境里"抓"一些铅进去。这些铅的来源五花八门:岩浆本身的、围岩带进来的、流体搬运来的。
关键在于:这些铅的量和这块矿物的年龄没有任何关系。 它们是"混进来"的,不是"长出来"的。
但是质谱仪不知道这件事。它只告诉你:"这个样品里有 ²⁰⁶Pb、²⁰⁷Pb、²⁰⁸Pb、²⁰⁴Pb。"
所以你测到的量永远是两部分之和:
实测 Pb = 放射成因 Pb*(来自本矿物 U/Th 衰变,携带年龄信息)
+ 普通 Pb_c (结晶时混进来的,不携带年龄信息)
定年的全部工作就是:**想办法估出 Pb_c,从实测值里减掉,只留下 Pb*。**
你测到的铅永远是两部分之和 质谱仪不会告诉你哪一部分是"长出来的"、哪一部分是"混进来的"。定年的全部工作就是把这个减法做对。 锆石 ²⁰⁶Pb/²⁰⁴Pb ≈ 1000 放射成因 Pb* —— 带年龄信息 1.9% 黑钨矿 ²⁰⁶Pb/²⁰⁴Pb ≈ 48 放射成因 Pb* —— 带年龄信息 39.0% 普通铅 磷灰石 ²⁰⁶Pb/²⁰⁴Pb ≈ 100 放射成因 Pb* —— 带年龄信息 18.7% 普通铅 ← 条越短,年龄信息被稀释得越厉害 实测的铅 ²⁰⁶Pb、²⁰⁷Pb、²⁰⁸Pb、²⁰⁴Pb 减掉普通铅 Pb_c 它不携带年龄信息,必须估出来 剩下放射成因 Pb* 这才是年龄信息的载体 难点只有一个:铅是"混进来"的,你不在场,只能靠同位素比值去推断它有多少。 第一段(放射成因)和第三段(普通铅)的可测性完全不同 —— 这就是四条路径的由来。 图 16 你测到的铅永远是两部分之和。条越短(²⁰⁶Pb/²⁰⁴Pb 越低),放射成因那一段占比越小,年龄信息被稀释得越厉害。
3.2 ²⁰⁴Pb:普通铅的指纹
铅有四个稳定同位素。其中三个会从 U 和 Th 变出来:
²⁰⁶Pb ← ²³⁸U ²⁰⁷Pb ← ²³⁵U ²⁰⁸Pb ← ²³²Th
只有 ²⁰⁴Pb 不是任何一条主要衰变链的产物。 (²³²Th 链有一个极稀有的分支会产生极微量 ²⁰⁴Pb,可以忽略。)
这是一个极其有用的性质。它意味着:
你测到的所有 ²⁰⁴Pb 都是普通铅。
所以 ²⁰⁴Pb 的净信号,就是普通铅总量的"指纹"。
有了这个指纹,再看代码里那些比值就很好懂了:
符号 含义 代码位置 Q8普通铅的 ²⁰⁶Pb/²⁰⁴Pb 第 3177 行 R8普通铅的 ²⁰⁷Pb/²⁰⁴Pb 第 3178 行 S8普通铅的 ²⁰⁸Pb/²⁰⁴Pb 第 3179 行 Pbc普通铅的 ²⁰⁷Pb/²⁰⁶Pb(= R8/Q8) 第 3180 行 P382放射成因 的 ²⁰⁷Pb/²⁰⁶Pb第 3175 行
这几个变量名看着不友好,但记住 8 代表 208 Pb、4 代表 204 Pb、6 代表 206 Pb、7 代表 207 Pb 就能读懂了。Q8 就是 "²⁰⁶/²⁰⁴",R8 是 "²⁰⁷/²⁰⁴",以此类推。
四个铅同位素里,只有一个不参与衰变 —— 这就是全部的突破口 ²⁰⁴Pb 不是任何一条主要衰变链的产物,所以它只能来自"混进来的普通铅"。 ²⁰⁶Pb ← ²³⁸U 天然丰度 24.1% ²⁰⁷Pb ← ²³⁵U 天然丰度 22.1% ²⁰⁸Pb ← ²³²Th 天然丰度 52.4% ²⁰⁴Pb 没有母体 天然丰度 1.4% 正因为如此: 你测到的 ²⁰⁴Pb 全部都是普通铅 —— 它的净信号就是普通铅总量的"指纹"。 但丰度只有 1.4%,还要跟 ²⁰⁴Hg 抢同一个质量数(见下一张图),所以这个指纹经常模糊得看不清。 代码里那五个名字,直接看赋值表达式最可靠 变量 含义 代码赋值 位置 Q8 普通铅的 ²⁰⁶Pb/²⁰⁴Pb 18.700 − a × 9.735 第 3177 行 R8 普通铅的 ²⁰⁷Pb/²⁰⁴Pb 15.628 − b × (9.735/137.818) 第 3178 行 S8 普通铅的 ²⁰⁸Pb/²⁰⁴Pb 38.630 − c × 36.837 第 3179 行 Pbc 普通铅的 ²⁰⁷Pb/²⁰⁶Pb R8 / Q8 第 3180 行 P382 放射成因的 ²⁰⁷Pb/²⁰⁶Pb (1/137.818) × (b/a) 第 3175 行 图 17 四个铅同位素里只有 ²⁰⁴Pb 不参与衰变,所以测到的 ²⁰⁴Pb 全部是普通铅 —— 这是整个方法的突破口。下半部分是代码里那五个变量的含义对照。
3.3 可是 ²⁰⁴Pb 特别难测
如果 ²⁰⁴Pb 这么有用,为什么还需要别的校正方法?因为它有一个致命弱点:
丰度太低。 自然界的铅里,²⁰⁴Pb 只占 1.4%,而 ²⁰⁶Pb 占 24%、²⁰⁸Pb 占 52%。你要在低 U 矿物本来就微弱的信号里,去精确测量那 1.4%。
再加上一个专门的干扰:
²⁰⁴Hg 的干扰。 汞和铅的原子质量都是 204(²⁰⁴Hg vs ²⁰⁴Pb),单靠质量分辨率分不开。而汞在 Ar 等离子体和样品气溶胶里并不少见。所以测"²⁰⁴Pb"的通道,实际上同时接收了 ²⁰⁴Pb 和 ²⁰⁴Hg。
代码试图处理这个问题:loaddata() 在读文件时额外读了一个 202Hg 通道(第 280 行),就是留着用来反推 ²⁰⁴Hg 的量。
但是这段计算实际上没有生效。 在 _net_signal() 里(第 1569-1571 行)有一段注释:
# 204 通道:原实现直接用原始 204 减背景,未做 Hg 干扰扣除
# (源码里那段 Hg204cal_s 恒为 0、m204_s 从未被引用)
'pb204': np.average(sig['Hg204_s']) - np.average(sig['Hg204_b']),
也就是说,软件直接把 204 通道的原始计数 当成普通铅的量,没有扣 Hg。如果仪器导出数据时已经做过 Hg 校正,这样是对的;如果没做,那 ²⁰⁴Pb 就是被高估的,所有基于 204 的校正都会偏。这条也在 README 的 Known issues 里。
你需要做的 :确认你的质谱仪导出数据时是否已经做了 Hg 校正(Thermo 软件的 "Hg correction" 之类)。这是你自己的数据你自己的责任。
²⁰⁴Pb 通道的三重困难(以及代码实际只解决了什么) 1 丰度只有 1.4% ²⁰⁴Pb 1.4% ²⁰⁷Pb 22.1% ²⁰⁶Pb 24.1% ²⁰⁸Pb 52.4% 要在本来就微弱的低 U 矿物信号里, 去精确测量这 1.4%。 2 ²⁰⁴Hg 干扰 m/z ²⁰⁴Hg ²⁰⁴Pb 两者质量数相同,质量分辨率分不开。 3 信号弱 → 相对误差大 0 1 2 3 4 ²⁰⁶Pb/²⁰⁴ 实测值 → 纵轴:²⁰⁶Pb* 相对误差(%) 代码做了什么、没做什么 做了 loaddata() 多读了一个 202Hg 通道 (第 280 行),本来就是为了反推 ²⁰⁴Hg。 202Hg/²⁰⁴Hg 的天然比值约 0.23, 思路是对的。 没做 源码里那段 Hg 校正的表达式是 np.average(x) − np.average(x) —— 恒等于 0, 而且结果从未被引用。所以软件完全没做 Hg 校正。 你要自己去确认仪器导出时是否已做。 图 18 ²⁰⁴Pb 通道的三重困难。右下角"没做"那一栏是重点:源码里那段 Hg 校正的表达式恒等于 0,所以软件实际上完全没有做 Hg 校正。
3.4 四条校正路径:各用什么当"比例尺"
既然 ²⁰⁴Pb 又弱又脏,就得有替代方案。于是有了四条路。
先看一个统一的思路。假设我们想知道"混合进来的普通铅贡献了多少 ²⁰⁶Pb"。如果我们知道普通铅的三个关键比值 (Q8、R8、Pbc 等),那么任何一条能反映"普通铅占比"的观测都能反算出这个比例。
四条路的区别,就是"用什么当比例尺" :
四条校正路径共用一个思路:把混合物解回"纯放射成因端元" 铅同位素比值是两端元的线性混合。只要知道两个端元,实测值落在哪儿就告诉你混合比例 f。 0.2 0.4 0.6 0.8 实测 ²⁰⁷Pb/²⁰⁶Pb 放射成因端元 0.07253 f = 0(一点普通铅都没有) 普通铅端元 0.90877 f = 1(全是普通铅) 样品 A f=0.15 样品 B f=0.45 样品 C f=0.78 207Pb 路径就是解这个方程: f206 = (n7/n6 − P382) / (Pbc − P382) 然后 r68 = (n6/nU) × (1 − f206) 当 f206 = 0(没有普通铅)时,r68 退化成最朴素的 n6/nU —— 校正的全部作用就是把它拉回纯放射成因端元。 区别只在于:四条路径用不同的观测去求同一个 f。有的用 ²⁰⁷/²⁰⁶ 的偏离,有的用 ²⁰⁸Pb,有的直接测 ²⁰⁴Pb。 图 19 四条路径共用一个思路:铅同位素比值是两端元的线性混合,实测值落在哪儿就告诉你混合比例 f。区别只在于用哪个观测量去求同一个 f。
路线一:²⁰⁷Pb 法(界面:207Pb)
比例尺:实测 ²⁰⁷Pb/²⁰⁶Pb 比放射成因值高出了多少。
原理:你要量的是 ²⁰⁶Pb 里有多少是普通铅带来的。虽然你测不出 ²⁰⁴Pb,但你知道普通铅的 ²⁰⁷/²⁰⁶ 是多少(Pbc)。而实测的 ²⁰⁷/²⁰⁶ 是普通铅和放射成因铅的加权混合,混合比例直接可以从"实测值偏离放射成因值多少"里解出来。
代码里的实现(_ratios_std207,第 1600 行):
f206 = (n7 / n6 - P382) / (Pbc - P382) # 第 1604 行:解出普通铅中 206 的占比
r['r76'] = (n7 - n6 * Pbc * f206) / (n6 * (1.0 - f206))
r['r68'] = (n6 / nU) * (1.0 - f206)
f206 就是"²⁰⁶Pb 里普通铅占的比例"(在代码里叫 f206,其实就是文献里的 f₂₀₆)。看出来了吗 —— 当 f206 = 0(没有普通铅)时,r68 就退化成 n6/nU,也就是最朴素的那个比值。整个校正就是"把混合物解回纯放射成因端元"。
优点 :不需要测 ²⁰⁴Pb,信号最强的两个通道(206、207)就够了。 代价 :你必须 知道普通铅的 ²⁰⁷/²⁰⁶ 是多少(下一节讲这个数字从哪来),而且这个假设错了会直接错到年龄上。
路线二:²⁰⁸Pb 法(界面:208Pb)
比例尺:²⁰⁸Pb 与 Th 的关系。
这一路来自 Zack et al. (2011),原本是为金红石 设计的。它不用 ²⁰⁴Pb,而是利用 ²⁰⁸Pb 和普通铅的 ²⁰⁶/²⁰⁸、²⁰⁷/²⁰⁸ 比值。
代码在 _ratios_std208(第 1647 行),核心是这一行:
r['r76'] = (pb207_t - pb208_t * Pbc_78) / (pb206_t - pb208_t * Pbc_68)
r['r68'] = (pb206_t - pb208_t * Pbc_68) / net['nU']
其中 Pbc_78、Pbc_68 就是由 SK2model() 算出来的模式铅 ²⁰⁷/²⁰⁸ 和 ²⁰⁶/²⁰⁸(第 119-120 行)。逻辑是"用 ²⁰⁸Pb 当参照物,把属于普通铅的那部分 206、207 减掉"。
优缺点 :适合 Th/U 比较稳定的矿物;如果矿物本身 Th 含量很低或者波动很大,²⁰⁸Pb 反而更不可靠。
路线三:²⁰⁴Pb 法(界面:204Pb)
比例尺:直接测的 ²⁰⁴Pb。
最直接、最少假设的一条路:既然 ²⁰⁴Pb 全部是普通铅,那测到多少 ²⁰⁴Pb 就知道有多少普通铅,再乘以 Q8、R8 就知道该扣掉多少 206、207。
代码在 _ratios_std204(第 1629 行):
m74 = (avg(Pb207_s) - avg(Pb207_b)) / net['pb204'] # 第 1639 行:实测 207/204
m64 = (avg(Pb206_s) - avg(Pb206_b)) / net['pb204'] # 第 1640 行:实测 206/204
r['r76'] = ((Pb207_s - avg(Pb207_b) * ((m74 - R8) / m74)) / n6 * ((m64 - Q8) / m64))
r['r68'] = n6 * ((m64 - Q8) / m64) / nU
优点 :物理上最直接,假设最少。 代价 :²⁰⁴Pb 信号弱(见 3.3),误差大;而且如果 ²⁰⁴Pb 的净信号为零,这里会直接除零崩溃 —— 这是 README 里的 Known issue 第 2 条。代码没有做任何防护。
路线四:标定 ²⁰⁴Pb 法(界面:Cal 204Pb)
比例尺:用标样实测的 204 通道反过来标定。
这一路来自 Chew et al. (2014),是专为"有普通铅的副矿物标样"设计的。它的巧妙之处在于:不追求 ²⁰⁴Pb 的绝对准确,只用它的相对变化。
在 _ratios_std_cal204(第 1613 行)里,先算一个 n383:
n383 = (r76_std * Pb206_s - avg(Pb206_b) * r76_std - Pb207_s + avg(Pb207_b)) \
/ (Q8 * r76_std - R8)
这一大串的意思是:用标样"应该有的"放射成因 ²⁰⁷/²⁰⁶(P382)去反推,标样每个时间点在 204 通道上"多出来"的那部分信号(含普通铅和 Hg)。然后把这个 n383 按 Q8、R8、S8 分配到 206、207、208 上扣掉。
这一路的好处 :它让 204 通道的系统性偏差(比如 Hg 干扰、绝对灵敏度标定不准)在"标样 vs 样品"的比较中被抵消掉一大半。这也是为什么 README 里建议"标样含普通铅时选 Standard Corr. + Cal 204Pb"。
四条路的对照表
界面选项 代码方法名 比例尺 关键假设 什么时候用 207Pb_METHOD_207²⁰⁷/²⁰⁶ 的偏离 知道普通铅 ²⁰⁷/²⁰⁶ 204 通道信号差,但 206/207 信号好 208Pb_METHOD_208²⁰⁸Pb 与 Th 知道普通铅 ²⁰⁶/²⁰⁸、²⁰⁷/²⁰⁸ Th 含量稳定的矿物 204Pb_METHOD_204直接测 ²⁰⁴Pb 204 通道干净、H 已校正 204 信号足够好 Cal 204Pb_METHOD_CAL204标样标定的 204 标样与样品基体相近 标样本身含普通铅 时的首选
注意界面上的 Standard Corr. / Standard not Corr.(第 3375-3376 行)是管标样的 ,上面这四条路既管标样也管样品 —— 样品那一组由右侧的 None / 207Pb / 204Pb / 208Pb / Cal 204Pb 按钮(第 3409-3413 行)决定。
四条路:区别只在于"拿什么当比例尺" 207Pb _METHOD_207 什么时候用 ²⁰⁴Pb 信号差、但 206/207 信号好 比例尺 实测 ²⁰⁷Pb/²⁰⁶Pb 比放射成因值高出了多少 关键假设 必须知道普通铅的 ²⁰⁷/²⁰⁶ 208Pb _METHOD_208 什么时候用 Th/U 稳定的矿物 比例尺 ²⁰⁸Pb 与 Th 的关系(Zack 2011,原为金红石设计) 关键假设 必须知道普通铅的 ²⁰⁶/²⁰⁸、²⁰⁷/²⁰⁸ 204Pb _METHOD_204 什么时候用 ²⁰⁴ 信号足够好 比例尺 直接测到的 ²⁰⁴Pb 通道 关键假设 ²⁰⁴Pb 干净、且仪器已做 Hg 校正 Cal 204Pb _METHOD_CAL204 什么时候用 标样本身含普通铅时的首选 比例尺 用标样实测的 204 通道反过来标定(Chew 2014) 关键假设 标样与样品基体相近 界面上还有一组 Standard Corr. / Standard not Corr.(第 3375-3376 行):那管的是"标样侧",和上面四条各管一边,别混。 图 20 四条路各自用什么当比例尺、背后是什么假设、什么时候该用它。选路的主要依据是²⁰⁴Pb 通道的信号好不好。
3.5 那个"普通铅比值"从哪来:Stacey–Kramers 两阶段模型
现在回到最关键的问题:普通铅的 ²⁰⁶/²⁰⁴、²⁰⁷/²⁰⁴、²⁰⁸/²⁰⁴ 是你不知道的,你怎么可能知道?
答案是一个模型。
地球的铅同位素比值不是恒定的 —— 它在几十亿年里一直在被 U 和 Th 衰变"喂养"而升高。Stacey & Kramers (1975) 提出了一个广被接受的两阶段模型 来描述这件事:地球的 U/Pb 比值在 3.7 Ga 前后发生过一次跃变,之后按新的比值演化到今天。
代码里有这个模型的全部常数。看 Cal_age()(第 3170 行):
def Cal_age(age):
a = exp(0.000000000155125 * age * 1000000) - 1 # 206Pb*/238U
b = exp(0.00000000098485 * age * 1000000) - 1 # 207Pb*/235U
c = exp(0.000000000049475 * age * 1000000) - 1 # 208Pb*/232Th
P382 = 1/137.818 * (b/a) # 放射成因 207Pb/206Pb
Q8 = 18.700 - a * 9.735 # 普通铅 206Pb/204Pb
R8 = 15.628 - b * (9.735/137.818) # 普通铅 207Pb/204Pb
S8 = 38.630 - c * 36.837 # 普通铅 208Pb/204Pb
Pbc = R8 / Q8 # 普通铅 207Pb/206Pb
逐个常数解释:
常数 含义 来历 18.700今天 地球铅的 ²⁰⁶Pb/²⁰⁴PbStacey & Kramers (1975) 现代值 15.628今天地球铅的 ²⁰⁷Pb/²⁰⁴Pb 同上 38.630今天地球铅的 ²⁰⁸Pb/²⁰⁴Pb 同上 9.735μ ,即 ²³⁸U/²⁰⁴Pb(第二阶段)同上,约 9.74 36.837μ × κ,即 ²³²Th/²⁰⁴Pb(κ ≈ 3.78 为 ²³²Th/²³⁸U) SK2model 里写作 36.8411.152 / 12.998 / 31.233.7 Ga 时 地球铅的三个比值见 SK2model(第 116-118 行) 3.7两阶段模型的分界时间(Ga) 同上
读法 :Q8 = 18.700 − a × 9.735 的意思是
"今天地球铅的 ²⁰⁶/²⁰⁴ 是 18.700。把从时间 T 到现在这段里长出来的放射成因 ²⁰⁶Pb(a × μ)减掉,就得到 T 时刻地球铅的 ²⁰⁶/²⁰⁴。"
R8、S8 同理。然后 Pbc = R8/Q8 就给出了"T 时刻模式铅的 ²⁰⁷/²⁰⁶"。
SK2model() 是同一个模型的另一种写法 ,从 3.7 Ga 的初始值往后加:
Pbc_64 = 11.152 + 9.74 * (exp(0.155125*3.7) - exp(0.155125*age/1000)) # 第 116 行
Pbc_74 = 12.998 + (9.74/137.818) * (exp(0.98485*3.7) - exp(0.98485*age/1000))
Pbc_84 = 31.23 + 36.84 * (exp(0.049475*3.7) - exp(0.049475*age/1000))
两者数学上等价。实测对照(同一 T 下两个函数各自算出的 ²⁰⁶Pb/²⁰⁴Pb):
T Cal_age 的 Q8SK2model 的 Pbc_64差异 1 Ma 18.6985 18.7018 0.003 100 Ma 18.5478 18.5510 0.003 1000 Ma 17.0664 17.0689 0.002 3700 Ma 11.1526 11.1520 0.001
两条曲线全程重合,残余不超过 0.003(约 0.02%,符号在 3 Ga 附近翻转一次),来自 μ 与 κ 在两处取了不同的舍入位数。它们说的是同一件事。
Stacey–Kramers 两阶段模型:普通铅的比值不是常数,它随时间变 地球的铅被 U 和 Th 一路"喂养"着长大。所以"普通铅长什么样"取决于它是哪个年代的。 0 0.5 1 2 3 3.7 4.57 9 12 15 18 时间(Ga,从现在往回数) ²⁰⁶Pb/²⁰⁴Pb 3.7 Ga:两阶段的分界 今天 18.700 483 Ma 17.94 100 Ma 18.55 3.7 Ga 11.152 第二阶段(代码只用这一段) 第一阶段 代码里两套写法,说的是同一件事 Cal_age(age) 从今天的 18.700 往回减:Q8 = 18.700 − a×9.735 SK2model(age) 从 3.7 Ga 的 11.152 往前加:Pbc_64 = 11.152 + … 图 21 Stacey–Kramers 两阶段模型:普通铅的比值不是常数,它随地球的 U/Pb 演化而变。所以"普通铅长什么样"取决于它是哪个年代的。
(顺带一提:Cal_age 用 36.837、SK2model 用 36.84,Cal_age 用 9.735、SK2model 用 9.74 —— 这是从原始文献不同舍入位数抄来的,差异小于 0.01%,可以忽略,但它确实说明这些常数是手工录入的,将来换模型或换引用时必须逐处核对 。)
两个函数到底是不是一回事:把差异放大 1000 倍去看 0 1 2 3 3.7 11 13 15 17 19 ²⁰⁶Pb/²⁰⁴Pb Cal_age 的 Q8(粗线) SK2model 的 Pbc_64(虚线) 两条线完全重合 —— 粗线把虚线盖住了 0 1 2 3 3.7 -0.001 0 0.002 0.004 时间(Ga,往回数) 差值 0 今天:+0.0033 3.7 Ga:-0.0006 全程不超过 0.003,占 18.7 的 0.02%;符号在 3 Ga 附近翻转一次 这点残差来自舍入:Cal_age 里 μ 取 9.735、κ 取 36.837,SK2model 里取 9.74 和 36.84。 绝对值可以忽略;但它说明这些常数是手工录入的 —— 换模型或换文献时必须逐处核对。 图 22 把两个函数的差值放大来看:从今天的 +0.0033 缓慢变到 3.7 Ga 的 −0.0006,全程不超过 0.02%,符号在 3 Ga 附近翻转一次。残差来自 μ 与 κ 的舍入位数不同 —— 这说明这些常数是手工录入的。
3.6 为什么用"标样年龄"来算这些参数
这是初学者最容易卡住的一点。
看 skmethod()(第 3188 行,界面选 "S & K" 时触发):
Standard_age = tk.simpledialog.askfloat(..., initialvalue='485') # 问你标样年龄
Standard_name = tk.simpledialog.askstring(..., initialvalue='MAD')
R8, Q8, S8, P382, a, b, c, Pbc = Cal_age(Standard_age) # 用标样年龄算参数
为什么是标样年龄,不是样品年龄?
因为标样里的普通铅,是标样结晶的那个时刻 混进去的。MAD-NEW 磷灰石标样结晶于约 483 Ma,它当时抓到的地球铅就是 483 Ma 前的地球铅 —— 其 ²⁰⁶/²⁰⁴ 是 18.700 − (e^(λ·0.483Ga) − 1) × 9.735 ≈ 17.94,而不是今天的 18.700。
所以:标样的普通铅参数要按标样年龄算。
而样品 的普通铅参数是另一套,由 PbCorrSamples()(第 2883 行)在界面上单独问你 —— 要么直接输入同位素比值,要么输入"样品的估计年龄"让程序用 S&K 模型算。
这就回答了"为什么那么设置" :普通铅的 ²⁰⁷/²⁰⁶ 不是常数,它随时间变化。你必须告诉软件"这块矿物的普通铅大概是哪个年代的",或者"它长什么样"。
为什么"普通铅比值"要用标样的年龄去算 标样里的普通铅是标样结晶那一刻混进去的 —— 它"冻住"在当年的地球铅组成上,不是今天的。 0 0.5 1 2 3 3.7 11 13 15 17 19 ²⁰⁶Pb/²⁰⁴Pb 483 Ma ²⁰⁶/²⁰⁴ = 17.94 ²⁰⁷/²⁰⁶ = 0.8686 100 Ma ²⁰⁶/²⁰⁴ = 18.55 ²⁰⁷/²⁰⁶ = 0.8422 两种做法差多少(把 age 代进 Cal_age 实算) 按标样年龄 483 Ma(MAD-NEW) ²⁰⁶Pb/²⁰⁴Pb = 17.9426 ²⁰⁷Pb/²⁰⁴Pb = 15.5850 ²⁰⁷Pb/²⁰⁶Pb = 0.868603 按界面默认 100 Ma ²⁰⁶Pb/²⁰⁴Pb = 18.5478 ²⁰⁷Pb/²⁰⁴Pb = 15.6207 ²⁰⁷Pb/²⁰⁶Pb = 0.842185 图 23 为什么普通铅参数要按标样年龄算:标样里的普通铅"冻住"在它结晶那一刻的地球铅组成上。483 Ma 与 100 Ma 对应的数值差得不少。
3.7 那些默认值是怎么来的(这一段能解答很多疑惑)
PbCorrSamples() 里每个分支都给了默认值。逐个验证一下,你会发现它们不是随便写的:
207Pb 法(第 2900、2905-2906 行)
initialvalue = '100' # 样品估计年龄,默认 100 Ma
initialvalue = '0.048015' # 放射成因 207Pb/206Pb
initialvalue = '0.842185' # 初始(普通)207Pb/206Pb
把 age = 100 Ma 代进第 2902-2903 行:
Radioactive_Pb = (1/137.818)(e^(λ₂₃₅·100Ma)−1)/(e^(λ₂₃₈·100Ma)−1)。算出来 = 0.048036 common_Pb = (15.628 − (e^(λ₂₃₅·100Ma)−1)·μ/137.818) / (18.700 − (e^(λ₂₃₈·100Ma)−1)·μ)。算出来 = 0.842185
所以那两个"神仙数字"就是 100 Ma 时的 S&K 模式铅值。 手动输入 0.048015 和 0.842185,等价于告诉程序"我的样品普通铅是 100 Ma 的模式铅"。
(0.842185 与精确值逐位吻合;0.048015 与精确值 0.048036 差 0.04%,这是原作者手工取整留下的痕迹。这类"看起来随意、其实有出处"的默认值,照抄之前一定要先反算一遍它对应什么假设 。)
204Pb 法(第 2926-2930 行)
initialvalue = '15.6207' # 初始 207Pb/204Pb
initialvalue = '18.5478' # 初始 206Pb/204Pb
同样代 age = 100 Ma:
18.700 − (e^(0.0155125) − 1) × 9.735 = 18.700 − 0.1522 = 18.548 ✓15.628 − (e^(0.098485) − 1) × 9.735/137.818 = 15.628 − 0.00731 = 15.621 ✓
完全对上。
208Pb 法(第 2914-2919 行)
initialvalue = '0.48240' # 初始 206Pb/208Pb
initialvalue = '0.40628' # 初始 207Pb/208Pb
18.548 / 38.447 = 0.48242 ✓15.621 / 38.447 = 0.40629 ✓
(其中 38.447 = 38.630 − (e^(0.0049475)−1) × 36.837,也是 100 Ma 的值。)
三个分支的默认值全部指向同一个年龄:100 Ma。 理解了这一点,你就知道这些数字该怎么改了 —— 它们应该对应你样品的真实估计年龄,而不是照抄默认值。
界面里那些"神仙默认值",反算之后全部指向同一个年龄 把默认值当成精确值反解,得到的是同一个数:100 Ma。这不是巧合,是原作者按 100 Ma 的模式铅算好的。 207Pb 法 放射成因 ²⁰⁷Pb/²⁰⁶Pb = 0.048015 初始 ²⁰⁷Pb/²⁰⁶Pb = 0.842185 (精确值应为 0.048036,差 0.04%, 是手工取整的痕迹) 204Pb 法 初始 ²⁰⁷Pb/²⁰⁴Pb = 15.6207 初始 ²⁰⁶Pb/²⁰⁴Pb = 18.5478 (与 Cal_age(100) 逐位吻合: 15.6207 / 18.5478) 208Pb 法 初始 ²⁰⁶Pb/²⁰⁸Pb = 0.48240 初始 ²⁰⁷Pb/²⁰⁸Pb = 0.40628 (由 18.54781 / 38.447 与 15.62069 / 38.447 得到) age = 100 Ma 三个分支的默认值 说的是同一个假设 样品普通铅 = 100 Ma 的模式铅 结论:这些数字不是"通用真理",而是一个假设。照抄之前,先把它反算成年龄 —— 再换成你自己样品的估计年龄。 图 24 把界面里的默认值当成精确值反算,三个分支得到同一个答案:100 Ma 的模式铅。这不是巧合,是原作者算好写进去的。
3.8 必须承认的事:模型铅只是"平均猜测"
上面整套东西有一个隐含前提:你的样品的普通铅,长得像那个年代的地球平均铅。
这个前提经常不成立:
如果样品来自地壳重熔的岩浆,普通铅可能明显富 ²⁰⁷Pb 和 ²⁰⁸Pb(地壳的 U/Pb、Th/Pb 更高); 如果样品周围有方铅矿等硫化物的铅混入,普通铅可能极端富放射成因; 如果样品经历了多期流体,普通铅可能本身就是一个混合物。
模型铅给的是"统计意义上最可能的值",不是"你这块样品的真实值"。
所以有一条原则要记住:
具体来说:
如果你的 ²⁰⁴Pb 信号足够好,优先用 204 或 Cal 204Pb 路线,因为它是"实测"的; 如果研究区有已知的普通铅同位素组成(比如同一矿区的方铅矿数据),直接输入那些比值,比用 S&K 模型强得多; 最重要的一条 :用 Tera-Wasserburg 图(第 5 章)让数据自己说出普通铅组成,而不是由你规定。这是唯一能让审稿人真正放心的做法。
模型铅只是"统计上最可能的值",不是你这块样品的真值 S&K 给的是"那个年代的地球平均铅"。下面三种情况下,这个前提会明显不成立。 地壳重熔的岩浆 普通铅明显富 ²⁰⁷Pb、² ⁰⁸Pb 地壳的 U/Pb、Th/Pb 更高 周围有硫化物 方铅矿等混入的铅极端富放射成 因 矿区尺度常见 经历多期流体 普通铅本身就是一个混合物 没有单一端元可假设 能实测的,就不要假设。 因为普通铅校正是"数据整理",不是"地质论证" —— 假设错了,年龄就错,而且看不出来。 按可信度从高到低,三个选择 ① ²⁰⁴Pb 信号足够好 → 用 204Pb 或 Cal 204Pb 因为它是"实测"的,不是假设的 ② 研究区有已知的普通铅组成(如同一矿区方铅矿数据)→ 直接输入那些比值 比套 S&K 模型强得多 ③ 用 Tera-Wasserburg 图让数据自己说出普通铅组成(第 5 章) 唯一能让审稿人真正放心的做法 图 25 模型铅只是"统计上最可能的值"。三种情况下这个假设会明显不成立,以及按可信度排序的三个替代选择。
第 4 章 从原始文件到年龄:每一步在干什么
这一章把整条流水线拆成 6 步,跟着数据走一遍:从仪器导出的原始文件,到最终输出表里的每一列。
第 4 章导览:6 步流水线加一次输出,与节号一一对应 跟着一个样品的数据走一遍 —— 每一步都能在磁盘上找到对应文件。 输入:三种仪器的原始文件 第 0 步 0 导出 8 通道 §4.2 第 1 步 1 读文件 loaddata() §4.3 第 2 步 2 划分积分窗 §4.4 第 3 步 3 背景扣除 dataprocess() §4.5 第 4 步 4 分馏校正 SSB §4.6 第 5 步 5 算年龄 Age76Pb() §4.7 输出 6 结果表 xls / csv §4.8 输出:xls 结果表 + Mean_Cps.csv 排查问题的抓手:先看磁盘上的中间结果,再回头找是哪一步的输入不对。 第 4.1 节那张流程图给的是数据流;这张图给的是"哪一节管哪个环节"。 图 26 第 4 章的节次索引:哪个小节管哪个环节、每步产出什么文件。排查问题时,先看磁盘上的中间结果,再回头找是哪一步的输入不对。
4.1 全局视野
界面上的九个步骤(README 里的 (1)–(9))对应的就是这条链。下面逐步展开。
从原始文件到年龄:一条链上的八个环节 界面上那九个步骤(README 的 (1)–(9))对应的就是这条链。左边是"数据",右边是"解释"。 原始质谱文件 Thermo / Agilent / Element 三种格式 第 (1)–(4) 步 loaddata() 读出固定的 8 个通道,多一个少一个都报错 第 (5) 步 积分窗口 背景段 + 信号段,存在 Time_setting.npy 第 (6) 步 dataprocess() 背景扣除 + 2σ 过滤 + 算六个比值 第 (8) 步 result_all.csv / Mean_Cps.csv 23 列比值 + 16 个通道均值 第 (8) 步 Age_Calculate_average() 整批标样求一个因子 → A*.xls Age_Calculate() 相邻标样分段求因子 → L*.xls 图 27 从原始文件到年龄的完整链条。左边是"数据",右边是"解释" —— 最后一步分叉成 Average 与 Liner 两条出口。
4.2 第 0 步:仪器必须导出哪 8 个通道
loaddata()(第 273 行)返回固定的 8 个通道:
序号 通道 用途 0 Time 时间轴,用于把秒换算成数组下标 1 202Hg 监控 Hg 干扰(注意:这段计算目前没生效 ,见 3.3) 2 204Pb 普通铅指纹(实际是"204 通道",含 Hg) 3 206Pb 主要年龄信息 4 207Pb 主要年龄信息 5 208Pb Th-Pb 体系 6 232Th 208Pb/232Th 7 238U 分母
少任何一个都会报错。这是 README 里"读这个再开始"四条注意事项的第 4 条。
为什么必须齐全 :因为不同校正路径要用不同的通道组合。哪怕你打算用 207Pb 法(不用 204),程序在设计上也要求 204 通道存在 —— 这是软件的强制项,不是科学上的必需。
仪器必须导出这八个通道 —— 少一个程序就罢工 注意:哪怕你打算走 207Pb 法(根本不用 204),程序在设计上仍强制要求 204 通道存在。 202 204 206 207 208 232 238 质量数 m/z(横轴不是等比例,只示意位置) ²⁰²Hg 监 Hg ²⁰⁴Pb 普通铅指纹 ²⁰⁶Pb + ²⁰⁷Pb 主要年龄信息 ²⁰⁸Pb Th-Pb 体系 ²³²Th 208/232 ²³⁸U 分母 ²⁰⁴ 通道是个特例: 它同时接收 ²⁰⁴Pb 和 ²⁰⁴Hg(质量数相同,分不开),而代码并没有做 Hg 校正。 所以你测到的 204 到底是"铅"还是"铅 + 汞",取决于仪器导出时有没有做过校正 —— 这是你的责任,不是软件的责任。 还有一个时间轴 Time 通道(序号 0)是第 8 个通道:它把"秒"换算成数组下标。激光剥蚀是时间过程,没有它就没法划积分窗口。 这八个通道的名字在代码里是固定的:Time / 202Hg / 204Pb / 206Pb / 207Pb / 208Pb / 232Th / 238U(第 273 行)。 图 28 八个通道各自的位置与用途。²⁰⁴ 通道要特别注意:它同时接收 ²⁰⁴Pb 和 ²⁰⁴Hg。
4.3 第 1 步:读文件(loaddata)
三种仪器的导出格式不一样,代码用三个分支处理(第 275 行起的 mode = ele.get()):
界面选项 mode 处理方式 Element 0 跳过 13 行,列名硬编码为 Time, 202Hg, 204Pb, ... Agilent 1 先试跳 3 行,失败再试跳 2 行;列名按"纯数字"匹配(204Pb 匹配 204) Thermo 2 跳过 7 行,列名按原名匹配
这一段经过一次重构,从"把整个文件读成字符串矩阵再逐列转换"改成"先读表头定位列,再用 np.loadtxt(usecols=...) 只读需要的那 8 列并直接按 float 解析"。对 3250 行的 Agilent 文件,从 39 ms 降到 9.5 ms(4.1 倍)。
这不是科学改动,是纯工程改动 :输入输出仍然逐位相同(24 个数组、0 处不一致)。
新手最容易踩的坑:输入目录里不能有别的东西。 程序会把目录里所有文件都当数据文件处理,多一个无关的 CSV 就报错。
读文件:三种仪器格式,三个分支,外加一次 4 倍提速 第 275 行起按界面选项分流。这一段是纯工程改动 —— 输入输出逐位相同,只是快了很多。 Element mode = 0 跳过 13 行 列名硬编码为 Time, 202Hg, 204Pb, … Agilent mode = 1 先试跳 3 行,失败再试 2 行 列名按"纯数字"匹配 即 204Pb 匹配 204 Thermo mode = 2 跳过 7 行 列名按原名匹配 (网页端目前只支持这一种) 提速:同样的 Agilent 文件(3250 行),读入快 4.1 倍 改法:不再"把整个文件读成字符串矩阵再逐列转换",而是先读表头定位列,再用 np.loadtxt(usecols=…) 只读需要的 8 列。 改之前 39.0 ms 改之后 9.5 ms 新手最容易踩的坑:输入目录里不能有别的东西。程序会把目录里所有文件都当数据文件处理,多一个无关 CSV 就报错。 图 29 三种仪器格式的三个分支,以及这次重构带来的 4.1 倍提速。这一段是纯工程改动 —— 输入输出逐位相同。
4.4 第 2 步:划分积分窗口
激光剥蚀是一个时间过程:先开激光打样品(信号段),然后关激光等背景(背景段),或者反过来。
界面上选中一个样品会画出信号曲线(showplt,第 1761 行),然后你框选两段:
背景段(background) :激光没打样品时测到的计数。它不是零,因为等离子体本身、载气里的杂质都会产生信号。信号段(signal) :激光打样品时的计数。
为什么要扣背景 :因为质谱仪测到的是"样品信号 + 环境底色"。你要的是样品那部分。
积分窗口存在 Time_setting.npy 里,dataprocess() 开头(第 1409-1417 行)有一个自检:
if numn[name][3] - numn[name][2] < 0 or numn[name][4] - numn[name][2] < 0:
wrong_samples.append(name) # 窗口越界 → 记入错误列表
elif numn[name][3] + 1 > numn[name][4] - 1:
numn[name][3] = numn[name][4] # 首尾太近 → 强行对齐
numn[name][4] = numn[name][3]
这是一个"带病运行"的设计 :窗口画错了,程序不中断,而是把一个明显不合理的窗口(起点=终点)硬塞进去算,最后给你一个垃圾结果。所以一定要看 wrong_samples 相关的日志信息 。
积分窗口:激光剥蚀是一个时间过程,你得告诉程序哪一段是信号 界面上选中样品会画出信号曲线(showplt,第 1761 行),然后你框选背景段和信号段。 0 15 30 45 60 75 90 100 1000 1e4 时间(秒) cps(对数轴) 背景段 4–26 s 信号段 32–86 s ²⁰⁶Pb 背景(载气 + 等离子体) 注意看信号段的形状:它不是平的,会随剥蚀坑变深而缓慢上升 —— 这就是 downhole 分馏的来源。 窗口画错了会怎样: dataprocess() 开头(第 1409–1417 行)有个自检,但它不中断程序。 如果起点终点太近,它会把窗口强行改成"起点 = 终点",然后给你一个垃圾结果,只在日志里记一笔 wrong_samples。 窗口不好会污染平均值,是下游所有误差的来源之一。选窗口时优先看 ²⁰⁶Pb 与 ²³⁸U 两条曲线的平台段。 窗口存在 Time_setting.npy 里(用的是 pickle),换一批数据要重新选。 图 30 激光剥蚀的信号曲线与积分窗口。注意信号段并不平 —— 它随剥蚀坑变深缓慢上升,这就是 downhole 分馏的来源。
4.5 第 3 步:背景扣除与比值(dataprocess + reduce_sample)
背景扣除
_net_signal()(第 1561 行):
'n6': Pb206_s - np.average(Pb206_b), # 净 206Pb 信号
'n7': Pb207_s - np.average(Pb207_b),
'n8': Pb208_s - np.average(Pb208_b),
'n2': Th232_s - np.average(Th232_b),
'nU': U238_s - np.average(U238_b),
'pb204': np.average(Hg204_s) - np.average(Hg204_b), # 204 通道
注意一个细节:前四个是"逐点数组减一个标量" (信号段每个时间点减去背景段均值),而 pb204 是"先各自求平均再相减"(得到一个标量)。这个不一致是从原始代码继承下来的,后面会带来麻烦(第 4.7 节的 r84)。
六个比值
_ratios_sample()(第 1588 行,不校正的基准版本):
比值 表达式 输出列 r76n7 / n6 ²⁰⁷Pb/²⁰⁶Pb r68n6 / nU ²⁰⁶Pb/²³⁸U r82n8 / n2 ²⁰⁸Pb/²³²Th r86n8 / n6 ²⁰⁸Pb/²⁰⁶Pb r26Th232_s / Pb206_s ²³²Th/²⁰⁶Pb(注意用的是原始信号,不是净信号 ) r84n8 / pb204 ²⁰⁸Pb/²⁰⁴Pb(分母是标量 )
r26 和 r84 这两处的"不走寻常路"是原样保留 的。改造时必须照抄,否则结果会变。
2σ 离群过滤
激光剥蚀的信号常有几个时间点被包裹体、裂纹、或者等离子体抖动污染。代码用最朴素的方法剔除(_filter_2s,第 1525 行):
return np.where(np.abs(x - np.average(x)) < 2.0 * np.std(x), x, nan)
把偏离均值超过 2 倍标准差的时间点替换成 nan(注意:不是删掉 ,是标记)。
这里有一个只有做过数值对照才会知道的细节:np.nanmean / np.nanstd 在 NumPy 里是把 nan 替换成 0 之后在整个原长数组上求和 ,而不是"压缩掉无效元素再求"。两种做法的浮点求和顺序不同,末位会漂。代码忠实复刻了 NumPy 的行为(第 1537 行、第 1533-1535 行的注释),所以不要"顺手优化"成 x[~isnan].mean() —— 那会让结果与原来不一致。
背景扣除的两个细节,和一个"看起来优化了其实会改数"的坑 _net_signal() 第 1561 行:信号段每个点减去背景段均值。 0 10 20 30 40 50 60 0 4000 8000 1e4 信号段内的逐秒点 净信号(cps) 均值 ±2σ → nan → nan 2σ 过滤(_filter_2s,第 1525 行):把偏离均值超过 2σ 的点替换成 nan —— 注意是标记,不是删掉。 千万别"顺手优化"成 x[~isnan].mean() NumPy 的 np.nanmean / np.nanstd 是把 nan 替换成 0 之后在整个原长数组上求和,而不是压缩掉无效元素再求。 两种写法的浮点求和顺序不同,末位会漂。重构时这一点必须照抄,否则结果与原版不一致。 数组式(逐点减标量) n6 = Pb206_s − average(Pb206_b) 标量式(先各自平均再相减) pb204 = average(Hg204_s) − average(Hg204_b) ← 注意这不一致 图 31 2σ 过滤把超出范围的点标记成 nan(不是删掉)。右下角那个"标量式"是个容易忽略的不一致:pb204 是先各自求平均再相减,其余四个通道是逐点减标量。
输出两个文件
Mean_Cps.csv:背景段 7 通道均值 + 信号段 7 通道均值(共 16 个数)result_all.csv:23 列的比值与计数(列定义见 4.7 节)
4.6 第 4 步:分馏与基体效应校正(SSB)
为什么需要这一步
激光剥蚀出来的物质,轻的同位素比重同位素更容易被带走(分馏)。而且随着剥蚀坑越打越深,坑底的成分会变,分馏也会变(这叫 downhole fractionation)。
结果就是:你测到的 ²⁰⁶Pb/²³⁸U 不等于样品真实的 ²⁰⁶Pb/²³⁸U,它有一个系统性的偏移。
怎么办:Sample-Standard-Bracketing
思路很朴素:
我有一块年龄已知 的标样。用同样的仪器、同样的激光条件去打它,测出来的比值和它的真实比值之间的差距,就代表了"这台仪器现在的偏差"。把这个偏差按比例应用到样品上。
代码里就是一行(factor,第 384 行):
def factor(stand_v, i):
return stand_v / date_all[i][standard].mean()
stand_v:标样理论 比值(由标样年龄算出)date_all[i][standard].mean():标样实测 比值的平均相除得到分馏因子 f
然后(第 441-448 行):
result_cal_207Pb_206Pb = date_all[0] * f207_206 # 注意:这是数组整体相乘
result_cal_206Pb_238U = date_all[2] * f206_238
result_cal_207Pb_235U = date_all[4] * f207_235
result_cal_208Pb_232Th = date_all[6] * f208_232
这就是核心:样品实测值 × f = 校正后真值。
Average 与 Liner 的区别
Average(Age_Calculate_average) Liner(Age_Calculate) 分馏因子来源 整批标样求一个平均值 相邻两个标样之间分段求值 代码 第 418-430 行 第 1024 行的 factor(SEP, ...) 能否校正时间漂移 不能 能(分段近似) 适用 仪器很稳定、批次短 批次长、仪器有漂移
Liner 的实现思路(第 961-992 行):把标样的位置序列切成段,每一段标样"夹住"一组样品,这组样品就用这段标样的平均因子。
注意 :Liner 是分段 ,不是插值 ,更不是回归 。它把两个标样之间的所有样品当成同一段处理,中间不做时间趋势外推。
分馏校正:用已知年龄的标样去"称"出仪器的偏差 factor = 标样理论比值 ÷ 标样实测比值。然后:样品实测值 × f = 校正后的真值。 一批数据的顺序(S = 标样,s = 样品) S s s s S s s s s S s s S Average 做法:所有标样求一个平均因子 整批一个 f ≈ 平均 Liner 做法:相邻标样之间分段,每段一个因子 第 1 段一个 f 第 2 段一个 f 第 3 段一个 f Liner 只是"分段",不是插值、也不是回归:两个标样之间隔 10 个样品,这 10 个样品用的是同一个因子,中间的时间趋势被完全忽略。 Average 代码:第 418–430 行 分馏因子:整批标样一个平均值 能否校正时间漂移:不能 Liner 代码:第 1024 行 factor(SEP, …) 分馏因子:分段各一个 能否校正时间漂移:能(分段近似) 代码里其实写了 4 种漂移回归模型(regression(),第 152 行,用 scipy.curve_fit),但从未被调用 —— 属于"写了没接线"。 图 32 Sample-Standard-Bracketing 的实际形态。Average 用整批标样求一个因子,Liner 分段各求一个 —— 所以 Liner 能近似校正时间漂移,Average 不能。
代码里其实写了 4 种回归模型(regression(),第 152 行:线性、对数、开方、幂函数,用 scipy.curve_fit 拟合),但这个函数从未被调用 —— 全局变量 regression_method 只在 main() 里被赋值(第 2998 行 regression_method = 1),界面上没有任何控件能改它。这是一个"写了但没接线"的功能,不是死代码,第 6 章会讨论怎么把它接上。
4.7 第 5 步:算年龄
四个年龄
(Age_Calculate_average,第 470-476 行)
输出列 公式 代码 ²⁰⁶Pb/²³⁸U 年龄 ln(r68_corr + 1) / λ₂₃₈第 470 行 ²⁰⁷Pb/²³⁵U 年龄 ln(r75_corr + 1) / λ₂₃₅第 472 行 ²⁰⁸Pb/²³²Th 年龄 ln(r82_corr + 1) / λ₂₃₂第 474 行 ²⁰⁷Pb/²⁰⁶Pb 年龄 迭代求解(Age76Pb) 第 476 行
误差传播与那个 ρ
这一步值得仔细看,因为它决定了你报告的误差有没有意义。
第 455-461 行:
w = sqrt((2s_r68 / r68)² + excess_V²) * 100 # 206/238 的相对误差(%)
s = sqrt((2s_r75 / r75)² + excess_V²) * 100 # 207/235 的相对误差(%)
o = sqrt((2s_r76 / r76)² + 0.0001) * 100 # 207/206 的相对误差(%)
if abs((w² + s² − o²) / (2ws)) < w/s:
rho = abs((w² + s² − o²) / (2ws))
else:
rho = w/s
三件事:
第一,excess_V 是"额外误差"。 它是界面上的一个输入框(第 3533 行),默认值 3 (第 3516 行 var5.set(3)),单位是百分比。它代表"测量本身之外的不确定性" —— 标样的长期重现性、基体差异、仪器日间波动等等。
这是整个软件里最需要你自己判断的一个数字。 默认 3% 是一个偏保守的通用值。如果你这批数据的标样重现性只有 1%,那么用 3% 会高估误差;如果标样重现性有 5%,用 3% 就是低估 ,审稿人一眼就能看出来。
第二,²⁰⁷Pb/²⁰⁶Pb 那一项用的是硬编码的 0.0001 (等于 1% 的平方),不是 excess_V²。这是原始代码里的不一致,属于"原样保留"。实际影响:不管你把 excess_V 设成多少,207/206 的额外误差永远按 1% 算。
第三,rho 是 ²⁰⁶/²³⁸ 与 ²⁰⁷/²³⁵ 两个误差的相关系数。 为什么需要它?因为:
²⁰⁷Pb/²⁰⁶Pb ≈ (²⁰⁷Pb/²³⁵U) / (²⁰⁶Pb/²³⁸U) × (常数)
三个比值不是独立的,它们的误差满足 o² ≈ w² + s² − 2ρws。反解就得到代码里那个式子。这个 ρ 必须输出给画协和图的软件 (IsoplotR 等),否则在图上画误差椭圆时会把两个不独立的误差当成独立的,椭圆画错,年龄也跟着错。
输出表里第 40 列就是 rho(第 879 行),这个设计是对的。
误差怎么合成,以及那个 ρ 为什么必须输出 第 455–461 行:三行代码决定你报出去的误差有没有意义。 w = sqrt((2s_r68 / r68)² + excess_V²) × 100 s = sqrt((2s_r75 / r75)² + excess_V²) × 100 o = sqrt((2s_r76 / r76)² + 0.0001) × 100 rho = |(w² + s² − o²) / (2ws)| (当它小于 w/s 时;否则取 rho = w/s) w → ²⁰⁶Pb/²³⁸U 的相对误差(%) 三个比值不独立 ²⁰⁷Pb/²⁰⁶Pb ≈ (²⁰⁷Pb/²³⁵U) / (²⁰⁶Pb/²³⁸U) × 常数 所以它们的误差满足 o² ≈ w² + s² − 2ρws 反解就得到代码里那个 ρ。 不输出 ρ,IsoplotR 会把两个 不独立的误差当独立的处理。 有一处不一致被原样保留了: ²⁰⁷Pb/²⁰⁶Pb 那一项用的是硬编码的 0.0001(= 1% 的平方),不是 excess_V²。 实际影响:不管你界面里把 excess_V 设成多少,²⁰⁷/²⁰⁶ 的额外误差永远按 1% 算。 excess_V 是整个软件里最需要你自己判断的一个数字 界面输入框在第 3533 行,默认值 3(第 3516 行 var5.set(3)),单位是百分比。 计数统计误差 信号越强、积分越久越小 (内部误差) 标样重现性 同一实验室的长期 2SD (外部误差) 基体差异 标样与样品的剥蚀行为差异 (外部误差) 仪器日间漂移 不同批次之间的系统差 (外部误差) 图 33 三行代码决定你报出去的误差有没有意义。右下角那个 ρ 必须输出,否则在协同图上画误差椭圆时会把两个不独立的误差当成独立的。
4.8 输出文件清单
文件 内容 什么时候产生 result_all.csv每样品 23 列比值与计数 第 (8) 步 Fractionation Correction Mean_Cps.csv每样品 16 个通道均值 同上 cal_age_result_A*.xls年龄表(Average) 第 (9) 步 Export (Average) cal_age_result_L*.xls年龄表(Liner) 第 (9) 步 Export (Liner) 信号图 每个样品选中时实时生成 第 (7) 步
年龄表(xls)的列很密,值得说明(Age_Calculate_average 第 806-888 行定义):
列号 内容 0-1 文件名、样品名 2-15 六个比值 + 各自的 2s 17-18 ²⁰⁶Pb/²³⁸U 年龄 + 2s 19-20 ²⁰⁷Pb/²³⁵U 年龄 + 2s 21-22 ²⁰⁸Pb/²³²Th 年龄 + 2s 23-24 ²⁰⁷Pb/²⁰⁶Pb 年龄 + 2s 25-29 校正后年龄(按所选普通铅路径) 31-34 Tera-Wasserburg 图用:²³⁸U/²⁰⁶Pb + 2s、²⁰⁷Pb/²⁰⁶Pb + 2s 36-40 作图用:²⁰⁷Pb/²³⁵U、²⁰⁶Pb/²³⁸U、各自的 2s、ρ 42-45 ²⁰⁸Pb/²³²Th、²⁰⁸Pb/²⁰⁶Pb + 2s 46-49 微量元素 U、Th、Pb(ppm)
31-34 这四列是这套软件最有价值的输出之一 ,第 5 章会讲怎么用。
年龄表里那 46 列,分成几块看就不乱了 Age_Calculate_average 第 806–888 行定义。最值钱的是 31–34 和 36–40 这十列。 2–15 17–24 25–29 31–34 36–40 42–45 46–49 0 10 20 30 40 50 列号 0–1 标识 文件 / 样品名 2–15 六个比值 + 各自的 2s 校正后的比值 17–24 四个未校正年龄 + 2s 206/238、207/235、208/232、207/206 25–29 校正后年龄 按所选普通铅路径 31–34 Tera-Wasserburg 用 ²³⁸U/²⁰⁶Pb 与 ²⁰⁷Pb/²⁰⁶Pb 各带 2s 36–40 普通协和图用 ²⁰⁷Pb/²³⁵U、²⁰⁶Pb/²³⁸U、2s、ρ 42–45 Th-Pb 体系 ²⁰⁸Pb/²³²Th、²⁰⁸Pb/²⁰⁶Pb + 2s 46–49 微量元素 U、Th、Pb(ppm) 31–34 是这套软件最有价值的输出之一: 它把"普通铅是多少"从假设变成拟合结果。 变量名难记没关系,真正要记住的是:Isoclock 的输出是"中间产品",最后一步要去 IsoplotR 做。 图 34 年龄表的 46 列分成八块来看。最值钱的是 31–34(TW 图用)与 36–40(普通协和图用)。
关于微量元素换算
第 352-379 行有一个 coefficient() 函数,用 NIST 玻璃标样的已知含量把 cps 换算成 ppm:
标样 U (ppm) Th (ppm) Pb (ppm) NIST610 461.5 457.2 426 NIST612 37.38 37.79 38.57 NIST614 0.832 0.748 2.32
用的是 系数 = 标样已知含量 / 标样实测 cps 均值,然后样品 cps × 系数。
注意 :这里假设"微量元素的外标"和"U-Pb 的标样"是两回事 —— 微量元素走 NIST 玻璃,U-Pb 走矿物标样。如果两者混用,换算出来的 U、Th、Pb 含量只能当半定量看。
U 含量这一列(第 18 列)还有一个用处:校验数据合理性 。如果你测的是磷灰石,U 含量算出来是 5000 ppm,那大概率哪里出问题了。
第 5 章 怎么让别人接受你的结果
这一章是实用的。审稿人和同行不会因为你用了某个软件就相信你,他们会问三个问题。
第 5 章导览:审稿人问三句,你准备四招 这一章是实用的 —— 它讲的是怎么把结果"讲清楚"。 1 "你怎么知道普通铅扣对了?" 2 "你怎么知道仪器分馏校正对了?" 3 "你报的误差是内部误差还是真实可重复性?" 对策一 把标样表现亮出来 §5.2 n / 均值 / 2SD 全写出来 对策二 用 Tera-Wasserburg 图自证 §5.3 最重要的一招 对策三 把外部误差算对 §5.4 区分内部误差与重现性 对策四 做敏感性检验 §5.5 换一条路再跑一遍 对策四对三问都适用 第 5.6 节给出一份可以直接抄进论文的清单;第 5.7 节提醒:别为了好看的年龄去挑方法。 图 35 审稿人的三个问题与四种对策的对应关系。注意对策二(TW 图)之所以最重要,是因为它同时回答了"扣得对不对"和"扣的过程可不可复现"。
5.1 审稿人会问的三个问题
"你怎么知道普通铅扣对了?" "你怎么知道仪器分馏校正对了?" "你报的误差是内部误差还是真实可重复性?"
三个问题各有对策。
审稿人会问的三个问题,以及对应的三招 他们不会因为你用了某个软件就相信你。这三个问题几乎一定会来。 "你怎么知道普通铅扣对了?" 对策 用 Tera-Wasserburg 图自证:让数据自己拟合下截距,并且报出 MSWD。 第 5.3 节 "你怎么知道分馏校正对了?" 对策 报出这批数据里标样的重复性(n、均值、2SD),并说明它和推荐值的关系。 第 5.2 节 "你报的误差是内部误差还是真实可重复性?" 对策 明确写出年龄不确定度含外部误差 X%,并给出依据。 第 5.4 节 最有力的一招是 TW 图:它把"普通铅是多少"从假设变成拟合结果。而且软件已经把需要的列输出好了。 图 36 审稿人的三个问题与各自的解法。最有力的一招是 Tera-Wasserburg 图 —— 它把"普通铅是多少"从假设变成拟合结果。
5.2 对策一:把你的标样表现亮出来
你的结果的可信度上限,就是你这批数据里标样的表现。
因为 SSB 的逻辑是"用标样校准样品",如果标样自己在这批数据里就飘了 5%,那你不可能比 5% 更准。
好消息是:代码本来就已经算了这些量(第 423、428 行):
std_68_2s = date_all[2][standard].std() / date_all[2][standard].mean()
std_75_2s = date_all[4][standard].std() / date_all[4][standard].mean()
但算完就丢掉了,没有输出到任何文件、也没有打印 。这是软件的一个实际缺陷(第 6 章列为改进项)。
你可以怎么补救 :
打开生成的 xls,找标样的行,看"²⁰⁶Pb/²³⁸U 年龄"那一列。如果标样名义年龄 483 Ma,而你这批数据里的标样年龄在 470–495 Ma 之间波动,那么你的外部误差就不该小于 ±2.5%。 论文里建议明确写一句 :"本批数据的标样 MAD-NEW 重复测量 ²⁰⁶Pb/²³⁸U 年龄为 483 ± 12 Ma(n=8,2SD),与推荐值一致。" —— 这一句话能省掉很多质疑。
你的结果的可信度上限,就是这批数据里标样的表现 SSB 的逻辑是"用标样校准样品"。标样自己飘了 5%,你不可能比 5% 更准。 460 470 480 490 500 ²⁰⁶Pb/²³⁸U 年龄(Ma) 推荐值 483 Ma 本批均值 484.3 Ma ±2SD 带(n = 10) ← 每个空心圆是一次标样测量 从这张图能读出两件事 外部误差该取多少 本批 2SD = ±6.3 Ma,即 ±0.65%。 界面里的 excess_V 不该小于这个量级。 而软件算完就丢掉了 std_68_2s、std_75_2s(第 423、428 行) 没有输出到任何文件、也没有打印。 补救办法:打开 xls,找标样的行,看 ²⁰⁶Pb/²³⁸U 年龄那一列,自己算一遍波动。 图 37 你的结果的可信度上限就是这批标样在数据里的表现。而软件把 std_68_2s 算完就丢掉了,所以这一步需要你手动看一眼 xls。
5.3 对策二:用 Tera-Wasserburg 图自证(最重要的一招)
这是最能说服审稿人的一步,而软件已经把需要的数据准备好了 。
什么是 Tera-Wasserburg 图
一种特殊的等时线图:
横轴 :²³⁸U/²⁰⁶Pb(注意是倒过来的)纵轴 :²⁰⁷Pb/²⁰⁶Pb
把所有数据点画上去,如果它们来自同一个年龄、同一批普通铅,它们会大致落在一条直线 上。这条直线:
下截距 (y 轴上,即 ²³⁸U/²⁰⁶Pb → 0 的位置)= 普通铅的 ²⁰⁷Pb/²⁰⁶Pb 上截距与协和线的交点 = 年龄
为什么它能解决问题
因为它把"普通铅是多少"从假设变成了测量结果。
前面第 3 章说过:S&K 模型给的普通铅组成只是"平均猜测"。而在 TW 图上,你用的是"数据自己拟合出来的截距",不需要假设普通铅是什么。
这就直接回答了审稿人的第一个问题 :不是"我相信模型铅",而是"我让 20 个数据点自己告诉我普通铅长什么样,而且它们线性度很好(MSWD 小),说明这个假设是自洽的"。
Tera-Wasserburg 图:让数据自己说出"普通铅是多少" 同一个结晶年龄、混入不同量普通铅的一组数据,在这张图上一定排成一条直线。 0 5 10 15 20 25 30 0 0.2 0.4 0.6 0.8 ²³⁸U / ²⁰⁶Pb (注意是倒过来的) ²⁰⁷Pb / ²⁰⁶Pb 500 1000 2000 3000 4000 协和线 刻度单位 Ma 下截距 = 普通铅的 ²⁰⁷Pb/²⁰⁶Pb = 0.852 上截距 = 年龄 = 250 Ma 每个椭圆是一次分析 普通铅越多,点越靠左上 为什么这一招最能说服审稿人 不是"我相信 S&K 模型铅",而是"我让 20 个数据点自己告诉我普通铅长什么样,而且它们线性度很好(MSWD 小), 说明这个假设是自洽的"。判据:下截距应与你选的 ²⁰⁷/²⁰⁶ 差不离;MSWD 在 1.5 以内都算可以。 图 38 Tera-Wasserburg 图。同一个结晶年龄、混入不同量普通铅的数据一定排成一条直线:下截距给出普通铅组成,上截距与协和线的交点给出年龄。
怎么操作
从 cal_age_result_*.xls 里取 31-34 列 (²³⁸U/²⁰⁶Pb + 2s、²⁰⁷Pb/²⁰⁶Pb + 2s); 也可以取 36-40 列 (²⁰⁷Pb/²³⁵U、²⁰⁶Pb/²³⁸U、ρ)画普通协和图; 把这些数据贴进 IsoplotR (在线版免费:isoplotr.london-geochron.com)或者 IsoplotR 的 R 包; 选择 "Tera-Wasserburg" 模式,让它拟合下截距和上截距。
判据 :
拟合出的下截距应该和你选的普通铅 ²⁰⁷/²⁰⁶ 差不离。如果差很多,说明你的假设错了 —— 这是好事 ,你提前发现了问题。 MSWD 应该在 1 附近(20 个点的话,1.5 以内都算可以)。MSWD 太大说明有一期以上的普通铅,或者有铅丢失,或者数据里有离群点。
这一招还有一个额外好处 :如果下截距显著不等于模型值,你可以直接把 TW 图拟合出的截距当作普通铅组成 输入回 Isoclock 重算一遍。这就形成了一个闭环。
5.4 对策三:把外部误差算对
回顾 4.7 节:误差 = 内部误差 ⊕ 外部误差(excess_V)。
内部误差 :来自这一次测量的计数统计。信号越强、积分时间越长,它越小。它是"这个点的精度"。外部误差 :来自两次测量的差异。它包含标样不确定性、基体差异、仪器漂移。它是"这个点的准确度"。
审稿人对只报内部误差的结果通常是不接受的 ,因为那会让误差条小得离谱(往往 ±0.5% 都不到),而实际上不同实验室测同一块标样能差 2–5%。
内部误差 ⊕ 外部误差:为什么只报 2s 会被拒 误差 = 内部误差 ⊕ 外部误差(就是界面里的 excess_V)。两者传播方式不同,作用也不同。 内部误差 来自这一次测量的计数统计。 信号越强、积分时间越长,它越小。 它是"这个点的精度"(precision)。 只报它,误差条会小到 ±0.5% 以内。 外部误差 来自两次测量之间的差异。 含标样不确定性、基体差异、仪器漂移。 它是"这个点的准确度"(accuracy)。 不同实验室测同一块标样能差 2–5%。 组合方式: 相对误差 = sqrt( (2s/r)² + excess_V² ) —— 两项是"平方和再开方",不是相加。 所以 excess_V 只要不是 0,它往往就主导了最终误差条的长度。 excess_V 怎么定(界面上那个输入框) 有长期的标样重现性数据 用那个 2SD 值 标样有推荐不确定度(如 91500 的 ²⁰⁶Pb/²³⁸U 约 ±1%) 直接用它 两者都有 取大的,或按误差传播合成 都没有 保守取 2–5%,并在文中说明 论文里一定要写明白:"年龄不确定度含外部误差 X%(依据为 …)"。含含糊糊只报一个 2s,最容易被抓。 图 39 内部误差与外部误差的区别,以及界面上那个 excess_V 该怎么定。两者是"平方和再开方",不是相加。
怎么定 excess_V :
情况 建议 有长期的标样重现性数据(同一实验室、同一仪器) 用那个 2SD 值 标样有推荐不确定度(如 91500 的 ²⁰⁶Pb/²³⁸U 推荐值有 ±1% 左右) 用它 两者都有 取大的,或者按误差传播合成 都没有 保守取 2–5%,并在文中说明
并且一定要在论文里写明白 :"年龄不确定度含外部误差 X%(依据为 …)"。含含糊糊地只报一个 2s,最容易被抓。
5.5 对策四:做敏感性检验(换一条路再跑一遍)
这是最省力、也最有力的稳健性检验。
具体做法 :同一批数据,用不同的普通铅校正路径各跑一遍:
跑法 界面设置 A(主结果) 样品 Cal 204Pb,标样 Cal 204Pb(Std),Standard Corr. B 样品 207Pb,标样 207Pb(Std),Standard Corr. C 样品 208Pb,标样 208Pb(Std),Standard Corr. D(对照) 样品 None,Standard not Corr. —— 看不校正 会偏多少
然后对比:
如果 A、B、C 的年龄在误差范围内一致 → 非常好,说明你的结果不依赖校正方法的选择,可以直接写进论文,附上一条"三种校正路径结果一致"的说明。如果它们差很多 (比如差 30 Ma,超过误差)→ 这是一个警示信号 。说明你的普通铅组成假设不稳定,或者数据本身有问题。这时候不要挑一个最好看的报,要做的是回头查原因(是不是有多个普通铅端元?TW 图上是不是散开了?)。D 与 A 的差值 ,就是你这次校正到底改了多少。这个差值应该和你样品的 ²⁰⁶Pb/²⁰⁴Pb 反向相关 —— 如果不对,逻辑上就有问题。
这个检验不花什么时间(软件跑得很快),但它在审稿意见里非常有说服力。 建议直接做成附表。
最省力的稳健性检验:换一条校正路径,再跑一遍 同一批数据,四种设置各跑一次。软件跑得很快,但这一步在审稿意见里非常有说服力。 230 235 240 245 250 255 260 加权平均年龄(Ma) A 样品 Cal 204Pb 251.4 ± 3.0 主结果 B 样品 207Pb 249.8 ± 3.2 C 样品 208Pb 253.1 ± 3.4 D 样品 None 238.6 ± 2.8 对照 A 的结果 怎么读这张图 A、B、C 在误差范围内一致 非常好。说明结果不依赖校正方法的选择, 可以直接写进论文,附一条"三种路径一致"。 差很多(比如超误差 30 Ma) 警示信号:普通铅组成假设不稳,或数据有问题。 不要挑好看的报,回头查 TW 图是不是散开了。 图 40 敏感性检验:同一批数据用四种设置各跑一遍。A/B/C 一致说明结果不依赖校正方法的选择;D 与 A 的差值就是"不校正会偏多少"。
5.6 一份"可以直接抄"的附录清单
论文方法部分或附录里,建议把下面这些写清楚。少写一条,就可能被追问一轮。
论文方法部分 / 附录必须写清楚的 16 条 少写一条,就可能被追问一轮。 数据采集 3 条 · 仪器型号、激光系统、 剥蚀策略(光斑、频率、 能量密度) · 采集的气体、载气流量 · 前冲洗 / 后冲洗时间 标样 3 条 · 标样名称 · 标样所用年龄及来源文献 · 本批数据的重复性 (n、均值、2SD) 数据处理 7 条 · 软件名称与版本 · 计算模式(Corr. / not Corr.) · 标样普通铅校正路径 · 样品普通铅校正路径 · 普通铅参数的来源 · excess_V 的取值与依据 · 是否做了 Hg 校正 结果校验 3 条 · TW 图的下截距与 MSWD · 不同校正路径的结果对比 · 标样与参考值的偏差 这 16 条里最容易漏的两条 —— "普通铅参数是怎么来的"和"是否做了 Hg 校正" —— 恰好都在普通铅那一环。 图 41 一份可以直接抄的附录清单,四大类共 16 条。最容易漏的两条恰好都在普通铅那一环 —— 因为它们是假设,而假设不会自己出现在输出表里。
【数据采集】
- 仪器型号、激光系统、剥蚀策略(光斑、频率、能量密度)
- 采集的气体、载气流量
- 每次分析的前冲洗/后冲洗时间
【标样】
- 标样名称(如 MAD-NEW)
- 标样所用年龄及其来源文献(如 483 Ma, 某文献)
- 本批数据中标样的重复性(n、均值、2SD)
【数据处理】
- 软件名称与版本(Isoclock v2.0)
- 计算模式:Standard Corr. / Standard not Corr.
- 标样普通铅校正路径:Cal 204Pb(Std) / 207Pb(Std) / ...
- 样品普通铅校正路径:Cal 204Pb / 207Pb / ...
- 普通铅参数的来源:S&K 模型(并给出所用年龄)或实测输入值
- excess_V 的取值与依据
- 是否做了 Hg 校正(仪器自动 / 未做)
【结果校验】
- Tera-Wasserburg 图的下截距与 MSWD
- 不同校正路径的结果对比(附表)
- 标样与参考值的偏差
5.7 一句提醒
不要为了得到"好看的年龄"而去挑校正方法。 这不仅是学术道德问题,也是实际风险问题 —— 挑出来的结果在后续被别的方法交叉验证时很容易被发现。
换句话说:第 5.5 节的敏感性检验,应该在你决定最终报哪个数之前做,而不是之后。
敏感性检验该在什么时候做 同一件事,顺序不同,性质就不同。 对的顺序 ① 先跑完一批 ② 四条路径都跑 ③ 看年龄差多少 ④ 再决定报哪个 错的顺序 ① 先跑完一批 ② 看哪个数好看 ③ 只报那一个 ④ 被交叉验证发现 敏感性检验不是"补一个附表" —— 它决定了你最终报出去的那个数。 图 42 敏感性检验不是"补一个附表",它决定了你最终报出去的那个数。顺序反了,性质就从"检验"变成了"挑选"。
第 6 章 局限与可以改进的方向
这一章我尽量说全,并且按"改了会不会影响已有结果"来分类。这是诚实的部分 —— 一个软件的真实能力边界,比它的功能列表更重要。
第 6 章导览:三种局限,按"改了会不会影响已有结果"分类 一个软件的真实能力边界,比它的功能列表更重要。 数值缺陷 §6.1 改了会改变输出 三条,都已写入 README 的 Known issues。 其中危害最大的那条 (Age76Pb 收敛判据) 修复最简单:一行。 但它会动数值,必须 先由作者确认。 方法局限 §6.2 不是 bug,是边界 六条(局限 4 – 局限 9)。 同龄假设、SSB 基体假设、 Liner 只分段不插值、 没有等时线与 MSWD、 ²⁰⁸Pb/²³²Th 通常不可靠、 abs() 掩盖失效。 工程局限 §6.3 不影响数值,影响使用 八条,分三组: · 已修复 / 已改进 3 条 · 影响使用体验 4 条 · 兼容与安全风险 1 条 大部分改起来不难, 但需要有人愿意改。 图 43 第 6 章的三种局限,分类依据只有一条:改了会不会影响已有结果。这个分类决定了"谁能改、谁必须由作者确认、谁改不了"。
6.1 三条需要留意的数值行为(已写入 README 的 Notes for users)
缺陷 1:Age76Pb 的收敛判据失效
位置 :第 122-145 行,特别是第 133 行。
Rap = (exp(0.00098485 * Tm) - 1) / (exp(0.000155125 * Tm) - 1) / 137.818 # 第 130 行,循环外
while abs(Rap76 - Rap) > 0.00005 and N < 10: # 第 133 行
...
Tm = Tmin + (Tmax - Tmin) * (Rap76 - Rapi) / (Raps - Rapi)
Age76Pb = Tm
N = N + 1 # Rap 从未更新
Rap 在进入循环前算了一次,循环体内没有任何地方重新计算它 。所以 abs(Rap76 - Rap) 的值在整个循环里是恒定的 —— 条件要么一开始就是假(循环不执行),要么永远为真(循环跑满 10 次)。
后果 :迭代次数被写死成 10。实测(把这段源码逐字复制出来,与二分法高精度解对比,输入为各年龄对应的理论 ²⁰⁷Pb/²⁰⁶Pb):
目标年龄 Age76Pb 返回精确解 偏差 100 Ma 100.00 100.00 −0.00 500 Ma 500.00 500.00 +0.00 1000 Ma 1000.03 1000.00 +0.03 1500 Ma 1502.26 1500.00 +2.26(+0.15%) 2100 Ma 2134.77 2100.00 +34.77(+1.66%) 2500 Ma 2499.31 2500.00 −0.69
这组数字与 README 里"约 +35 Ma(1.7%)@ 2.1 Ga"完全吻合。值得注意的是,误差不是单调的 :1.5 Ga 以下几乎为零,2.1 Ga 附近最差,2.5 Ga 又回落到零 —— 这正是"迭代次数被写死"的典型症状(不同年龄需要不同的迭代次数才能收敛,10 次恰好对某些区间够、对另一些不够)。
缺陷 1 实测:Age76Pb 的偏差不是单调的,2.1 Ga 处最差 把第 122–145 行的源码逐字复制出来,与二分法高精度解逐点对比。 0 500 1000 1500 2000 2500 3000 -10 0 10 20 30 40 真实年龄(Ma) Age76Pb 偏差(Ma) 2.1 Ga:+34.77 Ma(+1.66%) 1.5 Ga:+2.26 2.5 Ga:−0.69 1.5 Ga 以下几乎为零 这种形状本身就是诊断证据 不同年龄需要不同的迭代次数才能收敛。 10 次恰好对某些区间够、对另一些不够 —— 所以偏差会时大时小,而不是单调增长。 影响范围其实很有限 只影响 ²⁰⁷Pb/²⁰⁶Pb 年龄这一列(输出 23–24 列)。 ²⁰⁶Pb/²³⁸U、²⁰⁷Pb/²³⁵U 有解析解,不受影响。 所以 < 1 Ga 的样品实际可以忽略这条。 图 44 Age76Pb 偏差的实测曲线。偏差不是单调的 —— 这正是"迭代次数被写死成 10 次"的典型症状:不同年龄需要不同的迭代次数才能收敛。
影响范围 :只影响 ²⁰⁷Pb/²⁰⁶Pb 年龄这一列(输出第 23-24 列)。²⁰⁶Pb/²³⁸U、²⁰⁷Pb/²³⁵U 年龄有解析解,不受影响。所以对年轻样品(< 1 Ga)来说,这条缺陷实际可以忽略 ;只在老样品里才需要警惕。
README 建议 1.5–2.2 Ga 的年龄谨慎使用。
修复难度 :极低 。把 Rap 的更新移进循环即可。但这会改变数值输出,所以需要作者确认。
更稳妥的替代 :直接改用 SciPy 的 brentq 求根,几十年内不会再有收敛问题。
缺陷 2:²⁰⁴Pb 路径的除零
位置 :_ratios_std204,第 1639-1640 行。
m74 = (...) / net['pb204']
m64 = (...) / net['pb204']
如果某个时间点的净 ²⁰⁴ 信号为 0(或整段 204 通道的背景恰好等于信号),这里直接 ZeroDivisionError,整个批次的处理中断。
后果 :不是算错,是跑不完 。所以危害等级反而比缺陷 1 低(至少它不会骗你)。
修复难度 :低。加一个保护即可,但要想清楚"该怎么处理"(跳过该点?该点标记为无效?)—— 这需要作者决定语义。
缺陷 3:²⁰⁴Hg 校正恒为 0
位置 :_net_signal,第 1569-1571 行。
# 204 通道:原实现直接用原始 204 减背景,未做 Hg 干扰扣除
# (源码里那段 Hg204cal_s 恒为 0、m204_s 从未被引用)
'pb204': np.average(sig['Hg204_s']) - np.average(sig['Hg204_b']),
原始代码里有一段试图计算 Hg 对 204 的贡献,但表达式是 np.average(x) - np.average(x) —— 恒等于 0 ,而且它的结果从未被引用。所以软件完全没有做 Hg 校正 。
后果 :如果仪器导出数据时已经做了 Hg 校正,无害;如果没有,那么:
²⁰⁴Pb 被高估 → 普通铅被高估 → 年龄偏年轻(因为扣掉了过多的放射成因铅) 208Pb/204Pb 列(输出第 14-15 列)整体偏低
危害等级:中高 ,因为它安静地给出错误结果 ,不报错、不警告。
你能做的 :去确认仪器导出时到底有没有做 Hg 校正。这是数据层面的问题 ,软件层面改不了。
修复难度 :中。需要 ²⁰⁴Hg/²⁰²Hg 的天然比值(约 0.23)和仪器的质量歧视校正因子,后者需要专门的 Hg 标定。
三条已确认缺陷:危害等级与修复难度并不成正比 都已经写进仓库 README 的 Known issues。排在最前面的不是最严重的那个。 1 Age76Pb 收敛判据失效 第 122–145 行(特别是第 133 行) 迭代次数被写死成 10 次,1.5–2.2 Ga 偏差可达 +34.77 Ma 安静地给出偏大的年龄 危害:中高 极低:一行改动 2 ²⁰⁴Pb 路径的除零 第 1639–1640 行 net["pb204"] 为 0 时 ZeroDivisionError,整批处理中断 不是算错,是跑不完 危害:低 低:加保护,但要定语义 3 ²⁰⁴Hg 校正恒为 0 第 1569–1571 行 源码那段表达式是 average(x) − average(x),恒等于 0 若仪器未做 Hg 校正:²⁰⁴Pb 被高估 → 年龄偏年轻 危害:中高 中:需要 Hg 标定与天然比值 图 45 三条已确认缺陷的对照。注意:危害最大的那条修复最简单(一行),而最容易发现的那条(除零崩溃)危害反而最小。
6.2 方法层面的局限
这些不是 bug,是这套方法本身的边界。
六条方法局限,分别卡在流程的哪一环 不是"软件不好用",而是这套方法本身的边界。 A 读数据 原始信号 背景扣除 比值 这一段没有已知的方法局限:纯计算,已有测试覆盖。 B 校正 普通铅校正 分馏校正 局限 4 · 标样与样品"同龄假设" 普通铅参数是一个全局常数,对所有样品一视同仁。 局限 5 · SSB 假设基体相同 局限 6 · Liner 只是分段,不插值、不回归。 C 出年龄 算年龄 输出与统计 局限 8 · ²⁰⁸Pb/²³²Th 年龄通常不可靠 Th 的信号强度与稳定性都不如 U,初始假设更难满足。 局限 9 · abs() 让病态点不报错 比值小于 0.0460455 时返回 0,不是崩溃而是给个数。 局限 7 · 没有等时线、也没有 MSWD 这一步在软件之外 —— 必须导出到 IsoplotR 做。 图 46 六条方法局限分别卡在流程的哪一环。凡是落在"算年龄"那一环的,都会安静地给出一个数字而不是报错 —— 这也是它们最难被发现的原因。
局限 4:标样与样品"同龄假设"的传递
Cal_age(Standard_age) 用标样年龄定出 Q8/R8/S8,供标样校正使用 —— 这是对的。但样品侧的普通铅参数(common_Pb、common_Pb207_204 等)来自用户输入的一个年龄或一组比值 ,是一个全局常数 ,对所有样品一视同仁。
问题 :如果这批样品跨越了较宽的年龄区间,或者普通铅来源不同,用一个常数去校正所有人,会把系统误差引入到每个点上。
改进方向 :允许按样品组(而不是整批)指定普通铅参数;或者做迭代 —— 先用初值算年龄,再用算出的年龄反推普通铅,反复两三次。(这是很多文献里的标准做法,代码里目前没有。)
局限 5:SSB 假设"标样与样品分馏行为相同"
外标法的前提是标样和样品的基体足够接近。MAD-NEW 磷灰石标样配磷灰石样品是合理的;但用 NIST 玻璃做标样去测黑钨矿,两者的剥蚀行为、气溶胶传输效率、电离效率都不同,分馏因子不能直接套用。
这是方法层面的固有局限 ,不是软件问题。你能做的是:选基体尽量接近的标样,并在论文里说明。
局限 6:Liner 只是分段,不插值、不回归
前面说过,Age_Calculate 把标样分成若干段,每段一个因子。如果两个标样之间隔了 10 个样品,这 10 个样品用的是同一个因子,中间的时间趋势被完全忽略。
regression()(第 152 行)实现了 4 种漂移模型,但没有接到界面上 ,从未执行。
改进方向 :把 regression() 接线(工作量小),或者用更标准的时间插值(对每个样品,用它前后两个标样的因子做线性内插)。后者是当前文献里的普遍做法。
局限 7:没有等时线拟合、没有 MSWD
Isoclock 只做"逐点校正 + 逐点年龄",不做统计拟合 :
不给 TW 图的截距和误差 不给协和年龄(concordia age)和 MSWD 不给加权平均年龄
这意味着你必须把数据导出到 IsoplotR(或其他软件)做第二步 。这不是缺陷,但使用者必须知道:Isoclock 的输出是"中间产品",不是最终结果。
好消息是它输出的列(31-40)恰好是为这个目的设计的,导出很方便。
局限 8:²⁰⁸Pb/²³²Th 年龄通常不可靠
软件会输出这个年龄(第 21-22 列)。但要提醒:Th 和 U 的地球化学行为差异大,²³²Th 的信号强度和稳定性通常不如 ²³⁸U,而且初始 Th/Pb 假设更难满足。
建议 :除非你研究的是独居石这类富 Th 矿物,否则把 ²⁰⁸Pb/²³²Th 年龄当作参考,不要作为主要结论。
局限 9:abs() 掩盖了失效
前面第 1.2 节提过:所有年龄计算都套了 abs()。这避免了负数取对数导致崩溃,但也意味着当数据病态时,你得到的不是报错,而是一个看似正常的数字。
判断方法 :算完年龄后,回头检查输入的比值。如果某点校正后的 ²⁰⁶Pb/²³⁸U 是负数,或者年龄是 0(Age76Pb 在比值小于 0.0460455 时返回 0,第 128、144 行),那这个点就是无效的,必须剔除。
6.3 工程层面的局限
问题 影响 改进方向 输入/输出目录不能相同 必须开两个目录 加一个检查,或者允许同目录 输入目录里有无关文件就报错 容易踩坑 按扩展名过滤 每批数据必须重启程序 效率低 状态管理重构 输出表靠"列的位置"索引 改动列顺序就全错 引入列名 schema 状态文件用 pickle(Time_setting.npy 等) 安全与兼容性风险 改用 JSON 转换脚本曾用 eval() 解析文件名 任意代码执行风险 已修复 (改为 int(float(...)))异常静默失败 打包成 exe 后尤其严重 已改进 (日志带 traceback、Tk 回调异常已接管)缺自动化测试 改动容易引入回归 本 fork 已补 (网页端 67 项测试 + 桌面端对照验证)
八条工程局限:按"会不会影响已有结果"分三组 第一组省钱最多 —— 它们让"改动引入回归"变得看得见。 已修复 / 已改进 3 条 · 转换脚本的 eval() → 已改为 int(float(...)) · 异常静默失败 → 日志带 traceback · 缺自动化测试 → 网页端 67 项测试 + 桌面端对照验证 影响使用体验 4 条 · 输入与输出目录不能相同 · 输入目录有无关文件即报错 · 每批数据必须重启程序 · 输出表靠"列的位置"索引 → 改动列顺序就会全错 兼容与安全风险 1 条 · 状态文件用 pickle (Time_setting.npy 等) · 安全性与兼容性都有风险 · 改进方向:改用 JSON 合计 8 条:3 条已解决、4 条是使用体验、1 条是技术债。 图 47 八条工程局限分成三组。第一组(已修复/已改进)虽然不影响数值,却是后续所有改动能不能安全进行的前提。
6.4 如果只能改三件事
按"性价比"排序(性价比 = 影响 ÷ 改动量):
修 Age76Pb 的收敛判据。 一行改动,解决一个影响 1.7% 的系统偏差。但会改变输出,需要作者确认。把标样重复性输出出来。 现在 std_68_2s、std_75_2s 算完就丢。多写两行 print 或加两列到 xls,用户立刻就能看到自己数据的可信度上限。加一个"可疑值标记"机制。 现在 abs() 和兜底逻辑会把病态点变成正常数字。加一列 flag,标出"比值负数""年龄为 0""204 信号为零"的点。
再往后的优先级:Hg 校正(需要仪器配合)、regression() 接线、按样品组指定普通铅、JSON 状态文件、列名 schema。
如果只能改三件事:按"影响 ÷ 改动量"排序 2 5 8 2 5 8 改动量 → (越小越省事) 影响 → (越大越值得) 性价比高:先做这些 往后排 ① 修 Age76Pb 收敛判据 一行改动,消除 1.7% 偏差 ② 把标样重复性输出出来 std_68_2s 算完就丢 ③ 加"可疑值标记"机制 标出负数、零年龄、零信号 Hg 校正 需要仪器配合与标定 regression() 接线 4 种漂移模型未接线 按样品组指定普通铅 或改成迭代式求解 JSON 状态文件 替换 pickle 状态文件 列名 schema 输出表靠列位置索引 注意第 ① 条的附带条件: 修它会改变数值输出。凡是会改变输出的改动,都必须由原作者确认 —— 因为已经发表的结果要对得上。 凡是"不影响已有结果"的改动(②③④),可以先做;凡是"会改数"的改动(①),先沟通。 图 48 如果只能改三件事。"影响 ÷ 改动量"最高的是前三个,但第 ① 条会改变数值输出,必须先由作者确认。
6.5 一句话总结局限
Isoclock 能可靠地完成"从原始信号到校正后比值与年龄"这一段,并且它的四条普通铅校正路径是有文献支撑的正规做法。
它不能完成的是统计与论证那一段 —— 等时线、MSWD、加权平均、以及"这个年龄有没有地质意义"的判断。
后者必须由你在 IsoplotR 里、结合地质观察来完成。 这不是软件的缺陷,是分工。
一句话总结:这不是缺陷,是分工 Isoclock 负责的一段 从原始信号到"校正后比值与年龄"。 四条普通铅校正路径都有文献支撑, 这一段是可靠、可复现、可被审稿人接受的。 你和 IsoplotR 负责的一段 等时线拟合、MSWD、加权平均, 以及"这个年龄有没有地质意义"。 Isoclock 只把数据整理到门口。 分工线 不是缺陷线 所以:把 Isoclock 的输出直接当结论,会漏掉统计那一段; 因为它没做那一段,就说它不行,也不公平。 图 49 把"软件能力"和"研究者的工作"分开看。这条线是分工线,不是缺陷线 —— 但使用者必须知道它在哪里,否则会把中间产品当最终结论。
第 7 章 代码地图:新手该从哪读起
Isoclock2.0.py 有 3676 行,但真正需要理解的科学代码不到 300 行。剩下的是界面代码。
第 7 章导览:3676 行里,真正要读的不到 300 行 剩下的绝大部分是界面代码 —— 这份地图告诉你从哪读起。 科学代码 < 300 行(约 8%) 界面与其它 ≈ 3300 行(约 92%) 主界面、三个界面页、对话框、状态管理、文件读写…… ≈ 300 行:六个核心函数 + 四条普通铅校正路径 + 一个 ²⁰⁷Pb/²⁰⁶Pb 迭代求解 第 7 章的三个小节: §7.1 核心函数索引 §7.2 三条推荐阅读路径 §7.3 读代码的三个注意 结论:你不需要读懂 3676 行。读懂那不到 300 行,就看懂了整个方法。 图 50 3676 行里真正要读的部分有多大。这不是"代码写得不好",而是界面代码本来就占大头 —— 关键是有人告诉你哪 300 行值得读。
7.1 核心函数索引
函数 行号 职责 建议阅读顺序 SK2model40 S&K 模式铅(3.7 Ga 起算写法) 6 Age76Pb48 ²⁰⁷Pb/²⁰⁶Pb 年龄(迭代) 7 regression78 漂移回归模型(未接线 ) — loaddata199 读三种仪器文件 1 Cal_age3096 S&K 参数(现代值起算写法)→ Q8/R8/S8/P382/Pbc 5 Age_Calculate_average244 平均分馏因子 + 算年龄 + 出 xls 8 Age_Calculate852 分段分馏因子(Liner) 9 dataprocess1331 主处理流程 2 _filter_2s1451 2σ 离群标记 3 _mean_2sem1456 均值与 2 倍标准误 4 _net_signal1487 背景扣除 3 _ratios_std2071526 207Pb 校正 10 _ratios_std_cal2041539 Cal 204Pb 校正 11 _ratios_std2041555 204Pb 校正 12 _ratios_std2081573 208Pb 校正 13 reduce_sample1607 还原成 23 列输出行 14 PbCorrSamples2809 询问样品普通铅参数 15 instructure0/1/21781 / 2064 / 2348 三个界面页 — main2885 主界面与全局变量 —
核心函数的调用关系与建议阅读顺序 圆括号里是第 7.1 节表格给的阅读顺序编号;"—" 表示不在主线上。 A 读数据 loaddata (1) dataprocess (2) reduce_sample (14) B 工具函数(被 dataprocess 调用) _net_signal (3) _filter_2s (3) _mean_2sem (4) 调用 C 四条普通铅校正路径 _ratios_std207 (10) _ratios_std_cal204 (11) _ratios_std204 (12) _ratios_std208 (13) 校正后比值 D 科学内核(被 C 与 E 调用) Cal_age (5) SK2model (6) Age76Pb (7) 提供模式铅参数与 ²⁰⁷Pb/²⁰⁶Pb 年龄 E 算年龄与出表 factor (310) Age_Calculate_average (8) Age_Calculate (9) 出年龄 不在主线上的两个:regression (78) 的四种漂移模型从未接线; PbCorrSamples (2809) 只负责询问普通铅参数。界面部分 instructure0/1/2 (1781/2064/2348) 与 main (2885) 可以先跳过 —— 它们跟方法无关。 图 51 第 7.1 节那张表的图形版:五个层次、建议阅读顺序、以及两个不在主线上的函数。沿着 A → C → E 走一遍,整个方法就通了。
7.2 三条推荐阅读路径
路径 A:想理解科学逻辑(推荐先走这条)
SK2model (114) → Cal_age (3170) → Age76Pb (122)
↓
_net_signal (1561) → _ratios_std_cal204 (1613)
↓
factor (384) → Age_Calculate_average 第 418-484 行
这条路上的每个函数都不到 40 行,加起来不到 200 行,但包含了整个方法的核心。
路径 B:想理解数据处理
loaddata (273) → dataprocess (1405) → _load_channels (1375)
↓
_mean_cps_row (1393) → reduce_sample (1681)
重点是看数据从"文件里的数字"变成"比值"的每一步。
路径 C:想改界面
main (2959) → instructure0 (1855) → showplt (1761)
界面代码占了全文件 80% 以上,都是 Tk 的布局与回调,读的时候可以把里面所有的 Frame、Entry、Button 先跳过,只找 command= 后面接的函数名。
代码地图:3676 行里真正需要读懂的不到 300 行 剩下的是界面代码。按这三条路径读,很快就能上手。 路径 A 想理解科学逻辑(推荐先走这条) SK2model 第 114 行 Cal_age 第 3170 行 Age76Pb 第 122 行 _net_signal 第 1561 行 _ratios_std_cal204 第 1613 行 factor 第 384 行 每个函数都不到 40 行,加起来不到 200 行,但包含了整个方法的核心。 路径 B 想理解数据处理 loaddata 第 273 行 dataprocess 第 1405 行 _load_channels 第 1375 行 _mean_cps_row 第 1393 行 reduce_sample 第 1681 行 重点看数据从"文件里的数字"变成"比值"的每一步。 路径 C 想改界面 main 第 2959 行 instructure0 第 1855 行 showplt 第 1761 行 界面代码占全文件 80% 以上, 读的时候可以把所有 Frame / Entry / Button 先跳过。 读这份代码的三个注意事项 · 全局变量满天飞 standard、Q8、excess_V、PbCorrS 都通过 global 传递;先搜它在哪里被赋值。 · 很多"故意保留的不一致" 两处数学等价但写法不同时,先看注释 —— 浮点路径不同会改末位。 · 留意 abs() 和 except 这是两个"消灭错误信息"的机制。看到就要警惕。 图 52 三条阅读路径。科学逻辑那条路走完只要读不到 200 行,但包含了整个方法的骨架。
7.3 读这份代码的三个注意事项
全局变量满天飞。 像 standard、Standard_age、Q8、excess_V、PbCorrS 这些都在函数之间通过 global 传递。读的时候先用搜索功能找到它在哪里被赋值。很多写法是"故意保留的不一致"。 看到两处数学等价但写法不同的代码,不要急着统一 —— 代码注释里通常写明了为什么(浮点路径不同会改变末位)。留意 abs() 和 except。 这是两个"消灭错误信息"的机制。看到它们,就要提高警惕。
读这份代码时,三处最容易踩空的地方 这一段没有新知识,只有三个提醒。 ① 全局变量满天飞 standard、Standard_age、Q8、 excess_V、PbCorrS 这些都在 函数之间通过 global 传递。 读之前先用搜索功能找到 它在哪里被赋值。 ② 故意保留的不一致 看到两处数学等价、写法却 不同的代码 —— 别急着统一。 注释里通常写了为什么: 浮点路径不同,末位会变, 已发表的结果跟着变。 ③ 留意 abs() 和 except 这是两个"消灭错误信息"的 机制。 abs() 让负数比值不报错, except 让异常不出声。 看到它们,就要提高警惕。 图 53 读代码时最容易被绊住的三处。它们都不影响理解方法,但会让人误以为自己看错了 —— 所以先说在前面。
第 8 章 术语速查表
术语 英文 一句话解释 协和 concordant ²⁰⁶Pb/²³⁸U 和 ²⁰⁷Pb/²³⁵U 给出的年龄一致 不协和 discordant 两者不一致,通常是放射成因铅丢失 协和线 concordia 所有协和点连成的曲线,是年龄的"标尺" 普通铅 common Pb (Pb_c) 矿物结晶时混进来的铅,不含年龄信息 放射成因铅 radiogenic Pb (Pb*) 由本矿物内 U/Th 衰变产生的铅,含年龄信息 初始铅 initial Pb 同普通铅,强调"结晶时刻" 模式铅 model Pb 用地球演化模型算出的理论普通铅组成 μ mu ²³⁸U/²⁰⁴Pb 比值,S&K 模型取约 9.74 κ kappa ²³²Th/²³⁸U 比值,S&K 模型取约 3.78 分馏 fractionation 仪器造成同位素比值偏离真值的系统偏差 剥蚀坑分馏 downhole fractionation 激光越打越深导致的分馏变化 SSB Sample-Standard-Bracketing 用已知年龄标样校准样品的方法 外标 external standard 与样品分开测量的校准物质 Tera-Wasserburg TW 图 横轴 ²³⁸U/²⁰⁶Pb、纵轴 ²⁰⁷Pb/²⁰⁶Pb 的等时线图 下截距 lower intercept TW 图上给出普通铅 ²⁰⁷/²⁰⁶ 的截距 2s 2 sigma 2 倍标准差,约 95% 置信水平 内部误差 internal error 单次测量的计数统计误差 外部误差 external error 重复测量之间的重现性误差 MSWD mean square weighted deviation 衡量数据点与拟合线一致程度的指标,1 附近最好 ρ (rho) error correlation ²⁰⁶Pb/²³⁸U 与 ²⁰⁷Pb/²³⁵U 误差的相关系数 cps counts per second 计数率,质谱仪原始输出单位
一张图串起全部术语 左边这一支决定"减法扣得对不对",右边这一支决定"比值准不准"。它们最后汇到同一个年龄上。 一次激光打点测到的铅 永远是两部分之和 普通铅 Pb_c —— 不含年龄信息 放射成因铅 Pb* —— 携带年龄信息 ²⁰⁴Pb 指纹 唯一不参与衰变的铅同位素 分馏与 SSB 用已知年龄标样称出仪器偏差 模式铅 S&K μ ≈ 9.74、κ ≈ 3.78,随时间演化 协和线 concordia 两个时钟一致的点连成的线 四条校正路径 207 / 208 / 204 / Cal 204 不协和 discordia 铅丢失留下的弦 Tera-Wasserburg 图 把假设变成拟合结果 2s / ρ / MSWD 误差、相关性、拟合优度 校正后的比值 → 四个年龄 ²⁰⁶Pb/²³⁸U ²⁰⁷Pb/²³⁵U ²⁰⁷Pb/²⁰⁶Pb ²⁰⁸Pb/²³²Th 图 54 全部术语一张图。左支决定"减法扣得对不对",右支决定"比值准不准",最后汇到同一条出口:四个年龄。
附录 参考文献
本文中提到的所有方法出处:
参考文献与代码的对应关系 想追根溯源,就按这张图找 —— 每一处方法都能追到原文。 Chew, Petrus & Kamber 2014 Chemical Geology 363, 185–199 _ratios_std_cal204 Cal 204Pb 法 Stacey & Kramers 1975 Earth and Planetary Science Letters 26 SK2model 与 Cal_age 全部模式铅常数 Zack et al. 2011 Contributions to Mineralogy and Petrology 162 _ratios_std208 208Pb 法 Tera & Wasserburg 1972 Earth and Planetary Science Letters 14 第 5.3 节的 TW 图 等时线图的原始出处 标样推荐年龄(AY-4 = 154、MAD-NEW ≈ 483、91500 = 1062、wc-1 = 254.4 Ma)以各标样原始文献为准, 代码里的数值只是默认建议值 —— 用之前请核对你那一批的实际标样。 图 55 每一处方法都能追到原文。图里四个对应关系是本文所有科学论断的依据,也是被质疑时最直接的回应材料。
Chew, D.M., Petrus, J.A., Kamber, B.S., 2014. U–Pb LA–ICPMS dating using accessory mineral standards with variable common Pb. Chemical Geology 363, 185–199.
→ 对应代码里的 Cal 204Pb 法 (_ratios_std_cal204)。
Stacey, J.S., Kramers, J.D., 1975. Approximation of terrestrial lead isotope evolution by a two-stage model. Earth and Planetary Science Letters 26, 207–221.
→ 对应代码里的 SK2model 与 Cal_age 中的全部模式铅常数。
Zack, T., Stockli, D.F., Lander, R.H., McRae, C., Stockli, L.D., 2011. In situ U-Pb rutile dating by LA-ICP-MS: ²⁰⁸Pb correction and prospects for geological applications. Contributions to Mineralogy and Petrology 162, 515–530.
→ 对应代码里的 208Pb 法 (_ratios_std208)。
Tera, F., Wasserburg, G.J., 1972. U-Th-Pb systematics in three Apollo 14 basalts and the problem of initial Pb in lunar rocks. Earth and Planetary Science Letters 14, 281–304.
→ Tera-Wasserburg 图的原始出处。
软件说明书中引用的标样推荐年龄(AY-4 = 154 Ma、MAD-NEW ≈ 483 Ma、91500 = 1062 Ma、wc-1 = 254.4 Ma)请以各标样原始文献为准,代码中的数值只是默认建议值。
最后
这份文档的写法,是尽量把"为什么"讲清楚,而不是只讲"怎么点按钮"。
因为 U-Pb 定年里最容易出问题的从来不是操作 —— 是操作背后的那一步假设。软件把假设做成了默认值,默认值用起来毫无摩擦,于是大家就忘了它们存在。
记住三件事就够了 1 普通铅的参数不是常数 它是你告诉软件的一个假设 —— 假设写错了,年龄就错了,而且不会报错。 2 模型铅只是"平均猜测" 能用实测就用实测,能用等时线就用等时线。 3 报年龄时,顺便报你的假设 以及"换一种假设,这个年龄会怎么变"。 做到第三条,你的结果就比大多数已发表的结果更站得住。 图 56 整份文档最后剩下的三句话。如果只记住三件事,就记这三件 —— 它们都关于"假设",而不是关于"操作"。
记住三件事就够了:
普通铅的参数不是常数,是你告诉软件的一个假设 ; 模型铅只是平均猜测 ,能用实测就用实测,能用等时线就用等时线; 报年龄的时候,顺便报一下你对普通铅做了哪些假设、以及换一种假设年龄会怎么变 。
做到第三条,你的结果就比大多数已发表的结果更站得住。