From 5e04a05043da4df3841b9048c0e5b69e0f62bed6 Mon Sep 17 00:00:00 2001 From: Remco de Boer Date: Wed, 14 Jul 2021 14:06:27 +0200 Subject: [PATCH] docs!: describe helicity vs canonical (#95) * build: add rich to doc requirements * ci: upgrade pinned requirements (automatic) * docs: add spin formalism papers to bibliography Co-authored-by: GitHub Action <41898282+github-actions[bot]@users.noreply.github.com> --- .constraints/py3.6.txt | 18 +- .constraints/py3.7.txt | 18 +- .constraints/py3.8.txt | 18 +- .constraints/py3.9.txt | 18 +- .cspell.json | 4 + docs/bibliography.bib | 26 +++ docs/usage.md | 1 + docs/usage/formalism.ipynb | 358 +++++++++++++++++++++++++++++++++++++ setup.cfg | 1 + 9 files changed, 434 insertions(+), 28 deletions(-) create mode 100644 docs/usage/formalism.ipynb diff --git a/.constraints/py3.6.txt b/.constraints/py3.6.txt index 5e71e1899..4075796db 100644 --- a/.constraints/py3.6.txt +++ b/.constraints/py3.6.txt @@ -14,15 +14,17 @@ async-generator==1.10 attrs==20.3.0 babel==2.9.1 backcall==0.2.0 +backports.entry-points-selectable==1.1.0 beautifulsoup4==4.9.3 black==21.6b0 bleach==3.3.0 certifi==2021.5.30 cffi==1.14.6 cfgv==3.3.0 -chardet==4.0.0 +charset-normalizer==2.0.1 click==8.0.1 colorama==0.4.4 +commonmark==0.9.1 contextvars==2.4 coverage==5.5 cycler==0.10.0 @@ -48,10 +50,10 @@ future==0.18.2 gitdb==4.0.7 gitpython==3.1.18 gprof2dot==2021.2.21 -graphviz==0.16 +graphviz==0.17 hepunits==2.1.1 identify==2.2.11 -idna==2.10 +idna==3.2 imagesize==1.2.0 immutables==0.15 importlib-metadata==4.6.1 @@ -114,6 +116,7 @@ pexpect==4.8.0 pickleshare==0.7.5 pillow==8.3.1 pip-tools==6.2.0 +platformdirs==2.0.2 pluggy==0.13.1 pre-commit==2.13.0 prometheus-client==0.11.0 @@ -138,16 +141,17 @@ pytest-notebook==0.6.1 pytest-profiling==1.7.0 pytest-xdist==2.3.0 python-constraint==1.4.0 -python-dateutil==2.8.1 +python-dateutil==2.8.2 pytz==2021.1 pyyaml==5.4.1 pyzmq==22.1.0 -qrules==0.9.0 +qrules==0.9.1 radon==5.0.1 regex==2021.7.6 -requests==2.25.1 +requests==2.26.0 requests-unixsocket==0.2.0 restructuredtext-lint==1.3.2 +rich==10.6.0 send2trash==1.7.1 six==1.16.0 smmap==4.0.0 @@ -182,7 +186,7 @@ typed-ast==1.4.3 types-docutils==0.1.7 typing-extensions==3.10.0.0 ; python_version < "3.8.0" urllib3==1.26.6 -virtualenv==20.4.7 +virtualenv==20.5.0 wcwidth==0.2.5 webencodings==0.5.1 websocket-client==1.1.0 diff --git a/.constraints/py3.7.txt b/.constraints/py3.7.txt index 4a4a21a97..8aa541fe5 100644 --- a/.constraints/py3.7.txt +++ b/.constraints/py3.7.txt @@ -14,15 +14,17 @@ async-generator==1.10 attrs==20.3.0 babel==2.9.1 backcall==0.2.0 +backports.entry-points-selectable==1.1.0 beautifulsoup4==4.9.3 black==21.6b0 bleach==3.3.0 certifi==2021.5.30 cffi==1.14.6 cfgv==3.3.0 -chardet==4.0.0 +charset-normalizer==2.0.1 click==8.0.1 colorama==0.4.4 +commonmark==0.9.1 coverage==5.5 cycler==0.10.0 debugpy==1.3.0 @@ -47,10 +49,10 @@ future==0.18.2 gitdb==4.0.7 gitpython==3.1.18 gprof2dot==2021.2.21 -graphviz==0.16 +graphviz==0.17 hepunits==2.1.1 identify==2.2.11 -idna==2.10 +idna==3.2 imagesize==1.2.0 importlib-metadata==3.10.1 importlib-resources==5.2.0 @@ -113,6 +115,7 @@ pexpect==4.8.0 pickleshare==0.7.5 pillow==8.3.1 pip-tools==6.2.0 +platformdirs==2.0.2 pluggy==0.13.1 pre-commit==2.13.0 prometheus-client==0.11.0 @@ -137,16 +140,17 @@ pytest-notebook==0.6.1 pytest-profiling==1.7.0 pytest-xdist==2.3.0 python-constraint==1.4.0 -python-dateutil==2.8.1 +python-dateutil==2.8.2 pytz==2021.1 pyyaml==5.4.1 pyzmq==22.1.0 -qrules==0.9.0 +qrules==0.9.1 radon==5.0.1 regex==2021.7.6 -requests==2.25.1 +requests==2.26.0 requests-unixsocket==0.2.0 restructuredtext-lint==1.3.2 +rich==10.6.0 send2trash==1.7.1 six==1.16.0 smmap==4.0.0 @@ -181,7 +185,7 @@ typed-ast==1.4.3 types-docutils==0.1.7 typing-extensions==3.10.0.0 ; python_version < "3.8.0" urllib3==1.26.6 -virtualenv==20.4.7 +virtualenv==20.5.0 wcwidth==0.2.5 webencodings==0.5.1 websocket-client==1.1.0 diff --git a/.constraints/py3.8.txt b/.constraints/py3.8.txt index 56f97bffd..9942f2e43 100644 --- a/.constraints/py3.8.txt +++ b/.constraints/py3.8.txt @@ -14,15 +14,17 @@ async-generator==1.10 attrs==20.3.0 babel==2.9.1 backcall==0.2.0 +backports.entry-points-selectable==1.1.0 beautifulsoup4==4.9.3 black==21.6b0 bleach==3.3.0 certifi==2021.5.30 cffi==1.14.6 cfgv==3.3.0 -chardet==4.0.0 +charset-normalizer==2.0.1 click==8.0.1 colorama==0.4.4 +commonmark==0.9.1 coverage==5.5 cycler==0.10.0 debugpy==1.3.0 @@ -47,10 +49,10 @@ future==0.18.2 gitdb==4.0.7 gitpython==3.1.18 gprof2dot==2021.2.21 -graphviz==0.16 +graphviz==0.17 hepunits==2.1.1 identify==2.2.11 -idna==2.10 +idna==3.2 imagesize==1.2.0 importlib-metadata==4.6.1 importlib-resources==5.2.0 @@ -113,6 +115,7 @@ pexpect==4.8.0 pickleshare==0.7.5 pillow==8.3.1 pip-tools==6.2.0 +platformdirs==2.0.2 pluggy==0.13.1 pre-commit==2.13.0 prometheus-client==0.11.0 @@ -137,16 +140,17 @@ pytest-notebook==0.6.1 pytest-profiling==1.7.0 pytest-xdist==2.3.0 python-constraint==1.4.0 -python-dateutil==2.8.1 +python-dateutil==2.8.2 pytz==2021.1 pyyaml==5.4.1 pyzmq==22.1.0 -qrules==0.9.0 +qrules==0.9.1 radon==5.0.1 regex==2021.7.6 -requests==2.25.1 +requests==2.26.0 requests-unixsocket==0.2.0 restructuredtext-lint==1.3.2 +rich==10.6.0 send2trash==1.7.1 six==1.16.0 smmap==4.0.0 @@ -180,7 +184,7 @@ traitlets==5.0.5 types-docutils==0.1.7 typing-extensions==3.10.0.0 urllib3==1.26.6 -virtualenv==20.4.7 +virtualenv==20.5.0 wcwidth==0.2.5 webencodings==0.5.1 websocket-client==1.1.0 diff --git a/.constraints/py3.9.txt b/.constraints/py3.9.txt index b5712b61c..69bede48e 100644 --- a/.constraints/py3.9.txt +++ b/.constraints/py3.9.txt @@ -14,15 +14,17 @@ async-generator==1.10 attrs==20.3.0 babel==2.9.1 backcall==0.2.0 +backports.entry-points-selectable==1.1.0 beautifulsoup4==4.9.3 black==21.6b0 bleach==3.3.0 certifi==2021.5.30 cffi==1.14.6 cfgv==3.3.0 -chardet==4.0.0 +charset-normalizer==2.0.1 click==8.0.1 colorama==0.4.4 +commonmark==0.9.1 coverage==5.5 cycler==0.10.0 debugpy==1.3.0 @@ -47,10 +49,10 @@ future==0.18.2 gitdb==4.0.7 gitpython==3.1.18 gprof2dot==2021.2.21 -graphviz==0.16 +graphviz==0.17 hepunits==2.1.1 identify==2.2.11 -idna==2.10 +idna==3.2 imagesize==1.2.0 importlib-metadata==4.6.1 importlib-resources==5.2.0 @@ -113,6 +115,7 @@ pexpect==4.8.0 pickleshare==0.7.5 pillow==8.3.1 pip-tools==6.2.0 +platformdirs==2.0.2 pluggy==0.13.1 pre-commit==2.13.0 prometheus-client==0.11.0 @@ -137,16 +140,17 @@ pytest-notebook==0.6.1 pytest-profiling==1.7.0 pytest-xdist==2.3.0 python-constraint==1.4.0 -python-dateutil==2.8.1 +python-dateutil==2.8.2 pytz==2021.1 pyyaml==5.4.1 pyzmq==22.1.0 -qrules==0.9.0 +qrules==0.9.1 radon==5.0.1 regex==2021.7.6 -requests==2.25.1 +requests==2.26.0 requests-unixsocket==0.2.0 restructuredtext-lint==1.3.2 +rich==10.6.0 send2trash==1.7.1 six==1.16.0 smmap==4.0.0 @@ -180,7 +184,7 @@ traitlets==5.0.5 types-docutils==0.1.7 typing-extensions==3.10.0.0 urllib3==1.26.6 -virtualenv==20.4.7 +virtualenv==20.5.0 wcwidth==0.2.5 webencodings==0.5.1 websocket-client==1.1.0 diff --git a/.cspell.json b/.cspell.json index ec0cb1894..3239f96cb 100644 --- a/.cspell.json +++ b/.cspell.json @@ -156,6 +156,7 @@ "heurisch", "imag", "iplt", + "ipykernel", "ipyplot", "ipywidgets", "isfunction", @@ -166,6 +167,7 @@ "katex", "kernelspec", "kmatrix", + "kutschke", "kwargs", "linestyle", "linewidth", @@ -185,6 +187,7 @@ "nbsphinx", "ndarray", "noqa", + "nowrap", "nrows", "nsimplify", "numpycode", @@ -202,6 +205,7 @@ "pyproject", "pyright", "pytestconfig", + "richman", "rightarrow", "risch", "rtfd", diff --git a/docs/bibliography.bib b/docs/bibliography.bib index 7827059c3..0eca8a83d 100644 --- a/docs/bibliography.bib +++ b/docs/bibliography.bib @@ -47,6 +47,16 @@ @article{chungPartialWaveAnalysis1995 number = {5} } +@techreport{chungSpinFormalismsUpdated2014, + title = {Spin {{Formalisms}} ({{Updated Version}})}, + author = {Chung, Suh-Urk}, + year = {2014}, + month = jul, + pages = {BNL--76975-2006-IR, 890945}, + institution = {{Brookhaven National Laboratory}}, + url = {https://suchung.web.cern.ch/spinfm1.pdf} +} + @article{flatteCoupledchannelAnalysisPi1976, title = {Coupled-Channel Analysis of the Πη and {{KK̄}} Systems near {{KK̄}} Threshold}, author = {Flatté, S.M.}, @@ -61,6 +71,14 @@ @article{flatteCoupledchannelAnalysisPi1976 number = {2} } +@misc{kutschkeAngularDistributionCookbook1996, + title = {An {{Angular Distribution Cookbook}}}, + author = {Kutschke, Rob}, + year = {1996}, + month = jan, + url = {https://home.fnal.gov/~kutschke/Angdist/angdist.ps} +} + @misc{meyerMatrixTutorial2008, title = {A 𝐾-{{Matrix Tutorial}}}, author = {Meyer, Curtis A.}, @@ -79,4 +97,12 @@ @phdthesis{pychyGekoppeltePartialwellenanalyseAnnihilationen2016 school = {Ruhr-Universität Bochum} } +@misc{richmanExperimenterGuideHelicity1984, + title = {An {{Experimenter}}'s {{Guide}} to the {{Helicity Formalism}}}, + author = {Richman, Jeffrey D.}, + year = {1984}, + month = jun, + url = {https://inspirehep.net/literature/202987} +} + diff --git a/docs/usage.md b/docs/usage.md index cab5e433f..38ef3d220 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -11,5 +11,6 @@ maxdepth: 2 usage/amplitude usage/modify usage/interactive +usage/formalism usage/dynamics ``` diff --git a/docs/usage/formalism.ipynb b/docs/usage/formalism.ipynb new file mode 100644 index 000000000..2d437e5d6 --- /dev/null +++ b/docs/usage/formalism.ipynb @@ -0,0 +1,358 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "hideCode": true, + "hideOutput": true, + "hidePrompt": true, + "jupyter": { + "source_hidden": true + }, + "slideshow": { + "slide_type": "skip" + }, + "tags": [ + "remove-cell" + ] + }, + "outputs": [], + "source": [ + "%%capture\n", + "%config Completer.use_jedi = False\n", + "%config InlineBackend.figure_formats = ['svg']\n", + "import os\n", + "\n", + "STATIC_WEB_PAGE = {\"EXECUTE_NB\", \"READTHEDOCS\"}.intersection(os.environ)\n", + "\n", + "# Install on Google Colab\n", + "import subprocess\n", + "import sys\n", + "\n", + "from IPython import get_ipython\n", + "\n", + "install_packages = \"google.colab\" in str(get_ipython())\n", + "if install_packages:\n", + " for package in [\"ampform[doc]\", \"graphviz\"]:\n", + " subprocess.check_call(\n", + " [sys.executable, \"-m\", \"pip\", \"install\", package]\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Helicity versus canonical" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [ + "hide-cell" + ] + }, + "outputs": [], + "source": [ + "import logging\n", + "\n", + "import graphviz\n", + "import qrules\n", + "import sympy as sp\n", + "from IPython.display import HTML, Math\n", + "from rich.table import Table\n", + "\n", + "import ampform\n", + "\n", + "LOGGER = logging.getLogger()\n", + "LOGGER.setLevel(logging.ERROR)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this notebook, we have a look at the decay\n", + "\n", + "$$D_1(2420)^0 \\to a_1(1260)^+ K^- \\to (K^+K^0)K^-$$\n", + "\n", + "in order to see the difference between a {class}`.HelicityModel` formulated in the **canonical** basis and one formulated in the **helicity** basis. To simplify things, we only look at spin projection $+1$ for $D_1(2420)^0$, because the intensities for each of the spin projections of $D_1(2420)^0$ are incoherent, no matter which spin formalism we choose.\n", + "\n", + ":::{tip}\n", + "\n", + "For more information about the helicity formalism, see {cite}`chungSpinFormalismsUpdated2014`, {cite}`richmanExperimenterGuideHelicity1984`, and {cite}`kutschkeAngularDistributionCookbook1996`.\n", + "\n", + ":::\n", + "\n", + "First, we use {func}`qrules.generate_transitions` to generate a {class}`~qrules.transition.ReactionInfo` instance for both formalisms:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def generate_transitions(formalism: str):\n", + " reaction = qrules.generate_transitions(\n", + " initial_state=(\"D(1)(2420)0\", [+1]),\n", + " final_state=[\"K+\", \"K-\", \"K~0\"],\n", + " allowed_intermediate_particles=[\"a(1)(1260)+\"],\n", + " formalism=formalism,\n", + " )\n", + " builder = ampform.get_builder(reaction)\n", + " return builder.formulate()\n", + "\n", + "\n", + "cano_model = generate_transitions(\"canonical-helicity\")\n", + "heli_model = generate_transitions(\"helicity\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From {attr}`.components` and {attr}`.parameter_defaults`, we can see that the canonical formalism has a larger number of amplitudes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "table = Table(show_edge=False)\n", + "table.add_column(\"Formalism\")\n", + "table.add_column(\"Coefficients\", justify=\"right\")\n", + "table.add_column(\"Amplitudes\", justify=\"right\")\n", + "table.add_row(\n", + " \"Canonical\",\n", + " str(len(cano_model.parameter_defaults)),\n", + " str(len(cano_model.components) - 1),\n", + ")\n", + "table.add_row(\n", + " \"Helicity\",\n", + " str(len(heli_model.parameter_defaults)),\n", + " str(len(heli_model.components) - 1),\n", + ")\n", + "table" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The reason for this is that canonical basis distinguishes amplitudes over their $LS$-combinations. This becomes clear if we define $a$ to be the amplitude _without coefficient_ ($A = C a$), and consider what the full, coherent intensity looks like.\n", + "\n", + "If we write the full intensity as $I = \\left|\\sum_i A_i\\right|^2$, then we have, in the case of the **canonical** basis:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "def extract_amplitude_substitutions(model):\n", + " amplitude_to_symbol = {}\n", + " for name, expr in model.components.items():\n", + " if not name.startswith(\"A\"):\n", + " continue\n", + " for par in model.parameter_defaults:\n", + " if par in expr.args:\n", + " expr /= par\n", + " name = \"a\" + name[1:]\n", + " symbol = sp.Symbol(name)\n", + " amplitude_to_symbol[expr] = symbol\n", + " return amplitude_to_symbol\n", + "\n", + "\n", + "cano_amplitude_to_symbol = extract_amplitude_substitutions(cano_model)\n", + "heli_amplitude_to_symbol = extract_amplitude_substitutions(heli_model)\n", + "\n", + "\n", + "def render_amplitude_summation(model):\n", + " amplitude_to_symbol = extract_amplitude_substitutions(model)\n", + " collected_expr = sp.collect(\n", + " model.expression.subs(amplitude_to_symbol).args[0].args[0],\n", + " tuple(model.parameter_defaults),\n", + " )\n", + " terms = collected_expr.args\n", + " latex = \"\"\n", + " latex += R\"\\begin{align}\"\n", + " latex += fR\"\\sum_i A_i & = {sp.latex(terms[0])}\\\\\"\n", + " for term in terms[1:]:\n", + " latex += fR\"& + {sp.latex(term)} \\\\\"\n", + " latex += R\"\\end{align}\"\n", + " return Math(latex)\n", + "\n", + "\n", + "render_amplitude_summation(cano_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the **helicity** basis, the $LS$-combinations have been summed over already and we can only see an amplitude for each helicity:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "render_amplitude_summation(heli_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Amplitudes in the **canonical** basis are formulated with regard to their $LS$-couplings. As such, they contain additional [Clebsch-Gordan coefficients](https://en.wikipedia.org/wiki/Clebsch%E2%80%93Gordan_coefficients) that serve as _expansion coefficients_." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "def extract_amplitudes(model):\n", + " return {\n", + " expr: sp.Symbol(name)\n", + " for name, expr in model.components.items()\n", + " if name.startswith(\"A\")\n", + " }\n", + "\n", + "\n", + "cano_amplitudes = extract_amplitudes(cano_model)\n", + "heli_amplitudes = extract_amplitudes(heli_model)\n", + "\n", + "expression, symbol = next(iter(cano_amplitude_to_symbol.items()))\n", + "display(symbol, Math(fR\"\\quad = {sp.latex(expression)}\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the **helicity** basis, these Clebsch-Gordan coefficients and [Wigner-$D$ functions](https://en.wikipedia.org/wiki/Wigner_D-matrix) have been summed up, leaving only a Wigner-$D$ for each node in the decay chain (two in this case):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "expression, symbol = next(iter(heli_amplitude_to_symbol.items()))\n", + "display(symbol, Math(fR\"\\quad = {sp.latex(expression)}\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see this also from the original {class}`~qrules.transition.ReactionInfo` objects. Let's select only the {attr}`~qrules.transition.ReactionInfo.transitions` where the $a_1(1260)^+$ resonance has spin projection $-1$ (taken to be helicity $-1$ in the helicity formalism). We then see just one {class}`~qrules.transition.StateTransition` in the helicity basis and three transitions in the canonical basis:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [ + "hide-input" + ] + }, + "outputs": [], + "source": [ + "def render_selection(model):\n", + " transitions = model.adapter.reaction_info.transitions\n", + " selection = filter(\n", + " lambda s: s.states[3].spin_projection == -1, transitions\n", + " )\n", + " dot = qrules.io.asdot(\n", + " selection, render_node=True, render_final_state_id=False\n", + " )\n", + " return graphviz.Source(dot)\n", + "\n", + "\n", + "display(\n", + " HTML(\"Helicity basis:\"),\n", + " render_selection(heli_model),\n", + " HTML(\"Canonical basis:\"),\n", + " render_selection(cano_model),\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/setup.cfg b/setup.cfg index d164bb2c2..706d778e8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,6 +63,7 @@ doc = mdit-py-plugins < 0.2.7 # temporary fix mpl_interactions myst-nb >= 0.11 # myst_enable_extensions + rich Sphinx >= 3 sphinx-book-theme sphinx-copybutton