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.

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)
../_images/step1_7_0.svg

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)
\[\displaystyle C[J/\psi(1S) \to f_{0}(980)_{0} \gamma_{+1};f_{0}(980) \to \pi^{0}_{0} \pi^{0}_{0}]\]
\[\displaystyle C[J/\psi(1S) \to f_{0}(1710)_{0} \gamma_{+1};f_{0}(1710) \to \pi^{0}_{0} \pi^{0}_{0}]\]
\[\displaystyle C[J/\psi(1S) \to f_{0}(1370)_{0} \gamma_{+1};f_{0}(1370) \to \pi^{0}_{0} \pi^{0}_{0}]\]
\[\displaystyle C[J/\psi(1S) \to f_{0}(1500)_{0} \gamma_{+1};f_{0}(1500) \to \pi^{0}_{0} \pi^{0}_{0}]\]
\[\displaystyle C[J/\psi(1S) \to f_{0}(500)_{0} \gamma_{+1};f_{0}(500) \to \pi^{0}_{0} \pi^{0}_{0}]\]

The heart of the model is a sympy expression that contains the full description of the intensity model. Note two things:

  1. The coefficients for the different amplitudes are complex valued.

  2. 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 BlattWeisskopf form factor factor (no dynamics) 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.