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
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
"pyplot",
"pytest",
"qrules",
"regge",
"setuptools",
"spflueger",
"struct",
Expand Down
3 changes: 0 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,12 @@ def __get_newtypes(some_type: type) -> list:
"NodeQuantumNumber": ("obj", "qrules.quantum_numbers.NodeQuantumNumber"),
"NodeQuantumNumberTypes": ("obj", "qrules.quantum_numbers.NodeQuantumNumberTypes"),
"NodeType": "typing.TypeVar",
"ParticleWithSpin": ("obj", "qrules.particle.ParticleWithSpin"),
"Path": "pathlib.Path",
"QNTransition": ("obj", "qrules.workflow.QNTransition"),
"qrules.topology.EdgeType": "typing.TypeVar",
"qrules.topology.NodeType": "typing.TypeVar",
"Rule": ("obj", "qrules.argument_handling.Rule"),
"SpinFormalism": ("obj", "qrules.transition.SpinFormalism"),
"StateDefinition": ("obj", "qrules.combinatorics.StateDefinition"),
"StateDefinitionInput": ("obj", "qrules.combinatorics.StateDefinitionInput"),
"StateTransition": ("obj", "qrules.transition.StateTransition"),
"TypeAliasForwardRef": ("obj", "typing.TypeAlias"),
"typing.Literal[-1, 1]": "typing.Literal",
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ quantum numbers it has found.

The resulting state transition objects are particularly useful for **amplitude analysis
/ Partial Wave Analysis** as they contain all information (such as expected masses,
widths, and spin projections) that is needed to formulate an amplitude model.
widths, and spin quantum numbers) that is needed to formulate an amplitude model.

The {doc}`/usage` pages illustrate several features of {mod}`qrules`. You can run each
of them as Jupyter notebooks with the {fa}`rocket` launch button in the top-right
Expand Down
1 change: 1 addition & 0 deletions docs/usage.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
"---\n",
"usage/reaction\n",
"usage/qn-transitions\n",
"usage/spin-projections\n",
"usage/production\n",
"usage/particle\n",
"usage/visualize\n",
Expand Down
44 changes: 5 additions & 39 deletions docs/usage/conservation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, have a look at the edge and node properties, and use the underlying {class}`.Topology` to extract one of the node {class}`.InteractionProperties` with the surrounding states (these are {obj}`tuple`s of a {class}`.Particle` and a {obj}`float` spin projection)."
"Next, have a look at the edge and node properties, and use the underlying {class}`.Topology` to extract one of the node {class}`.InteractionProperties` with the surrounding states (these are {class}`.Particle` instances)."
]
},
{
Expand Down Expand Up @@ -244,10 +244,10 @@
"interaction = transition.interactions[node_id]\n",
"\n",
"spin_magnitude_conservation(\n",
" ingoing_spin_magnitudes=[{\"spin_magnitude\": incoming_state.particle.spin}],\n",
" ingoing_spin_magnitudes=[{\"spin_magnitude\": incoming_state.spin}],\n",
" outgoing_spin_magnitudes=[\n",
" {\"spin_magnitude\": outgoing_state1.particle.spin},\n",
" {\"spin_magnitude\": outgoing_state2.particle.spin},\n",
" {\"spin_magnitude\": outgoing_state1.spin},\n",
" {\"spin_magnitude\": outgoing_state2.spin},\n",
" ],\n",
" interaction_qns={\n",
" \"l_magnitude\": interaction.l_magnitude,\n",
Expand All @@ -260,41 +260,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Contrary to expectations, this transition does not conserve spin **projection** and therefore {func}`.spin_conservation` returns {obj}`False`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"spin_conservation(\n",
" ingoing_spins=[\n",
" SpinEdgeInput(\n",
" spin_magnitude=incoming_state.particle.spin,\n",
" spin_projection=incoming_state.spin_projection,\n",
" )\n",
" ],\n",
" outgoing_spins=[\n",
" SpinEdgeInput(\n",
" spin_magnitude=outgoing_state1.particle.spin,\n",
" spin_projection=outgoing_state1.spin_projection,\n",
" ),\n",
" SpinEdgeInput(\n",
" spin_magnitude=outgoing_state2.particle.spin,\n",
" spin_projection=outgoing_state2.spin_projection,\n",
" ),\n",
" ],\n",
" interaction_qns=interaction,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The reason is that AmpForm formulates the {class}`~ampform.helicity.HelicityModel` with the helicity formalism first and then uses a transformation to get the model in the canonical basis (see {func}`~ampform.helicity.formulate_isobar_cg_coefficients`). The canonical basis does not conserve helicity (taken to be {attr}`.State.spin_projection`)."
"{func}`.spin_conservation` additionally checks the spin **projections**, as illustrated in the {ref}`examples above <usage/conservation:Spin conservation>`. The states and interactions generated by the workflow carry no spin projections, so that rule can only be evaluated with manually supplied projections — or by re-enabling spin projections in the problem sets, as demonstrated in {doc}`/usage/spin-projections`."
]
},
{
Expand Down
120 changes: 117 additions & 3 deletions docs/usage/production.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Production reactions also work through the classic {func}`.generate_transitions` interface, which matches all intermediate edges — including the exchange edges — to particles and generates the spin projections required for a helicity amplitude model. {meth}`.ReactionInfo.group_by_channel` shows which resonances and exchange particles appear in each channel:"
"Production reactions also work through the classic {func}`.generate_transitions` interface, which matches all intermediate edges — including the exchange edges — to particles from a database. {meth}`.ReactionInfo.group_by_channel` shows which resonances and exchange particles appear in each channel:"
]
},
{
Expand All @@ -264,7 +264,7 @@
")\n",
"{\n",
" channel: sorted({\n",
" state.particle.name\n",
" state.name\n",
" for transition in transitions\n",
" for state in transition.intermediate_states.values()\n",
" })\n",
Expand All @@ -288,6 +288,120 @@
"Markdown(qrules.io.asmermaid(particle_reaction, collapse=\"topology\", markdown=True))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Three-body production and double exchange"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For three or more final-state particles, the topology set contains more than the single-exchange $s$-, $t$-, and $u$-channels, in which the exchange edge fuses into a resonance that continues as a sequence of two-body decays. In the additional **double-exchange** topologies, two exchange edges fuse into a final-state particle — the central-production and ladder diagrams known from double-Regge phenomenology. {func}`.determine_reaction_channel` labels them with one letter per exchange edge:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"topologies_3body = create_isobar_topologies(\n",
" number_of_final_states=3,\n",
" number_of_initial_states=2,\n",
")\n",
"[determine_reaction_channel(topology) for topology in topologies_3body]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Markdown(qrules.io.asmermaid(topologies_3body, markdown=True))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As an example with particles, add a pion to the $\\eta$ photoproduction final state: $\\gamma p \\to p\\pi^0\\eta$, with a small selection of allowed intermediate states. All channel types contribute. In the two-letter channels, the final-state assignment determines whether each exchange edge is of $t$- or $u$-type:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"reaction_3body = qrules.generate_transitions(\n",
" initial_state=[\"gamma\", \"p\"],\n",
" final_state=[\"p\", \"pi0\", \"eta\"],\n",
" allowed_intermediate_particles=[\"N(1535)\", \"a(2)(1320)\", \"omega(782)\"],\n",
" allowed_interaction_types=[\"strong\", \"em\"],\n",
" formalism=\"helicity\",\n",
" particle_db=PDG,\n",
")\n",
"{\n",
" channel: sorted({\n",
" state.name\n",
" for transition in transitions\n",
" for state in transition.intermediate_states.values()\n",
" })\n",
" for channel, transitions in reaction_3body.group_by_channel().items()\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The single-letter channels are the familiar diagrams from the two-body case, now continued with a two-body decay of the resonance. In the double-exchange channels, the collapsed diagrams show how a final-state particle emerges from the two exchange edges. The :code:`\"uu\"` channel proceeds through double $\\omega$ exchange in a ladder configuration:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"channels_3body = reaction_3body.group_by_channel()\n",
"Markdown(qrules.io.asmermaid(channels_3body[\"uu\"], collapse=\"topology\", markdown=True))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"while the :code:`\"tt\"` channel combines central-production diagrams — both initial states emit a final-state particle and the two baryon exchanges fuse — with double baryon-exchange ladders:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"Markdown(qrules.io.asmermaid(channels_3body[\"tt\"], collapse=\"topology\", markdown=True))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
":::{tip}\n",
"If the double-exchange contributions are not of interest, pass e.g. :code:`allowed_channels=[\"s\", \"t\", \"u\"]` to drop those topologies before any solving happens.\n",
":::\n",
"\n",
":::{warning}\n",
"For production reactions, always restrict :code:`allowed_intermediate_particles`. The problem-set count grows rapidly with the number of final-state particles (number of topologies × final-state permutations × interaction-type combinations — already 240 problem sets for this $2 \\to 3$ example), and each problem set contains *two* internal edges. Without a particle selection, the quantum-number domains of those edges are unconstrained, so the constraint solver has to backtrack through orders of magnitude more combinations per problem set. A :code:`allowed_channels` selection reduces the number of problem sets further.\n",
":::"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -366,7 +480,7 @@
"metadata": {},
"source": [
":::{seealso}\n",
"{doc}`/usage/qn-transitions` for solving reactions at the quantum-number level — without spin projections — which keeps many-body final states tractable, and {doc}`/usage/reaction` for the general workflow.\n",
"{doc}`/usage/qn-transitions` for solving reactions at the quantum-number level, which keeps many-body final states tractable, and {doc}`/usage/reaction` for the general workflow.\n",
":::"
]
}
Expand Down
Loading
Loading