Source code for ampform.dynamics.builder

"""Build :mod:`.lineshape` with correct variable names and values."""

import inspect
from collections import OrderedDict
from typing import Callable, Dict, Optional, Tuple

import attr
import sympy as sp
from qrules.particle import Particle

from .lineshape import (
    BlattWeisskopf,
    breakup_momentum,
    relativistic_breit_wigner,
    relativistic_breit_wigner_with_ff,
)

try:
    from typing import Protocol
except ImportError:
    from typing_extensions import Protocol  # type: ignore


[docs]@attr.s(frozen=True) class TwoBodyKinematicVariableSet: in_edge_inv_mass: sp.Symbol = attr.ib() out_edge_inv_mass1: sp.Symbol = attr.ib() out_edge_inv_mass2: sp.Symbol = attr.ib() helicity_theta: sp.Symbol = attr.ib() helicity_phi: sp.Symbol = attr.ib() angular_momentum: Optional[int] = attr.ib(default=None)
[docs]def create_non_dynamic( resonance: Particle, variable_pool: TwoBodyKinematicVariableSet ) -> Tuple[sp.Expr, Dict[sp.Symbol, float]]: # pylint: disable=unused-argument return (1, {})
[docs]def create_non_dynamic_with_ff( resonance: Particle, variable_pool: TwoBodyKinematicVariableSet ) -> Tuple[sp.Expr, Dict[sp.Symbol, float]]: angular_momentum = variable_pool.angular_momentum if angular_momentum is None: raise ValueError( "Angular momentum is not defined but is required in the form factor!" ) q = breakup_momentum( variable_pool.in_edge_inv_mass, variable_pool.out_edge_inv_mass1, variable_pool.out_edge_inv_mass2, ) meson_radius = sp.Symbol(f"d_{resonance.name}") return ( BlattWeisskopf(q, meson_radius, angular_momentum), {meson_radius: 1}, )
[docs]def create_relativistic_breit_wigner( resonance: Particle, variable_pool: TwoBodyKinematicVariableSet ) -> Tuple[sp.Expr, Dict[sp.Symbol, float]]: inv_mass = variable_pool.in_edge_inv_mass res_mass = sp.Symbol(f"m_{resonance.name}") res_width = sp.Symbol(f"Gamma_{resonance.name}") expression = relativistic_breit_wigner(inv_mass, res_mass, res_width) parameter_defaults = { res_mass: resonance.mass, res_width: resonance.width, } return expression, parameter_defaults
[docs]def create_relativistic_breit_wigner_with_ff( resonance: Particle, variable_pool: TwoBodyKinematicVariableSet ) -> Tuple[sp.Expr, Dict[sp.Symbol, float]]: if variable_pool.angular_momentum is None: raise ValueError( "Angular momentum is not defined but is required in the form factor!" ) inv_mass = variable_pool.in_edge_inv_mass res_mass = sp.Symbol(f"m_{resonance.name}") res_width = sp.Symbol(f"Gamma_{resonance.name}") product1_inv_mass = variable_pool.out_edge_inv_mass1 product2_inv_mass = variable_pool.out_edge_inv_mass2 angular_momentum = variable_pool.angular_momentum meson_radius = sp.Symbol(f"d_{resonance.name}") expression = relativistic_breit_wigner_with_ff( inv_mass, res_mass, res_width, product1_inv_mass, product2_inv_mass, angular_momentum, meson_radius, ) parameter_defaults = { res_mass: resonance.mass, res_width: resonance.width, meson_radius: 1, } return expression, parameter_defaults
[docs]class ResonanceDynamicsBuilder(Protocol): """Protocol that is used by `.set_dynamics`. Follow this `~typing.Protocol` when defining a builder function that is to be used by `.set_dynamics`. For an example, see the source code `.create_relativistic_breit_wigner`, which creates a `.relativistic_breit_wigner`. .. seealso:: :doc:`/usage/dynamics/custom` """
[docs] def __call__( self, resonance: Particle, variable_pool: TwoBodyKinematicVariableSet ) -> Tuple[sp.Expr, Dict[sp.Symbol, float]]: ...
[docs]def verify_signature(builder: Callable) -> None: """Check signature of a builder function dynamically. Dynamically check whether a builder has the same signature as `.ResonanceDynamicsBuilder`. This function is needed because `typing.runtime_checkable` does only checks members and methods, not the signature of those methods. """ signature = inspect.signature(builder) if signature.return_annotation != __EXPECTED.return_annotation: raise ValueError( f'Builder "{builder.__name__}" has return type {__EXPECTED.return_annotation};' f" expected {signature.return_annotation}" ) expected_parameters = OrderedDict(__EXPECTED.parameters.items()) del expected_parameters["self"] assert signature.return_annotation == __EXPECTED.return_annotation if signature.parameters != expected_parameters: raise ValueError( f'Builder "{builder.__name__}" has parameters\n' f" {list(signature.parameters.values())}\n" "This should be\n" f" {list(expected_parameters.values())}" )
__EXPECTED = inspect.signature(ResonanceDynamicsBuilder.__call__)