Note
Go to the end to download the full example code.
Examples for config.yml file
Configuration file config.yml use YAML (https://yaml.org) format to describe decay process.
The main parts of config.yml is decay and particle.
The decay part describe the particle (or an id of a list of particle)
decay into which particles, it can be a list of a list of list.
A list means that there is ony one decay mode, A list of list is the list of possible decay mode.
The list item can be the particle name (or a dict to describe the decay parameters).
All name should appear in particle part.
The particle part describe the parameters of particles.
There are two special parts $top and $finals describe the top and finals particles.
The other parts are lists of particle name or dicts of particle parameters.
The list is the same type particle in decay part.
The dict is the parameters of the particle name.
22 config_str = """
23
24 decay:
25 A:
26 - [R1, B]
27 - [R2, C]
28 - [R3, D]
29 R1: [C, D]
30 R2: [B, D]
31 R3: [B, C]
32
33 particle:
34 $top:
35 A: { mass: 1.86, J: 0, P: -1}
36 $finals:
37 B: { mass: 0.494, J: 0, P: -1}
38 C: { mass: 0.139, J: 0, P: -1}
39 D: { mass: 0.139, J: 0, P: -1}
40 R1: [ R1_a, R1_b ]
41 R1_a: { mass: 0.7, width: 0.05, J: 1, P: -1}
42 R1_b: { mass: 0.5, width: 0.05, J: 0, P: +1}
43 R2: { mass: 0.824, width: 0.05, J: 0, P: +1}
44 R3: { mass: 0.824, width: 0.05, J: 0, P: +1}
45
46 """
The config file can be loaded by yaml library.
52 import matplotlib.pyplot as plt
53 import yaml
54
55 from tf_pwa.config_loader import ConfigLoader
56 from tf_pwa.histogram import Hist1D
The simple way to create config is write it to config.yml file
and then you can load it as config = ConfigLoader("config.yml").
Here we used config_str directly.
64 config = ConfigLoader(yaml.full_load(config_str))
We set parameters to a blance value. And we can generate some toy data and calclute the weights
The full params can be found by print(config.get_params()).
71 input_params = {
72 "A->R1_a.BR1_a->C.D_total_0r": 6.0,
73 "A->R1_b.BR1_b->C.D_total_0r": 1.0,
74 "A->R2.CR2->B.D_total_0r": 2.0,
75 "A->R3.DR3->B.C_total_0r": 1.0,
76 }
77 config.set_params(input_params)
True
Here we generate some toy data and phsp mc to show the model
82 data = config.generate_toy(1000)
83 phsp = config.generate_phsp(10000)
9.1%[▓▓▓▓>---------------------------------------------] 0.41/4.55s eff: 90.000000%
56.2%[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓>---------------------] 1.15/2.04s eff: 7.522486%
102.0%[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓>] 1.76/1.72s eff: 3.878746%
100.0%[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 1.76/1.76s eff: 3.790466%
You can also fit the data fit to the data We can omit the args when writen in config.yml
90 fit_result = config.fit([data], [phsp])
91 # After the fit, you can get the uncertaities as
92 err = config.get_params_error(fit_result, [data], [phsp])
Using Model
decay chains included:
[A->R1_a+B, R1_a->C+D] ls: ((1, 1),) ((1, 0),)
[A->R1_b+B, R1_b->C+D] ls: ((0, 0),) ((0, 0),)
[A->R2+C, R2->B+D] ls: ((0, 0),) ((0, 0),)
[A->R3+D, R3->B+C] ls: ((0, 0),) ((0, 0),)
########### initial parameters
{
"R1_a_mass": 0.7,
"R1_a_width": 0.05,
"R1_b_mass": 0.5,
"R1_b_width": 0.05,
"R2_mass": 0.824,
"R2_width": 0.05,
"R3_mass": 0.824,
"R3_width": 0.05,
"A->R1_a.BR1_a->C.D_total_0r": 6.0,
"A->R1_a.BR1_a->C.D_total_0i": 0.0,
"A->R1_a.B_g_ls_0r": 1.0,
"A->R1_a.B_g_ls_0i": 0.0,
"R1_a->C.D_g_ls_0r": 1.0,
"R1_a->C.D_g_ls_0i": 0.0,
"A->R1_b.BR1_b->C.D_total_0r": 1.0,
"A->R1_b.BR1_b->C.D_total_0i": 2.0118377772474645,
"A->R1_b.B_g_ls_0r": 1.0,
"A->R1_b.B_g_ls_0i": 0.0,
"R1_b->C.D_g_ls_0r": 1.0,
"R1_b->C.D_g_ls_0i": 0.0,
"A->R2.CR2->B.D_total_0r": 2.0,
"A->R2.CR2->B.D_total_0i": 2.0687057121861594,
"A->R2.C_g_ls_0r": 1.0,
"A->R2.C_g_ls_0i": 0.0,
"R2->B.D_g_ls_0r": 1.0,
"R2->B.D_g_ls_0i": 0.0,
"A->R3.DR3->B.C_total_0r": 1.0,
"A->R3.DR3->B.C_total_0i": -3.1213519850281184,
"A->R3.D_g_ls_0r": 1.0,
"A->R3.D_g_ls_0i": 0.0,
"R3->B.C_g_ls_0r": 1.0,
"R3->B.C_g_ls_0i": 0.0
}
initial NLL: tf.Tensor(-783.2149247715024, shape=(), dtype=float64)
nll_grad cost time: 0.2186119556427002
nll_grad cost time: 0.21996760368347168
nll_grad cost time: 0.2197856903076172
tf.Tensor(-785.0202945529009, shape=(), dtype=float64)
nll_grad cost time: 0.2186124324798584
nll_grad cost time: 0.21710896492004395
tf.Tensor(-785.4400439333403, shape=(), dtype=float64)
nll_grad cost time: 0.2188405990600586
tf.Tensor(-786.0021972742334, shape=(), dtype=float64)
nll_grad cost time: 0.22063493728637695
nll_grad cost time: 0.2196054458618164
tf.Tensor(-786.0200308168432, shape=(), dtype=float64)
nll_grad cost time: 0.2187347412109375
nll_grad cost time: 0.21707916259765625
tf.Tensor(-786.0246339350379, shape=(), dtype=float64)
nll_grad cost time: 0.21822786331176758
tf.Tensor(-786.0307638418062, shape=(), dtype=float64)
nll_grad cost time: 0.22521114349365234
nll_grad cost time: 0.2205202579498291
tf.Tensor(-786.0790680504488, shape=(), dtype=float64)
nll_grad cost time: 0.21915364265441895
tf.Tensor(-786.1021691437363, shape=(), dtype=float64)
nll_grad cost time: 0.22006487846374512
tf.Tensor(-786.1021915235533, shape=(), dtype=float64)
nll_grad cost time: 0.22339105606079102
tf.Tensor(-786.1021916581713, shape=(), dtype=float64)
Optimization terminated successfully.
Current function value: -786.102192
Iterations: 10
Function evaluations: 16
Gradient evaluations: 16
message: Optimization terminated successfully.
success: True
status: 0
fun: -786.1021916581713
x: [ 1.115e+00 1.980e+00 2.104e+00 2.047e+00 1.149e+00
-3.225e+00]
nit: 10
jac: [-5.619e-04 -1.478e-04 5.700e-04 1.293e-04 -5.181e-04
6.854e-06]
hess_inv: [[ 6.462e-03 -3.777e-04 ... 5.372e-03 -2.184e-04]
[-3.777e-04 1.246e-02 ... 1.511e-04 3.959e-03]
...
[ 5.372e-03 1.511e-04 ... 8.298e-03 -5.152e-06]
[-2.184e-04 3.959e-03 ... -5.152e-06 1.525e-02]]
nfev: 16
njev: 16
fit cost time: 3.756481409072876
Using Model
Time for calculating errors: 0.5266296863555908
hesse_error: [0.08131388704490601, 0.11283119287945828, 0.14281744560172993, 0.10741827581192308, 0.09324334155826125, 0.12455274069341889]
we can see that thre fit results consistant with inputs, the first one is fixed.
97 for var in input_params:
98 print(
99 f"in: {input_params[var]} => out: {fit_result.params[var]} +/- {err.get(var, 0.)}"
100 )
in: 6.0 => out: 6.0 +/- 0.0
in: 1.0 => out: 1.1151145388492583 +/- 0.08131388704490601
in: 2.0 => out: 2.104457026741137 +/- 0.14281744560172993
in: 1.0 => out: 1.1487910799207282 +/- 0.09324334155826125
We can use the amplitude to plot the fit results
105 amp = config.get_amplitude()
This is the total \(|A|^2\)
110 weight = amp(phsp)
This is the \(|A_i|^2\) for each decay chain
115 partial_weight = amp.partial_weight(phsp)
We can plot the data, Hist1D include some plot method base on matplotlib.
120 data_hist = Hist1D.histogram(
121 data.get_mass("(C, D)"), bins=60, range=(0.25, 1.45)
122 )
Read the mass var from phsp.
127 mass_phsp = phsp.get_mass("(C, D)")
For helicity angle it can be read as phsp.get_angle("(C, D)", "C")
The first arg is used to deteminate the decay chain. (decay chain include all particles)
The second arg is used to deteminate the angle in decay chain.
The return value is a dict as {"alpha": phi, "beta": theta, "gamma": 0}
136 phsp_hist = Hist1D.histogram(
137 mass_phsp, weights=weight, bins=60, range=(0.25, 1.45)
138 )
139 scale = phsp_hist.scale_to(data_hist)
140
141 pw_hist = []
142 for w in partial_weight:
143 # here we used more bins for a smooth plot
144 hist = Hist1D.histogram(
145 mass_phsp, weights=w, bins=60 * 2, range=(0.25, 1.45)
146 )
147 pw_hist.append(scale * hist * 2)
Then we can plot the histogram into matplotlib
152 for hist, dec in zip(pw_hist, config.get_decay()):
153 hist.draw_kde(label=str(dec))
154 phsp_hist.draw(label="total amplitude")
155 data_hist.draw_error(label="toy data", color="black")
156
157 plt.legend()
158 plt.ylim((0, None))
159 plt.xlabel("M(CD)/ GeV")
160 plt.ylabel("Events/ 20 MeV")
161 plt.show()

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