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,1199 @@
|
|
|
1
|
+
"""Compute thermodynamic and gravitational-wave observables for transitions.
|
|
2
|
+
|
|
3
|
+
Part of TransitionListener v2.0
|
|
4
|
+
Documentation: https://tasillo.de/TransitionListener/
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Jonas Matuszak <jonas.matuszak@kit.edu>
|
|
8
|
+
Carlo Tasillo <carlo.tasillo@ific.uv.es>
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import Sequence
|
|
14
|
+
from scipy import interpolate, optimize
|
|
15
|
+
from transitionlistener.generic_potential import generic_potential
|
|
16
|
+
from transitionlistener.phases import PhaseInfo
|
|
17
|
+
from transitionlistener import config
|
|
18
|
+
from transitionlistener.transitions import TransitionInfo
|
|
19
|
+
from . import thermodynamics as td
|
|
20
|
+
from . import errors
|
|
21
|
+
from transitionlistener.hydrodynamics import Hydrodynamics, calc_kappas
|
|
22
|
+
from transitionlistener.bubbledynamics import (
|
|
23
|
+
calcPercAndEvolve,
|
|
24
|
+
calcAlphas,
|
|
25
|
+
calc_betaH_S3,
|
|
26
|
+
calc_betaH_S3_approx,
|
|
27
|
+
calcMeanBubbleSeparation,
|
|
28
|
+
calcTf,
|
|
29
|
+
Tb_criterion,
|
|
30
|
+
HubbleParameter,
|
|
31
|
+
energyDensity,
|
|
32
|
+
PercolationDiagnostics,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
from . import console, print
|
|
36
|
+
import rich
|
|
37
|
+
from rich.panel import Panel
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extract_instanton_profile_arrays(instanton) -> tuple[np.ndarray, np.ndarray] | tuple[None, None]:
|
|
41
|
+
"""Return radius/field arrays from either supported instanton representation.
|
|
42
|
+
|
|
43
|
+
Most tunneling paths return an object with ``instanton.profile1D.R`` and
|
|
44
|
+
``instanton.profile1D.Phi``. The lightweight WIP tunneling approximation
|
|
45
|
+
instead returns a plain dictionary carrying raw arrays under
|
|
46
|
+
``{"profile1D": r, "Phi": phis}``. The observable pipeline only needs the
|
|
47
|
+
radius/field samples to estimate the half-maximum bubble radius ``R0``, so
|
|
48
|
+
we normalize both representations here and let callers degrade gracefully
|
|
49
|
+
when neither layout is available.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
if instanton is None:
|
|
53
|
+
return None, None
|
|
54
|
+
|
|
55
|
+
profile = getattr(instanton, "profile1D", None)
|
|
56
|
+
if profile is not None and hasattr(profile, "R") and hasattr(profile, "Phi"):
|
|
57
|
+
return np.asarray(profile.R, dtype=float), np.asarray(profile.Phi, dtype=float)
|
|
58
|
+
|
|
59
|
+
if isinstance(instanton, dict):
|
|
60
|
+
profile_dict = instanton.get("profile1D")
|
|
61
|
+
if profile_dict is not None and hasattr(profile_dict, "R") and hasattr(profile_dict, "Phi"):
|
|
62
|
+
return np.asarray(profile_dict.R, dtype=float), np.asarray(profile_dict.Phi, dtype=float)
|
|
63
|
+
|
|
64
|
+
if profile_dict is not None and "Phi" in instanton:
|
|
65
|
+
return np.asarray(profile_dict, dtype=float), np.asarray(instanton["Phi"], dtype=float)
|
|
66
|
+
|
|
67
|
+
return None, None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _extract_instanton_plot_arrays(instanton) -> tuple[np.ndarray, np.ndarray] | tuple[None, None]:
|
|
71
|
+
"""Return radius/field-path arrays suitable for rendering bubble profiles.
|
|
72
|
+
|
|
73
|
+
The plotting routines need the physical field path ``Phi(r)`` rather than
|
|
74
|
+
the 1D path coordinate stored in ``profile1D.Phi``. Support both the
|
|
75
|
+
standard full-tunnelling object layout and the lightweight dictionary
|
|
76
|
+
layout used by the adaptive seedless workflow caches.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
if instanton is None:
|
|
80
|
+
return None, None
|
|
81
|
+
|
|
82
|
+
profile = getattr(instanton, "profile1D", None)
|
|
83
|
+
fields = getattr(instanton, "Phi", None)
|
|
84
|
+
if profile is not None and hasattr(profile, "R") and fields is not None:
|
|
85
|
+
return np.asarray(profile.R, dtype=float), np.asarray(fields, dtype=float)
|
|
86
|
+
|
|
87
|
+
if isinstance(instanton, dict):
|
|
88
|
+
radii = instanton.get("profile1D")
|
|
89
|
+
fields = instanton.get("Phi")
|
|
90
|
+
if radii is None or fields is None:
|
|
91
|
+
return None, None
|
|
92
|
+
if hasattr(radii, "R"):
|
|
93
|
+
radii = radii.R
|
|
94
|
+
return np.asarray(radii, dtype=float), np.asarray(fields, dtype=float)
|
|
95
|
+
|
|
96
|
+
return None, None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _select_instanton_near_temperature(outdict: dict, temperature: float):
|
|
100
|
+
"""Return the instanton stored closest to ``temperature`` if available."""
|
|
101
|
+
|
|
102
|
+
if not isinstance(outdict, dict) or not np.isfinite(temperature):
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
candidates: list[tuple[float, object]] = []
|
|
106
|
+
for key, entry in outdict.items():
|
|
107
|
+
try:
|
|
108
|
+
temp = float(key)
|
|
109
|
+
except Exception: # noqa: BLE001
|
|
110
|
+
continue
|
|
111
|
+
if not np.isfinite(temp) or not isinstance(entry, dict):
|
|
112
|
+
continue
|
|
113
|
+
instanton = entry.get("instanton")
|
|
114
|
+
if instanton is None:
|
|
115
|
+
continue
|
|
116
|
+
if isinstance(instanton, float) and not np.isfinite(instanton):
|
|
117
|
+
continue
|
|
118
|
+
candidates.append((abs(temp - temperature), instanton))
|
|
119
|
+
|
|
120
|
+
if not candidates:
|
|
121
|
+
return None
|
|
122
|
+
candidates.sort(key=lambda item: item[0])
|
|
123
|
+
return candidates[0][1]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class _BoundedPchipInterpolator:
|
|
127
|
+
"""Shape-preserving spline with explicit out-of-range behaviour."""
|
|
128
|
+
|
|
129
|
+
def __init__(
|
|
130
|
+
self,
|
|
131
|
+
x: Sequence[float],
|
|
132
|
+
y: Sequence[float],
|
|
133
|
+
*,
|
|
134
|
+
fill_low: float | None = None,
|
|
135
|
+
fill_high: float | None = None,
|
|
136
|
+
clip: tuple[float, float] | None = None,
|
|
137
|
+
transform: str = "identity",
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Prepare a monotonic spline with optional clipping and endpoint fills."""
|
|
140
|
+
x_arr = np.asarray(x, dtype=float)
|
|
141
|
+
y_arr = np.asarray(y, dtype=float)
|
|
142
|
+
mask = np.isfinite(x_arr) & np.isfinite(y_arr)
|
|
143
|
+
x_arr = x_arr[mask]
|
|
144
|
+
y_arr = y_arr[mask]
|
|
145
|
+
|
|
146
|
+
if x_arr.size < 2:
|
|
147
|
+
raise ValueError("Need at least two finite samples for interpolation.")
|
|
148
|
+
|
|
149
|
+
order = np.argsort(x_arr)
|
|
150
|
+
x_arr = x_arr[order]
|
|
151
|
+
y_arr = y_arr[order]
|
|
152
|
+
|
|
153
|
+
# Remove duplicate temperatures to satisfy the spline requirements.
|
|
154
|
+
keep = np.concatenate(([True], np.diff(x_arr) > 0))
|
|
155
|
+
x_arr = x_arr[keep]
|
|
156
|
+
y_arr = y_arr[keep]
|
|
157
|
+
|
|
158
|
+
if x_arr.size < 2:
|
|
159
|
+
raise ValueError("Need at least two unique temperature samples for interpolation.")
|
|
160
|
+
|
|
161
|
+
self.x = x_arr
|
|
162
|
+
self._transform = transform
|
|
163
|
+
transformed = self._forward_transform(y_arr)
|
|
164
|
+
self._interp = interpolate.PchipInterpolator(x_arr, transformed, extrapolate=False)
|
|
165
|
+
self._fill_low = fill_low
|
|
166
|
+
self._fill_high = fill_high
|
|
167
|
+
self._clip = clip
|
|
168
|
+
|
|
169
|
+
def _forward_transform(self, values: np.ndarray) -> np.ndarray:
|
|
170
|
+
"""Map physical values into the interpolation space."""
|
|
171
|
+
arr = np.asarray(values, dtype=float)
|
|
172
|
+
if self._transform == "identity":
|
|
173
|
+
return arr
|
|
174
|
+
if self._transform == "log_false_fraction":
|
|
175
|
+
clipped = np.clip(arr, 0.0, 1.0 - 1e-15)
|
|
176
|
+
return np.log(np.clip(1.0 - clipped, 1e-300, 1.0))
|
|
177
|
+
raise ValueError(f"Unknown spline transform: {self._transform}")
|
|
178
|
+
|
|
179
|
+
def _inverse_transform(self, values: np.ndarray) -> np.ndarray:
|
|
180
|
+
"""Map interpolated values back to the physical space."""
|
|
181
|
+
arr = np.asarray(values, dtype=float)
|
|
182
|
+
if self._transform == "identity":
|
|
183
|
+
return arr
|
|
184
|
+
if self._transform == "log_false_fraction":
|
|
185
|
+
false_fraction = np.exp(np.clip(arr, -700.0, 0.0))
|
|
186
|
+
return 1.0 - false_fraction
|
|
187
|
+
raise ValueError(f"Unknown spline transform: {self._transform}")
|
|
188
|
+
|
|
189
|
+
def __call__(self, values):
|
|
190
|
+
"""Evaluate the spline while honouring the configured fill and clip rules."""
|
|
191
|
+
arr = np.asarray(values, dtype=float)
|
|
192
|
+
out = self._inverse_transform(np.asarray(self._interp(arr), dtype=float))
|
|
193
|
+
|
|
194
|
+
if self._fill_low is not None:
|
|
195
|
+
out = np.where(arr < self.x[0], self._fill_low, out)
|
|
196
|
+
if self._fill_high is not None:
|
|
197
|
+
out = np.where(arr > self.x[-1], self._fill_high, out)
|
|
198
|
+
if self._clip is not None:
|
|
199
|
+
out = np.clip(out, self._clip[0], self._clip[1])
|
|
200
|
+
|
|
201
|
+
if np.isscalar(values):
|
|
202
|
+
return float(np.asarray(out).reshape(()))
|
|
203
|
+
return out
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass
|
|
207
|
+
class TransitionObservableInfo:
|
|
208
|
+
"""Collection of transition observable data."""
|
|
209
|
+
v_wall: float = np.nan
|
|
210
|
+
c_s: float = np.nan
|
|
211
|
+
c_s_sym: float = np.nan
|
|
212
|
+
c_s_bro: float = np.nan
|
|
213
|
+
Tperc: float = np.nan
|
|
214
|
+
Tperc_SM_GeV: float = np.nan
|
|
215
|
+
Tcrit_SM_GeV: float = np.nan
|
|
216
|
+
xi_crit: float = np.nan
|
|
217
|
+
Tnuc_SM_GeV: float = np.nan
|
|
218
|
+
Treh: float = np.nan
|
|
219
|
+
Treh_SM_GeV: float = np.nan
|
|
220
|
+
alpha: float = np.nan
|
|
221
|
+
alpha_theta: float = np.nan
|
|
222
|
+
alpha_thetabar: float = np.nan
|
|
223
|
+
alpha_p: float = np.nan
|
|
224
|
+
alpha_e: float = np.nan
|
|
225
|
+
alpha_hyd: float = np.nan
|
|
226
|
+
alpha_inf: float = np.nan
|
|
227
|
+
alpha_eq: float = np.nan
|
|
228
|
+
RH: float = np.nan
|
|
229
|
+
g_eff_tot_reh: float = np.nan
|
|
230
|
+
h_eff_tot_reh: float = np.nan
|
|
231
|
+
g0: float = np.nan
|
|
232
|
+
h0: float = np.nan
|
|
233
|
+
betaH_S3: float = np.nan
|
|
234
|
+
betaH_RH: float = np.nan
|
|
235
|
+
Tf_SM_GeV: float = np.nan
|
|
236
|
+
kappa_phi: float = np.nan
|
|
237
|
+
kappa_sw: float = np.nan
|
|
238
|
+
kappa_turb: float = np.nan
|
|
239
|
+
D: float = np.nan
|
|
240
|
+
step: float = np.nan
|
|
241
|
+
total_steps: float = np.nan
|
|
242
|
+
WARNING_too_weak_to_compute_perc: bool = False
|
|
243
|
+
WARNING_no_perc_splines: bool = False
|
|
244
|
+
WARNING_betaH_small: bool = False
|
|
245
|
+
WARNING_betaH_very_small: bool = False
|
|
246
|
+
WARNING_betaH_mismatch: bool = False
|
|
247
|
+
WARNING_betaH_nonfinite: bool = False
|
|
248
|
+
WARNING_nucleationRate_nonexponential: bool = False
|
|
249
|
+
WARNING_not_T0_global_min: bool = False
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@dataclass
|
|
253
|
+
class TransitionContext:
|
|
254
|
+
"""Collect shared inputs and outputs for a single transition."""
|
|
255
|
+
|
|
256
|
+
tr: TransitionInfo
|
|
257
|
+
pot: generic_potential
|
|
258
|
+
phases: list[PhaseInfo]
|
|
259
|
+
GWconfig: config.GWConf
|
|
260
|
+
PercolationConf: config.PercolationConf
|
|
261
|
+
derived_param_names: list[str]
|
|
262
|
+
verbose: bool
|
|
263
|
+
derived_params: dict
|
|
264
|
+
Tnuc: float | None
|
|
265
|
+
outdict: dict
|
|
266
|
+
phase_symmetric: PhaseInfo
|
|
267
|
+
phase_broken: PhaseInfo
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass
|
|
271
|
+
class PercolationResult:
|
|
272
|
+
"""Hold percolation-related outputs and splines when available."""
|
|
273
|
+
|
|
274
|
+
Tperc: float
|
|
275
|
+
TSYM: np.ndarray | None
|
|
276
|
+
H: np.ndarray | None
|
|
277
|
+
P: np.ndarray | None
|
|
278
|
+
TBRO: np.ndarray | None
|
|
279
|
+
S: np.ndarray | None
|
|
280
|
+
Pint: interpolate.interp1d | None
|
|
281
|
+
Sint: interpolate.interp1d | None
|
|
282
|
+
Hint: interpolate.interp1d | None
|
|
283
|
+
entropyInt: interpolate.interp1d | None
|
|
284
|
+
coolingInt: interpolate.interp1d | None
|
|
285
|
+
metadata: PercolationDiagnostics | None
|
|
286
|
+
successful: bool
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class TransitionObservables:
|
|
290
|
+
"""Evaluate derived observables for every transition of a potential."""
|
|
291
|
+
|
|
292
|
+
def __init__(self, pot: generic_potential, phases: list[PhaseInfo],
|
|
293
|
+
transitions: list[TransitionInfo], verbose=False):
|
|
294
|
+
"""Store inputs and immediately compute all derived quantities."""
|
|
295
|
+
self.pot = pot
|
|
296
|
+
self.phases = phases
|
|
297
|
+
self.GWconfig = pot.config.gwConf
|
|
298
|
+
self.PercolationConf = pot.config.percolationConf
|
|
299
|
+
self.transitions = transitions
|
|
300
|
+
self.verbose = verbose
|
|
301
|
+
self.derived_param_names = [name for name in config.all_observables.keys()]
|
|
302
|
+
|
|
303
|
+
if verbose:
|
|
304
|
+
console.print(
|
|
305
|
+
"[green bold]\nStart percolation computation...[/]",
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
self.transitionObservables = self.computeTransitionObservables()
|
|
309
|
+
self.check_T0_global_min()
|
|
310
|
+
|
|
311
|
+
def check_T0_global_min(self):
|
|
312
|
+
"""Check whether the global minimum at T = Tmin is the last minimum.
|
|
313
|
+
If not, raise an error.
|
|
314
|
+
"""
|
|
315
|
+
T0 = self.pot.Tmin
|
|
316
|
+
last_vev = self.phases[self.transitions[-1].low_phase].valAt(self.pot.Tmin)
|
|
317
|
+
|
|
318
|
+
if not np.allclose(last_vev, self.pot.X0):
|
|
319
|
+
# The global minimum at T = 0 is not the last minimum!
|
|
320
|
+
|
|
321
|
+
for i, tr in enumerate(self.transitions):
|
|
322
|
+
if tr is not None and tr.type == 1:
|
|
323
|
+
self.transitions[i].derived_params["WARNING:not_T0_global_min"] = True
|
|
324
|
+
self.transitionObservables[i]["WARNING:not_T0_global_min"] = True
|
|
325
|
+
|
|
326
|
+
msg = (
|
|
327
|
+
"The global minimum at T = {} is not the last minimum found "
|
|
328
|
+
"by the phase tracing algorithm. This may be unwanted, for "
|
|
329
|
+
"example if the potential is expected to have a unique "
|
|
330
|
+
"global minimum at T = {} or when identifying the last phase "
|
|
331
|
+
"with the electroweak vacuum."
|
|
332
|
+
).format(T0 * self.pot.conversionFactor, T0 * self.pot.conversionFactor)
|
|
333
|
+
|
|
334
|
+
if self.GWconfig.check_if_T0_global_min:
|
|
335
|
+
console.print(f"[bold red]ERROR:[/bold red] {msg}")
|
|
336
|
+
raise errors.GlobalMinimumError(msg)
|
|
337
|
+
elif self.verbose:
|
|
338
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
339
|
+
console.print(
|
|
340
|
+
"[bold yellow]WARNING:[/bold yellow] "
|
|
341
|
+
"Continuing, because the check_if_T0_global_min flag is "
|
|
342
|
+
"set to False."
|
|
343
|
+
)
|
|
344
|
+
else:
|
|
345
|
+
# The global minimum at T = 0 is the last minimum.
|
|
346
|
+
for i, tr in enumerate(self.transitions):
|
|
347
|
+
if tr is not None and tr.type == 1:
|
|
348
|
+
self.transitions[i].derived_params["WARNING:not_T0_global_min"] = False
|
|
349
|
+
self.transitionObservables[i]["WARNING:not_T0_global_min"] = False
|
|
350
|
+
|
|
351
|
+
if self.verbose:
|
|
352
|
+
msg = (
|
|
353
|
+
"The global minimum at T_min is the last minimum found "
|
|
354
|
+
"by the phase tracing algorithm."
|
|
355
|
+
)
|
|
356
|
+
console.print(
|
|
357
|
+
f"[bold cyan]INFO:[/bold cyan] {msg}"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
def computeTransitionObservables(self):
|
|
361
|
+
"""
|
|
362
|
+
Calculate the GW observables for each transition in the
|
|
363
|
+
transitions_dict. This is done by calling the
|
|
364
|
+
`computeTransitionObservables_one_transition`
|
|
365
|
+
function from the `bubbledynamics` module.
|
|
366
|
+
|
|
367
|
+
Raises
|
|
368
|
+
------
|
|
369
|
+
err
|
|
370
|
+
Raises an error if GW observables cannot be calculated.
|
|
371
|
+
|
|
372
|
+
Returns
|
|
373
|
+
-------
|
|
374
|
+
transitionObservables: dict
|
|
375
|
+
A dictionary with the GW observables for each transition.
|
|
376
|
+
"""
|
|
377
|
+
self.transitionObservables = {i: {} for i in range(len(self.transitions))}
|
|
378
|
+
self.percolationResults = {i: None for i in range(len(self.transitions))}
|
|
379
|
+
self.outdicts = {i: None for i in range(len(self.transitions))}
|
|
380
|
+
|
|
381
|
+
for i, tr in enumerate(self.transitions):
|
|
382
|
+
if tr is not None and tr.type == 1:
|
|
383
|
+
try:
|
|
384
|
+
derived_params_dict_tr, percol, outdict = self.computeTransitionObservables_one_transition(
|
|
385
|
+
tr, nAction=self.PercolationConf.n_action
|
|
386
|
+
)
|
|
387
|
+
self.transitions[i].derived_params = derived_params_dict_tr
|
|
388
|
+
self.percolationResults[i] = percol
|
|
389
|
+
self.outdicts[i] = outdict
|
|
390
|
+
self.transitionObservables[i] = derived_params_dict_tr
|
|
391
|
+
except Exception as err:
|
|
392
|
+
if self.verbose:
|
|
393
|
+
print("Error calculating GW parameters: ", err)
|
|
394
|
+
raise err
|
|
395
|
+
return self.transitionObservables
|
|
396
|
+
|
|
397
|
+
def _initialize_transition_context(self, tr: TransitionInfo) -> TransitionContext:
|
|
398
|
+
"""Prepare reusable data structures for a single transition."""
|
|
399
|
+
derived_params = {
|
|
400
|
+
"WARNING:too_weak_to_compute_perc": False,
|
|
401
|
+
"WARNING:no_perc_splines": False,
|
|
402
|
+
"WARNING:betaH_small": False,
|
|
403
|
+
"WARNING:betaH_very_small": False,
|
|
404
|
+
"WARNING:betaH_mismatch": False,
|
|
405
|
+
"WARNING:betaH_nonfinite": False,
|
|
406
|
+
"WARNING:nucleationRate_nonexponential": False,
|
|
407
|
+
"WARNING:spline_tnuc_unavailable": False,
|
|
408
|
+
"WARNING:spline_tnuc_not_reached": False,
|
|
409
|
+
"WARNING:spline_tnuc_failed": False,
|
|
410
|
+
}
|
|
411
|
+
seed_temperature = tr.Tnuc
|
|
412
|
+
use_seedless_cache = seed_temperature is None or not np.isfinite(seed_temperature)
|
|
413
|
+
return TransitionContext(
|
|
414
|
+
tr=tr,
|
|
415
|
+
pot=self.pot,
|
|
416
|
+
phases=self.phases,
|
|
417
|
+
GWconfig=self.GWconfig,
|
|
418
|
+
PercolationConf=self.PercolationConf,
|
|
419
|
+
derived_param_names=self.derived_param_names,
|
|
420
|
+
verbose=self.verbose,
|
|
421
|
+
derived_params=derived_params,
|
|
422
|
+
Tnuc=seed_temperature,
|
|
423
|
+
outdict={} if use_seedless_cache else tr.full_tunneling_info,
|
|
424
|
+
phase_symmetric=self.phases[tr.high_phase],
|
|
425
|
+
phase_broken=self.phases[tr.low_phase],
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
def _ensure_wall_velocity(self, ctx: TransitionContext, temperature: float) -> None:
|
|
429
|
+
"""Populate v_wall if requested by configuration at one milestone temperature.
|
|
430
|
+
|
|
431
|
+
Accepts ``"LTE"``, ``"c"``, or a numeric value in ``(0, 1]``.
|
|
432
|
+
"""
|
|
433
|
+
if "v_wall" not in ctx.derived_param_names:
|
|
434
|
+
return
|
|
435
|
+
GWconfig = ctx.GWconfig
|
|
436
|
+
derived = ctx.derived_params
|
|
437
|
+
if GWconfig.wall_velocity == "LTE":
|
|
438
|
+
hydr = Hydrodynamics(ctx.pot, ctx.phase_symmetric, ctx.phase_broken, ctx.verbose)
|
|
439
|
+
derived["v_wall"] = hydr.calcWallVelocityLTE(temperature)
|
|
440
|
+
elif GWconfig.wall_velocity == "c":
|
|
441
|
+
derived["v_wall"] = 1.0
|
|
442
|
+
elif GWconfig.wall_velocity == "WallGo":
|
|
443
|
+
raise NotImplementedError("WallGo is not implemented yet.")
|
|
444
|
+
else:
|
|
445
|
+
try:
|
|
446
|
+
vw = float(GWconfig.wall_velocity)
|
|
447
|
+
except (TypeError, ValueError):
|
|
448
|
+
raise ValueError(
|
|
449
|
+
"wall_velocity must be 'LTE', 'c', or a float in (0, 1]; "
|
|
450
|
+
f"got {GWconfig.wall_velocity!r}."
|
|
451
|
+
)
|
|
452
|
+
if vw <= 0.0 or vw > 1.0:
|
|
453
|
+
raise ValueError(
|
|
454
|
+
f"wall_velocity must satisfy 0 < vw <= 1; got {vw}."
|
|
455
|
+
)
|
|
456
|
+
derived["v_wall"] = vw
|
|
457
|
+
|
|
458
|
+
def _ensure_sound_speed(self, ctx: TransitionContext, temperature: float) -> None:
|
|
459
|
+
"""Populate sound speed values when requested at one milestone temperature."""
|
|
460
|
+
if "c_s" not in ctx.derived_param_names:
|
|
461
|
+
return
|
|
462
|
+
derived = ctx.derived_params
|
|
463
|
+
GWconfig = ctx.GWconfig
|
|
464
|
+
if ctx.verbose:
|
|
465
|
+
print("Calculating sound speed...")
|
|
466
|
+
if GWconfig.sound_speed == "compute":
|
|
467
|
+
hydr = Hydrodynamics(ctx.pot, ctx.phase_symmetric, ctx.phase_broken, ctx.verbose)
|
|
468
|
+
c_sb = hydr.calc_cs(temperature, sym=False)
|
|
469
|
+
c_ss = hydr.calc_cs(temperature, sym=True)
|
|
470
|
+
derived["c_s"] = c_sb
|
|
471
|
+
derived["c_s_sym"] = c_ss
|
|
472
|
+
derived["c_s_bro"] = c_sb
|
|
473
|
+
elif GWconfig.sound_speed == "1/3":
|
|
474
|
+
value = 1 / np.sqrt(3)
|
|
475
|
+
derived["c_s"] = value
|
|
476
|
+
derived["c_s_sym"] = value
|
|
477
|
+
derived["c_s_bro"] = value
|
|
478
|
+
else:
|
|
479
|
+
raise NotImplementedError(
|
|
480
|
+
"The GWconfig.sound_speed option is not implemented yet: "
|
|
481
|
+
+ GWconfig.sound_speed
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
def _compute_transition_strength(self, ctx: TransitionContext, temperature: float) -> float:
|
|
485
|
+
"""Return alpha_theta at the supplied milestone temperature."""
|
|
486
|
+
_, alpha_theta, _, _, _, _, _ = calcAlphas(
|
|
487
|
+
temperature, ctx.pot, ctx.phase_symmetric, ctx.phase_broken,
|
|
488
|
+
verbose=ctx.verbose,
|
|
489
|
+
)
|
|
490
|
+
if ctx.verbose:
|
|
491
|
+
print("alpha at milestone temperature =", np.squeeze(alpha_theta))
|
|
492
|
+
return np.squeeze(alpha_theta)
|
|
493
|
+
|
|
494
|
+
def _compute_percolation(
|
|
495
|
+
self,
|
|
496
|
+
ctx: TransitionContext,
|
|
497
|
+
rtol: float,
|
|
498
|
+
nAction: int,
|
|
499
|
+
) -> PercolationResult:
|
|
500
|
+
"""Perform the percolation computation and build the associated splines."""
|
|
501
|
+
pot = ctx.pot
|
|
502
|
+
derived = ctx.derived_params
|
|
503
|
+
verbose = ctx.verbose
|
|
504
|
+
|
|
505
|
+
try:
|
|
506
|
+
Tperc, TSYM, H, P, TBRO, S, metadata = calcPercAndEvolve(
|
|
507
|
+
ctx.outdict,
|
|
508
|
+
ctx.Tnuc,
|
|
509
|
+
ctx.phase_symmetric,
|
|
510
|
+
ctx.phase_broken,
|
|
511
|
+
pot,
|
|
512
|
+
rtol=rtol,
|
|
513
|
+
nAction=nAction,
|
|
514
|
+
verbose=verbose,
|
|
515
|
+
vw=ctx.derived_params.get("v_wall", 1.0),
|
|
516
|
+
return_metadata=True,
|
|
517
|
+
)
|
|
518
|
+
except Exception as err:
|
|
519
|
+
if verbose:
|
|
520
|
+
print("Fatal error in calcPercAndEvolve: ", err)
|
|
521
|
+
raise
|
|
522
|
+
|
|
523
|
+
if verbose:
|
|
524
|
+
print("Calculating percolation splines...")
|
|
525
|
+
Pint = Sint = Hint = entropyInt = coolingInt = None
|
|
526
|
+
core_spline_error = None
|
|
527
|
+
try:
|
|
528
|
+
Pint = _BoundedPchipInterpolator(
|
|
529
|
+
TSYM,
|
|
530
|
+
P,
|
|
531
|
+
fill_low=1.0,
|
|
532
|
+
fill_high=0.0,
|
|
533
|
+
clip=(0.0, 1.0),
|
|
534
|
+
)
|
|
535
|
+
except Exception as err:
|
|
536
|
+
core_spline_error = err
|
|
537
|
+
|
|
538
|
+
try:
|
|
539
|
+
finite_s = S[np.isfinite(S)]
|
|
540
|
+
Sint = _BoundedPchipInterpolator(
|
|
541
|
+
TSYM,
|
|
542
|
+
S,
|
|
543
|
+
fill_low=float(finite_s[-1]),
|
|
544
|
+
fill_high=float(finite_s[0]),
|
|
545
|
+
)
|
|
546
|
+
except Exception as err:
|
|
547
|
+
if core_spline_error is None:
|
|
548
|
+
core_spline_error = err
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
finite_h = H[np.isfinite(H)]
|
|
552
|
+
Hint = _BoundedPchipInterpolator(
|
|
553
|
+
TSYM,
|
|
554
|
+
H,
|
|
555
|
+
fill_low=float(finite_h[-1]),
|
|
556
|
+
fill_high=float(finite_h[0]),
|
|
557
|
+
)
|
|
558
|
+
except Exception as err:
|
|
559
|
+
if core_spline_error is None:
|
|
560
|
+
core_spline_error = err
|
|
561
|
+
|
|
562
|
+
if core_spline_error is not None:
|
|
563
|
+
derived["WARNING:no_perc_splines"] = True
|
|
564
|
+
if verbose:
|
|
565
|
+
print("Error in computing percolation splines: ", core_spline_error)
|
|
566
|
+
msg = (
|
|
567
|
+
"WARNING: Cannot calculate the splines. Continue with the "
|
|
568
|
+
"percolation temperature, but no splines will be available "
|
|
569
|
+
"for further calculations."
|
|
570
|
+
)
|
|
571
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
572
|
+
Pint = Sint = Hint = entropyInt = coolingInt = None
|
|
573
|
+
|
|
574
|
+
return PercolationResult(
|
|
575
|
+
Tperc=Tperc,
|
|
576
|
+
TSYM=TSYM,
|
|
577
|
+
H=H,
|
|
578
|
+
P=P,
|
|
579
|
+
TBRO=TBRO,
|
|
580
|
+
S=S,
|
|
581
|
+
Pint=Pint,
|
|
582
|
+
Sint=Sint,
|
|
583
|
+
Hint=Hint,
|
|
584
|
+
entropyInt=entropyInt,
|
|
585
|
+
coolingInt=coolingInt,
|
|
586
|
+
metadata=metadata,
|
|
587
|
+
successful=True,
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
def _populate_temperature_observables(
|
|
591
|
+
self,
|
|
592
|
+
ctx: TransitionContext,
|
|
593
|
+
percolation: PercolationResult,
|
|
594
|
+
) -> None:
|
|
595
|
+
"""Fill temperature-related entries in the derived parameters."""
|
|
596
|
+
derived = ctx.derived_params
|
|
597
|
+
pot = ctx.pot
|
|
598
|
+
|
|
599
|
+
if "Tperc_SM_GeV" in ctx.derived_param_names:
|
|
600
|
+
if ctx.verbose:
|
|
601
|
+
print("Calculating percolation temperature...")
|
|
602
|
+
derived["Tperc"] = percolation.Tperc
|
|
603
|
+
derived["Tperc_SM_GeV"] = percolation.Tperc * pot.conversionFactor
|
|
604
|
+
|
|
605
|
+
if "Tcrit_SM_GeV" in ctx.derived_param_names:
|
|
606
|
+
if ctx.verbose:
|
|
607
|
+
print("Calculating critical temperature...")
|
|
608
|
+
derived["Tcrit_SM_GeV"] = ctx.tr.Tcrit * pot.conversionFactor
|
|
609
|
+
|
|
610
|
+
if "xi_crit" in ctx.derived_param_names:
|
|
611
|
+
if ctx.verbose:
|
|
612
|
+
print("Calculating critical vev over temperature...")
|
|
613
|
+
T_crit = ctx.tr.Tcrit
|
|
614
|
+
vev_crit = np.sqrt(np.sum(ctx.phase_broken.valAt(T_crit) ** 2))
|
|
615
|
+
derived["xi_crit"] = vev_crit / T_crit
|
|
616
|
+
|
|
617
|
+
if "Tnuc_SM_GeV" in ctx.derived_param_names:
|
|
618
|
+
if ctx.verbose:
|
|
619
|
+
print("Calculating nucleation temperature...")
|
|
620
|
+
metadata_tnuc = None
|
|
621
|
+
if percolation.metadata is not None:
|
|
622
|
+
metadata_tnuc = getattr(percolation.metadata, "spline_tnuc", None)
|
|
623
|
+
warning = getattr(percolation.metadata, "spline_tnuc_warning", None)
|
|
624
|
+
if warning is not None:
|
|
625
|
+
warning_key = f"WARNING:{warning}"
|
|
626
|
+
derived[warning_key] = True
|
|
627
|
+
if ctx.tr.Tnuc is not None and np.isfinite(ctx.tr.Tnuc):
|
|
628
|
+
derived["Tnuc_SM_GeV"] = ctx.tr.Tnuc * pot.conversionFactor
|
|
629
|
+
elif metadata_tnuc is not None and np.isfinite(metadata_tnuc):
|
|
630
|
+
derived["Tnuc_SM_GeV"] = float(metadata_tnuc) * pot.conversionFactor
|
|
631
|
+
else:
|
|
632
|
+
derived["Tnuc_SM_GeV"] = np.nan
|
|
633
|
+
|
|
634
|
+
if "Treh_SM_GeV" in ctx.derived_param_names:
|
|
635
|
+
if ctx.verbose:
|
|
636
|
+
print("Calculating reheating temperature...")
|
|
637
|
+
try:
|
|
638
|
+
Treh = optimize.brentq(
|
|
639
|
+
Tb_criterion,
|
|
640
|
+
float(ctx.phase_broken.Tmin),
|
|
641
|
+
float(ctx.phase_broken.Tmax),
|
|
642
|
+
args=(
|
|
643
|
+
percolation.Tperc,
|
|
644
|
+
ctx.phase_symmetric,
|
|
645
|
+
ctx.phase_broken,
|
|
646
|
+
pot,
|
|
647
|
+
),
|
|
648
|
+
)
|
|
649
|
+
except ValueError:
|
|
650
|
+
if ctx.verbose:
|
|
651
|
+
msg = (
|
|
652
|
+
"WARNING: Tb_criterion does not bracket a root in the "
|
|
653
|
+
"broken-phase temperature range; falling back to Treh = Tperc."
|
|
654
|
+
)
|
|
655
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
656
|
+
Treh = percolation.Tperc
|
|
657
|
+
derived["Treh"] = Treh
|
|
658
|
+
derived["Treh_SM_GeV"] = Treh * pot.conversionFactor
|
|
659
|
+
|
|
660
|
+
def _compute_alpha_parameters(
|
|
661
|
+
self,
|
|
662
|
+
ctx: TransitionContext,
|
|
663
|
+
percolation: PercolationResult,
|
|
664
|
+
) -> None:
|
|
665
|
+
"""Compute alpha-related quantities if requested."""
|
|
666
|
+
if "alpha" not in ctx.derived_param_names:
|
|
667
|
+
return
|
|
668
|
+
if ctx.verbose:
|
|
669
|
+
print("Calculating alpha parameters...")
|
|
670
|
+
alpha_p, alpha_theta, alpha_thetabar, alpha_e, alpha_hyd, alpha_inf, alpha_eq = calcAlphas(
|
|
671
|
+
percolation.Tperc, ctx.pot, ctx.phase_symmetric, ctx.phase_broken,
|
|
672
|
+
verbose=ctx.verbose,
|
|
673
|
+
)
|
|
674
|
+
derived = ctx.derived_params
|
|
675
|
+
derived["alpha"] = np.squeeze(alpha_thetabar)
|
|
676
|
+
derived["alpha_theta"] = np.squeeze(alpha_theta)
|
|
677
|
+
derived["alpha_thetabar"] = np.squeeze(alpha_thetabar)
|
|
678
|
+
derived["alpha_p"] = np.squeeze(alpha_p)
|
|
679
|
+
derived["alpha_e"] = np.squeeze(alpha_e)
|
|
680
|
+
derived["alpha_hyd"] = np.squeeze(alpha_hyd)
|
|
681
|
+
derived["alpha_inf"] = np.squeeze(alpha_inf)
|
|
682
|
+
derived["alpha_eq"] = np.squeeze(alpha_eq)
|
|
683
|
+
|
|
684
|
+
def _compute_beta_and_separation(
|
|
685
|
+
self,
|
|
686
|
+
ctx: TransitionContext,
|
|
687
|
+
percolation: PercolationResult,
|
|
688
|
+
tmin: float,
|
|
689
|
+
tmax: float,
|
|
690
|
+
) -> None:
|
|
691
|
+
"""Populate beta/H variants, RH and Tf if configured."""
|
|
692
|
+
derived = ctx.derived_params
|
|
693
|
+
pot = ctx.pot
|
|
694
|
+
verbose = ctx.verbose
|
|
695
|
+
outdict = ctx.outdict
|
|
696
|
+
phase_symmetric = ctx.phase_symmetric
|
|
697
|
+
phase_broken = ctx.phase_broken
|
|
698
|
+
beta_time_factor = self._beta_time_temperature_factor(ctx, percolation.Tperc)
|
|
699
|
+
|
|
700
|
+
if "betaH_S3_approx" in ctx.derived_param_names:
|
|
701
|
+
if verbose:
|
|
702
|
+
print("Calculating beta/H (approximation through derivative of bounce action)...")
|
|
703
|
+
derived["betaH_S3_approx"] = calc_betaH_S3_approx(
|
|
704
|
+
percolation.Tperc, outdict, pot, phase_symmetric, phase_broken, tmin, tmax, verbose=verbose
|
|
705
|
+
)
|
|
706
|
+
if np.isfinite(derived.get("betaH_S3_approx", np.nan)):
|
|
707
|
+
derived["betaH_S3_approx"] *= beta_time_factor
|
|
708
|
+
|
|
709
|
+
if "RH" in ctx.derived_param_names:
|
|
710
|
+
if verbose:
|
|
711
|
+
print("Calculating mean bubble separation...")
|
|
712
|
+
if (
|
|
713
|
+
derived.get("WARNING:no_perc_splines", False)
|
|
714
|
+
or percolation.Sint is None
|
|
715
|
+
or percolation.TSYM is None
|
|
716
|
+
):
|
|
717
|
+
if verbose:
|
|
718
|
+
msg = (
|
|
719
|
+
"WARNING: Set RH to NaN "
|
|
720
|
+
"because no percolation splines were found."
|
|
721
|
+
)
|
|
722
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
723
|
+
derived["RH"] = np.nan
|
|
724
|
+
else:
|
|
725
|
+
support_tmax = float(np.nanmax(percolation.TSYM))
|
|
726
|
+
tmax_rh = support_tmax
|
|
727
|
+
metadata_tnuc = (
|
|
728
|
+
None
|
|
729
|
+
if percolation.metadata is None
|
|
730
|
+
else getattr(percolation.metadata, "spline_tnuc", None)
|
|
731
|
+
)
|
|
732
|
+
for tnuc_candidate in (ctx.tr.Tnuc, metadata_tnuc):
|
|
733
|
+
if (
|
|
734
|
+
tnuc_candidate is not None
|
|
735
|
+
and np.isfinite(tnuc_candidate)
|
|
736
|
+
and float(tnuc_candidate) > percolation.Tperc
|
|
737
|
+
):
|
|
738
|
+
tmax_rh = min(float(tnuc_candidate), support_tmax)
|
|
739
|
+
break
|
|
740
|
+
Rsep = calcMeanBubbleSeparation(
|
|
741
|
+
percolation.Tperc,
|
|
742
|
+
tmax_rh,
|
|
743
|
+
percolation.Sint,
|
|
744
|
+
percolation.Pint,
|
|
745
|
+
percolation.Hint,
|
|
746
|
+
percolation.entropyInt,
|
|
747
|
+
percolation.coolingInt,
|
|
748
|
+
verbose=verbose,
|
|
749
|
+
)
|
|
750
|
+
RsepHperc = Rsep * percolation.Hint(percolation.Tperc)
|
|
751
|
+
derived["RH"] = RsepHperc
|
|
752
|
+
|
|
753
|
+
# This combines the dark-sector contribution with the SM bath and
|
|
754
|
+
# therefore assumes a DS + SM setup.
|
|
755
|
+
if "g_eff_tot_reh" in ctx.derived_param_names:
|
|
756
|
+
if verbose:
|
|
757
|
+
print("Calculating g_eff_tot_reh...")
|
|
758
|
+
bosons = pot.boson_massSq(ctx.phase_broken.valAt(percolation.Tperc), percolation.Tperc)
|
|
759
|
+
fermions = pot.fermion_massSq(ctx.phase_broken.valAt(percolation.Tperc))
|
|
760
|
+
gefftotreh = td.e_geffDS(
|
|
761
|
+
bosons*(~pot.mass_spectrum.is_SM_bosons),
|
|
762
|
+
fermions*(~pot.mass_spectrum.is_SM_fermions),
|
|
763
|
+
percolation.Tperc,
|
|
764
|
+
) + td.e_geffSM(percolation.Tperc, pot.conversionFactor)
|
|
765
|
+
derived["g_eff_tot_reh"] = gefftotreh
|
|
766
|
+
|
|
767
|
+
# This combines the dark-sector contribution with the SM bath and
|
|
768
|
+
# therefore assumes a DS + SM setup.
|
|
769
|
+
if "h_eff_tot_reh" in ctx.derived_param_names:
|
|
770
|
+
if verbose:
|
|
771
|
+
print("Calculating h_eff_tot_reh...")
|
|
772
|
+
bosons = pot.boson_massSq(ctx.phase_broken.valAt(percolation.Tperc), percolation.Tperc)
|
|
773
|
+
fermions = pot.fermion_massSq(ctx.phase_broken.valAt(percolation.Tperc))
|
|
774
|
+
hefftotreh = td.s_geffDS(
|
|
775
|
+
bosons*(~pot.mass_spectrum.is_SM_bosons),
|
|
776
|
+
fermions*(~pot.mass_spectrum.is_SM_fermions),
|
|
777
|
+
percolation.Tperc,
|
|
778
|
+
) + td.s_geffSM(percolation.Tperc, pot.conversionFactor)
|
|
779
|
+
derived["h_eff_tot_reh"] = hefftotreh
|
|
780
|
+
|
|
781
|
+
if "g0" in ctx.derived_param_names:
|
|
782
|
+
derived["g0"] = 2
|
|
783
|
+
|
|
784
|
+
if "h0" in ctx.derived_param_names:
|
|
785
|
+
derived["h0"] = 3.91
|
|
786
|
+
|
|
787
|
+
if (
|
|
788
|
+
"betaH_S3" in ctx.derived_param_names
|
|
789
|
+
or "betaH_RH" in ctx.derived_param_names
|
|
790
|
+
or "Tf_SM_GeV" in ctx.derived_param_names
|
|
791
|
+
):
|
|
792
|
+
self._populate_beta_related(
|
|
793
|
+
ctx,
|
|
794
|
+
percolation,
|
|
795
|
+
tmin,
|
|
796
|
+
tmax,
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
def _populate_beta_related(
|
|
800
|
+
self,
|
|
801
|
+
ctx: TransitionContext,
|
|
802
|
+
percolation: PercolationResult,
|
|
803
|
+
tmin: float,
|
|
804
|
+
tmax: float,
|
|
805
|
+
) -> None:
|
|
806
|
+
"""Gather betaH variants and Tf once percolation data is available."""
|
|
807
|
+
derived = ctx.derived_params
|
|
808
|
+
verbose = ctx.verbose
|
|
809
|
+
pot = ctx.pot
|
|
810
|
+
beta_time_factor = self._beta_time_temperature_factor(ctx, percolation.Tperc)
|
|
811
|
+
|
|
812
|
+
if "betaH_S3" in ctx.derived_param_names:
|
|
813
|
+
if verbose:
|
|
814
|
+
print("Calculating betaH_S3 (through the percolation splines)...")
|
|
815
|
+
if derived.get("WARNING:no_perc_splines", False) or percolation.Sint is None:
|
|
816
|
+
if verbose:
|
|
817
|
+
msg = (
|
|
818
|
+
"WARNING: Approximate betaH_S3 only by using betaH_approx "
|
|
819
|
+
"because no percolation splines were found."
|
|
820
|
+
)
|
|
821
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
822
|
+
|
|
823
|
+
derived["betaH_S3"] = calc_betaH_S3_approx(
|
|
824
|
+
percolation.Tperc,
|
|
825
|
+
ctx.outdict,
|
|
826
|
+
ctx.pot,
|
|
827
|
+
ctx.phase_symmetric,
|
|
828
|
+
ctx.phase_broken,
|
|
829
|
+
tmin,
|
|
830
|
+
tmax,
|
|
831
|
+
verbose=verbose,
|
|
832
|
+
)
|
|
833
|
+
else:
|
|
834
|
+
derived["betaH_S3"] = calc_betaH_S3(
|
|
835
|
+
percolation.Tperc,
|
|
836
|
+
percolation.Sint,
|
|
837
|
+
ctx.outdict,
|
|
838
|
+
ctx.pot,
|
|
839
|
+
ctx.phase_symmetric,
|
|
840
|
+
ctx.phase_broken,
|
|
841
|
+
verbose,
|
|
842
|
+
)
|
|
843
|
+
if np.isfinite(derived.get("betaH_S3", np.nan)):
|
|
844
|
+
derived["betaH_S3"] *= beta_time_factor
|
|
845
|
+
|
|
846
|
+
if "betaH_RH" in ctx.derived_param_names:
|
|
847
|
+
RH = derived.get("RH", np.nan)
|
|
848
|
+
if not np.isfinite(RH) or RH <= 0.0:
|
|
849
|
+
if verbose:
|
|
850
|
+
msg = (
|
|
851
|
+
"Set betaH_RH to np.nan "
|
|
852
|
+
"because RH is non-positive or non-finite."
|
|
853
|
+
)
|
|
854
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
855
|
+
derived["betaH_RH"] = np.nan
|
|
856
|
+
else:
|
|
857
|
+
f_perc = ctx.PercolationConf.f_perc
|
|
858
|
+
derived["betaH_RH"] = (
|
|
859
|
+
(8 * np.pi) ** (1 / 3)
|
|
860
|
+
/ RH
|
|
861
|
+
* max(derived.get("v_wall", 1.0), derived.get("c_s", 1 / np.sqrt(3)))
|
|
862
|
+
* (1 / f_perc) ** (1 / 3)
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
if "Tf_SM_GeV" in ctx.derived_param_names:
|
|
866
|
+
if verbose:
|
|
867
|
+
print("Calculating Tf_SM_GeV...")
|
|
868
|
+
if (
|
|
869
|
+
derived.get("WARNING:no_perc_splines", False)
|
|
870
|
+
or percolation.Pint is None
|
|
871
|
+
or percolation.TSYM is None
|
|
872
|
+
):
|
|
873
|
+
if verbose:
|
|
874
|
+
msg = (
|
|
875
|
+
"Set Tf_SM_GeV to np.nan because no "
|
|
876
|
+
"percolation splines were found."
|
|
877
|
+
)
|
|
878
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
879
|
+
Tf_val = np.nan
|
|
880
|
+
else:
|
|
881
|
+
try:
|
|
882
|
+
Tf_val = calcTf(
|
|
883
|
+
percolation.Tperc,
|
|
884
|
+
percolation.TSYM[-1],
|
|
885
|
+
percolation.Pint,
|
|
886
|
+
pot,
|
|
887
|
+
verbose=verbose,
|
|
888
|
+
)
|
|
889
|
+
except errors.PercolationError as err:
|
|
890
|
+
if verbose:
|
|
891
|
+
msg = (
|
|
892
|
+
"Could not compute the temperature at which the "
|
|
893
|
+
"transition ends (Tf_SM_GeV). Set it to np.nan instead."
|
|
894
|
+
)
|
|
895
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
896
|
+
console.print("Error message: ", err)
|
|
897
|
+
Tf_val = np.nan
|
|
898
|
+
derived["Tf_SM_GeV"] = Tf_val * pot.conversionFactor if np.isfinite(Tf_val) else np.nan
|
|
899
|
+
|
|
900
|
+
def _beta_time_temperature_factor(self, ctx: TransitionContext, temperature: float) -> float:
|
|
901
|
+
"""Return the factor converting temperature derivatives to beta/H.
|
|
902
|
+
|
|
903
|
+
``calc_betaH_S3`` evaluates the bag-limit expression
|
|
904
|
+
``T d(S_3/T)/dT``. For the generalized time-temperature relation,
|
|
905
|
+
``dT/dt = -3 c_s^2 H T``, the physical beta/H obtains an extra
|
|
906
|
+
factor ``3 c_s^2`` evaluated in the symmetric phase.
|
|
907
|
+
"""
|
|
908
|
+
if str(ctx.PercolationConf.time_temperature_mode) != "sound_speed":
|
|
909
|
+
return 1.0
|
|
910
|
+
c_s_sym = ctx.derived_params.get("c_s_sym")
|
|
911
|
+
if c_s_sym is None or not np.isfinite(c_s_sym):
|
|
912
|
+
hydr = Hydrodynamics(ctx.pot, ctx.phase_symmetric, ctx.phase_broken, ctx.verbose)
|
|
913
|
+
c_s_sym = hydr.calc_cs(temperature, sym=True)
|
|
914
|
+
return float(3.0 * c_s_sym * c_s_sym)
|
|
915
|
+
|
|
916
|
+
def _compute_kappa_parameters(
|
|
917
|
+
self,
|
|
918
|
+
ctx: TransitionContext,
|
|
919
|
+
percolation: PercolationResult
|
|
920
|
+
) -> None:
|
|
921
|
+
"""Calculate kappa parameters if required.
|
|
922
|
+
|
|
923
|
+
Parameters
|
|
924
|
+
----------
|
|
925
|
+
ctx : TransitionContext
|
|
926
|
+
percolation : PercolationResult
|
|
927
|
+
|
|
928
|
+
Returns
|
|
929
|
+
-------
|
|
930
|
+
None.
|
|
931
|
+
"""
|
|
932
|
+
names = ctx.derived_param_names
|
|
933
|
+
if not {"kappa_phi", "kappa_sw", "kappa_turb"} & set(names):
|
|
934
|
+
return
|
|
935
|
+
if ctx.verbose:
|
|
936
|
+
print("Calculating kappa parameters...")
|
|
937
|
+
_, alpha_thetaP, _, _, alpha_hydP, alpha_infP, alpha_eqP = calcAlphas(
|
|
938
|
+
percolation.Tperc, ctx.pot, ctx.phase_symmetric, ctx.phase_broken,
|
|
939
|
+
verbose=ctx.verbose,
|
|
940
|
+
)
|
|
941
|
+
# Compute the initial bubble radius R0. We take it to be
|
|
942
|
+
# the radius of the bubble at the nucleation temperature,
|
|
943
|
+
# which is the radius at which the field is halfway
|
|
944
|
+
# between the start and endpoint of the transition. Note
|
|
945
|
+
# that the endpoint of the transition is not necessarily
|
|
946
|
+
# the broken phase minimum, rather the point from which on
|
|
947
|
+
# the field just rolls down to the broken phase minimum, i.e.
|
|
948
|
+
# the endpoint in the instanton solution.
|
|
949
|
+
|
|
950
|
+
# The fixed_step_size solver historically uses the nucleation instanton.
|
|
951
|
+
# In the adaptive_step_size workflow there is no trustworthy Tnuc seed,
|
|
952
|
+
# so use the instanton at Tperc when present, otherwise the closest
|
|
953
|
+
# outdict instanton to Tperc. This keeps R0 tied to the actual
|
|
954
|
+
# percolation solution used for the GW spectrum instead of falling
|
|
955
|
+
# back to NaN.
|
|
956
|
+
instanton = _select_instanton_near_temperature(ctx.outdict, percolation.Tperc)
|
|
957
|
+
if instanton is None:
|
|
958
|
+
instanton = ctx.tr.instanton
|
|
959
|
+
R_, phi_ = _extract_instanton_profile_arrays(instanton)
|
|
960
|
+
if (
|
|
961
|
+
R_ is None
|
|
962
|
+
or phi_ is None
|
|
963
|
+
or R_.size == 0
|
|
964
|
+
or phi_.size == 0
|
|
965
|
+
or R_.size != phi_.size
|
|
966
|
+
or not np.all(np.isfinite(R_))
|
|
967
|
+
or not np.all(np.isfinite(phi_))
|
|
968
|
+
):
|
|
969
|
+
R0 = np.nan
|
|
970
|
+
else:
|
|
971
|
+
phi_middle = (phi_[0] + phi_[-1]) / 2
|
|
972
|
+
idx = np.argmin(np.abs(phi_ - phi_middle))
|
|
973
|
+
|
|
974
|
+
# this is the radius at which the field is halfway between the
|
|
975
|
+
# start and endpoint of the transition
|
|
976
|
+
R0 = R_[idx]
|
|
977
|
+
|
|
978
|
+
RH = ctx.derived_params.get("RH")
|
|
979
|
+
if RH is None or not np.isfinite(RH) or RH <= 0:
|
|
980
|
+
overlap_tmin = max(float(ctx.phase_symmetric.Tmin), float(ctx.phase_broken.Tmin))
|
|
981
|
+
overlap_tmax = min(float(ctx.phase_symmetric.Tmax), float(ctx.phase_broken.Tmax))
|
|
982
|
+
betaH_approx = calc_betaH_S3_approx(
|
|
983
|
+
percolation.Tperc,
|
|
984
|
+
ctx.outdict,
|
|
985
|
+
ctx.pot,
|
|
986
|
+
ctx.phase_symmetric,
|
|
987
|
+
ctx.phase_broken,
|
|
988
|
+
overlap_tmin,
|
|
989
|
+
overlap_tmax,
|
|
990
|
+
verbose=ctx.verbose,
|
|
991
|
+
)
|
|
992
|
+
if np.isfinite(betaH_approx) and betaH_approx > 0:
|
|
993
|
+
f_perc = ctx.PercolationConf.f_perc
|
|
994
|
+
RH = (8 * np.pi / f_perc) ** (1 / 3) / betaH_approx * max(
|
|
995
|
+
ctx.derived_params.get("v_wall", 1.0), ctx.derived_params.get("c_s", 1 / np.sqrt(3))
|
|
996
|
+
)
|
|
997
|
+
else:
|
|
998
|
+
RH = np.nan
|
|
999
|
+
|
|
1000
|
+
# Bubble size at time of collision (percolation)
|
|
1001
|
+
Hstar = HubbleParameter(
|
|
1002
|
+
energyDensity(ctx.pot, ctx.phase_symmetric, percolation.Tperc), ctx.pot.conversionFactor
|
|
1003
|
+
)
|
|
1004
|
+
Rstar = RH / Hstar if np.isfinite(RH) and RH > 0 else np.nan
|
|
1005
|
+
|
|
1006
|
+
kappa_phi, kappa_sw, kappa_turb = calc_kappas(
|
|
1007
|
+
alpha_hydP,
|
|
1008
|
+
alpha_infP,
|
|
1009
|
+
alpha_eqP,
|
|
1010
|
+
ctx.derived_params.get("v_wall", 1.0),
|
|
1011
|
+
ctx.derived_params.get("c_s", 1 / np.sqrt(3)),
|
|
1012
|
+
Rstar,
|
|
1013
|
+
R0,
|
|
1014
|
+
ctx.GWconfig,
|
|
1015
|
+
)
|
|
1016
|
+
ctx.derived_params["kappa_phi"] = kappa_phi
|
|
1017
|
+
ctx.derived_params["kappa_sw"] = kappa_sw
|
|
1018
|
+
ctx.derived_params["kappa_turb"] = kappa_turb
|
|
1019
|
+
|
|
1020
|
+
def _finalize_results(self, ctx: TransitionContext) -> dict:
|
|
1021
|
+
"""Ensure all requested parameters are present and return dict."""
|
|
1022
|
+
derived = ctx.derived_params
|
|
1023
|
+
beta_S3 = derived.get("betaH_S3", np.nan)
|
|
1024
|
+
beta_RH = derived.get("betaH_RH", np.nan)
|
|
1025
|
+
|
|
1026
|
+
if "WARNING:betaH_small" in ctx.derived_param_names:
|
|
1027
|
+
derived["WARNING:betaH_small"] = derived.get("betaH_RH", np.inf) < 10
|
|
1028
|
+
|
|
1029
|
+
if "WARNING:betaH_very_small" in ctx.derived_param_names:
|
|
1030
|
+
derived["WARNING:betaH_very_small"] = derived.get("betaH_RH", np.inf) < 3
|
|
1031
|
+
|
|
1032
|
+
if "WARNING:nucleationRate_nonexponential" in ctx.derived_param_names:
|
|
1033
|
+
derived["WARNING:nucleationRate_nonexponential"] = (
|
|
1034
|
+
np.isfinite(beta_S3) and beta_S3 < 0
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
if "WARNING:betaH_nonfinite" in ctx.derived_param_names:
|
|
1038
|
+
derived["WARNING:betaH_nonfinite"] = (
|
|
1039
|
+
not np.isfinite(beta_S3) or not np.isfinite(beta_RH)
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
if "WARNING:betaH_mismatch" in ctx.derived_param_names:
|
|
1043
|
+
mismatch = False
|
|
1044
|
+
if np.isfinite(beta_S3) and np.isfinite(beta_RH):
|
|
1045
|
+
abs_beta_S3 = abs(beta_S3)
|
|
1046
|
+
abs_beta_RH = abs(beta_RH)
|
|
1047
|
+
if abs_beta_S3 == 0 or abs_beta_RH == 0:
|
|
1048
|
+
mismatch = not np.isclose(abs_beta_S3, abs_beta_RH)
|
|
1049
|
+
else:
|
|
1050
|
+
mismatch = max(
|
|
1051
|
+
abs_beta_RH / abs_beta_S3,
|
|
1052
|
+
abs_beta_S3 / abs_beta_RH,
|
|
1053
|
+
) >= 10
|
|
1054
|
+
derived["WARNING:betaH_mismatch"] = mismatch
|
|
1055
|
+
|
|
1056
|
+
if "WARNING:not_T0_global_min" in ctx.derived_param_names:
|
|
1057
|
+
# This flag is set in the check_T0_global_min function,
|
|
1058
|
+
# when checking all transitions after initialization.
|
|
1059
|
+
derived["WARNING:not_T0_global_min"] = False
|
|
1060
|
+
|
|
1061
|
+
# Check if there are any names in derived_param_names which are not
|
|
1062
|
+
# in the derived_params_dict
|
|
1063
|
+
for name in ctx.derived_param_names:
|
|
1064
|
+
if name not in derived:
|
|
1065
|
+
msg = (
|
|
1066
|
+
f"{name} not found in derived_params_dict. "
|
|
1067
|
+
"It might be that this variable still needs to be implemented. "
|
|
1068
|
+
"It is added to the dictionary with value NaN."
|
|
1069
|
+
)
|
|
1070
|
+
console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
|
|
1071
|
+
derived[name] = np.nan
|
|
1072
|
+
|
|
1073
|
+
return derived
|
|
1074
|
+
|
|
1075
|
+
def computeTransitionObservables_one_transition(self, tr : TransitionInfo,
|
|
1076
|
+
rtol=1e-4, nAction=50):
|
|
1077
|
+
"""Calculate the GW observables after the phase tracing and the
|
|
1078
|
+
computation of the nucleation temperature have been performed.
|
|
1079
|
+
This function assumes kinetic equilibrium between the dark sector and
|
|
1080
|
+
the SM.
|
|
1081
|
+
|
|
1082
|
+
Parameters
|
|
1083
|
+
----------
|
|
1084
|
+
tr : TransitionInfo
|
|
1085
|
+
The transition information for which to compute observables.
|
|
1086
|
+
rtol : float, optional
|
|
1087
|
+
Relative tolerance for the computation. Default is 1e-4.
|
|
1088
|
+
nAction : int, optional
|
|
1089
|
+
Number of action points to consider. Default is 50.
|
|
1090
|
+
Returns
|
|
1091
|
+
----------
|
|
1092
|
+
GWparams : tuple (float)
|
|
1093
|
+
Dictionary with the GW parameters."""
|
|
1094
|
+
|
|
1095
|
+
ctx = self._initialize_transition_context(tr)
|
|
1096
|
+
if ctx.verbose:
|
|
1097
|
+
if tr.Tnuc is None or not np.isfinite(tr.Tnuc):
|
|
1098
|
+
print(
|
|
1099
|
+
"\nStart calculating GW observables for transition from phase",
|
|
1100
|
+
tr.high_phase,
|
|
1101
|
+
"to phase",
|
|
1102
|
+
tr.low_phase,
|
|
1103
|
+
"without a nucleation-temperature seed",
|
|
1104
|
+
)
|
|
1105
|
+
else:
|
|
1106
|
+
print(
|
|
1107
|
+
"\nStart calculating GW observables for transition from phase",
|
|
1108
|
+
tr.high_phase,
|
|
1109
|
+
"to phase",
|
|
1110
|
+
tr.low_phase,
|
|
1111
|
+
"at Tnuc =",
|
|
1112
|
+
tr.Tnuc * self.pot.conversionFactor,
|
|
1113
|
+
"GeV",
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
tmin = max(float(ctx.phase_symmetric.Tmin), float(ctx.phase_broken.Tmin))
|
|
1117
|
+
tmax = min(float(ctx.phase_symmetric.Tmax), float(ctx.phase_broken.Tmax))
|
|
1118
|
+
if tmin >= tmax:
|
|
1119
|
+
raise errors.PercolationError(
|
|
1120
|
+
"No overlapping temperature interval between traced phases: "
|
|
1121
|
+
f"Tsym in [{ctx.phase_symmetric.Tmin:.8g}, {ctx.phase_symmetric.Tmax:.8g}], "
|
|
1122
|
+
f"Tbro in [{ctx.phase_broken.Tmin:.8g}, {ctx.phase_broken.Tmax:.8g}]."
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
percolation = self._compute_percolation(ctx, rtol, nAction)
|
|
1126
|
+
milestone_temperature = percolation.Tperc
|
|
1127
|
+
self._ensure_wall_velocity(ctx, milestone_temperature)
|
|
1128
|
+
self._ensure_sound_speed(ctx, milestone_temperature)
|
|
1129
|
+
alpha_theta_milestone = self._compute_transition_strength(ctx, milestone_temperature)
|
|
1130
|
+
ctx.derived_params["WARNING:too_weak_to_compute_perc"] = (
|
|
1131
|
+
np.isfinite(alpha_theta_milestone)
|
|
1132
|
+
and alpha_theta_milestone < ctx.PercolationConf.weak_threshold
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
self._populate_temperature_observables(ctx, percolation)
|
|
1136
|
+
self._compute_alpha_parameters(ctx, percolation)
|
|
1137
|
+
self._compute_beta_and_separation(ctx, percolation, tmin, tmax)
|
|
1138
|
+
self._compute_kappa_parameters(ctx, percolation)
|
|
1139
|
+
|
|
1140
|
+
derived = ctx.derived_params
|
|
1141
|
+
if "D" in ctx.derived_param_names:
|
|
1142
|
+
derived["D"] = 1
|
|
1143
|
+
if "step" in ctx.derived_param_names:
|
|
1144
|
+
derived["step"] = ctx.tr.step
|
|
1145
|
+
if "total_steps" in ctx.derived_param_names:
|
|
1146
|
+
derived["total_steps"] = ctx.tr.total_steps
|
|
1147
|
+
|
|
1148
|
+
return self._finalize_results(ctx), percolation, ctx.outdict
|
|
1149
|
+
|
|
1150
|
+
def __iter__(self):
|
|
1151
|
+
"""Enable iteration over the derived observables dictionary."""
|
|
1152
|
+
return iter(self.transitionObservables)
|
|
1153
|
+
|
|
1154
|
+
def __getitem__(self, key: str):
|
|
1155
|
+
"""Allow dictionary-style access to an individual transition."""
|
|
1156
|
+
return self.transitionObservables[key]
|
|
1157
|
+
|
|
1158
|
+
def __len__(self):
|
|
1159
|
+
"""Return the number of transitions for which observables were computed."""
|
|
1160
|
+
return len(self.transitionObservables)
|
|
1161
|
+
|
|
1162
|
+
def keys(self):
|
|
1163
|
+
"""Expose the transition keys."""
|
|
1164
|
+
return self.transitionObservables.keys()
|
|
1165
|
+
|
|
1166
|
+
def items(self):
|
|
1167
|
+
"""Return key/value pairs of the transition observables."""
|
|
1168
|
+
return self.transitionObservables.items()
|
|
1169
|
+
|
|
1170
|
+
def printDerivedParams(self, derived_param_names):
|
|
1171
|
+
"""Print a rich-formatted table of the derived parameters per transition."""
|
|
1172
|
+
nTransitions = len(self.transitions)
|
|
1173
|
+
print("\nComputed thermodynamic quantities for", nTransitions, "transition(s):\n")
|
|
1174
|
+
for i, tr in enumerate(self.transitions):
|
|
1175
|
+
if tr.type == 1:
|
|
1176
|
+
derived_params_dict = self.transitionObservables[i]
|
|
1177
|
+
table = rich.table.Table(
|
|
1178
|
+
title=f"Transition {i+1}: First-order phase transition",
|
|
1179
|
+
title_justify="left",
|
|
1180
|
+
box=rich.box.ROUNDED,
|
|
1181
|
+
)
|
|
1182
|
+
table.add_column("Parameter", style="cyan", no_wrap=True)
|
|
1183
|
+
table.add_column("Value", style="orange1")
|
|
1184
|
+
for j, name in enumerate(derived_param_names):
|
|
1185
|
+
table.add_row(name, f"{derived_params_dict[name]:.10e}")
|
|
1186
|
+
console.print(table)
|
|
1187
|
+
|
|
1188
|
+
if tr.type == 2:
|
|
1189
|
+
table = rich.table.Table(
|
|
1190
|
+
title=f"Transition {i+1}: Second-order phase transition",
|
|
1191
|
+
title_justify="left",
|
|
1192
|
+
box=rich.box.ROUNDED,
|
|
1193
|
+
)
|
|
1194
|
+
table.add_column("Parameter", style="cyan", no_wrap=True)
|
|
1195
|
+
table.add_column("Value", style="orange1")
|
|
1196
|
+
table.add_row("Start phase", str(tr.high_phase))
|
|
1197
|
+
table.add_row("End phase", str(tr.low_phase))
|
|
1198
|
+
table.add_row("Tcrit / GeV", str(tr.Tcrit * self.pot.conversionFactor))
|
|
1199
|
+
console.print(table)
|