Source code for polarimetry.dynamics

"""Functions for dynamics lineshapes and kinematics.

.. seealso:: :doc:`/appendix/dynamics`
"""

# pyright: reportPrivateUsage=false
from __future__ import annotations

import sympy as sp
from ampform.kinematics.phasespace import Kallen
from ampform.sympy import (
    UnevaluatedExpression,
    create_expression,
    implement_doit_method,
    make_commutative,
)


[docs]@make_commutative @implement_doit_method class P(UnevaluatedExpression): def __new__(cls, s, mi, mj, **hints): return create_expression(cls, s, mi, mj, **hints) def evaluate(self): s, mi, mj = self.args return sp.sqrt(Kallen(s, mi**2, mj**2)) / (2 * sp.sqrt(s)) def _latex(self, printer, *args): s, mi, mj = map(printer._print, self.args) return Rf"p_{{{mi},{mj}}}\left({s}\right)"
[docs]@make_commutative @implement_doit_method class Q(UnevaluatedExpression): def __new__(cls, s, m0, mk, **hints): return create_expression(cls, s, m0, mk, **hints) def evaluate(self): s, m0, mk = self.args return sp.sqrt(Kallen(s, m0**2, mk**2)) / (2 * m0) # <-- not s! def _latex(self, printer, *args): s, m0, mk = map(printer._print, self.args) return Rf"q_{{{m0},{mk}}}\left({s}\right)"
[docs]@make_commutative @implement_doit_method class BreitWignerMinL(UnevaluatedExpression): def __new__( cls, s, decaying_mass, spectator_mass, resonance_mass, resonance_width, child2_mass, child1_mass, l_dec, l_prod, R_dec, R_prod, ): return create_expression( cls, s, decaying_mass, spectator_mass, resonance_mass, resonance_width, child2_mass, child1_mass, l_dec, l_prod, R_dec, R_prod, ) def evaluate(self): s, m_top, m_spec, m0, Γ0, m1, m2, l_dec, l_prod, R_dec, R_prod = self.args q = Q(s, m_top, m_spec) q0 = Q(m0**2, m_top, m_spec) p = P(s, m1, m2) p0 = P(m0**2, m1, m2) width = EnergyDependentWidth(s, m0, Γ0, m1, m2, l_dec, R_dec) return sp.Mul( (q / q0) ** l_prod, BlattWeisskopf(q * R_prod, l_prod) / BlattWeisskopf(q0 * R_prod, l_prod), 1 / (m0**2 - s - sp.I * m0 * width), (p / p0) ** l_dec, BlattWeisskopf(p * R_dec, l_dec) / BlattWeisskopf(p0 * R_dec, l_dec), evaluate=False, ) def _latex(self, printer, *args) -> str: s = printer._print(self.args[0]) return Rf"\mathcal{{R}}\left({s}\right)"
[docs]@make_commutative @implement_doit_method class BuggBreitWigner(UnevaluatedExpression): def __new__(cls, s, m0, Γ0, m1, m2, γ): return create_expression(cls, s, m0, Γ0, m1, m2, γ) def evaluate(self): s, m0, Γ0, m1, m2, γ = self.args s_A = m1**2 - m2**2 / 2 # Adler zero g_squared = sp.Mul( (s - s_A) / (m0**2 - s_A), m0 * Γ0 * sp.exp(-γ * s), evaluate=False, ) return 1 / (m0**2 - s - sp.I * g_squared) def _latex(self, printer, *args) -> str: s = printer._print(self.args[0], *args) return Rf"\mathcal{{R}}_\mathrm{{Bugg}}\left({s}\right)"
[docs]@make_commutative @implement_doit_method class FlattéSWave(UnevaluatedExpression): # https://github.com/ComPWA/polarimetry/blob/34f5330/julia/notebooks/model0.jl#L151-L161 def __new__(cls, s, m0, widths, masses1, masses2): return create_expression(cls, s, m0, widths, masses1, masses2) def evaluate(self): s, m0, (Γ1, Γ2), (ma1, mb1), (ma2, mb2) = self.args p = P(s, ma1, mb1) p0 = P(m0**2, ma2, mb2) q = P(s, ma2, mb2) q0 = P(m0**2, ma2, mb2) Γ1 *= (p / p0) * m0 / sp.sqrt(s) Γ2 *= (q / q0) * m0 / sp.sqrt(s) Γ = Γ1 + Γ2 return 1 / (m0**2 - s - sp.I * m0 * Γ) def _latex(self, printer, *args) -> str: s = printer._print(self.args[0]) return Rf"\mathcal{{R}}_\mathrm{{Flatté}}\left({s}\right)"
[docs]@make_commutative @implement_doit_method class EnergyDependentWidth(UnevaluatedExpression): def __new__(cls, s, m0, Γ0, m1, m2, L, R): return create_expression(cls, s, m0, Γ0, m1, m2, L, R) def evaluate(self): s, m0, Γ0, m1, m2, L, R = self.args p = P(s, m1, m2) p0 = P(m0**2, m1, m2) ff = BlattWeisskopf(p * R, L) ** 2 ff0 = BlattWeisskopf(p0 * R, L) ** 2 return sp.Mul( Γ0, (p / p0) ** (2 * L + 1), m0 / sp.sqrt(s), ff / ff0, evaluate=False, ) def _latex(self, printer, *args) -> str: s = printer._print(self.args[0]) return Rf"\Gamma\left({s}\right)"
[docs]@make_commutative @implement_doit_method class BlattWeisskopf(UnevaluatedExpression): def __new__(cls, z, L, **hints): return create_expression(cls, z, L, **hints) def evaluate(self) -> sp.Piecewise: z, L = self.args cases = { 0: 1, 1: 1 / (1 + z**2), 2: 1 / (9 + 3 * z**2 + z**4), } return sp.Piecewise( *[(sp.sqrt(expr), sp.Eq(L, l_val)) for l_val, expr in cases.items()] ) def _latex(self, printer, *args): z, L = map(printer._print, self.args) return Rf"F_{{{L}}}\left({z}\right)"