Note
Go to the end to download the full example code.
Particle and amplitude
Amplitude = DecayGroup + Variable
We will use following parameters for a toy model
11 from tf_pwa.amp import DecayChain, DecayGroup, get_decay, get_particle
12
13 resonances = {
14 "R0": {"J": 0, "P": 1, "mass": 1.0, "width": 0.07},
15 "R1": {"J": 0, "P": 1, "mass": 1.0, "width": 0.07},
16 "R2": {"J": 1, "P": -1, "mass": 1.225, "width": 0.08},
17 }
18 m_A, m_B, m_C, m_D = 1.8, 0.18, 0.18, 0.18
19 a, b, c, d = [
20 get_particle(i, J=0, P=-1, mass=m)
21 for i, m in zip("ABCD", [m_A, m_B, m_C, m_D])
22 ]
23 r1, r2, r3 = [get_particle(i, **resonances[i]) for i in resonances.keys()]
24
25
26 decay_group = DecayGroup(
27 [
28 DecayChain([get_decay(a, [r1, c]), get_decay(r1, [b, d])]),
29 DecayChain([get_decay(a, [r2, b]), get_decay(r2, [c, d])]),
30 DecayChain([get_decay(a, [r3, b]), get_decay(r3, [c, d])]),
31 ]
32 )
The above parts can be represented as config.yml used by ConfigLoader.
We can get AmplitudeModel form decay_group and a optional Variables Managerager. It has parameters, so we can get and set parameters for the amplitude model
39 from tf_pwa.amp import AmplitudeModel
40 from tf_pwa.variable import VarsManager
41
42 vm = VarsManager()
43 amp = AmplitudeModel(decay_group, vm=vm)
44
45 print(amp.get_params())
46 amp.set_params(
47 {
48 "A->R0.CR0->B.D_total_0r": 1.0,
49 "A->R1.BR1->C.D_total_0r": 1.0,
50 "A->R2.BR2->C.D_total_0r": 7.0,
51 }
52 )
{'R0_mass': np.float64(1.0), 'R0_width': np.float64(0.07), 'R1_mass': np.float64(1.0), 'R1_width': np.float64(0.07), 'R2_mass': np.float64(1.225), 'R2_width': np.float64(0.08), 'A->R0.CR0->B.D_total_0r': np.float64(1.5780601004948638), 'A->R0.CR0->B.D_total_0i': np.float64(-3.1250368539102666), 'A->R0.C_g_ls_0r': np.float64(1.0), 'A->R0.C_g_ls_0i': np.float64(0.0), 'R0->B.D_g_ls_0r': np.float64(1.0), 'R0->B.D_g_ls_0i': np.float64(0.0), 'A->R1.BR1->C.D_total_0r': np.float64(1.1500324511399964), 'A->R1.BR1->C.D_total_0i': np.float64(-0.9835438927660642), 'A->R1.B_g_ls_0r': np.float64(1.0), 'A->R1.B_g_ls_0i': np.float64(0.0), 'R1->C.D_g_ls_0r': np.float64(1.0), 'R1->C.D_g_ls_0i': np.float64(0.0), 'A->R2.BR2->C.D_total_0r': np.float64(1.0207189172253965), 'A->R2.BR2->C.D_total_0i': np.float64(-0.9559377350419731), 'A->R2.B_g_ls_0r': np.float64(1.0), 'A->R2.B_g_ls_0i': np.float64(0.0), 'R2->C.D_g_ls_0r': np.float64(1.0), 'R2->C.D_g_ls_0i': np.float64(0.0)}
For the calculation, we generate some phase space data.
57 from tf_pwa.phasespace import PhaseSpaceGenerator
58
59 m_A, m_B, m_C, m_D = 1.8, 0.18, 0.18, 0.18
60 p1, p2, p3 = PhaseSpaceGenerator(m_A, [m_B, m_C, m_D]).generate(100000)
and the calculate helicity angle from the data
65 from tf_pwa.cal_angle import cal_angle_from_momentum
66
67 data = cal_angle_from_momentum({b: p1, c: p2, d: p3}, decay_group)
we can index mass from data as
72 from tf_pwa.data import data_index
73
74 m_bd = data_index(data, ("particle", "(B, D)", "m"))
75 # m_bc = data_index(data, ("particle", "(B, C)", "m"))
76 m_cd = data_index(data, ("particle", "(C, D)", "m"))
Note
If DecayGroup do not include resonant of (B, C), the data will not include its mass too. We can use different DecayGroup for cal_angle and AmplitudeModel when they have the same initial and final particle.
The amplitde square is calculated by amplitude model simply as
Now by using matplotlib we can get the Dalitz plot as
91 import matplotlib.pyplot as plt
92
93 plt.clf()
94 plt.hist2d(
95 m_bd.numpy() ** 2,
96 m_cd.numpy() ** 2,
97 weights=amp_s2.numpy(),
98 bins=60,
99 cmin=1,
100 cmap="jet",
101 )
102 plt.colorbar()
103 plt.xlabel("$m^2(BD)$")
104 plt.ylabel("$m^2(CD)$")
105 plt.show()

Total running time of the script: (0 minutes 1.893 seconds)