This page demonstrates deserialization and evaluation of a partial-wave analysis of a diffractively produced \(3\pi\) system. The analysis is performed independently in bins of \(3\pi\) mass and transferred momentum. The full analysis contains about 170 decay chains (about 88 waves, symmetrized for the two \(\pi^+\pi^-\) pairs) per bin. See INSPIRE-HEP 1391643 for details.
withopen(THIS_DIR.parent.parent /"models"/"x2pipipi-compass-1391643.json") as f: MODEL_DEFINITION = json.load(f)
The JSON file contains four distributions from the same mass bin. Since the Python deserializer constructs one distribution at a time, each distribution is selected and formulated separately.
pd.DataFrame({"Distribution": list(MODELS),"Decay chains": [len(get_decay_chains(model)) for model in SELECTED_DEFINITIONS.values()],})
Distribution
Decay chains
0
compass_3pi_JP=1+_M=0_1540_1560
16
1
compass_3pi_JP=1-_M=1_1540_1560
2
2
compass_3pi_JP=2+_M=1_1540_1560
6
3
compass_3pi_JP=4+_M=1_1540_1560
4
Validation
The table compares all serialized checkpoints with the Python implementation. The marks โ๐ขโ, โ๐กโ, and โ๐ดโ indicate an absolute difference of \(<10^{-10}\), \(<10^{-2}\), or \(\ge10^{-2}\), respectively. The numerical results are shown even when a checkpoint does not agree with the reference value.
Validation helpers
checksum_points = { point["name"]: {parameter["name"]: parameter["value"] for parameter in point["parameters"]}for point in MODEL_DEFINITION["parameter_points"]}def to_number(value: float|str) ->float|complex:ifisinstance(value, str): value =complex(value.replace(" ", "").replace("i", "j")) number =complex(value)return number.real if number.imag ==0else numberdef round_number(value: float|complex, digits: int=6) ->float|complex: number =complex(value)if number.imag ==0:returnround(number.real, digits)returncomplex(round(number.real, digits), round(number.imag, digits))def label_diff(difference: complex) ->str: absolute_difference =abs(difference)if absolute_difference <1e-10:return"๐ข"if absolute_difference <1e-2:return"๐ก"return"๐ด"def create_intensity_function(model, subsystem: int): invariant =next(s for s in model.invariants ifstr(s) ==f"sigma{subsystem}") intensity_expr = cached.xreplace(cached.unfold(model), model.variables) intensity_expr = cached.xreplace(intensity_expr, model.parameter_defaults) invariant_expr = model.invariants[invariant].xreplace(model.masses).doit() intensity_expr = cached.doit(intensity_expr.xreplace({invariant: invariant_expr}))return cached.lambdify(intensity_expr, backend="jax")def evaluate_intensity(model, point: dict[str, float], pair: tuple[int, int]) ->float: i, j = pair k, *_ = {1, 2, 3} -set(pair) invariants = {str(s): s for s in model.invariants} sigma_j = invariants[f"sigma{j}"] sigma_k = invariants[f"sigma{k}"] angle_expr =next( expressionfor angle, expression in model.variables.items()ifstr(angle) ==f"theta_{i}{j}" ) sigma_k_value = point[f"m_{i}{j}"] **2 equation = sp.Eq( sp.cos(angle_expr).xreplace(model.parameter_defaults), point[f"cos_theta_{i}{j}"], ) sigma_j_value = sp.solve(equation.subs(sigma_k, sigma_k_value), sigma_j)[0] function = create_intensity_function(model, subsystem=i) value = function({str(sigma_j): float(sigma_j_value), str(sigma_k): sigma_k_value})returnfloat(jnp.real(value))