Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: implement BreitWigner expression classes #423

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
55 changes: 41 additions & 14 deletions docs/_extend_docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ def extend_BreakupMomentumSquared() -> None:
_append_latex_doit_definition(expr, deep=True)


def extend_BreitWigner() -> None:
from ampform.dynamics import BreitWigner

s, m0, w0, m1, m2, d = sp.symbols("s m0 Gamma0 m1 m2 d", nonnegative=True)
L = sp.Symbol("L", integer=True, nonnegative=True)
expr = BreitWigner(s, m0, w0, m1, m2, angular_momentum=L, meson_radius=d)
_append_latex_doit_definition(expr)


def extend_ComplexSqrt() -> None:
from ampform.sympy.math import ComplexSqrt

Expand Down Expand Up @@ -358,6 +367,30 @@ def extend_is_within_phasespace() -> None:
)


def extend_MultichannelBreitWigner() -> None:
from ampform.dynamics import (
ChannelArguments,
MultichannelBreitWigner,
SimpleBreitWigner,
)

s, m0, w0 = sp.symbols("s m0 Gamma0", nonnegative=True)
channels = [
ChannelArguments(*sp.symbols("Gamma1 m_a1 m_b1 L1 d", nonnegative=True)),
ChannelArguments(*sp.symbols("Gamma2 m_a2 m_b2 L2 d", nonnegative=True)),
]
expr = MultichannelBreitWigner(s, m0, channels=channels)
_append_latex_doit_definition(expr)
bw = SimpleBreitWigner(s, m0, w0)
_append_to_docstring(
MultichannelBreitWigner,
Rf"""
with :math:`{sp.latex(bw)}` defined by Equation :eq:`SimpleBreitWigner`, a
`SimpleBreitWigner`.
""",
)


def extend_PhaseSpaceFactor() -> None:
from ampform.dynamics.phasespace import PhaseSpaceFactor

Expand Down Expand Up @@ -473,6 +506,14 @@ def extend_RotationZMatrix() -> None:
)


def extend_SimpleBreitWigner() -> None:
from ampform.dynamics import SimpleBreitWigner

s, m0, w0 = sp.symbols("s m0 Gamma0", nonnegative=True)
expr = SimpleBreitWigner(s, m0, w0)
_append_latex_doit_definition(expr)


def extend_SphericalHankel1() -> None:
from ampform.dynamics.form_factor import SphericalHankel1

Expand Down Expand Up @@ -604,20 +645,6 @@ def extend_get_boost_chain_suffix() -> None:
)


def extend_relativistic_breit_wigner() -> None:
from ampform.dynamics import relativistic_breit_wigner

s, m0, w0 = sp.symbols("s m0 Gamma0")
rel_bw = relativistic_breit_wigner(s, m0, w0)
_append_to_docstring(
relativistic_breit_wigner,
f"""
.. math:: {sp.latex(rel_bw)}
:label: relativistic_breit_wigner
""",
)


def extend_relativistic_breit_wigner_with_ff() -> None:
from ampform.dynamics import relativistic_breit_wigner_with_ff

Expand Down
6 changes: 4 additions & 2 deletions docs/usage.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@
"source": [
"import sympy as sp\n",
"\n",
"from ampform.dynamics import relativistic_breit_wigner\n",
"from ampform.dynamics import SimpleBreitWigner\n",
"from ampform.io import aslatex\n",
"\n",
"m, m0, w0 = sp.symbols(\"m m0 Gamma0\")\n",
"relativistic_breit_wigner(s=m**2, mass0=m0, gamma0=w0)"
"bw = SimpleBreitWigner(s=m**2, mass=m0, width=w0)\n",
"Math(aslatex({bw: bw.doit(deep=False)}))"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/amplitude.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@
"source": [
"To set dynamics for specific resonances, use {meth}`.DynamicsSelector.assign` on the same {attr}`.HelicityAmplitudeBuilder.dynamics` attribute. You can set the dynamics to be any kind of {class}`~sympy.core.expr.Expr`, as long as you keep track of which {class}`~sympy.core.symbol.Symbol` names you use (see {doc}`/usage/dynamics/custom`).\n",
"\n",
"AmpForm does provide a few common {mod}`.dynamics` functions, which can be constructed as {class}`~sympy.core.expr.Expr` with the correct {class}`~sympy.core.symbol.Symbol` names using {meth}`.DynamicsSelector.assign`. This function takes specific {mod}`.dynamics.builder` functions and classes, such as {class}`.RelativisticBreitWignerBuilder`, which can create {func}`.relativistic_breit_wigner` functions for specific resonances. Here's an example for a relativistic Breit-Wigner _with form factor_ for the intermediate resonances and use a Blatt-Weisskopf barrier factor for the production decay:"
"AmpForm does provide a few common {mod}`.dynamics` functions, which can be constructed as {class}`~sympy.core.expr.Expr` with the correct {class}`~sympy.core.symbol.Symbol` names using {meth}`.DynamicsSelector.assign`. This function takes specific {mod}`.dynamics.builder` functions and classes, such as {class}`.RelativisticBreitWignerBuilder`, which can create {class}`.SimpleBreitWigner` functions for specific resonances. Here's an example for a relativistic Breit-Wigner _with form factor_ for the intermediate resonances and use a Blatt-Weisskopf barrier factor for the production decay:"
]
},
{
Expand Down
172 changes: 154 additions & 18 deletions docs/usage/dynamics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -368,14 +368,14 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Relativistic Breit-Wigner"
"## Relativistic BreitWigner"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"AmpForm has two types of relativistic Breit-Wigner functions. Both are compared below ― for more info, see the links to the API."
"AmpForm has two types of relativistic BreitWigner functions. Both are compared below ― for more info, see the links to the API."
]
},
{
Expand All @@ -391,7 +391,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The 'normal' {func}`.relativistic_breit_wigner` looks as follows:"
"The {class}`.SimpleBreitWigner` looks as follows:"
]
},
{
Expand All @@ -402,11 +402,11 @@
},
"outputs": [],
"source": [
"from ampform.dynamics import relativistic_breit_wigner\n",
"from ampform.dynamics import SimpleBreitWigner\n",
"\n",
"m, m0, w0 = sp.symbols(\"m, m0, Gamma0\", nonnegative=True)\n",
"rel_bw = relativistic_breit_wigner(s=m**2, mass0=m0, gamma0=w0)\n",
"rel_bw"
"bw = SimpleBreitWigner(m**2, m0, w0)\n",
"Math(aslatex({bw: bw.doit(deep=False)}))"
]
},
{
Expand All @@ -420,7 +420,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The relativistic Breit-Wigner can be adapted slightly, so that its amplitude goes to zero at threshold ($m_0 = m1 + m2$) and that it becomes normalizable. This is done with {ref}`form factors <usage/dynamics:Form factor>` and can be obtained with the function {func}`.relativistic_breit_wigner_with_ff`:"
"The relativistic BreitWigner can be adapted slightly, so that its amplitude goes to zero at threshold, $s = \\left(m_1 + m_2\\right)^2$, and that it becomes normalizable. This is done with {ref}`form factors <usage/dynamics:Form factor>` and can be obtained with the function {func}`.relativistic_breit_wigner_with_ff`:"
]
},
{
Expand All @@ -433,7 +433,7 @@
"source": [
"from ampform.dynamics import PhaseSpaceFactorSWave, relativistic_breit_wigner_with_ff\n",
"\n",
"rel_bw_with_ff = relativistic_breit_wigner_with_ff(\n",
"bw_with_ff = relativistic_breit_wigner_with_ff(\n",
" s=s,\n",
" mass0=m0,\n",
" gamma0=w0,\n",
Expand All @@ -443,7 +443,7 @@
" meson_radius=1,\n",
" phsp_factor=PhaseSpaceFactorSWave,\n",
")\n",
"rel_bw_with_ff"
"bw_with_ff"
]
},
{
Expand All @@ -457,16 +457,12 @@
"cell_type": "code",
"execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
},
"tags": []
},
"outputs": [],
"source": [
"from ampform.dynamics import EnergyDependentWidth\n",
"\n",
"L = sp.Symbol(\"L\", integer=True)\n",
"width = EnergyDependentWidth(\n",
" s=s,\n",
" mass0=m0,\n",
Expand All @@ -476,8 +472,34 @@
" angular_momentum=L,\n",
" meson_radius=1,\n",
" phsp_factor=PhaseSpaceFactorSWave,\n",
")\n",
"Math(aslatex({width: width.evaluate()}))"
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"editable": true,
"jupyter": {
"source_hidden": true
},
"slideshow": {
"slide_type": ""
},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"from ampform.dynamics import BreitWigner\n",
"\n",
"exprs = [\n",
" BreitWigner(s, m0, w0, m1, m2, angular_momentum=L, meson_radius=d),\n",
" SimpleBreitWigner(s, m0, w0),\n",
" width,\n",
"]\n",
"Math(aslatex({e: e.doit(deep=False) for e in exprs}))"
]
},
{
Expand All @@ -490,6 +512,110 @@
{
"cell_type": "markdown",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"### Multi-channel Breit–Wigner"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"from ampform.dynamics import ChannelArguments, MultichannelBreitWigner\n",
"\n",
"channels = [\n",
" ChannelArguments(\n",
" width=sp.Symbol(f\"Gamma{i}\"),\n",
" m1=sp.Symbol(f\"m_{{a,{i}}}\"),\n",
" m2=sp.Symbol(f\"m_{{b,{i}}}\"),\n",
" angular_momentum=sp.Symbol(f\"L{i}\"),\n",
" meson_radius=d,\n",
" )\n",
" for i in [1, 2]\n",
"]\n",
"multi_bw = MultichannelBreitWigner(s, m0, channels)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"editable": true,
"jupyter": {
"source_hidden": true
},
"slideshow": {
"slide_type": ""
},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"exprs = [\n",
" multi_bw,\n",
" SimpleBreitWigner(s, m0, w0),\n",
" EnergyDependentWidth(s, m0, w0, m1, m2, L, d),\n",
"]\n",
"Math(aslatex({e: e.doit(deep=False) for e in exprs}))"
]
},
{
"cell_type": "markdown",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"Note that {class}`.MultichannelBreitWigner` simplifies to a Flatté function. This is especially clear for $L=0$:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"editable": true,
"jupyter": {
"source_hidden": true
},
"slideshow": {
"slide_type": ""
},
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"channels = [ChannelArguments(sp.Symbol(f\"Gamma{i}\")) for i in [1, 2]]\n",
"expr = MultichannelBreitWigner(s, m0, channels)\n",
"Math(aslatex({expr: expr.doit().simplify()}))"
]
},
{
"cell_type": "markdown",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
Expand All @@ -498,18 +624,28 @@
},
{
"cell_type": "markdown",
"metadata": {},
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"The following shows the effect of {doc}`/usage/dynamics/analytic-continuation` a on relativistic Breit-Wigner:"
"The following shows the effect of {doc}`/usage/dynamics/analytic-continuation` a on relativistic BreitWigner:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"editable": true,
"jupyter": {
"source_hidden": true
},
"slideshow": {
"slide_type": ""
},
"tags": [
"hide-cell",
"remove-output"
Expand Down Expand Up @@ -552,7 +688,7 @@
")\n",
"np_rel_bw = sp.lambdify(\n",
" args=(m, w0, L, d, m0, m1, m2),\n",
" expr=rel_bw.doit(),\n",
" expr=bw.doit(),\n",
")\n",
"\n",
"# Set sliders\n",
Expand Down
Loading