Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/_extend_docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,9 @@ def extend_relativistic_breit_wigner_with_ff() -> None:
.. math:: {sp.latex(rel_bw_with_ff)}
:label: relativistic_breit_wigner_with_ff

where :math:`\Gamma(s)` is defined by :eq:`EnergyDependentWidth`, :math:`B_L^2` is
defined by :eq:`BlattWeisskopfSquared`, and :math:`q^2` is defined by
:eq:`BreakupMomentumSquared`.
where :math:`\Gamma(s)` is defined by :eq:`EnergyDependentWidth`,
:math:`\hat{{B}}_L^2` is defined by :eq:`BlattWeisskopfSquared`, and :math:`q^2` is
defined by :eq:`BreakupMomentumSquared`.
""",
)

Expand Down
2 changes: 1 addition & 1 deletion docs/analyticity/integration-algorithms.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
" \\frac\n",
" {\\rho\\!\\left(s'\\right) n_\\ell^2\\!\\left(s'\\right) ds'}\n",
" {\\left(s' - s_\\mathrm{thr}\\right) \\left(s'- s\\right)} \\\\\n",
"n_\\ell^2(s') \\;&=\\; \\mathcal{F}_\\ell^2\\!\\left(s', m_1, m_2\\right) \\\\\n",
"n_\\ell^2(s') \\;&=\\; \\hat{\\mathcal{F}}_\\ell^2\\!\\left(s', m_1, m_2\\right) \\\\\n",
"s_\\mathrm{thr} \\;&=\\; (m_1 + m_2)^2\n",
"\\end{aligned}\n",
"$$\n",
Expand Down
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ def _get_dataclasses(module):
"sphinx_comments",
"sphinx_copybutton",
"sphinx_design",
"sphinx_hep_pdgref",
"sphinx_pybtex_etal_style",
"sphinx_thebe",
"sphinx_togglebutton",
Expand Down
6 changes: 3 additions & 3 deletions docs/dynamics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"AmpForm uses Blatt–Weisskopf functions $B_L$ as _barrier factors_ (also called _form factors_, see {class}`.BlattWeisskopfSquared` and **[TR-029](https://compwa.github.io/report/029)**):"
"AmpForm uses Blatt–Weisskopf functions $\\hat{B}_L$ as _barrier factors_ (also called _form factors_, see {class}`.BlattWeisskopfSquared` and **[TR-029](https://compwa.github.io/report/029)**). The hat indicates that they are normalized to $\\hat{B}_L(1)=1$:"
]
},
{
Expand Down Expand Up @@ -151,7 +151,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The Blatt–Weisskopf form factor is used to 'dampen' the breakup-momentum at threshold and when going to infinity. A usual choice for $z$ is therefore $z=q^2d^2$ with $q^2$ the {class}`.BreakupMomentumSquared` and $d$ the impact parameter (also called meson radius). The {class}`.FormFactor` expression class can be used for this:"
"The Blatt–Weisskopf form factor is used to 'dampen' the breakup-momentum at threshold and when going to infinity. A usual choice for $z$ is therefore $z=q^2d^2$ with $q^2$ the {class}`.BreakupMomentumSquared` and $d$ the impact parameter (also called meson radius). The {class}`.FormFactor` expression class can be used for this. Set `normalize=False` to omit the normalization constant $\\left|h_L^{(1)}(1)\\right|$, which gives the non-normalized vertex factor $n_L$ of Equation (50.33) in the [PDG review on resonances](https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12):"
]
},
{
Expand Down Expand Up @@ -692,7 +692,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.13"
"version": "3.13.15"
}
},
"nbformat": 4,
Expand Down
12 changes: 8 additions & 4 deletions docs/dynamics/k-matrix.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"While {mod}`ampform` does not yet provide a generic way to formulate an amplitude model with $\\boldsymbol{K}$-matrix dynamics, the (experimental) {mod}`.kmatrix` module makes it fairly simple to produce a symbolic expression for a parameterized $\\boldsymbol{K}$-matrix with an arbitrary number of poles and channels and play around with it interactively. For more info on the $\\boldsymbol{K}$-matrix, see the classic paper by Chung {cite}`Chung:1995dx`, {pdg-review}`2021; Resonances`, or this instructive presentation {cite}`Meyer:2008-MatrixTutorial`.\n",
"While {mod}`ampform` does not yet provide a generic way to formulate an amplitude model with $\\boldsymbol{K}$-matrix dynamics, the (experimental) {mod}`.kmatrix` module makes it fairly simple to produce a symbolic expression for a parameterized $\\boldsymbol{K}$-matrix with an arbitrary number of poles and channels and play around with it interactively. For more info on the $\\boldsymbol{K}$-matrix, see the classic paper by Chung {cite}`Chung:1995dx`, [PDG2026, §Resonances, p.14](https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=14), or this instructive presentation {cite}`Meyer:2008-MatrixTutorial`.\n",
"\n",
"Section {ref}`dynamics/k-matrix:Physics` summarizes {cite}`Chung:1995dx`, so that the {mod}`.kmatrix` module can reference to the equations. It also points out some subtleties and deviations.\n",
"\n",
Expand Down Expand Up @@ -415,7 +415,11 @@
"\n",
"with $\\gamma_{R,i}$ some _real_ constants and $\\Gamma^0_{R,i}$ the **partial width** of each pole. In the Lorentz-invariant form, the fixed width $\\Gamma^0$ is replaced by an \"energy dependent\" {class}`.EnergyDependentWidth` $\\Gamma(s)$.[^phase-space-factor-normalization] The **width** for each pole can be computed as $\\Gamma^0_R = \\sum_i\\Gamma^0_{R,i}$.\n",
"\n",
"[^phase-space-factor-normalization]: Unlike Eq. (77) in {cite}`Chung:1995dx`, AmpForm defines {class}`.EnergyDependentWidth` as in {pdg-review}`2021; Resonances; p.6`, Eq. (50.28). The difference is that the phase space factor denoted by $\\rho_i$ in Eq. (77) in {cite}`Chung:1995dx` is divided by the phase space factor at the pole position $m_R$. So in AmpForm, the choice is $\\rho_i \\to \\frac{\\rho_i(s)}{\\rho_i(m_R)}$."
":::{warning}\n",
"Eq. (50.28) no longer appears in [PDG2026, §Resonances, p.12](https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12). The width is now defined in terms of bare couplings, $\\Gamma_b(s) = g_b^2\\rho_b(s)n_b^2(s)/m_\\mathrm{BW}$ (Eq. (50.32)), and Eq. (50.35) trades $g_b$ for the partial width $\\Gamma_{\\mathrm{BW},b}$. Combining the two gives the old Eq. (50.28), but the PDG stresses that this substitution is only valid for narrow resonances with all channel thresholds below $m_\\mathrm{BW}$.\n",
":::\n",
"\n",
"[^phase-space-factor-normalization]: Unlike Eq. (77) in {cite}`Chung:1995dx`, AmpForm defines {class}`.EnergyDependentWidth` as in [PDG2021, Eq. (50.28)](https://pdg.lbl.gov/2021/reviews/rpp2021-rev-resonances.pdf#page=9). The difference is that the phase space factor denoted by $\\rho_i$ in Eq. (77) in {cite}`Chung:1995dx` is divided by the phase space factor at the pole position $m_R$. So in AmpForm, the choice is $\\rho_i \\to \\frac{\\rho_i(s)}{\\rho_i(m_R)}$."
]
},
{
Expand All @@ -441,7 +445,7 @@
"\n",
"with $B_{R,i}(q(s))$ the **centrifugal damping factor** (see {class}`.FormFactor` and {class}`.BlattWeisskopfSquared`) for channel $i$ and $\\beta_R^0$ some (generally complex) constants that describe the production information of the decaying state $R$. Usually, these constants are rescaled just like the residue functions in {eq}`residue-function`:\n",
"\n",
"[^damping-factor-P-parametrization]: Just as with [^phase-space-factor-normalization], we have smuggled a bit in the last equation in order to be able to reproduce Equation (50.23) in {pdg-review}`2021; Resonances; p.9` in the case $n=1,n_R=1$, on which {func}`.relativistic_breit_wigner_with_ff` is based.\n",
"[^damping-factor-P-parametrization]: Just as with [^phase-space-factor-normalization], we have smuggled a bit in the last equation in order to be able to reproduce [PDG2026, Eq. (50.37)](https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=13) in the case $n=1,n_R=1$, on which {func}`.relativistic_breit_wigner_with_ff` is based.\n",
"\n",
"```{margin}\n",
"{cite}`Chung:1995dx` Eq. (121)\n",
Expand Down Expand Up @@ -1332,7 +1336,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"[^pole-vs-resonance]: See {pdg-review}`2021; Resonances`, Section 50.1, for a discussion about what poles and resonances are. See also the intro to Section 5 in {cite}`Chung:1995dx`."
"[^pole-vs-resonance]: See [PDG2026, §50.1](https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=1) for a discussion about what poles and resonances are. See also the intro to Section 5 in {cite}`Chung:1995dx`."
]
},
{
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ doc = [
"sphinx-comments",
"sphinx-copybutton",
"sphinx-design",
"sphinx-hep-pdgref",
"sphinx-pybtex-etal-style",
"sphinx-thebe",
"sphinx-togglebutton",
Expand Down
92 changes: 77 additions & 15 deletions src/ampform/dynamics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Literal
from warnings import warn

import sympy as sp
Expand Down Expand Up @@ -35,24 +35,47 @@

@unevaluated
class SimpleBreitWigner(sp.Expr):
r"""Simple Breit–Wigner with :math:`m_0 \Gamma_0` in the numerator."""
r"""Simple Breit–Wigner with a configurable numerator.

With the default ``numerator="mass-width"``, the propagator is multiplied by
:math:`m_0 \Gamma_0`, so that
:math:`\left|\hat{\mathcal{R}}^\mathrm{BW}(m_0^2)\right| = 1`. Set
``numerator="unity"`` for the dressed propagator of `PDG2026, Eq. (50.31)
<https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12>`__, which is
also the convention of `MultichannelBreitWigner`.
"""

s: Any
mass: Any
width: Any
_latex_repr_ = R"\mathcal{{R}}^\mathrm{{BW}}\left({s}; {mass}, {width}\right)"
numerator: Literal["mass-width", "unity"] = argument(
default="mass-width", kw_only=True, sympify=False
)

def evaluate(self):
s, m0, w0 = self.args
return m0 * w0 * _formulate_breit_wigner(s, m0, w0)
numerator = _formulate_numerator(self.numerator, m0, w0)
return numerator * _formulate_breit_wigner(s, m0, w0)

def _latex_repr_(self, printer: LatexPrinter, *args) -> str:
s, mass, width = map(printer._print, self.args)
function_symbol = _get_breit_wigner_symbol(self.numerator)
return Rf"{function_symbol}\left({s}; {mass}, {width}\right)"


@unevaluated
class BreitWigner(sp.Expr):
r"""Relativistic Breit–Wigner with :math:`m_0 \Gamma_0` in the numerator.

Uses an `EnergyDependentWidth` in the denominator (see Equations
:eq:`BreitWigner` and :eq:`EnergyDependentWidth`).
r"""Relativistic Breit–Wigner with a configurable numerator.

Uses an `EnergyDependentWidth` in the denominator (see Equations :eq:`BreitWigner`
and :eq:`EnergyDependentWidth`). With the default ``numerator="mass-width"``, the
propagator is multiplied by :math:`m_0 \Gamma_0`, so that
:math:`\left|\hat{\mathcal{R}}^\mathrm{BW}(m_0^2)\right| = 1`, because
:math:`\Gamma(m_0^2) = \Gamma_0`. Set ``numerator="unity"`` for the dressed
propagator of `PDG2026, Eq. (50.31)
<https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12>`__, which is
also the convention of `MultichannelBreitWigner`. The numerator does not affect the
`.FormFactor` inside the `EnergyDependentWidth`, where its normalization cancels.
"""

s: Any
Expand All @@ -65,12 +88,14 @@ class BreitWigner(sp.Expr):
phsp_factor: PhaseSpaceFactorProtocol = argument(
default=PhaseSpaceFactor, sympify=False
) # ty: ignore[invalid-assignment]
numerator: Literal["mass-width", "unity"] = argument(
default="mass-width", kw_only=True, sympify=False
)

def evaluate(self):
width = self.energy_dependent_width()
return (
self.mass * self.width * _formulate_breit_wigner(self.s, self.mass, width)
)
numerator = _formulate_numerator(self.numerator, self.mass, self.width)
return numerator * _formulate_breit_wigner(self.s, self.mass, width)

def energy_dependent_width(self) -> EnergyDependentWidth | sp.Basic:
s, m0, w0, m1, m2, ang_mom, d = self.args
Expand All @@ -80,7 +105,7 @@ def energy_dependent_width(self) -> EnergyDependentWidth | sp.Basic:

def _latex_repr_(self, printer: LatexPrinter, *args) -> str:
s = printer._print(self.s)
function_symbol = R"\mathcal{R}^\mathrm{BW}"
function_symbol = _get_breit_wigner_symbol(self.numerator)
mass = printer._print(self.mass)
width = printer._print(self.width)
arg = Rf"\left({s}; {mass}, {width}\right)"
Expand All @@ -94,10 +119,20 @@ def _latex_repr_(self, printer: LatexPrinter, *args) -> str:
class EnergyDependentWidth(sp.Expr):
r"""Mass-dependent width, coupled to the pole position of the resonance.

See Equation (50.28) in :pdg-review:`2021; Resonances; p.9` and
See `PDG2021, Eq. (50.28)
<https://pdg.lbl.gov/2021/reviews/rpp2021-rev-resonances.pdf#page=9>`__ and
:cite:`ParticleDataGroup:2012pjm`, equation (6). Default value for
:code:`phsp_factor` is `.PhaseSpaceFactor`.

.. warning:: Equation (50.28) no longer appears in
`PDG2026, §Resonances, p.12 <https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12>`__.
The width is now defined in terms of bare couplings, :math:`\Gamma_b(s) =
g_b^2 \rho_b(s) n_b^2(s) / m_\mathrm{BW}` (Equation (50.32)), and Equation (50.35)
trades :math:`g_b` for the partial width :math:`\Gamma_{\mathrm{BW},b}`.
Combining the two gives the old Equation (50.28), but the PDG stresses that this
substitution is only valid for narrow resonances with all channel thresholds
below :math:`m_\mathrm{BW}`.

Note that the `.FormFactor` of AmpForm is normalized in the sense that equal powers
of :math:`z` appear in the nominator and the denominator, while the definition in
the PDG (as well as some other sources), always have :math:`1` in the nominator of
Expand Down Expand Up @@ -144,7 +179,9 @@ class MultichannelBreitWigner(sp.Expr):

where :math:`g_i^2` is the coupling squared, :math:`\rho_i` is a
`.PhaseSpaceFactor`, and :math:`F_{L_i}` is a `.FormFactor`. Unlike an
`EnergyDependentWidth`, a channel term is not normalized at the pole position.
`EnergyDependentWidth`, a channel term is not normalized at the pole position. See
`PDG2026, Eqs. (50.31) and (50.32)
<https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12>`__.
"""

s: Any
Expand Down Expand Up @@ -174,6 +211,9 @@ class ChannelArguments(sp.Expr):
.. math::

\Gamma_i^\text{ch}(s) = \frac{g_i^2}{m_0} \rho_i(s) F_{L_i}^2(s)

See `PDG2026, Eq. (50.32)
<https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12>`__.
"""

s: Any
Expand All @@ -196,6 +236,27 @@ def _formulate_breit_wigner(s: Any, mass: Any, width: Any) -> sp.Expr:
return 1 / (mass**2 - s - sp.I * mass * width)


def _formulate_numerator(numerator: str, mass: Any, width: Any) -> Any:
_check_numerator(numerator)
if numerator == "mass-width":
return mass * width
return sp.S.One


def _get_breit_wigner_symbol(numerator: str) -> str:
_check_numerator(numerator)
if numerator == "mass-width":
return R"\hat{\mathcal{R}}^\mathrm{BW}"
return R"\mathcal{R}^\mathrm{BW}"


def _check_numerator(numerator: str) -> None:
allowed = ("mass-width", "unity")
if numerator not in allowed:
msg = f"Unknown numerator {numerator!r}, expected one of {', '.join(map(repr, allowed))}"
raise ValueError(msg)


def relativistic_breit_wigner(s, mass0, gamma0) -> sp.Expr:
"""Relativistic Breit–Wigner lineshape.

Expand All @@ -220,7 +281,8 @@ def relativistic_breit_wigner_with_ff( # ruff: ignore[too-many-positional-argum
) -> sp.Expr:
"""Relativistic Breit–Wigner with `.FormFactor`.

See :ref:`dynamics:_With_ form factor` and :pdg-review:`2021; Resonances; p.9`.
See :ref:`dynamics:_With_ form factor` and `PDG2026, §Resonances, p.12
<https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12>`__.
"""
ff = FormFactor(s, m_a, m_b, angular_momentum, meson_radius)
bw = BreitWigner(
Expand Down
5 changes: 3 additions & 2 deletions src/ampform/dynamics/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ class RelativisticBreitWignerBuilder:

Args:
form_factor: Formulate a relativistic Breit–Wigner function multiplied
by a Blatt–Weisskopf form factor (`.FormFactor`), like in Equation (50.26)
on :pdg-review:`2021; Resonances; p.9`.
by a Blatt–Weisskopf form factor (`.FormFactor`), like in `PDG2026, Eqs.
(50.33) and (50.37)
<https://pdg.lbl.gov/2026/reviews/rpp2026-rev-resonances.pdf#page=12>`__.
energy_dependent_width: Use an `.EnergyDependentWidth` in the
denominator of the Breit–Wigner.
phsp_factor: A class that complies with the
Expand Down
Loading
Loading