|
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": "markdown", |
| 79 | + "metadata": {}, |
| 80 | + "source": [ |
| 81 | + "```{autolink-skip}\n", |
| 82 | + "\n", |
| 83 | + "```" |
| 84 | + ] |
| 85 | + }, |
| 86 | + { |
| 87 | + "cell_type": "code", |
| 88 | + "execution_count": null, |
| 89 | + "metadata": { |
| 90 | + "tags": [ |
| 91 | + "remove-cell" |
| 92 | + ] |
| 93 | + }, |
| 94 | + "outputs": [], |
| 95 | + "source": [ |
| 96 | + "UnevaluatableIntegral.dummify = False" |
| 97 | + ] |
| 98 | + }, |
| 99 | + { |
| 100 | + "cell_type": "code", |
| 101 | + "execution_count": null, |
| 102 | + "metadata": { |
| 103 | + "tags": [ |
| 104 | + "hide-input", |
| 105 | + "full-width" |
| 106 | + ] |
| 107 | + }, |
| 108 | + "outputs": [], |
| 109 | + "source": [ |
| 110 | + "s, m1, m2, z = sp.symbols(\"s m1 m2 z\", nonnegative=True)\n", |
| 111 | + "ell = sp.Symbol(\"ell\", integer=True, nonnegative=True)\n", |
| 112 | + "cm = ChewMandelstamIntegral(s, m1, m2, ell)\n", |
| 113 | + "ff = FormFactor(s, m1, m2, ell)\n", |
| 114 | + "rho = PhaseSpaceFactor(s, m1, m2)\n", |
| 115 | + "bl = BlattWeisskopfSquared(z, ell)\n", |
| 116 | + "max_ell = 5\n", |
| 117 | + "src = aslatex({\n", |
| 118 | + " **{e: e.doit(deep=False) for e in (cm, ff, rho)},\n", |
| 119 | + " **{bl.subs(ell, i): bl.subs(ell, i).doit() for i in range(max_ell + 1)},\n", |
| 120 | + "})\n", |
| 121 | + "Math(src)" |
| 122 | + ] |
| 123 | + }, |
71 | 124 | { |
72 | 125 | "cell_type": "code", |
73 | 126 | "execution_count": null, |
|
88 | 141 | " s: npt.NDArray[np.float64],\n", |
89 | 142 | " m1: float,\n", |
90 | 143 | " m2: float,\n", |
| 144 | + " ell: int = 0,\n", |
91 | 145 | " start_offset: float = 0,\n", |
92 | 146 | " algorithm: Callable = quadax.quadcc,\n", |
93 | 147 | " **configuration,\n", |
94 | 148 | "):\n", |
95 | 149 | " s_thr = (m1 + m2) ** 2\n", |
96 | 150 | " if algorithm is quad_vec:\n", |
97 | 151 | " integral, _ = algorithm(\n", |
98 | | - " partial(integrand, s=s, m1=m1, m2=m2),\n", |
| 152 | + " partial(integrand, s=s, m1=m1, m2=m2, ell=ell),\n", |
99 | 153 | " s_thr + start_offset,\n", |
100 | 154 | " np.inf,\n", |
101 | 155 | " **configuration,\n", |
102 | 156 | " )\n", |
103 | 157 | " else:\n", |
104 | 158 | " integral, _ = algorithm(\n", |
105 | | - " jax.tree_util.Partial(integrand, s=s, m1=m1, m2=m2),\n", |
| 159 | + " jax.tree_util.Partial(integrand, s=s, m1=m1, m2=m2, ell=ell),\n", |
106 | 160 | " interval=[s_thr + start_offset, jnp.inf],\n", |
107 | 161 | " **configuration,\n", |
108 | 162 | " )\n", |
109 | 163 | " return (s - s_thr) * integral / jnp.pi\n", |
110 | 164 | "\n", |
111 | 165 | "\n", |
112 | 166 | "@jax.jit\n", |
113 | | - "def integrand(sp, s, m1, m2):\n", |
| 167 | + "def integrand(sp, s, m1, m2, ell):\n", |
114 | 168 | " s_thr = (m1 + m2) ** 2\n", |
115 | | - " return rho(sp, m1, m2) / ((sp - s_thr) * (sp - s))\n", |
| 169 | + " return rho_func(sp, m1, m2) * n2(s, m1, m2, ell) / ((sp - s_thr) * (sp - s))\n", |
116 | 170 | "\n", |
117 | 171 | "\n", |
118 | 172 | "@jax.jit\n", |
119 | | - "def rho(s, m1, m2):\n", |
120 | | - " return jnp.sqrt((s - (m1 - m2) ** 2) * (s - (m1 + m2) ** 2)) / s" |
| 173 | + "def rho_func(s, m1, m2):\n", |
| 174 | + " return jnp.sqrt(s - (m1 - m2) ** 2) * jnp.sqrt(s - (m1 + m2) ** 2) / s\n", |
| 175 | + "\n", |
| 176 | + "\n", |
| 177 | + "def n2(s, m1, m2, ell):\n", |
| 178 | + " return blatt_weisskopf_squared(q(s, m1, m2), ell)\n", |
| 179 | + "\n", |
| 180 | + "\n", |
| 181 | + "def blatt_weisskopf_squared(z, ell):\n", |
| 182 | + " return jnp.select(\n", |
| 183 | + " [ell == 0, ell == 1, ell == 2, ell == 3, ell == 4, ell == 5],\n", |
| 184 | + " [\n", |
| 185 | + " 1,\n", |
| 186 | + " 2 * z / (z + 1),\n", |
| 187 | + " 13 * z**2 / (z**2 + 3 * z + 9),\n", |
| 188 | + " 277 * z**3 / (z**3 + 6 * z**2 + 45 * z + 225),\n", |
| 189 | + " 12746 * z**4 / (z**4 + 10 * z**3 + 135 * z**2 + 1575 * z + 11025),\n", |
| 190 | + " 998881\n", |
| 191 | + " * z**5\n", |
| 192 | + " / (z**5 + 15 * z**4 + 315 * z**3 + 6300 * z**2 + 99225 * z + 893025),\n", |
| 193 | + " ],\n", |
| 194 | + " default=jnp.nan,\n", |
| 195 | + " )\n", |
| 196 | + "\n", |
| 197 | + "\n", |
| 198 | + "@jax.jit\n", |
| 199 | + "def q(s, m1, m2):\n", |
| 200 | + " return (\n", |
| 201 | + " jnp.sqrt(s - (m1 - m2) ** 2) * jnp.sqrt(s - (m1 + m2) ** 2) / (2 * jnp.sqrt(s))\n", |
| 202 | + " )" |
| 203 | + ] |
| 204 | + }, |
| 205 | + { |
| 206 | + "cell_type": "markdown", |
| 207 | + "metadata": {}, |
| 208 | + "source": [ |
| 209 | + "In the case of S-waves, we can compare the result of the integration to the analytical result.\n", |
| 210 | + "\n", |
| 211 | + "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`)." |
| 212 | + ] |
| 213 | + }, |
| 214 | + { |
| 215 | + "cell_type": "code", |
| 216 | + "execution_count": null, |
| 217 | + "metadata": { |
| 218 | + "tags": [ |
| 219 | + "hide-input", |
| 220 | + "full-width" |
| 221 | + ] |
| 222 | + }, |
| 223 | + "outputs": [], |
| 224 | + "source": [ |
| 225 | + "from ampform.dynamics.phasespace import ChewMandelstamSWave\n", |
| 226 | + "\n", |
| 227 | + "CM0 = ChewMandelstamSWave(s, m1, m2)\n", |
| 228 | + "Math(aslatex({CM0: CM0.doit(deep=False)}))" |
121 | 229 | ] |
122 | 230 | }, |
123 | 231 | { |
|
142 | 250 | " (2 * q(s, m1, m2) / jnp.sqrt(s))\n", |
143 | 251 | " * jnp.log((m1**2 + m2**2 - s + 2 * q(s, m1, m2) * jnp.sqrt(s)) / (2 * m1 * m2))\n", |
144 | 252 | " - (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))" |
| 253 | + " )" |
151 | 254 | ] |
152 | 255 | }, |
153 | 256 | { |
|
177 | 280 | " ),\n", |
178 | 281 | " m1=w.FloatSlider(value=0.13, min=0.0, max=2.0, step=0.01, description=\"m₁\", **cont),\n", |
179 | 282 | " m2=w.FloatSlider(value=0.98, min=0.0, max=2.0, step=0.01, description=\"m₂\", **cont),\n", |
| 283 | + " ell=w.IntSlider(value=0, min=0, max=5, description=\"ℓ\", **cont),\n", |
180 | 284 | " y_lim=w.FloatRangeSlider(\n", |
181 | 285 | " description=\"y range\",\n", |
182 | 286 | " min=-5,\n", |
|
314 | 418 | " tabs := w.Tab([\n", |
315 | 419 | " w.HBox([\n", |
316 | 420 | " physics_sliders[\"projection\"],\n", |
317 | | - " w.VBox(list(physics_sliders.values())[1:4]),\n", |
318 | | - " w.VBox(list(physics_sliders.values())[4:]),\n", |
| 421 | + " w.VBox(list(physics_sliders.values())[1:5]),\n", |
| 422 | + " w.VBox(list(physics_sliders.values())[5:]),\n", |
319 | 423 | " ]),\n", |
320 | 424 | " w.HBox([\n", |
321 | 425 | " algorithm_sliders[\"algorithm_name\"],\n", |
|
381 | 485 | " projection: Literal[\"real\", \"imag\", \"abs\"],\n", |
382 | 486 | " m1: float,\n", |
383 | 487 | " m2: float,\n", |
| 488 | + " ell: int,\n", |
384 | 489 | " epsilon: float,\n", |
385 | 490 | " start_offset: float,\n", |
386 | 491 | " z_max: float,\n", |
|
400 | 505 | " z_ana = sigma0(s, m1, m2)\n", |
401 | 506 | " alg_kwargs = get_algorithm_options(algorithm_name, **alg_kwargs)\n", |
402 | 507 | " start_time = time.perf_counter()\n", |
403 | | - " z_num = integrate_numerically(s, m1, m2, start_offset, algorithm, **alg_kwargs)\n", |
| 508 | + " z_num = integrate_numerically(\n", |
| 509 | + " s, m1, m2, ell, start_offset, algorithm, **alg_kwargs\n", |
| 510 | + " )\n", |
404 | 511 | " z_num.block_until_ready()\n", |
405 | 512 | " end_time = time.perf_counter()\n", |
406 | 513 | " z = z_exact, z_ana, z_num\n", |
407 | 514 | " duration = end_time - start_time\n", |
408 | 515 | " timer_box.value = f\"Computation time: <b>{format_time(duration)}</b> for {resolution:,} points\"\n", |
409 | 516 | " if np.all(np.isnan(z_num)):\n", |
410 | 517 | " timer_box.value += \" (<font color='red'>all values are NaN</font>)\"\n", |
411 | | - " Z = rho(S, m1, m2)\n", |
| 518 | + " Z = rho_func(S, m1, m2) * n2(S, m1, m2, ell)\n", |
412 | 519 | " if projection == \"abs\":\n", |
413 | 520 | " Z = jnp.abs(Z)\n", |
414 | 521 | " else:\n", |
|
541 | 648 | " ax.spines[\"right\"].set_visible(False)\n", |
542 | 649 | " ax.spines[\"top\"].set_visible(False)\n", |
543 | 650 | "ax1.spines[\"bottom\"].set_visible(False)\n", |
544 | | - "ax1.set_title(R\"$\\rho(s)$\")\n", |
| 651 | + "ax1.set_title(R\"$\\rho(s) \\, n_\\ell^2(s)$\")\n", |
545 | 652 | "ax1.set_ylabel(\"Im $s$\")\n", |
546 | 653 | "ax2.set_ylabel(R\"Im $\\Sigma_0(s)$\")\n", |
547 | 654 | "ax3.set_ylabel(R\"Re $\\Sigma_0(s)$\")\n", |
|
0 commit comments