TransitionListener 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- transitionlistener/__init__.py +21 -0
- transitionlistener/bubbledynamics.py +2250 -0
- transitionlistener/bubbledynamics_fixedstep.py +2929 -0
- transitionlistener/check_installation.py +309 -0
- transitionlistener/ckm.py +91 -0
- transitionlistener/colors.py +288 -0
- transitionlistener/config.py +185 -0
- transitionlistener/constants.py +64 -0
- transitionlistener/counterterms/__init__.py +230 -0
- transitionlistener/counterterms/cw.py +298 -0
- transitionlistener/counterterms/tensors.py +343 -0
- transitionlistener/counterterms/twohdm.py +520 -0
- transitionlistener/counterterms/twohdm_solver.py +374 -0
- transitionlistener/errors.py +315 -0
- transitionlistener/examples/bp_et.yaml +64 -0
- transitionlistener/examples/bp_lisa.yaml +70 -0
- transitionlistener/examples/bp_muares.yaml +69 -0
- transitionlistener/examples/bp_pta.yaml +64 -0
- transitionlistener/examples/example_dark_photon.yaml +73 -0
- transitionlistener/examples/example_grid.yaml +55 -0
- transitionlistener/examples/example_line.yaml +51 -0
- transitionlistener/examples/example_point.yaml +73 -0
- transitionlistener/examples/example_random_conformal.yaml +42 -0
- transitionlistener/examples/example_ultranest.yaml +39 -0
- transitionlistener/finiteT.py +468 -0
- transitionlistener/generic_potential.py +1521 -0
- transitionlistener/gridplots.py +592 -0
- transitionlistener/gwfopt.py +604 -0
- transitionlistener/helper_functions.py +843 -0
- transitionlistener/hydrodynamics.py +647 -0
- transitionlistener/interface/__init__.py +95 -0
- transitionlistener/interface/check.py +51 -0
- transitionlistener/interface/cli.py +211 -0
- transitionlistener/interface/config_loader.py +264 -0
- transitionlistener/interface/logging_utils.py +30 -0
- transitionlistener/interface/output_schema.py +296 -0
- transitionlistener/interface/pipeline.py +300 -0
- transitionlistener/interface/samplers.py +565 -0
- transitionlistener/interface/scans.py +468 -0
- transitionlistener/interface/single_point.py +581 -0
- transitionlistener/interface/state.py +61 -0
- transitionlistener/lineplots.py +597 -0
- transitionlistener/logo/TL-logo_large.png +0 -0
- transitionlistener/logo/TL-logo_small.png +0 -0
- transitionlistener/models/TL_2HDM.py +512 -0
- transitionlistener/models/TL_conformal_dark_u1.py +202 -0
- transitionlistener/models/TL_dark_U1.py +198 -0
- transitionlistener/models/TL_dark_U1_g_parameterization.py +198 -0
- transitionlistener/models/TL_dark_flipflop.py +274 -0
- transitionlistener/models/templatePotential.py +260 -0
- transitionlistener/nucleation.py +412 -0
- transitionlistener/observability.py +790 -0
- transitionlistener/particles.py +436 -0
- transitionlistener/pathDeformation.py +1462 -0
- transitionlistener/percolation_adaptive_gridbuilders.py +1385 -0
- transitionlistener/percolation_adaptive_rate.py +1049 -0
- transitionlistener/percolation_adaptivestepsize.py +1632 -0
- transitionlistener/percolation_fixedstepsize.py +618 -0
- transitionlistener/phases.py +1499 -0
- transitionlistener/plot_settings.py +121 -0
- transitionlistener/plots.py +1781 -0
- transitionlistener/runtime_options.py +278 -0
- transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/bandwidths.npy +0 -0
- transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/density.npy +0 -0
- transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/freqs.npy +0 -0
- transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log.txt +16 -0
- transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log10rhogrid.npy +0 -0
- transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log10rholabels.txt +30 -0
- transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/pulsar_list.txt +1 -0
- transitionlistener/tab_data/HLV-O2-PLI.csv +82 -0
- transitionlistener/tab_data/HLV-O2-noise.csv +83 -0
- transitionlistener/tab_data/HLVK-design-PLI.csv +61 -0
- transitionlistener/tab_data/HLVK-design-noise.csv +83 -0
- transitionlistener/tab_data/NG15_PLI_hc.csv +31 -0
- transitionlistener/tab_data/NG15_noise_hc.csv +56 -0
- transitionlistener/tab_data/PLI-dataset.csv +327 -0
- transitionlistener/tab_data/energydensities.dat +1001 -0
- transitionlistener/tab_data/finiteT_b.dat.txt +1999 -0
- transitionlistener/tab_data/finiteT_f.dat.txt +1000 -0
- transitionlistener/tab_data/geff_SM.dat +10009 -0
- transitionlistener/tab_data/muAres_f_Hz_PLI_h2OmegaGW.csv +131 -0
- transitionlistener/tab_data/muAres_f_Hz_h2Omega_noise.csv +99 -0
- transitionlistener/tab_data/noise-dataset.csv +327 -0
- transitionlistener/tab_data/numberdensities.dat +1001 -0
- transitionlistener/tab_data/pressures.dat +1001 -0
- transitionlistener/thermodynamics.py +686 -0
- transitionlistener/transitionObservables.py +1199 -0
- transitionlistener/transitionObservables_fixedstep.py +976 -0
- transitionlistener/transitions.py +1000 -0
- transitionlistener/tunneling1D.py +910 -0
- transitionlistener-2.0.0.dist-info/METADATA +817 -0
- transitionlistener-2.0.0.dist-info/RECORD +95 -0
- transitionlistener-2.0.0.dist-info/WHEEL +4 -0
- transitionlistener-2.0.0.dist-info/entry_points.txt +2 -0
- transitionlistener-2.0.0.dist-info/licenses/LICENSE.txt +674 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""Installation self-test for TransitionListener.
|
|
2
|
+
|
|
3
|
+
Called by ``tl --check`` (via src/transitionlistener/interface/check.py).
|
|
4
|
+
Can also be run directly: ``python tests/check_installation.py``.
|
|
5
|
+
|
|
6
|
+
The quick checks (~300 ms) use only code that ships inside the package.
|
|
7
|
+
They are followed automatically by an end-to-end physics smoke test
|
|
8
|
+
(~1-2 min) that runs ``example_point.yaml`` and validates key physical
|
|
9
|
+
outputs.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import math
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
import shutil
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
import tempfile
|
|
21
|
+
import time
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _result(label: str, ok: bool, detail: str = "") -> None:
|
|
26
|
+
from transitionlistener import console
|
|
27
|
+
status = "[bold green]PASS[/bold green]" if ok else "[bold red]FAIL[/bold red]"
|
|
28
|
+
line = f" {status} {label}"
|
|
29
|
+
if detail:
|
|
30
|
+
line += f" ({detail})"
|
|
31
|
+
console.print(line)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _find_examples_root() -> Path | None:
|
|
35
|
+
"""Return the directory that contains both examples/ and models/.
|
|
36
|
+
|
|
37
|
+
Works for repo clones (this file lives at tests/check_installation.py,
|
|
38
|
+
so the repo root is one level up) and for pip installs where hatchling
|
|
39
|
+
has bundled examples/ as transitionlistener/examples/.
|
|
40
|
+
"""
|
|
41
|
+
# Repo clone: tests/check_installation.py → repo root is parents[1]
|
|
42
|
+
candidate = Path(__file__).resolve().parents[1]
|
|
43
|
+
if (candidate / "examples" / "example_point.yaml").exists():
|
|
44
|
+
return candidate
|
|
45
|
+
# pip install: hatchling bundled examples/ as transitionlistener/examples/
|
|
46
|
+
try:
|
|
47
|
+
import importlib.resources as ir
|
|
48
|
+
pkg_root = Path(str(ir.files("transitionlistener")))
|
|
49
|
+
if (pkg_root / "examples" / "example_point.yaml").exists():
|
|
50
|
+
return pkg_root
|
|
51
|
+
except Exception:
|
|
52
|
+
pass
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _run_smoke(console, failures: int) -> int:
|
|
57
|
+
"""Run the end-to-end physics smoke test. Returns updated failure count."""
|
|
58
|
+
from rich.panel import Panel
|
|
59
|
+
|
|
60
|
+
console.print()
|
|
61
|
+
console.print(Panel.fit("[bold]End-to-end physics check[/bold]", border_style="cyan"))
|
|
62
|
+
console.print()
|
|
63
|
+
|
|
64
|
+
repo_root = _find_examples_root()
|
|
65
|
+
if repo_root is None:
|
|
66
|
+
console.print(" [bold yellow]SKIP[/bold yellow] smoke test (examples/ directory not found)")
|
|
67
|
+
return failures
|
|
68
|
+
|
|
69
|
+
yaml_src = repo_root / "examples" / "example_point.yaml"
|
|
70
|
+
model_src = repo_root / "models" / "TL_conformal_dark_u1.py"
|
|
71
|
+
|
|
72
|
+
with tempfile.TemporaryDirectory() as _tmp:
|
|
73
|
+
tmpdir = Path(_tmp)
|
|
74
|
+
(tmpdir / "examples").mkdir()
|
|
75
|
+
(tmpdir / "models").mkdir()
|
|
76
|
+
shutil.copy(yaml_src, tmpdir / "examples" / "example_point.yaml")
|
|
77
|
+
shutil.copy(model_src, tmpdir / "models" / "TL_conformal_dark_u1.py")
|
|
78
|
+
|
|
79
|
+
env = os.environ.copy()
|
|
80
|
+
env.update({"OMP_NUM_THREADS": "1", "OPENBLAS_NUM_THREADS": "1", "MKL_NUM_THREADS": "1"})
|
|
81
|
+
|
|
82
|
+
console.print(" Running example_point.yaml [dim](conformal U(1), SinglePoint — takes ~1-2 min)[/dim]")
|
|
83
|
+
t0 = time.perf_counter()
|
|
84
|
+
# Prefer the `tl` script from the same env as sys.executable.
|
|
85
|
+
tl_script = Path(sys.executable).parent / "tl"
|
|
86
|
+
if not tl_script.exists():
|
|
87
|
+
tl_script = Path(sys.executable).parent / "tl.exe" # Windows
|
|
88
|
+
cmd = (
|
|
89
|
+
[str(tl_script), "-c", "examples/example_point.yaml", "-j", "1"]
|
|
90
|
+
if tl_script.exists()
|
|
91
|
+
else [sys.executable, "-c",
|
|
92
|
+
"from transitionlistener.interface import main; main()",
|
|
93
|
+
"-c", "examples/example_point.yaml", "-j", "1"]
|
|
94
|
+
)
|
|
95
|
+
try:
|
|
96
|
+
proc = subprocess.run(
|
|
97
|
+
cmd,
|
|
98
|
+
cwd=tmpdir,
|
|
99
|
+
env=env,
|
|
100
|
+
capture_output=True,
|
|
101
|
+
text=True,
|
|
102
|
+
timeout=300,
|
|
103
|
+
)
|
|
104
|
+
except subprocess.TimeoutExpired:
|
|
105
|
+
_result("smoke test", False, "timed out after 300 s")
|
|
106
|
+
return failures + 1
|
|
107
|
+
elapsed = time.perf_counter() - t0
|
|
108
|
+
|
|
109
|
+
if proc.returncode != 0:
|
|
110
|
+
_result("smoke test", False, f"exit code {proc.returncode}")
|
|
111
|
+
console.print(f" [dim]{proc.stderr[-500:]}[/dim]")
|
|
112
|
+
return failures + 1
|
|
113
|
+
|
|
114
|
+
out_file = tmpdir / "scans" / "example_point" / "1_All_params.txt"
|
|
115
|
+
if not out_file.exists():
|
|
116
|
+
_result("smoke test", False, "output file not created")
|
|
117
|
+
return failures + 1
|
|
118
|
+
|
|
119
|
+
# Parse key-value output table
|
|
120
|
+
values: dict[str, float] = {}
|
|
121
|
+
for line in out_file.read_text().splitlines():
|
|
122
|
+
m = re.match(r"^\s*(\S+)\s+(\S.*?)\s*$", line)
|
|
123
|
+
if m:
|
|
124
|
+
try:
|
|
125
|
+
values[m.group(1)] = float(m.group(2))
|
|
126
|
+
except ValueError:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
EXPECTED = {
|
|
130
|
+
"Tperc_SM_GeV": (1.0e-3, 5.0e-2),
|
|
131
|
+
"alpha": (1.0e+1, 1.0e+4),
|
|
132
|
+
"RH": (1.0e-6, 1.0e+0),
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
smoke_ok = True
|
|
136
|
+
for key, (lo, hi) in EXPECTED.items():
|
|
137
|
+
if key not in values:
|
|
138
|
+
_result(f" {key}", False, "missing from output")
|
|
139
|
+
smoke_ok = False
|
|
140
|
+
failures += 1
|
|
141
|
+
elif not math.isfinite(values[key]) or not (lo <= values[key] <= hi):
|
|
142
|
+
_result(f" {key}", False, f"{values[key]:.3e} outside [{lo:.0e}, {hi:.0e}]")
|
|
143
|
+
smoke_ok = False
|
|
144
|
+
failures += 1
|
|
145
|
+
else:
|
|
146
|
+
_result(f" {key}", True, f"{values[key]:.3e}")
|
|
147
|
+
|
|
148
|
+
_result("smoke test (example_point.yaml)", smoke_ok, f"{elapsed:.0f} s")
|
|
149
|
+
if not smoke_ok:
|
|
150
|
+
failures += 1
|
|
151
|
+
|
|
152
|
+
return failures
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def run_checks() -> int:
|
|
156
|
+
"""Run all installation checks. Returns 0 on success, 1 on failure."""
|
|
157
|
+
from transitionlistener import console
|
|
158
|
+
from rich.panel import Panel
|
|
159
|
+
|
|
160
|
+
console.print(Panel.fit("[bold]TransitionListener installation check[/bold]", border_style="cyan"))
|
|
161
|
+
console.print()
|
|
162
|
+
|
|
163
|
+
failures = 0
|
|
164
|
+
|
|
165
|
+
# ------------------------------------------------------------------
|
|
166
|
+
# 1. Core scientific imports
|
|
167
|
+
# ------------------------------------------------------------------
|
|
168
|
+
for name in ("numpy", "scipy", "matplotlib", "pandas", "rich", "tqdm", "yaml"):
|
|
169
|
+
t0 = time.perf_counter()
|
|
170
|
+
try:
|
|
171
|
+
__import__(name)
|
|
172
|
+
_result(f"import {name}", True, f"{(time.perf_counter()-t0)*1e3:.0f} ms")
|
|
173
|
+
except ImportError as exc:
|
|
174
|
+
_result(f"import {name}", False, str(exc))
|
|
175
|
+
failures += 1
|
|
176
|
+
|
|
177
|
+
# ------------------------------------------------------------------
|
|
178
|
+
# 2. Thermodynamics (reads tab_data shipped with the package)
|
|
179
|
+
# ------------------------------------------------------------------
|
|
180
|
+
try:
|
|
181
|
+
t0 = time.perf_counter()
|
|
182
|
+
from transitionlistener.thermodynamics import e_geffSM, p_geffSM, s_geffSM
|
|
183
|
+
import numpy as np
|
|
184
|
+
T, CF = 10.0, 1.0
|
|
185
|
+
e = (math.pi**2 / 30) * e_geffSM(T, CF) * T**4
|
|
186
|
+
p = (math.pi**2 / 90) * p_geffSM(T, CF) * T**4
|
|
187
|
+
s = (2 * math.pi**2 / 45) * s_geffSM(T, CF) * T**3
|
|
188
|
+
residual = abs(s - (e + p) / T)
|
|
189
|
+
ok = residual < 1e-4
|
|
190
|
+
_result("thermodynamics (e+p=sT)", ok, f"residual={residual:.2e}, {(time.perf_counter()-t0)*1e3:.0f} ms")
|
|
191
|
+
if not ok:
|
|
192
|
+
failures += 1
|
|
193
|
+
except Exception as exc:
|
|
194
|
+
_result("thermodynamics", False, str(exc))
|
|
195
|
+
failures += 1
|
|
196
|
+
|
|
197
|
+
# ------------------------------------------------------------------
|
|
198
|
+
# 3. Percolation integral (ODE solver, no model file needed)
|
|
199
|
+
# ------------------------------------------------------------------
|
|
200
|
+
try:
|
|
201
|
+
t0 = time.perf_counter()
|
|
202
|
+
import numpy as np
|
|
203
|
+
from transitionlistener.bubbledynamics import percIntegralODE, percIntegral
|
|
204
|
+
T = np.geomspace(50.0, 0.5, 80)
|
|
205
|
+
s_over_t = 120.0 * (T / T[0]) ** 0.5 + 5.0
|
|
206
|
+
S = s_over_t * T
|
|
207
|
+
H = T**2 / 1e6
|
|
208
|
+
I_ode = percIntegralODE(T, H, S, vw=1.0)
|
|
209
|
+
ok = I_ode.shape == T.shape and float(I_ode[-1]) > 0.0
|
|
210
|
+
_result("percolation ODE integral", ok, f"{(time.perf_counter()-t0)*1e3:.0f} ms")
|
|
211
|
+
if not ok:
|
|
212
|
+
failures += 1
|
|
213
|
+
except Exception as exc:
|
|
214
|
+
_result("percolation ODE integral", False, str(exc))
|
|
215
|
+
failures += 1
|
|
216
|
+
|
|
217
|
+
# ------------------------------------------------------------------
|
|
218
|
+
# 4. Bounce action (asymmetric double-well: V = (x^2-1)^2/4 - eps*x)
|
|
219
|
+
# Minima at x≈±1; tiny eps=0.01 makes x≈+1 the true vacuum.
|
|
220
|
+
# Barrier at x≈0 sits well above both minima.
|
|
221
|
+
# ------------------------------------------------------------------
|
|
222
|
+
try:
|
|
223
|
+
t0 = time.perf_counter()
|
|
224
|
+
import numpy as np
|
|
225
|
+
from transitionlistener import pathDeformation
|
|
226
|
+
|
|
227
|
+
def V(x):
|
|
228
|
+
phi = np.asarray(x, dtype=float).ravel()
|
|
229
|
+
vals = 0.25 * (phi**2 - 1.0)**2 - 0.01 * phi
|
|
230
|
+
return float(vals[0]) if vals.size == 1 else vals
|
|
231
|
+
|
|
232
|
+
def dV(x):
|
|
233
|
+
x = np.asarray(x, dtype=float)
|
|
234
|
+
phi = x.ravel()
|
|
235
|
+
return (phi**3 - phi - 0.01).reshape(x.shape)
|
|
236
|
+
|
|
237
|
+
x_true = np.array([1.0])
|
|
238
|
+
x_false = np.array([-1.0])
|
|
239
|
+
tobj = pathDeformation.fullTunneling(
|
|
240
|
+
[x_true, x_false], V, dV,
|
|
241
|
+
callback_data=1.0,
|
|
242
|
+
)
|
|
243
|
+
finite = math.isfinite(float(tobj.action))
|
|
244
|
+
_result("bounce action (synthetic 1D)", finite, f"S3={tobj.action:.4f}, {(time.perf_counter()-t0)*1e3:.0f} ms")
|
|
245
|
+
if not finite:
|
|
246
|
+
failures += 1
|
|
247
|
+
except Exception as exc:
|
|
248
|
+
_result("bounce action (synthetic 1D)", False, str(exc))
|
|
249
|
+
failures += 1
|
|
250
|
+
|
|
251
|
+
# ------------------------------------------------------------------
|
|
252
|
+
# 5. Optional: MPI (informational — never fails)
|
|
253
|
+
# ------------------------------------------------------------------
|
|
254
|
+
try:
|
|
255
|
+
t0 = time.perf_counter()
|
|
256
|
+
from mpi4py import MPI # noqa: F401
|
|
257
|
+
size = MPI.COMM_WORLD.Get_size()
|
|
258
|
+
console.print(f" [bold cyan]INFO[/bold cyan] mpi4py [green]found[/green] (MPI size={size}, {(time.perf_counter()-t0)*1e3:.0f} ms)")
|
|
259
|
+
except Exception:
|
|
260
|
+
console.print(" [bold cyan]INFO[/bold cyan] mpi4py [yellow]not installed[/yellow] (MPI parallelism disabled)")
|
|
261
|
+
|
|
262
|
+
# ------------------------------------------------------------------
|
|
263
|
+
# 6. Optional: ptarcade / enterprise (informational — never fails)
|
|
264
|
+
# ------------------------------------------------------------------
|
|
265
|
+
try:
|
|
266
|
+
t0 = time.perf_counter()
|
|
267
|
+
import ptarcade # noqa: F401
|
|
268
|
+
from enterprise.signals.parameter import Uniform # noqa: F401
|
|
269
|
+
console.print(f" [bold cyan]INFO[/bold cyan] ptarcade + enterprise [green]found[/green] ({(time.perf_counter()-t0)*1e3:.0f} ms)")
|
|
270
|
+
except Exception:
|
|
271
|
+
console.print(" [bold cyan]INFO[/bold cyan] ptarcade + enterprise [yellow]not installed[/yellow] (PTA likelihood disabled)")
|
|
272
|
+
|
|
273
|
+
# ------------------------------------------------------------------
|
|
274
|
+
# Quick-check summary
|
|
275
|
+
# ------------------------------------------------------------------
|
|
276
|
+
console.print()
|
|
277
|
+
if failures == 0:
|
|
278
|
+
console.print(Panel.fit(
|
|
279
|
+
f"[bold green]Quick checks passed[/bold green] "
|
|
280
|
+
f"(Python {sys.version.split()[0]}, TL on {sys.platform})",
|
|
281
|
+
border_style="green",
|
|
282
|
+
))
|
|
283
|
+
else:
|
|
284
|
+
console.print(Panel.fit(
|
|
285
|
+
f"[bold red]{failures} quick check(s) failed[/bold red] — see above",
|
|
286
|
+
border_style="red",
|
|
287
|
+
))
|
|
288
|
+
|
|
289
|
+
# ------------------------------------------------------------------
|
|
290
|
+
# 7. End-to-end physics smoke test (automatic follow-up)
|
|
291
|
+
# ------------------------------------------------------------------
|
|
292
|
+
failures = _run_smoke(console, failures)
|
|
293
|
+
|
|
294
|
+
# ------------------------------------------------------------------
|
|
295
|
+
# Final summary
|
|
296
|
+
# ------------------------------------------------------------------
|
|
297
|
+
console.print()
|
|
298
|
+
if failures == 0:
|
|
299
|
+
console.print(Panel.fit("[bold green]All checks passed[/bold green]", border_style="green"))
|
|
300
|
+
else:
|
|
301
|
+
console.print(Panel.fit(
|
|
302
|
+
f"[bold red]{failures} check(s) failed[/bold red] — see above",
|
|
303
|
+
border_style="red",
|
|
304
|
+
))
|
|
305
|
+
return 0 if failures == 0 else 1
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
if __name__ == "__main__":
|
|
309
|
+
raise SystemExit(run_checks())
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Cabibbo–Kobayashi–Maskawa matrix helpers.
|
|
3
|
+
|
|
4
|
+
The functions defined here provide ready-to-use parametrisations of the CKM
|
|
5
|
+
matrix that can be shared between models. The default implementation follows
|
|
6
|
+
the Wolfenstein expansion,
|
|
7
|
+
|
|
8
|
+
.. math::
|
|
9
|
+
V_{\mathrm{CKM}} \approx
|
|
10
|
+
\begin{pmatrix}
|
|
11
|
+
1 - \tfrac{1}{2}\lambda^2 & \lambda &
|
|
12
|
+
A \lambda^3 (\rho - i \eta) \\
|
|
13
|
+
-\lambda & 1 - \tfrac{1}{2}\lambda^2 & A \lambda^2 \\
|
|
14
|
+
A \lambda^3 \left(1 - \rho - i \eta\right) &
|
|
15
|
+
-A \lambda^2 & 1
|
|
16
|
+
\end{pmatrix},
|
|
17
|
+
|
|
18
|
+
with higher-order terms resummed to guarantee unitarity. The phases are
|
|
19
|
+
returned as complex numbers so that downstream code can keep track of CP
|
|
20
|
+
violating effects without additional bookkeeping.
|
|
21
|
+
|
|
22
|
+
Part of TransitionListener v2.0
|
|
23
|
+
Documentation: https://tasillo.de/TransitionListener/
|
|
24
|
+
|
|
25
|
+
Authors:
|
|
26
|
+
Jonas Matuszak <jonas.matuszak@kit.edu>
|
|
27
|
+
Carlo Tasillo <carlo.tasillo@ific.uv.es>
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import cmath
|
|
33
|
+
import math
|
|
34
|
+
from typing import Dict
|
|
35
|
+
|
|
36
|
+
CKMElementMap = Dict[str, complex]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def wolfenstein_ckm(
|
|
40
|
+
lambda_w: float = 0.22537,
|
|
41
|
+
A: float = 0.814,
|
|
42
|
+
rho: float = 0.117,
|
|
43
|
+
eta: float = 0.353,
|
|
44
|
+
) -> CKMElementMap:
|
|
45
|
+
r"""
|
|
46
|
+
Return the CKM matrix elements in the Wolfenstein parametrisation.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
lambda_w:
|
|
51
|
+
Cabibbo angle expansion parameter :math:`\\lambda`.
|
|
52
|
+
A:
|
|
53
|
+
Overall strength of the 2–3 mixing.
|
|
54
|
+
rho:
|
|
55
|
+
Real part of the complex phase.
|
|
56
|
+
eta:
|
|
57
|
+
Imaginary part of the complex phase.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
dict[str, complex]
|
|
62
|
+
Mapping ``Vxy -> V_{xy}`` covering all nine CKM entries.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
theta12 = math.asin(lambda_w)
|
|
66
|
+
theta23 = math.asin(A * lambda_w**2)
|
|
67
|
+
complex_phase = A * (lambda_w**3) * (rho + 1j * eta)
|
|
68
|
+
delta = cmath.phase(complex_phase)
|
|
69
|
+
theta13 = math.asin(abs(complex_phase))
|
|
70
|
+
|
|
71
|
+
Vud = math.cos(theta12) * math.cos(theta13)
|
|
72
|
+
Vus = math.sin(theta12) * math.cos(theta13)
|
|
73
|
+
Vub = math.sin(theta13) * cmath.exp(-1j * delta)
|
|
74
|
+
Vcd = -math.sin(theta12) * math.cos(theta23) - math.cos(theta12) * math.sin(theta23) * math.sin(theta13) * cmath.exp(1j * delta)
|
|
75
|
+
Vcs = math.cos(theta12) * math.cos(theta23) - math.sin(theta12) * math.sin(theta23) * math.sin(theta13) * cmath.exp(1j * delta)
|
|
76
|
+
Vcb = math.sin(theta23) * math.cos(theta13)
|
|
77
|
+
Vtd = math.sin(theta12) * math.sin(theta23) - math.cos(theta12) * math.cos(theta23) * math.sin(theta13) * cmath.exp(1j * delta)
|
|
78
|
+
Vts = -math.cos(theta12) * math.sin(theta23) - math.sin(theta12) * math.cos(theta23) * math.sin(theta13) * cmath.exp(1j * delta)
|
|
79
|
+
Vtb = math.cos(theta23) * math.cos(theta13)
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
"Vud": Vud,
|
|
83
|
+
"Vus": Vus,
|
|
84
|
+
"Vub": Vub,
|
|
85
|
+
"Vcd": Vcd,
|
|
86
|
+
"Vcs": Vcs,
|
|
87
|
+
"Vcb": Vcb,
|
|
88
|
+
"Vtd": Vtd,
|
|
89
|
+
"Vts": Vts,
|
|
90
|
+
"Vtb": Vtb,
|
|
91
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Color palettes and Matplotlib helpers used across TransitionListener plots.
|
|
2
|
+
|
|
3
|
+
The constants exposed here encode the corporate identity color palettes that the
|
|
4
|
+
project uses for plots in the documentation as well as publication-quality
|
|
5
|
+
figures. A couple of helper utilities are provided to make it easy to size
|
|
6
|
+
figures consistently and to configure Matplotlib's global font settings.
|
|
7
|
+
|
|
8
|
+
Part of TransitionListener v2.0
|
|
9
|
+
Documentation: https://tasillo.de/TransitionListener/
|
|
10
|
+
|
|
11
|
+
Authors:
|
|
12
|
+
Jonas Matuszak <jonas.matuszak@kit.edu>
|
|
13
|
+
Carlo Tasillo <carlo.tasillo@ific.uv.es>
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import matplotlib.pyplot as plt
|
|
17
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
18
|
+
from cycler import cycler
|
|
19
|
+
|
|
20
|
+
RWTHblau = (0 / 255, 84 / 255, 159 / 255)
|
|
21
|
+
RWTHblau75 = (0 / 255, 84 / 255, 159 / 255, 0.75)
|
|
22
|
+
RWTHblau50 = (0 / 255, 84 / 255, 159 / 255, 0.5)
|
|
23
|
+
RWTHblau25 = (0 / 255, 84 / 255, 159 / 255, 0.25)
|
|
24
|
+
RWTHblau10 = (0 / 255, 84 / 255, 159 / 255, 0.1)
|
|
25
|
+
RWTHblau20 = (0 / 255, 84 / 255, 159 / 255, 0.2)
|
|
26
|
+
RWTHblau40 = (0 / 255, 84 / 255, 159 / 255, 0.4)
|
|
27
|
+
RWTHblau30 = (0 / 255, 84 / 255, 159 / 255, 0.3)
|
|
28
|
+
RWTHblau60 = (0 / 255, 84 / 255, 159 / 255, 0.6)
|
|
29
|
+
RWTHschwarz = (0 / 255, 0 / 255, 0 / 255)
|
|
30
|
+
RWTHmagenta = (227 / 255, 0 / 255, 102 / 255)
|
|
31
|
+
RWTHgelb = (255 / 255, 237 / 255, 0 / 255)
|
|
32
|
+
RWTHpetrol = (0 / 255, 97 / 255, 101 / 255)
|
|
33
|
+
RWTHpetrol25 = (0 / 255, 97 / 255, 101 / 255, 0.25)
|
|
34
|
+
RWTHtuerkis = (0 / 255, 152 / 255, 161 / 255)
|
|
35
|
+
RWTHgruen = (87 / 255, 171 / 255, 39 / 255)
|
|
36
|
+
RWTHgruen10 = (87 / 255, 171 / 255, 39 / 255, 0.1)
|
|
37
|
+
RWTHgruen25 = (87 / 255, 171 / 255, 39 / 255, 0.25)
|
|
38
|
+
RWTHmaigruen = (189 / 255, 205 / 255, 0 / 255)
|
|
39
|
+
RWTHmaigruen25 = (189 / 255, 205 / 255, 0 / 255, 0.25)
|
|
40
|
+
RWTHmaigruen50 = (189 / 255, 205 / 255, 0 / 255, 0.5)
|
|
41
|
+
RWTHorange = (246 / 255, 168 / 255, 0 / 255)
|
|
42
|
+
RWTHorange25 = (246 / 255, 168 / 255, 0 / 255, 0.25)
|
|
43
|
+
RWTHorange50 = (246 / 255, 168 / 255, 0 / 255, 0.5)
|
|
44
|
+
RWTHrot = (204 / 255, 7 / 255, 30 / 255)
|
|
45
|
+
RWTHrot25 = (204 / 255, 7 / 255, 30 / 255, 0.25)
|
|
46
|
+
RWTHrot50 = (204 / 255, 7 / 255, 30 / 255, 0.5)
|
|
47
|
+
RWTHbordeaux = (161 / 255, 16 / 255, 53 / 255)
|
|
48
|
+
RWTHbordeaux50 = (161 / 255, 16 / 255, 53 / 255, 0.5)
|
|
49
|
+
RWTHbordeaux25 = (161 / 255, 16 / 255, 53 / 255, 0.25)
|
|
50
|
+
RWTHbordeaux10 = (161 / 255, 16 / 255, 53 / 255, 0.1)
|
|
51
|
+
RWTHviolett = (97 / 255, 33 / 255, 88 / 255)
|
|
52
|
+
RWTHviolett50 = (97 / 255, 33 / 255, 88 / 255, 0.5)
|
|
53
|
+
RWTHlila = (122 / 255, 111 / 255, 172 / 255)
|
|
54
|
+
|
|
55
|
+
DESYcyan = (0 / 255, 159 / 255, 223 / 255)
|
|
56
|
+
DESYcyan50 = (0 / 255, 159 / 255, 223 / 255, 0.5)
|
|
57
|
+
DESYcyan25 = (0 / 255, 159 / 255, 223 / 255, 0.25)
|
|
58
|
+
DESYcyan75 = (0 / 255, 159 / 255, 223 / 255, 0.75)
|
|
59
|
+
DESYcyan10 = (0 / 255, 159 / 255, 223 / 255, 0.10)
|
|
60
|
+
DESYrot = (230 / 255, 65 / 255, 35 / 255)
|
|
61
|
+
DESYrot50 = (230 / 255, 65 / 255, 35 / 255, 0.5)
|
|
62
|
+
DESYdunkelrot = (153 / 255, 0 / 255, 0 / 255)
|
|
63
|
+
DESYorange = (241 / 255, 143 / 255, 31 / 255)
|
|
64
|
+
DESYorange25 = (241 / 255, 143 / 255, 31 / 255, 0.25)
|
|
65
|
+
DESYdunkelblau = (0 / 255, 74 / 255, 110 / 255)
|
|
66
|
+
DESYaubergine = (139 / 255, 60 / 255, 126 / 255)
|
|
67
|
+
DESYaubergine50 = (139 / 255, 60 / 255, 126 / 255, 0.5)
|
|
68
|
+
DESYviolett = (146 / 255, 124 / 255, 184 / 255)
|
|
69
|
+
DESYviolett50 = (146 / 255, 124 / 255, 184 / 255, 0.5)
|
|
70
|
+
DESYlila = (82 / 255, 78 / 255, 156 / 255)
|
|
71
|
+
DESYlila50 = (82 / 255, 78 / 255, 156 / 255, 0.5)
|
|
72
|
+
DESYhellgruen = (122 / 255, 158 / 255, 31 / 255)
|
|
73
|
+
DESYhellgruen50 = (122 / 255, 158 / 255, 31 / 255, 0.5)
|
|
74
|
+
DESYtuerkis = (45 / 255, 164 / 255, 164 / 255)
|
|
75
|
+
DESYmagenta = (208 / 255, 0 / 255, 111 / 255)
|
|
76
|
+
DESYolive = (158 / 255, 150 / 255, 0 / 255)
|
|
77
|
+
DESYhellbraun = (170 / 255, 132 / 255, 106 / 255)
|
|
78
|
+
DESYdunkelbraun = (133 / 255, 88 / 255, 62 / 255)
|
|
79
|
+
DESYdunkelolive = (89 / 255, 98 / 255, 29 / 255)
|
|
80
|
+
DESYpetrol = (0 / 255, 106 / 255, 133 / 255)
|
|
81
|
+
DESYgruen = (0 / 255, 168 / 255, 62 / 255)
|
|
82
|
+
DESYgelb = (230 / 255, 169 / 255, 15 / 255)
|
|
83
|
+
|
|
84
|
+
HumboldtGreen = (129 / 255, 238 / 255, 169 / 255)
|
|
85
|
+
|
|
86
|
+
invisible = (255 / 255, 255 / 255, 255 / 255, 0)
|
|
87
|
+
|
|
88
|
+
colors = [DESYcyan, DESYcyan25]
|
|
89
|
+
cmapDESYcyan = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
90
|
+
|
|
91
|
+
colors = [DESYorange, DESYorange25]
|
|
92
|
+
cmapDESYorange = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
93
|
+
|
|
94
|
+
colors = [DESYcyan25, DESYcyan]
|
|
95
|
+
cmapNG = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
96
|
+
|
|
97
|
+
colors = [DESYorange25, DESYorange]
|
|
98
|
+
cmapIPTA = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
99
|
+
|
|
100
|
+
NG = DESYcyan
|
|
101
|
+
IPTA = DESYorange
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
colors = [DESYorange, DESYcyan]
|
|
105
|
+
cmapDESYcyanorange = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
106
|
+
|
|
107
|
+
colors = [
|
|
108
|
+
DESYpetrol,
|
|
109
|
+
DESYtuerkis,
|
|
110
|
+
DESYgruen,
|
|
111
|
+
DESYhellgruen,
|
|
112
|
+
DESYorange,
|
|
113
|
+
DESYrot,
|
|
114
|
+
DESYdunkelrot,
|
|
115
|
+
DESYviolett,
|
|
116
|
+
DESYlila,
|
|
117
|
+
]
|
|
118
|
+
cmapDESYrainbow = LinearSegmentedColormap.from_list("DESYrainbow", colors)
|
|
119
|
+
|
|
120
|
+
colors = [
|
|
121
|
+
DESYorange,
|
|
122
|
+
HumboldtGreen
|
|
123
|
+
]
|
|
124
|
+
cmapTL = LinearSegmentedColormap.from_list("TL", colors)
|
|
125
|
+
|
|
126
|
+
colors = [
|
|
127
|
+
DESYorange,
|
|
128
|
+
HumboldtGreen,
|
|
129
|
+
DESYcyan
|
|
130
|
+
]
|
|
131
|
+
cmapTL2 = LinearSegmentedColormap.from_list("TL2", colors)
|
|
132
|
+
|
|
133
|
+
colors = [RWTHblau10, RWTHblau]
|
|
134
|
+
cmapRWTHblau = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
135
|
+
|
|
136
|
+
colors = [RWTHblau25, RWTHblau]
|
|
137
|
+
cmapRWTHblau2 = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
138
|
+
|
|
139
|
+
colors = [RWTHblau50, RWTHblau]
|
|
140
|
+
cmapRWTHblau3 = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
141
|
+
|
|
142
|
+
colors = [RWTHgruen10, RWTHgruen]
|
|
143
|
+
cmapRWTHgruen = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
144
|
+
|
|
145
|
+
colors = [DESYhellgruen, invisible]
|
|
146
|
+
cmapDESYhellgruen = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
147
|
+
|
|
148
|
+
colors = [RWTHbordeaux10, RWTHbordeaux]
|
|
149
|
+
cmapRWTHbordeaux = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
150
|
+
|
|
151
|
+
colors = [
|
|
152
|
+
RWTHpetrol,
|
|
153
|
+
RWTHtuerkis,
|
|
154
|
+
RWTHgruen,
|
|
155
|
+
RWTHmaigruen,
|
|
156
|
+
RWTHorange,
|
|
157
|
+
RWTHrot,
|
|
158
|
+
RWTHbordeaux,
|
|
159
|
+
RWTHviolett,
|
|
160
|
+
RWTHlila,
|
|
161
|
+
]
|
|
162
|
+
cmapRWTHrainbow = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
163
|
+
|
|
164
|
+
colors = [
|
|
165
|
+
RWTHtuerkis,
|
|
166
|
+
RWTHgruen,
|
|
167
|
+
RWTHmaigruen,
|
|
168
|
+
RWTHorange,
|
|
169
|
+
RWTHrot,
|
|
170
|
+
RWTHbordeaux,
|
|
171
|
+
RWTHviolett,
|
|
172
|
+
]
|
|
173
|
+
cmapRWTHrainbow2 = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
174
|
+
|
|
175
|
+
colors = [RWTHgruen, RWTHmaigruen, RWTHorange, RWTHrot, RWTHbordeaux, RWTHviolett]
|
|
176
|
+
cmapRWTHrainbow3 = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
177
|
+
|
|
178
|
+
colors = [RWTHblau, RWTHrot]
|
|
179
|
+
cmapRWTHtemp = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
180
|
+
|
|
181
|
+
colors = ["white", "white"]
|
|
182
|
+
cmapWhite = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
183
|
+
|
|
184
|
+
colors = [
|
|
185
|
+
(255 / 255, 255 / 255, 255 / 255, 0),
|
|
186
|
+
(200 / 255, 200 / 255 / 255, 200 / 255, 0.1),
|
|
187
|
+
(150 / 255, 150 / 255, 150 / 255, 0.4),
|
|
188
|
+
(100 / 255, 100 / 255, 100 / 255, 0.5),
|
|
189
|
+
(50 / 255, 50 / 255, 50 / 255, 0.6),
|
|
190
|
+
(0, 0, 0, 0.75),
|
|
191
|
+
]
|
|
192
|
+
cmapGreyshade = LinearSegmentedColormap.from_list("mycmap", colors)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def set_size(width: float | str, fraction : float = 1, subplots: tuple[int, int] = (1, 1)) -> tuple[float, float]:
|
|
196
|
+
"""Return figure dimensions that avoid downstream scaling artefacts.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
width: float | str
|
|
201
|
+
Either the Matplotlib figure width in points or a preset identifier
|
|
202
|
+
such as ``\"JCAP\"`` or ``\"beamer\"`` that maps to a known text width.
|
|
203
|
+
fraction: float, optional
|
|
204
|
+
Fraction of the available width the figure should occupy. Defaults to
|
|
205
|
+
``1`` which uses the full width.
|
|
206
|
+
subplots: tuple[int, int], optional
|
|
207
|
+
Tuple describing the subplot layout (rows, columns). The information is
|
|
208
|
+
used to slightly enlarge multi-panel figures so that axis labels do not
|
|
209
|
+
overlap.
|
|
210
|
+
|
|
211
|
+
Returns
|
|
212
|
+
-------
|
|
213
|
+
fig_dim: tuple[float, float]
|
|
214
|
+
Figure width and height in inches.
|
|
215
|
+
"""
|
|
216
|
+
if width == "JCAP":
|
|
217
|
+
width_pt = 440.0
|
|
218
|
+
elif width == "beamer":
|
|
219
|
+
width_pt = 307.28987
|
|
220
|
+
else:
|
|
221
|
+
width_pt = width
|
|
222
|
+
|
|
223
|
+
# Width of figure (in pts)
|
|
224
|
+
fig_width_pt = width_pt * fraction
|
|
225
|
+
|
|
226
|
+
# Convert from pt to inches
|
|
227
|
+
inches_per_pt = 1 / 72.27
|
|
228
|
+
|
|
229
|
+
# Golden ratio to set aesthetic figure height
|
|
230
|
+
# https://disq.us/p/2940ij3
|
|
231
|
+
golden_ratio = (5**0.5 - 1) / 2
|
|
232
|
+
|
|
233
|
+
# Figure width in inches
|
|
234
|
+
fig_width_in = fig_width_pt * inches_per_pt
|
|
235
|
+
|
|
236
|
+
# Figure height in inches
|
|
237
|
+
if subplots == (1, 3):
|
|
238
|
+
fig_height_in = fig_width_in * golden_ratio / 2 * 1.2
|
|
239
|
+
elif subplots != (1, 1):
|
|
240
|
+
fig_height_in = (
|
|
241
|
+
fig_width_in * golden_ratio * 1.2
|
|
242
|
+
) # to include title, some extra space
|
|
243
|
+
else: # (1,1) case
|
|
244
|
+
fig_height_in = fig_width_in * golden_ratio
|
|
245
|
+
|
|
246
|
+
fig_dim = (fig_width_in, fig_height_in)
|
|
247
|
+
|
|
248
|
+
return fig_dim
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def set_font():
|
|
252
|
+
"""Update Matplotlib's global ``rcParams`` with the project's stylistic defaults.
|
|
253
|
+
|
|
254
|
+
The configuration mirrors the publication layout used in the documentation:
|
|
255
|
+
LaTeX-rendered text, a sans-serif font family, harmonised font sizes for titles
|
|
256
|
+
and axis labels, and a colour cycle based on the DESY palette defined in this
|
|
257
|
+
module. Calling this function is idempotent and affects figures created
|
|
258
|
+
afterwards.
|
|
259
|
+
"""
|
|
260
|
+
plt.rcParams.update(
|
|
261
|
+
{
|
|
262
|
+
"text.usetex": True,
|
|
263
|
+
"font.family": "sans-serif",
|
|
264
|
+
"font.sans-serif": ["DejaVu Sans", "Arial", "Liberation Sans"],
|
|
265
|
+
"mathtext.fontset": "dejavusans",
|
|
266
|
+
"axes.labelsize": 10,
|
|
267
|
+
"font.size": 10,
|
|
268
|
+
"axes.titlesize": 10,
|
|
269
|
+
"figure.titlesize": 10,
|
|
270
|
+
"legend.fontsize": 8,
|
|
271
|
+
"xtick.labelsize": 8,
|
|
272
|
+
"ytick.labelsize": 8,
|
|
273
|
+
"lines.markersize": 5,
|
|
274
|
+
"axes.prop_cycle": cycler(
|
|
275
|
+
color=[
|
|
276
|
+
DESYcyan,
|
|
277
|
+
DESYorange,
|
|
278
|
+
DESYmagenta,
|
|
279
|
+
DESYgruen,
|
|
280
|
+
DESYrot,
|
|
281
|
+
DESYdunkelblau,
|
|
282
|
+
DESYviolett,
|
|
283
|
+
DESYhellgruen,
|
|
284
|
+
DESYgelb,
|
|
285
|
+
]
|
|
286
|
+
),
|
|
287
|
+
}
|
|
288
|
+
)
|