Skip to content

Commit 2ef3479

Browse files
authored
BREAK: replace EnergyDependentWidth with AmpForm's implementation (#190)
* FEAT: allow selecting phase space factor protocol
1 parent e34efbe commit 2ef3479

File tree

5 files changed

+31
-40
lines changed

5 files changed

+31
-40
lines changed

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"sp.Basic": "sympy.core.basic.Basic",
6262
"sp.Expr": "sympy.core.expr.Expr",
6363
"sp.Indexed": "sympy.tensor.indexed.Indexed",
64+
"PhaseSpaceFactorProtocol": "ampform.dynamics.phasespace.PhaseSpaceFactorProtocol",
6465
"sp.Rational": "sympy.core.numbers.Rational",
6566
"sp.Symbol": "sympy.core.symbol.Symbol",
6667
"StateID": ("obj", "ampform_dpd.decay.StateID"),

docs/index.ipynb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,12 +472,15 @@
472472
"metadata": {},
473473
"outputs": [],
474474
"source": [
475+
"from ampform.dynamics.phasespace import PhaseSpaceFactor\n",
476+
"\n",
475477
"from ampform_dpd.dynamics.builder import BreitWignerBuilder\n",
476478
"\n",
477479
"dynamics_builder = BreitWignerBuilder(\n",
478480
" energy_dependent_width=True,\n",
479481
" production_form_factor=True,\n",
480482
" decay_form_factor=True,\n",
483+
" phsp_factor=PhaseSpaceFactor,\n",
481484
")\n",
482485
"for chain in model_builder.decay.chains:\n",
483486
" model_builder.dynamics_choices.register_builder(chain, dynamics_builder)\n",

docs/serialization.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"import matplotlib.pyplot as plt\n",
4747
"import pandas as pd\n",
4848
"import sympy as sp\n",
49+
"from ampform.dynamics import EnergyDependentWidth\n",
4950
"from ampform.dynamics.form_factor import BlattWeisskopfSquared, FormFactor\n",
5051
"from ampform.dynamics.phasespace import BreakupMomentum, BreakupMomentumSquared\n",
5152
"from IPython.display import JSON, Math\n",
@@ -57,7 +58,6 @@
5758
" BreitWigner,\n",
5859
" BuggBreitWigner,\n",
5960
" ChannelArguments,\n",
60-
" EnergyDependentWidth,\n",
6161
" MultichannelBreitWigner,\n",
6262
" SimpleBreitWigner,\n",
6363
")\n",

src/ampform_dpd/dynamics/__init__.py

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
from typing import TYPE_CHECKING, Any
77

88
import sympy as sp
9+
from ampform.dynamics import EnergyDependentWidth
910
from ampform.dynamics.form_factor import FormFactor
10-
from ampform.dynamics.phasespace import BreakupMomentum
11-
from ampform.sympy import unevaluated
11+
from ampform.dynamics.phasespace import (
12+
BreakupMomentum,
13+
PhaseSpaceFactor,
14+
PhaseSpaceFactorProtocol,
15+
)
16+
from ampform.sympy import argument, unevaluated
1217

1318
if TYPE_CHECKING:
1419
from sympy.printing.latex import LatexPrinter
@@ -23,13 +28,14 @@ class RelativisticBreitWigner(sp.Expr):
2328
m2: Any
2429
angular_momentum: Any
2530
meson_radius: Any
31+
phsp_factor: PhaseSpaceFactorProtocol = argument( # type:ignore[assignment]
32+
default=PhaseSpaceFactor, sympify=False
33+
)
2634
_latex_repr_ = (
2735
R"\mathcal{{R}}_{{{angular_momentum}}}\left({s}, {mass0}, {gamma0}\right)"
2836
)
2937

3038
def evaluate(self):
31-
from ampform.dynamics import EnergyDependentWidth # noqa: PLC0415
32-
3339
s, m0, w0, m1, m2, angular_momentum, meson_radius = self.args
3440
width = EnergyDependentWidth(
3541
s=s,
@@ -39,6 +45,7 @@ def evaluate(self):
3945
m_b=m2,
4046
angular_momentum=angular_momentum,
4147
meson_radius=meson_radius,
48+
phsp_factor=self.phsp_factor,
4249
name=Rf"\Gamma_{{{sp.latex(angular_momentum)}}}",
4350
)
4451
return (m0 * w0) / (m0**2 - s - width * m0 * sp.I)
@@ -57,6 +64,9 @@ class BreitWignerMinL(sp.Expr):
5764
l_prod: Any
5865
R_dec: Any
5966
R_prod: Any
67+
phsp_factor: PhaseSpaceFactorProtocol = argument( # type:ignore[assignment]
68+
default=PhaseSpaceFactor, sympify=False
69+
)
6070
_latex_repr_ = R"\mathcal{{R}}^\mathrm{{BW}}_{{{l_dec},{l_prod}}}\left({s}\right)"
6171

6272
def evaluate(self): # noqa: PLR0914
@@ -65,7 +75,7 @@ def evaluate(self): # noqa: PLR0914
6575
ff0_prod = FormFactor(m_top**2, m0, m_spec, l_prod, R_prod)
6676
ff_dec = FormFactor(s, m1, m2, l_dec, R_dec)
6777
ff0_dec = FormFactor(m0**2, m1, m2, l_dec, R_dec)
68-
width = EnergyDependentWidth(s, m0, Γ0, m1, m2, l_dec, R_dec)
78+
width = EnergyDependentWidth(s, m0, Γ0, m1, m2, l_dec, R_dec, self.phsp_factor)
6979
return sp.Mul(
7080
ff_prod / ff0_prod,
7181
1 / (m0**2 - s - sp.I * m0 * width),
@@ -117,37 +127,6 @@ def evaluate(self):
117127
return 1 / (m0**2 - s - sp.I * m0 * Γ)
118128

119129

120-
@unevaluated
121-
class EnergyDependentWidth(sp.Expr):
122-
s: Any
123-
m0: Any
124-
Γ0: Any
125-
m1: Any
126-
m2: Any
127-
L: Any
128-
R: Any
129-
130-
def evaluate(self):
131-
s, m0, Γ0, m1, m2, L, R = self.args
132-
ff = FormFactor(s, m1, m2, L, R) ** 2
133-
ff0 = FormFactor(m0**2, m1, m2, L, R) ** 2
134-
return sp.Mul(
135-
Γ0,
136-
m0 / sp.sqrt(s),
137-
ff / ff0,
138-
evaluate=False,
139-
)
140-
141-
def _latex_repr_(self, printer: LatexPrinter) -> str:
142-
s = printer._print(self.s) # pyright:ignore[reportPrivateUsage]
143-
if self.L == 0:
144-
if self.m1 == 0 and self.m2 == 0:
145-
return printer._print(self.Γ0) # pyright:ignore[reportPrivateUsage]
146-
return Rf"\Gamma\left({s}\right)"
147-
L = printer._print(self.L) # pyright:ignore[reportPrivateUsage]
148-
return Rf"\Gamma_{{{L}}}\left({s}\right)"
149-
150-
151130
@unevaluated
152131
class MultichannelBreitWigner(sp.Expr):
153132
s: Any
@@ -194,6 +173,9 @@ class BreitWigner(sp.Expr):
194173
m2: Any = 0
195174
angular_momentum: Any = 0
196175
meson_radius: Any = 1
176+
phsp_factor: PhaseSpaceFactorProtocol = argument( # type:ignore[assignment]
177+
default=PhaseSpaceFactor, sympify=False
178+
)
197179

198180
def evaluate(self):
199181
width = self.energy_dependent_width()
@@ -206,7 +188,7 @@ def energy_dependent_width(self) -> sp.Expr:
206188
s, m0, Γ0, m1, m2, L, d = self.args
207189
if L == 0 and m1 == 0 and m2 == 0:
208190
return Γ0 # type:ignore[return-value]
209-
return EnergyDependentWidth(s, m0, Γ0, m1, m2, L, d)
191+
return EnergyDependentWidth(s, m0, Γ0, m1, m2, L, d, self.phsp_factor)
210192

211193
def _latex_repr_(self, printer: LatexPrinter, *args) -> str:
212194
s = printer._print(self.s)

src/ampform_dpd/dynamics/builder.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import sympy as sp
1212
from ampform.dynamics.form_factor import FormFactor
13+
from ampform.dynamics.phasespace import PhaseSpaceFactor, PhaseSpaceFactorProtocol
1314
from attrs import define
1415

1516
from ampform_dpd import DefinedExpression, create_mass_symbol, to_particle
@@ -22,13 +23,14 @@ class BreitWignerBuilder:
2223
energy_dependent_width: bool = True
2324
decay_form_factor: bool = True
2425
production_form_factor: bool = True
26+
phsp_factor: PhaseSpaceFactorProtocol = PhaseSpaceFactor # type:ignore[assignment]
2527

2628
def __call__(self, decay_chain: ThreeBodyDecayChain) -> DefinedExpression:
2729
"""Formulate a (relativistic) Breit-Wigner for this resonance."""
2830
decay_node = decay_chain.decay_node
2931
s = get_mandelstam_s(decay_node)
3032
if self.energy_dependent_width:
31-
expression = _create_breit_wigner(s, decay_node)
33+
expression = _create_breit_wigner(s, decay_node, self.phsp_factor)
3234
else:
3335
expression = _create_simple_breit_wigner(s, decay_node)
3436
if self.decay_form_factor:
@@ -66,7 +68,9 @@ def _create_form_factor(s: sp.Symbol, isobar: IsobarNode) -> DefinedExpression:
6668
return DefinedExpression(form_factor, parameter_defaults)
6769

6870

69-
def _create_breit_wigner(s: sp.Symbol, isobar: DecayNode) -> DefinedExpression:
71+
def _create_breit_wigner(
72+
s: sp.Symbol, isobar: DecayNode, phsp_factor: PhaseSpaceFactorProtocol
73+
) -> DefinedExpression:
7074
outgoing_state_mass1 = create_mass_symbol(isobar.child1)
7175
outgoing_state_mass2 = create_mass_symbol(isobar.child2)
7276
angular_momentum = _get_angular_momentum(isobar)
@@ -81,6 +85,7 @@ def _create_breit_wigner(s: sp.Symbol, isobar: DecayNode) -> DefinedExpression:
8185
m2=outgoing_state_mass2,
8286
angular_momentum=angular_momentum,
8387
meson_radius=meson_radius,
88+
phsp_factor=phsp_factor,
8489
)
8590
parameter_defaults: dict[sp.Symbol, complex | float] = {
8691
res_mass: isobar.parent.mass,

0 commit comments

Comments
 (0)