Analytic continuation#
Sometimes qrules finds resonances that lie outside phase space, because resonances can ‘leak’ into phase space. The example below is simplified and the two selected resonances are a bit unusual, but it serves to illustrate how to handle these sub-threshold resonances.
reaction = qrules.generate_transitions(
initial_state="D0",
final_state=["K-", "K+", "K0"],
allowed_intermediate_particles=["a(0)(980)0", "a(2)(1320)0"],
formalism="canonical-helicity",
)
dot = qrules.io.asdot(
reaction,
collapse_graphs=True,
render_node=False,
)
graphviz.Source(dot)
Of the two resonances, \(a_0(980)\) lies just below threshold ― it’s mass is smaller than the the masses of the two decay products combined:
To correctly describe the dynamics for this resonance, we should use make use of analytic continuation. As opposed to Step 1: Formulate model, we now construct a RelativisticBreitWignerBuilder where we set its phase space factor to PhaseSpaceFactorSWave:
from ampform.dynamics import PhaseSpaceFactorSWave
from ampform.dynamics.builder import (
RelativisticBreitWignerBuilder,
create_non_dynamic_with_ff,
create_relativistic_breit_wigner_with_ff,
)
model_builder = ampform.get_builder(reaction)
analytic_breit_wigner_builder = RelativisticBreitWignerBuilder(
form_factor=True,
energy_dependent_width=True,
phsp_factor=PhaseSpaceFactorSWave, # ty:ignore[invalid-argument-type]
)
model_builder.dynamics.assign("D0", create_non_dynamic_with_ff)
model_builder.dynamics.assign("a(0)(980)0", analytic_breit_wigner_builder)
model_builder.dynamics.assign("a(2)(1320)0", create_relativistic_breit_wigner_with_ff)
model = model_builder.formulate()
The effect can be seen once we generate data. Despite the fact that the resonance lies outside phase space, there is still a contribution to the intensity:
for symbol in model.parameter_defaults:
if not str(symbol).startswith("C") or "a_{0}(980)^{0}" not in str(symbol):
continue
model.parameter_defaults[symbol] = 0.1
intensity = create_parametrized_function(
expression=model.expression.doit(),
parameters=model.parameter_defaults,
backend="jax",
)
helicity_transformer = SympyDataTransformer.from_sympy(
model.kinematic_variables, backend="numpy"
)
rng = TFUniformRealNumberGenerator(seed=0)
phsp_generator = TFPhaseSpaceGenerator(
initial_state_mass=reaction.initial_state[-1].mass,
final_state_masses={i: p.mass for i, p in reaction.final_state.items()},
)
phsp_momenta = phsp_generator.generate(100_000, rng)