Skip to content

Commit a7caba3

Browse files
authored
BREAK: convert chew_manelstam_s_wave() to expression class (#493)
* DOC: disable full-screen button on website * DOC: improve phase space expressions in API * DOC: update `README` * DOC: use en-dash in names * DX: make `_get_subscript()` positional only * ENH: let `name` attribute overwrite function LaTeX * FIX: remove redundant `typings/` directory * MAINT: reduce number of declared variables * MAINT: remove `pyright` comments
1 parent 90f467f commit a7caba3

22 files changed

Lines changed: 255 additions & 319 deletions

.cspell.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
"CITATION.cff",
3636
"codecov.yml",
3737
"docs/conf.py",
38-
"pyproject.toml",
39-
"typings"
38+
"pyproject.toml"
4039
],
4140
"ignoreWords": [
4241
"Autoupdate",

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,16 @@ an overview of **upcoming releases and planned functionality**, see
4343
- [Helicity formalism](https://ampform.readthedocs.io/en/stable/usage/helicity/formalism.html)
4444
- Canonical formalism
4545
- [Spin alignment](https://ampform.readthedocs.io/en/stable/usage/helicity/spin-alignment.html)
46-
for generic, multi-body decays that feature different decay topologies
46+
for generic, multi-body decays that feature different decay topologies. **See also [`ampform-dpd`](https://github.com/ComPWA/ampform-dpd)!**
4747
- **Dynamics**
48-
- [Relativistic Breit-Wigner](https://ampform.readthedocs.io/en/stable/api/ampform.dynamics.html#ampform.dynamics.relativistic_breit_wigner_with_ff),
48+
- [Relativistic BreitWigner](https://ampform.readthedocs.io/en/stable/api/ampform.dynamics.html#ampform.dynamics.relativistic_breit_wigner_with_ff),
4949
optionally with form factors and/or
5050
[energy-dependent width](https://ampform.readthedocs.io/en/stable/api/ampform.dynamics.html#ampform.dynamics.EnergyDependentWidth)
5151
- [Symbolic _K_-matrix](https://ampform.readthedocs.io/en/stable/usage/dynamics/k-matrix.html#non-relativistic-k-matrix)
5252
for an arbitrary number of poles and channels
5353
- [Symbolic _P_-vector](https://ampform.readthedocs.io/en/stable/usage/dynamics/k-matrix.html#p-vector)
5454
for an arbitrary number of poles and channels
55+
- Chew–Mandelstam dispersion integral for a correct treatment of analyticity in the presence of thresholds.
5556

5657
## Contribute
5758

docs/_extend_docstrings.py

Lines changed: 112 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@
1111
import textwrap
1212
from importlib.metadata import version as get_package_version
1313
from pathlib import Path
14-
from typing import TYPE_CHECKING
14+
from typing import TYPE_CHECKING, cast
1515

1616
import attrs
1717
import graphviz # sphinx.ext.graphviz does not work well on RTD
1818
import qrules
1919
import sympy as sp
2020
from sympy.printing.numpy import NumPyPrinter
2121

22+
from ampform.dynamics.form_factor import FormFactor
2223
from ampform.dynamics.phasespace import (
24+
ChewMandelstamIntegral,
25+
ChewMandelstamSWave,
26+
EqualMassPhaseSpaceFactor,
2327
PhaseSpaceFactor,
28+
PhaseSpaceFactorAbs,
29+
PhaseSpaceFactorComplex,
2430
PhaseSpaceFactorKallen,
31+
PhaseSpaceFactorPWave,
2532
PhaseSpaceFactorSplitSqrt,
2633
PhaseSpaceFactorSWave,
2734
)
@@ -33,9 +40,11 @@
3340
BreakupMomentumKallen,
3441
BreakupMomentumSplitSqrt,
3542
BreakupMomentumSquared,
43+
Kallen,
3644
)
3745
from ampform.sympy._array_expressions import ArrayMultiplication
3846
from ampform.sympy._cache import get_readable_hash, make_hashable
47+
from ampform.sympy.math import ComplexSqrt
3948

4049
if TYPE_CHECKING:
4150
from collections.abc import Callable
@@ -49,15 +58,107 @@
4958

5059

5160
def append_phsp_doit(
52-
phsp_factor: PhaseSpaceFactorProtocol,
53-
wide: bool = True,
54-
) -> Callable[[], None]:
55-
def _extend() -> None:
56-
s, m1, m2 = sp.symbols("s m1 m2", nonnegative=True)
57-
expr = phsp_factor(s, m1, m2)
58-
_append_latex_doit_definition(expr, full_width=wide)
61+
_phsp_factor: PhaseSpaceFactorProtocol,
62+
*,
63+
extra_args: list[sp.Symbol] | None = None,
64+
subexpression: list[sp.Expr] | sp.Expr | None = None,
65+
wide: bool = False,
66+
) -> None:
67+
expr = _phsp_factor(s, m1, m2, *(extra_args or []))
68+
_append_latex_doit_definition(expr, full_width=wide)
69+
if subexpression is not None:
70+
if not isinstance(subexpression, list):
71+
subexpression = [subexpression]
72+
items = []
73+
for expr in subexpression:
74+
expression_type = f"{type(expr).__name__}"
75+
items.append(f":math:`{sp.latex(expr)}` defined by `.{expression_type}`")
76+
match len(items):
77+
case 1:
78+
appended_text = items[0]
79+
case 2:
80+
appended_text = f"{items[0]} and {items[1]}"
81+
case _:
82+
appended_text = ", ".join(items[:-1]) + f", and {items[-1]}"
83+
_append_to_docstring(_phsp_factor, f"\n\nwith {appended_text}.")
84+
85+
86+
def _append_latex_doit_definition(
87+
expr: sp.Expr,
88+
deep: bool = False,
89+
full_width: bool = False,
90+
inline: bool = False,
91+
) -> None:
92+
if inline:
93+
return _append_to_docstring(
94+
type(expr),
95+
f":math:`{sp.latex(expr)}={sp.latex(expr.doit(deep=deep))}`",
96+
)
97+
latex = _create_latex_doit_definition(expr, deep)
98+
extras = ""
99+
if full_width:
100+
extras = """
101+
:class: full-width
102+
"""
103+
return _append_to_docstring(
104+
type(expr),
105+
f"""\n
106+
.. math::
107+
:label: {type(expr).__name__}{extras}
108+
109+
{latex}
110+
""",
111+
)
112+
113+
114+
def _append_to_docstring(class_type: Callable | type, appended_text: str) -> None:
115+
appended_text = textwrap.dedent(appended_text)
116+
if class_type.__doc__ is None:
117+
class_type.__doc__ = appended_text
118+
else:
119+
class_type.__doc__ += appended_text
59120

60-
return _extend
121+
122+
def _create_latex_doit_definition(expr: sp.Expr, deep: bool = False) -> str:
123+
latex = aslatex({expr: expr.doit(deep=deep)})
124+
return textwrap.indent(latex, prefix=8 * " ")
125+
126+
127+
s, m1, m2, x, y, z = cast("list[sp.Symbol]", sp.symbols("s m1 m2 x:z"))
128+
ell = sp.Symbol("ell", integer=True, nonnegative=True)
129+
append_phsp_doit(BreakupMomentum)
130+
append_phsp_doit(BreakupMomentumComplex, subexpression=ComplexSqrt(x))
131+
append_phsp_doit(BreakupMomentumKallen, subexpression=Kallen(x, y, z))
132+
append_phsp_doit(BreakupMomentumSplitSqrt)
133+
append_phsp_doit(BreakupMomentumSquared)
134+
append_phsp_doit(
135+
EqualMassPhaseSpaceFactor,
136+
subexpression=PhaseSpaceFactorAbs(s, m1, m2),
137+
)
138+
append_phsp_doit(PhaseSpaceFactor)
139+
append_phsp_doit(PhaseSpaceFactorAbs, subexpression=BreakupMomentumSquared(s, m1, m2))
140+
append_phsp_doit(PhaseSpaceFactorComplex, subexpression=ComplexSqrt(x))
141+
append_phsp_doit(PhaseSpaceFactorKallen, subexpression=Kallen(x, y, z))
142+
append_phsp_doit(PhaseSpaceFactorSplitSqrt)
143+
append_phsp_doit(
144+
ChewMandelstamSWave,
145+
subexpression=BreakupMomentumComplex(s, m1, m2),
146+
wide=True,
147+
)
148+
append_phsp_doit(
149+
ChewMandelstamIntegral,
150+
extra_args=[ell],
151+
subexpression=[FormFactor(x, m1, m2, ell), PhaseSpaceFactor(x, m1, m2)],
152+
wide=True,
153+
)
154+
append_phsp_doit(
155+
PhaseSpaceFactorSWave,
156+
subexpression=ChewMandelstamSWave(s, m1, m2),
157+
)
158+
append_phsp_doit(
159+
PhaseSpaceFactorPWave,
160+
subexpression=ChewMandelstamIntegral(s, m1, m2, ell),
161+
)
61162

62163

63164
def extend_docstrings() -> None:
@@ -83,17 +184,6 @@ def extend_docstrings() -> None:
83184
definition()
84185

85186

86-
extend_BreakupMomentum = append_phsp_doit(BreakupMomentum)
87-
extend_BreakupMomentumComplex = append_phsp_doit(BreakupMomentumComplex)
88-
extend_BreakupMomentumKallen = append_phsp_doit(BreakupMomentumKallen)
89-
extend_BreakupMomentumSplitSqrt = append_phsp_doit(BreakupMomentumSplitSqrt)
90-
extend_BreakupMomentumSquared = append_phsp_doit(BreakupMomentumSquared)
91-
extend_PhaseSpaceFactor = append_phsp_doit(PhaseSpaceFactor)
92-
extend_PhaseSpaceFactorKallen = append_phsp_doit(PhaseSpaceFactorKallen)
93-
extend_PhaseSpaceFactorSplitSqrt = append_phsp_doit(PhaseSpaceFactorSplitSqrt)
94-
extend_PhaseSpaceFactorSWave = append_phsp_doit(PhaseSpaceFactorSWave, wide=True)
95-
96-
97187
def extend_BlattWeisskopfSquared() -> None:
98188
from ampform.dynamics.form_factor import BlattWeisskopfSquared, SphericalHankel1
99189

@@ -205,17 +295,7 @@ def extend_BoostZMatrix() -> None:
205295
)
206296

207297

208-
def extend_ChewMandelstamIntegral() -> None:
209-
from ampform.dynamics.phasespace import ChewMandelstamIntegral
210-
211-
s, m_a, m_b, ell = sp.symbols(R"s m_a m_b \ell")
212-
expr = ChewMandelstamIntegral(s, m_a, m_b, ell)
213-
_append_latex_doit_definition(expr)
214-
215-
216298
def extend_ComplexSqrt() -> None:
217-
from ampform.sympy.math import ComplexSqrt
218-
219299
x = sp.Symbol("x", real=True)
220300
expr = ComplexSqrt(x)
221301
_append_to_docstring(
@@ -245,25 +325,6 @@ def extend_compute_third_mandelstam() -> None:
245325
)
246326

247327

248-
def extend_EqualMassPhaseSpaceFactor() -> None:
249-
from ampform.dynamics.phasespace import (
250-
EqualMassPhaseSpaceFactor,
251-
PhaseSpaceFactorAbs,
252-
)
253-
254-
s, m_a, m_b = sp.symbols(R"s, m_a, m_b")
255-
expr = EqualMassPhaseSpaceFactor(s, m_a, m_b)
256-
_append_latex_doit_definition(expr)
257-
rho_hat = PhaseSpaceFactorAbs(s, m_a, m_b)
258-
_append_to_docstring(
259-
EqualMassPhaseSpaceFactor,
260-
f"""
261-
with :math:`{sp.latex(rho_hat)}` defined by `.PhaseSpaceFactorAbs`
262-
:eq:`PhaseSpaceFactorAbs`.
263-
""",
264-
)
265-
266-
267328
def extend_EnergyDependentWidth() -> None:
268329
from ampform.dynamics import EnergyDependentWidth
269330

@@ -275,8 +336,7 @@ def extend_EnergyDependentWidth() -> None:
275336
""",
276337
)
277338
L = sp.Symbol("L", integer=True)
278-
symbols: tuple[sp.Symbol, ...] = sp.symbols("s m0 Gamma0 m_a m_b")
279-
s, m0, w0, m_a, m_b = symbols
339+
s, m0, w0, m_a, m_b = sp.symbols("s m0 Gamma0 m_a m_b")
280340
expr = EnergyDependentWidth(
281341
s=s,
282342
mass0=m0,
@@ -328,15 +388,12 @@ def extend_EuclideanNorm() -> None:
328388

329389

330390
def extend_FormFactor() -> None:
331-
from ampform.dynamics.form_factor import FormFactor
332-
333391
s, m_a, m_b, L, d = sp.symbols("s m_a m_b L d")
334392
form_factor = FormFactor(s, m_a, m_b, angular_momentum=L, meson_radius=d)
335393
_append_latex_doit_definition(form_factor)
336394

337395

338396
def extend_Kallen() -> None:
339-
from ampform.kinematics.phasespace import Kallen
340397

341398
x, y, z = sp.symbols("x:z")
342399
expr = Kallen(x, y, z)
@@ -390,34 +447,6 @@ def extend_is_within_phasespace() -> None:
390447
)
391448

392449

393-
def extend_PhaseSpaceFactorAbs() -> None:
394-
from ampform.dynamics.phasespace import PhaseSpaceFactorAbs
395-
396-
s, m_a, m_b = sp.symbols("s, m_a, m_b")
397-
expr = PhaseSpaceFactorAbs(s, m_a, m_b)
398-
_append_latex_doit_definition(expr)
399-
_append_to_docstring(
400-
PhaseSpaceFactorAbs,
401-
"""
402-
with :math:`q^2(s)` defined as :eq:`BreakupMomentumSquared`.
403-
""",
404-
)
405-
406-
407-
def extend_PhaseSpaceFactorComplex() -> None:
408-
from ampform.dynamics.phasespace import PhaseSpaceFactorComplex
409-
410-
s, m_a, m_b = sp.symbols("s, m_a, m_b")
411-
expr = PhaseSpaceFactorComplex(s, m_a, m_b)
412-
_append_latex_doit_definition(expr)
413-
_append_to_docstring(
414-
PhaseSpaceFactorComplex,
415-
"""
416-
with :math:`q^2(s)` defined as :eq:`BreakupMomentumSquared`.
417-
""",
418-
)
419-
420-
421450
def extend_Phi() -> None:
422451
from ampform.kinematics.angles import Phi
423452

@@ -510,26 +539,6 @@ def extend_ThreeMomentum() -> None:
510539
_append_code_rendering(expr)
511540

512541

513-
def extend_chew_mandelstam_s_wave() -> None:
514-
from ampform.dynamics.phasespace import chew_mandelstam_s_wave
515-
516-
s, m_a, m_b = sp.symbols("s m_a m_b")
517-
expr = chew_mandelstam_s_wave(s, m_a, m_b)
518-
_append_to_docstring(
519-
chew_mandelstam_s_wave,
520-
Rf"""
521-
522-
.. math:: {sp.latex(expr)}
523-
:class: full-width
524-
:label: chew_mandelstam_s_wave
525-
526-
with :math:`q^2(s)` defined as :eq:`BreakupMomentumSquared`.
527-
528-
.. seealso:: :doc:`compwa-report:003/index`
529-
""",
530-
)
531-
532-
533542
def extend_formulate_isobar_cg_coefficients() -> None:
534543
from ampform.helicity import formulate_isobar_cg_coefficients
535544

@@ -645,7 +654,7 @@ def extend_relativistic_breit_wigner_with_ff() -> None:
645654
_append_to_docstring(
646655
relativistic_breit_wigner_with_ff,
647656
Rf"""
648-
The general form of a relativistic Breit-Wigner with Blatt-Weisskopf form factor is:
657+
The general form of a relativistic BreitWigner with BlattWeisskopf form factor is:
649658
650659
.. math:: {sp.latex(rel_bw_with_ff)}
651660
:label: relativistic_breit_wigner_with_ff
@@ -696,47 +705,6 @@ def __get_text_width(text: str) -> int:
696705
return max(widths)
697706

698707

699-
def _append_latex_doit_definition(
700-
expr: sp.Expr,
701-
deep: bool = False,
702-
full_width: bool = False,
703-
inline: bool = False,
704-
) -> None:
705-
if inline:
706-
return _append_to_docstring(
707-
type(expr),
708-
f":math:`{sp.latex(expr)}={sp.latex(expr.doit(deep=deep))}`",
709-
)
710-
latex = _create_latex_doit_definition(expr, deep)
711-
extras = ""
712-
if full_width:
713-
extras = """
714-
:class: full-width
715-
"""
716-
return _append_to_docstring(
717-
type(expr),
718-
f"""\n
719-
.. math::
720-
:label: {type(expr).__name__}{extras}
721-
722-
{latex}
723-
""",
724-
)
725-
726-
727-
def _create_latex_doit_definition(expr: sp.Expr, deep: bool = False) -> str:
728-
latex = aslatex({expr: expr.doit(deep=deep)})
729-
return textwrap.indent(latex, prefix=8 * " ")
730-
731-
732-
def _append_to_docstring(class_type: Callable | type, appended_text: str) -> None:
733-
appended_text = textwrap.dedent(appended_text)
734-
if class_type.__doc__ is None:
735-
class_type.__doc__ = appended_text
736-
else:
737-
class_type.__doc__ += appended_text
738-
739-
740708
def __generate_transitions_cached(
741709
initial_state: list[tuple[str, list[float]] | str],
742710
final_state: list[tuple[str, list[float]] | str],

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ def _get_scipy_url() -> str:
275275
"show_toc_level": 2,
276276
"use_download_button": False,
277277
"use_edit_page_button": True,
278+
"use_fullscreen_button": False,
278279
"use_issues_button": True,
279280
"use_repository_button": True,
280281
"use_source_button": True,

0 commit comments

Comments
 (0)