如何使用 DecayTreeFitter(DTF)?
目标
- 理解什么是 DecayTreeFitter 以及如何正确使用它!
DecayTreeFitter(DTF)是一种用于确定粒子衰变运动学的算法。若不使用 DTF ,粒子的衰变过程会按顺序从末态稳定径迹逐步重建。例如,考虑衰变链 \( D^{\ast +} \to D^{0}\pi^{+} \) 其中 \( D^{0} \to K^{-} \pi^{+} \)。通常做法是从末态的径迹出发:对 \( K^{-} \pi^{+} \) 径迹做顶点拟合并组合其四动量,得到一个 \(D^{0}\) 候选;再将该\(D^{0}\)与\(\pi^{+}\)组合,得到 \( D^{\ast +} \)候选。虽然每一步都对粒子轨迹做了顶点拟合,但整个衰变链是自下而上逐级构建的。
与此相反,DTF 一次性对整个衰变链进行运动学整体拟合。这样做的好处是可以在拟合过程中对衰变链中各个复合粒子施加约束。例如:可以要求组合 \(K^{-}\pi^{+}\) 后的不变质量严格等于已知的 \(D^{0}\) 质量,从而显著提升重建 \(D^{\ast}\) 不变质量的分辨率;也可以额外施加约束,要求来自 \( D^{\ast +} \) 的 \( \pi^{+} \) 的衰变顶点必须指向主顶点(PV)。
一般而言(但不绝对),当处理天然宽度极窄的粒子(如\(D^{\ast}\),\(J/\psi\), ...)时,使用 DTF 带来的改进效果最为明显。
DTF 的更多信息可以从下面查看:
本教程内容取自 tutorial in DaVinci ,及其相关示例包括 PV constraints 与 PID substitution 的实例。
带质量约束的 DecayTreeFitter 示例
考虑衰变链\( B_{s}^{0} \to J/\psi \phi \),其中有 \( J/\psi \to \mu^{+}\mu^{-} \) 和 \( \phi \to K^{+}K^{-} \)。我们知道 \( J/\psi \) 是一个窄共振,因此可在拟合中将 \( \mu^{+}\mu^{-} \) 质量约束为 PDG 值 \( 3.0969\,{\rm GeV} \)。
from PyConf.reading import get_particles, get_pvs
from DecayTreeFitter import DecayTreeFitter
# Load data from dst onto a TES
turbo_line = "Hlt2B2CC_BsToJpsiPhi_Detached"
input_data = get_particles(f"/Event/HLT2/{turbo_line}/Particles")
# Make the DTF
DTF = DecayTreeFitter(
name="DTF", input_particles=input_data, mass_constraints=["J/psi(1S)"]
)
# Now make the kinematic functors for the particles that have been fitted with DTF
kin = FC.Kinematics()
dtf_kin = FunctorCollection(
{"DTF_" + k: DTF(v) for k, v in kin.get_thor_functors().items()}
)
dtf_kin 中的函数可以像其他变量一样,以常规方式一并加入 FunTuple。
下方两图展示了 DecayTreeFitter 对\(B_{s}^{0}\)质量分辨率的影响:
- 红色直方图------施加对 \(J/\psi \) 质量约束后的结果;
- 蓝色直方图------未使用 DTF 的原始结果。
第一张图中,红色曲线清晰地显示了对 \(J/\psi \) 的质量约束;第二张图中,同样由于该质量约束\(B_{s}^{0} \)红线的整体分布明显更窄。

DecayTreeFitter 的主顶点(PV)约束
亦可对候选粒子施加 PV 约束,即要求其动量矢量必须指向某条主顶点。此时需明确采用哪一条 PV:
- 粒子已关联的主顶点;
-
粒子已关联的主顶点,但在 PV 拟合时 移除了信号径迹(无偏);
- 要实现此选项,必须确保 HLT2 阶段已用 pv_tracks=True 保存 PV 径迹!
-
所有可能 PV 中的最佳顶点。
具体选择由分析人员决定,随后在 DaVinci 配置中实现即可,操作十分直接。
# 1: the PV already associated with the particle
DTF_OWNPV = DecayTreeFitter(
name="DTF_OwnPV",
input_particles=input_data,
mass_constraints=["J/psi(1S)"],
constrain_to_ownpv=True,
)
# 2: The "unbiased" PV
# First create a new B list with unbiased PVs
from PyConf.reading import get_extended_pvs
from PyConf.Algorithms import ParticleUnbiasedPVAdder
B_Data_unbiasedpv = ParticleUnbiasedPVAdder(
InputParticles=input_data, PrimaryVertices=get_extended_pvs()
).OutputParticles
DTF_UNBIASEDPV = DecayTreeFitter(
name="DTF_UnbiasedPV",
input_particles=B_Data_unbiasedpv,
mass_constraints=["J/psi(1S)"],
constrain_to_ownpv=True,
)
# 3: The "best" PV:
# First get the list of possible PVs
pvs = get_pvs()
DTF_BESTPV = DecayTreeFitter(
"DTF_BESTPV", # name of algorithm
input_data, # input particles
input_pvs=pvs,
mass_constraints=["J/psi(1S)"],
)
# Define some functors that will be affected by the PV constraint
pv_fun = {}
pv_fun["BPVLTIME"] = F.BPVLTIME(pvs)
pv_fun["BPVIPCHI2"] = F.BPVIPCHI2(pvs)
pv_coll = FunctorCollection(pv_fun)
# Again, make the DTF versions of the functors but this time add them to the existing functor collection
pv_coll += FunctorCollection(
{"DTF_OWNPV_" + k: DTF_OWNPV(v) for k, v in pv_coll.get_thor_functors().items()},
{"DTF_BESTPV_" + k: DTF_BESTPV(v) for k, v in pv_coll.get_thor_functors().items()},
{"DTF_UNBIASEDPV_" + k: DTF_UNBIASEDPV(v) for k, v in pv_coll.get_thor_functors().items()},
)
DecayTreeFitterResults 函数集合
系统已为你预定义了一个便捷的函子集合,名为 DecayTreeFitterResults,用于一次性引入常用的 DTF 变量。其配置示例如下:
dtf_vars = FC.DecayTreeFitterResults(
DTF = DTF_PV,
prefix = 'DTF_PV_FC',
decay_origin = True, # Add the variables describing the origin vertex of the decaying particle
with_lifetime = True, # Add the decay time, the flight distance and their uncertainties
with_kinematics = True, # Add the 4-momentum components
)