|
50 | 50 | "import numpy as np\n", |
51 | 51 | "import numpy.typing as npt\n", |
52 | 52 | "import quadax\n", |
| 53 | + "import sympy as sp\n", |
53 | 54 | "from ipympl.backend_nbagg import Canvas\n", |
54 | | - "from IPython.display import SVG, display\n", |
| 55 | + "from IPython.display import SVG, Math, display\n", |
55 | 56 | "from matplotlib.axes import Axes\n", |
56 | 57 | "from matplotlib.collections import LineCollection, QuadMesh\n", |
57 | 58 | "from matplotlib.lines import Line2D\n", |
58 | 59 | "from scipy.integrate import quad_vec\n", |
59 | 60 | "\n", |
| 61 | + "from ampform.dynamics.form_factor import BlattWeisskopfSquared, FormFactor\n", |
| 62 | + "from ampform.dynamics.phasespace import ChewMandelstamIntegral, PhaseSpaceFactor\n", |
| 63 | + "from ampform.io import aslatex\n", |
| 64 | + "from ampform.sympy import UnevaluatableIntegral\n", |
| 65 | + "\n", |
60 | 66 | "# cspell:disable-next-line\n", |
61 | 67 | "Algorithm = Literal[\"quadcc\", \"quadgk\", \"quadts\", \"romberg\", \"rombergts\", \"quad_vec\"]\n", |
62 | 68 | "jax.config.update(\"jax_enable_x64\", True)\n", |
|
68 | 74 | " canvas.toolbar_visible = False" |
69 | 75 | ] |
70 | 76 | }, |
| 77 | + { |
| 78 | + "cell_type": "code", |
| 79 | + "execution_count": null, |
| 80 | + "metadata": { |
| 81 | + "tags": [ |
| 82 | + "hide-input", |
| 83 | + "full-width" |
| 84 | + ] |
| 85 | + }, |
| 86 | + "outputs": [], |
| 87 | + "source": [ |
| 88 | + "UnevaluatableIntegral.dummify = False\n", |
| 89 | + "s, m1, m2, z = sp.symbols(\"s m1 m2 z\", nonnegative=True)\n", |
| 90 | + "ell = sp.Symbol(\"ell\", integer=True, nonnegative=True)\n", |
| 91 | + "cm = ChewMandelstamIntegral(s, m1, m2, ell)\n", |
| 92 | + "ff = FormFactor(s, m1, m2, ell)\n", |
| 93 | + "rho = PhaseSpaceFactor(s, m1, m2)\n", |
| 94 | + "bl = BlattWeisskopfSquared(z, ell)\n", |
| 95 | + "max_ell = 5\n", |
| 96 | + "src = aslatex({\n", |
| 97 | + " **{e: e.doit(deep=False) for e in (cm, ff, rho)},\n", |
| 98 | + " **{bl.subs(ell, i): bl.subs(ell, i).doit() for i in range(max_ell + 1)},\n", |
| 99 | + "})\n", |
| 100 | + "display(Math(src))" |
| 101 | + ] |
| 102 | + }, |
71 | 103 | { |
72 | 104 | "cell_type": "code", |
73 | 105 | "execution_count": null, |
|
88 | 120 | " s: npt.NDArray[np.float64],\n", |
89 | 121 | " m1: float,\n", |
90 | 122 | " m2: float,\n", |
| 123 | + " ell: int = 0,\n", |
91 | 124 | " start_offset: float = 0,\n", |
92 | 125 | " algorithm: Callable = quadax.quadcc,\n", |
93 | 126 | " **configuration,\n", |
94 | 127 | "):\n", |
95 | 128 | " s_thr = (m1 + m2) ** 2\n", |
96 | 129 | " if algorithm is quad_vec:\n", |
97 | 130 | " integral, _ = algorithm(\n", |
98 | | - " partial(integrand, s=s, m1=m1, m2=m2),\n", |
| 131 | + " partial(integrand, s=s, m1=m1, m2=m2, ell=ell),\n", |
99 | 132 | " s_thr + start_offset,\n", |
100 | 133 | " np.inf,\n", |
101 | 134 | " **configuration,\n", |
102 | 135 | " )\n", |
103 | 136 | " else:\n", |
104 | 137 | " integral, _ = algorithm(\n", |
105 | | - " jax.tree_util.Partial(integrand, s=s, m1=m1, m2=m2),\n", |
| 138 | + " jax.tree_util.Partial(integrand, s=s, m1=m1, m2=m2, ell=ell),\n", |
106 | 139 | " interval=[s_thr + start_offset, jnp.inf],\n", |
107 | 140 | " **configuration,\n", |
108 | 141 | " )\n", |
109 | 142 | " return (s - s_thr) * integral / jnp.pi\n", |
110 | 143 | "\n", |
111 | 144 | "\n", |
112 | 145 | "@jax.jit\n", |
113 | | - "def integrand(sp, s, m1, m2):\n", |
| 146 | + "def integrand(sp, s, m1, m2, ell):\n", |
114 | 147 | " s_thr = (m1 + m2) ** 2\n", |
115 | | - " return rho(sp, m1, m2) / ((sp - s_thr) * (sp - s))\n", |
| 148 | + " return rho_func(sp, m1, m2) * n2(s, m1, m2, ell) / ((sp - s_thr) * (sp - s))\n", |
| 149 | + "\n", |
| 150 | + "\n", |
| 151 | + "@jax.jit\n", |
| 152 | + "def rho_func(s, m1, m2):\n", |
| 153 | + " return jnp.sqrt(s - (m1 - m2) ** 2) * jnp.sqrt(s - (m1 + m2) ** 2) / s\n", |
| 154 | + "\n", |
| 155 | + "\n", |
| 156 | + "def n2(s, m1, m2, ell):\n", |
| 157 | + " return blatt_weisskopf_squared(q(s, m1, m2), ell)\n", |
| 158 | + "\n", |
| 159 | + "\n", |
| 160 | + "def blatt_weisskopf_squared(z, ell):\n", |
| 161 | + " return jnp.select(\n", |
| 162 | + " [ell == 0, ell == 1, ell == 2, ell == 3, ell == 4, ell == 5],\n", |
| 163 | + " [\n", |
| 164 | + " 1,\n", |
| 165 | + " 2 * z / (z + 1),\n", |
| 166 | + " 13 * z**2 / (z**2 + 3 * z + 9),\n", |
| 167 | + " 277 * z**3 / (z**3 + 6 * z**2 + 45 * z + 225),\n", |
| 168 | + " 12746 * z**4 / (z**4 + 10 * z**3 + 135 * z**2 + 1575 * z + 11025),\n", |
| 169 | + " 998881\n", |
| 170 | + " * z**5\n", |
| 171 | + " / (z**5 + 15 * z**4 + 315 * z**3 + 6300 * z**2 + 99225 * z + 893025),\n", |
| 172 | + " ],\n", |
| 173 | + " default=jnp.nan,\n", |
| 174 | + " )\n", |
116 | 175 | "\n", |
117 | 176 | "\n", |
118 | 177 | "@jax.jit\n", |
119 | | - "def rho(s, m1, m2):\n", |
120 | | - " return jnp.sqrt((s - (m1 - m2) ** 2) * (s - (m1 + m2) ** 2)) / s" |
| 178 | + "def q(s, m1, m2):\n", |
| 179 | + " return (\n", |
| 180 | + " jnp.sqrt(s - (m1 - m2) ** 2) * jnp.sqrt(s - (m1 + m2) ** 2) / (2 * jnp.sqrt(s))\n", |
| 181 | + " )" |
| 182 | + ] |
| 183 | + }, |
| 184 | + { |
| 185 | + "cell_type": "markdown", |
| 186 | + "metadata": {}, |
| 187 | + "source": [ |
| 188 | + "In the case of S-waves, we can compare the result of the integration to the analytical result.\n", |
| 189 | + "\n", |
| 190 | + "Note that in this case, we define the break-up momentum in the same way as in {class}`.BreakupMomentum`, because in the widget below, we only evaluate this function along the real axis, where the cut structure doesn't matter and we prefer performance (see {ref}`usage/dynamics/analytic-continuation:Numerical precision and performance`)." |
| 191 | + ] |
| 192 | + }, |
| 193 | + { |
| 194 | + "cell_type": "code", |
| 195 | + "execution_count": null, |
| 196 | + "metadata": { |
| 197 | + "tags": [ |
| 198 | + "hide-input", |
| 199 | + "full-width" |
| 200 | + ] |
| 201 | + }, |
| 202 | + "outputs": [], |
| 203 | + "source": [ |
| 204 | + "from ampform.dynamics.phasespace import ChewMandelstamSWave\n", |
| 205 | + "\n", |
| 206 | + "CM0 = ChewMandelstamSWave(s, m1, m2)\n", |
| 207 | + "Math(aslatex({CM0: CM0.doit(deep=False)}))" |
121 | 208 | ] |
122 | 209 | }, |
123 | 210 | { |
|
142 | 229 | " (2 * q(s, m1, m2) / jnp.sqrt(s))\n", |
143 | 230 | " * jnp.log((m1**2 + m2**2 - s + 2 * q(s, m1, m2) * jnp.sqrt(s)) / (2 * m1 * m2))\n", |
144 | 231 | " - (m1**2 - m2**2) * (1 / s - 1 / (m1 + m2) ** 2) * jnp.log(m1 / m2)\n", |
145 | | - " )\n", |
146 | | - "\n", |
147 | | - "\n", |
148 | | - "@jax.jit\n", |
149 | | - "def q(s, m1, m2):\n", |
150 | | - " return jnp.sqrt((s - (m1 - m2) ** 2) * (s - (m1 + m2) ** 2)) / (2 * jnp.sqrt(s))" |
| 232 | + " )" |
151 | 233 | ] |
152 | 234 | }, |
153 | 235 | { |
|
177 | 259 | " ),\n", |
178 | 260 | " m1=w.FloatSlider(value=0.13, min=0.0, max=2.0, step=0.01, description=\"m₁\", **cont),\n", |
179 | 261 | " m2=w.FloatSlider(value=0.98, min=0.0, max=2.0, step=0.01, description=\"m₂\", **cont),\n", |
| 262 | + " ell=w.IntSlider(value=0, min=0, max=5, description=\"ℓ\", **cont),\n", |
180 | 263 | " y_lim=w.FloatRangeSlider(\n", |
181 | 264 | " description=\"y range\",\n", |
182 | 265 | " min=-5,\n", |
|
313 | 396 | " tabs := w.Tab([\n", |
314 | 397 | " w.HBox([\n", |
315 | 398 | " physics_sliders[\"projection\"],\n", |
316 | | - " w.VBox(list(physics_sliders.values())[1:4]),\n", |
317 | | - " w.VBox(list(physics_sliders.values())[4:]),\n", |
| 399 | + " w.VBox(list(physics_sliders.values())[1:5]),\n", |
| 400 | + " w.VBox(list(physics_sliders.values())[5:]),\n", |
318 | 401 | " ]),\n", |
319 | 402 | " w.HBox([\n", |
320 | 403 | " algorithm_sliders[\"algorithm_name\"],\n", |
|
380 | 463 | " projection: Literal[\"real\", \"imag\", \"abs\"],\n", |
381 | 464 | " m1: float,\n", |
382 | 465 | " m2: float,\n", |
| 466 | + " ell: int,\n", |
383 | 467 | " epsilon: float,\n", |
384 | 468 | " start_offset: float,\n", |
385 | 469 | " z_max: float,\n", |
|
399 | 483 | " z_ana = sigma0(s, m1, m2)\n", |
400 | 484 | " alg_kwargs = get_algorithm_options(algorithm_name, **alg_kwargs)\n", |
401 | 485 | " start_time = time.perf_counter()\n", |
402 | | - " z_num = integrate_numerically(s, m1, m2, start_offset, algorithm, **alg_kwargs)\n", |
| 486 | + " z_num = integrate_numerically(\n", |
| 487 | + " s, m1, m2, ell, start_offset, algorithm, **alg_kwargs\n", |
| 488 | + " )\n", |
403 | 489 | " z_num.block_until_ready()\n", |
404 | 490 | " end_time = time.perf_counter()\n", |
405 | 491 | " z = z_exact, z_ana, z_num\n", |
406 | 492 | " duration = end_time - start_time\n", |
407 | 493 | " timer_box.value = f\"Computation time: <b>{format_time(duration)}</b> for {resolution:,} points\"\n", |
408 | 494 | " if np.all(np.isnan(z_num)):\n", |
409 | 495 | " timer_box.value += \" (<font color='red'>all values are NaN</font>)\"\n", |
410 | | - " Z = rho(S, m1, m2)\n", |
| 496 | + " Z = rho_func(S, m1, m2) * n2(S, m1, m2, ell)\n", |
411 | 497 | " if projection == \"abs\":\n", |
412 | 498 | " Z = jnp.abs(Z)\n", |
413 | 499 | " else:\n", |
|
540 | 626 | " ax.spines[\"right\"].set_visible(False)\n", |
541 | 627 | " ax.spines[\"top\"].set_visible(False)\n", |
542 | 628 | "ax1.spines[\"bottom\"].set_visible(False)\n", |
543 | | - "ax1.set_title(R\"$\\rho(s)$\")\n", |
| 629 | + "ax1.set_title(R\"$\\rho(s) \\, n_\\ell^2(s)$\")\n", |
544 | 630 | "ax1.set_ylabel(\"Im $s$\")\n", |
545 | 631 | "ax2.set_ylabel(R\"Im $\\Sigma_0(s)$\")\n", |
546 | 632 | "ax3.set_ylabel(R\"Re $\\Sigma_0(s)$\")\n", |
|
0 commit comments