Step 1: Create amplitude model¶
TensorWaves requires you to first formulate a mathematical Model
that you want to fit to your data set. When this model is an amplitude model, it is most convenient to construct it with ampform
.
This notebook briefly illustrates how to create such an amplitude model with ampform
and how to write it to a recipe file that can be understood by tensorwaves
. For more control, have a look at the usage guides of AmpForm.
In this example, we use the helicity formalism, but you can also use formalism_type="canonical-helicity"
. As you can see, we analyze the decay \(J/\psi \to \pi^0\pi^0\gamma\) here.
Simplified model: \(J/\psi \to f_0\gamma\)
As Step 3: Perform fit serves to illustrate usage only, we make the amplitude model here a bit simpler by not allowing \(\omega\) resonances (which are narrow and therefore hard to fit). For this reason, we can also limit the InteractionType
to STRONG
.
import qrules as q
result = q.generate_transitions(
initial_state=("J/psi(1S)", [-1, +1]),
final_state=["gamma", "pi0", "pi0"],
allowed_intermediate_particles=["f(0)"],
allowed_interaction_types=["strong", "EM"],
)
As a small goodie, you can use graphviz
to visualize the generated graphs:
from graphviz import Source
dot = q.io.asdot(result, collapse_graphs=True)
Source(dot)
Next we convert the transitions
into an amplitude model (here: HelicityModel
). This can be done with get_builder()
and generate()
.
import ampform
model_builder = ampform.get_builder(result)
model = model_builder.generate()
display(*model.parameter_defaults)
The heart of the model is a sympy expression that contains the full description of the intensity model. Note two things:
The coefficients for the different amplitudes are complex valued.
By default there is no dynamics in the model, so it still has to be specified.
We choose to use relativistic_breit_wigner_with_ff()
as the lineshape for all resonances and use a Blatt-Weisskopf form factor (no dynamics; see BlattWeisskopfSquared
) for the production decay. The set_dynamics()
is a convenience interface for replacing the dynamics for intermediate states.
from ampform.dynamics.builder import (
create_non_dynamic_with_ff,
create_relativistic_breit_wigner_with_ff,
)
model_builder.set_dynamics("J/psi(1S)", create_non_dynamic_with_ff)
for name in result.get_intermediate_particles().names:
model_builder.set_dynamics(name, create_relativistic_breit_wigner_with_ff)
model = model_builder.generate()
Now let’s take another look at the parameters of the model to see which new parameters are there:
sorted(model.parameter_defaults, key=lambda s: s.name)
[C[J/\psi(1S) \to f_{0}(1370)_{0} \gamma_{+1}; f_{0}(1370) \to \pi^{0}_{0} \pi^{0}_{0}],
C[J/\psi(1S) \to f_{0}(1500)_{0} \gamma_{+1}; f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}],
C[J/\psi(1S) \to f_{0}(1710)_{0} \gamma_{+1}; f_{0}(1710) \to \pi^{0}_{0} \pi^{0}_{0}],
C[J/\psi(1S) \to f_{0}(500)_{0} \gamma_{+1}; f_{0}(500) \to \pi^{0}_{0} \pi^{0}_{0}],
C[J/\psi(1S) \to f_{0}(980)_{0} \gamma_{+1}; f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}],
Gamma_f(0)(1370),
Gamma_f(0)(1500),
Gamma_f(0)(1710),
Gamma_f(0)(500),
Gamma_f(0)(980),
d_J/psi(1S),
d_f(0)(1370),
d_f(0)(1500),
d_f(0)(1710),
d_f(0)(500),
d_f(0)(980),
m_f(0)(1370),
m_f(0)(1500),
m_f(0)(1710),
m_f(0)(500),
m_f(0)(980)]
Finally, we can write the HelicityModel
to disk via pickle
, as well as store the Result
as JSON:
import pickle
q.io.write(result, "transitions.json")
with open("helicity_model.pickle", "wb") as stream:
pickle.dump(model, stream)
Cool, that’s it! We now have a template for an amplitude model with which to generate data and perform a fit. In the next steps, we will use use this HelicityModel
as a fit model template for tensorwaves
.