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.
Files changed (95) hide show
  1. transitionlistener/__init__.py +21 -0
  2. transitionlistener/bubbledynamics.py +2250 -0
  3. transitionlistener/bubbledynamics_fixedstep.py +2929 -0
  4. transitionlistener/check_installation.py +309 -0
  5. transitionlistener/ckm.py +91 -0
  6. transitionlistener/colors.py +288 -0
  7. transitionlistener/config.py +185 -0
  8. transitionlistener/constants.py +64 -0
  9. transitionlistener/counterterms/__init__.py +230 -0
  10. transitionlistener/counterterms/cw.py +298 -0
  11. transitionlistener/counterterms/tensors.py +343 -0
  12. transitionlistener/counterterms/twohdm.py +520 -0
  13. transitionlistener/counterterms/twohdm_solver.py +374 -0
  14. transitionlistener/errors.py +315 -0
  15. transitionlistener/examples/bp_et.yaml +64 -0
  16. transitionlistener/examples/bp_lisa.yaml +70 -0
  17. transitionlistener/examples/bp_muares.yaml +69 -0
  18. transitionlistener/examples/bp_pta.yaml +64 -0
  19. transitionlistener/examples/example_dark_photon.yaml +73 -0
  20. transitionlistener/examples/example_grid.yaml +55 -0
  21. transitionlistener/examples/example_line.yaml +51 -0
  22. transitionlistener/examples/example_point.yaml +73 -0
  23. transitionlistener/examples/example_random_conformal.yaml +42 -0
  24. transitionlistener/examples/example_ultranest.yaml +39 -0
  25. transitionlistener/finiteT.py +468 -0
  26. transitionlistener/generic_potential.py +1521 -0
  27. transitionlistener/gridplots.py +592 -0
  28. transitionlistener/gwfopt.py +604 -0
  29. transitionlistener/helper_functions.py +843 -0
  30. transitionlistener/hydrodynamics.py +647 -0
  31. transitionlistener/interface/__init__.py +95 -0
  32. transitionlistener/interface/check.py +51 -0
  33. transitionlistener/interface/cli.py +211 -0
  34. transitionlistener/interface/config_loader.py +264 -0
  35. transitionlistener/interface/logging_utils.py +30 -0
  36. transitionlistener/interface/output_schema.py +296 -0
  37. transitionlistener/interface/pipeline.py +300 -0
  38. transitionlistener/interface/samplers.py +565 -0
  39. transitionlistener/interface/scans.py +468 -0
  40. transitionlistener/interface/single_point.py +581 -0
  41. transitionlistener/interface/state.py +61 -0
  42. transitionlistener/lineplots.py +597 -0
  43. transitionlistener/logo/TL-logo_large.png +0 -0
  44. transitionlistener/logo/TL-logo_small.png +0 -0
  45. transitionlistener/models/TL_2HDM.py +512 -0
  46. transitionlistener/models/TL_conformal_dark_u1.py +202 -0
  47. transitionlistener/models/TL_dark_U1.py +198 -0
  48. transitionlistener/models/TL_dark_U1_g_parameterization.py +198 -0
  49. transitionlistener/models/TL_dark_flipflop.py +274 -0
  50. transitionlistener/models/templatePotential.py +260 -0
  51. transitionlistener/nucleation.py +412 -0
  52. transitionlistener/observability.py +790 -0
  53. transitionlistener/particles.py +436 -0
  54. transitionlistener/pathDeformation.py +1462 -0
  55. transitionlistener/percolation_adaptive_gridbuilders.py +1385 -0
  56. transitionlistener/percolation_adaptive_rate.py +1049 -0
  57. transitionlistener/percolation_adaptivestepsize.py +1632 -0
  58. transitionlistener/percolation_fixedstepsize.py +618 -0
  59. transitionlistener/phases.py +1499 -0
  60. transitionlistener/plot_settings.py +121 -0
  61. transitionlistener/plots.py +1781 -0
  62. transitionlistener/runtime_options.py +278 -0
  63. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/bandwidths.npy +0 -0
  64. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/density.npy +0 -0
  65. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/freqs.npy +0 -0
  66. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log.txt +16 -0
  67. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log10rhogrid.npy +0 -0
  68. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log10rholabels.txt +30 -0
  69. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/pulsar_list.txt +1 -0
  70. transitionlistener/tab_data/HLV-O2-PLI.csv +82 -0
  71. transitionlistener/tab_data/HLV-O2-noise.csv +83 -0
  72. transitionlistener/tab_data/HLVK-design-PLI.csv +61 -0
  73. transitionlistener/tab_data/HLVK-design-noise.csv +83 -0
  74. transitionlistener/tab_data/NG15_PLI_hc.csv +31 -0
  75. transitionlistener/tab_data/NG15_noise_hc.csv +56 -0
  76. transitionlistener/tab_data/PLI-dataset.csv +327 -0
  77. transitionlistener/tab_data/energydensities.dat +1001 -0
  78. transitionlistener/tab_data/finiteT_b.dat.txt +1999 -0
  79. transitionlistener/tab_data/finiteT_f.dat.txt +1000 -0
  80. transitionlistener/tab_data/geff_SM.dat +10009 -0
  81. transitionlistener/tab_data/muAres_f_Hz_PLI_h2OmegaGW.csv +131 -0
  82. transitionlistener/tab_data/muAres_f_Hz_h2Omega_noise.csv +99 -0
  83. transitionlistener/tab_data/noise-dataset.csv +327 -0
  84. transitionlistener/tab_data/numberdensities.dat +1001 -0
  85. transitionlistener/tab_data/pressures.dat +1001 -0
  86. transitionlistener/thermodynamics.py +686 -0
  87. transitionlistener/transitionObservables.py +1199 -0
  88. transitionlistener/transitionObservables_fixedstep.py +976 -0
  89. transitionlistener/transitions.py +1000 -0
  90. transitionlistener/tunneling1D.py +910 -0
  91. transitionlistener-2.0.0.dist-info/METADATA +817 -0
  92. transitionlistener-2.0.0.dist-info/RECORD +95 -0
  93. transitionlistener-2.0.0.dist-info/WHEEL +4 -0
  94. transitionlistener-2.0.0.dist-info/entry_points.txt +2 -0
  95. transitionlistener-2.0.0.dist-info/licenses/LICENSE.txt +674 -0
@@ -0,0 +1,976 @@
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 scipy import interpolate
14
+ from transitionlistener.generic_potential import generic_potential
15
+ from transitionlistener.phases import PhaseInfo
16
+ from transitionlistener import config
17
+ from transitionlistener.transitions import TransitionInfo
18
+ from . import thermodynamics as td
19
+ from . import errors
20
+ from transitionlistener.hydrodynamics import Hydrodynamics, calc_kappas
21
+ from transitionlistener.bubbledynamics_fixedstep import (
22
+ calcPercAndEvolve,
23
+ calcAlphas,
24
+ calc_betaH_S3,
25
+ calc_betaH_S3_approx,
26
+ calcMeanBubbleSeparation,
27
+ calcMeanBubbleSeparationwExp,
28
+ calcTf,
29
+ HubbleParameter,
30
+ energyDensity,
31
+ )
32
+
33
+ from . import console, print
34
+ import rich
35
+ from rich.panel import Panel
36
+
37
+
38
+ @dataclass
39
+ class TransitionObservableInfo:
40
+ """Collection of transition observable data."""
41
+ v_wall: float = np.nan
42
+ c_s: float = np.nan
43
+ c_s_sym: float = np.nan
44
+ c_s_bro: float = np.nan
45
+ Tperc: float = np.nan
46
+ Tperc_SM_GeV: float = np.nan
47
+ Tcrit_SM_GeV: float = np.nan
48
+ xi_crit: float = np.nan
49
+ Tnuc_SM_GeV: float = np.nan
50
+ Treh: float = np.nan
51
+ Treh_SM_GeV: float = np.nan
52
+ alpha: float = np.nan
53
+ alpha_theta: float = np.nan
54
+ alpha_p: float = np.nan
55
+ alpha_e: float = np.nan
56
+ alpha_hyd: float = np.nan
57
+ alpha_inf: float = np.nan
58
+ alpha_eq: float = np.nan
59
+ RH: float = np.nan
60
+ g_eff_tot_reh: float = np.nan
61
+ h_eff_tot_reh: float = np.nan
62
+ g0: float = np.nan
63
+ h0: float = np.nan
64
+ betaH_S3: float = np.nan
65
+ betaH_RH: float = np.nan
66
+ Tf_SM_GeV: float = np.nan
67
+ kappa_phi: float = np.nan
68
+ kappa_sw: float = np.nan
69
+ kappa_turb: float = np.nan
70
+ D: float = np.nan
71
+ step: float = np.nan
72
+ total_steps: float = np.nan
73
+ WARNING_too_weak_to_compute_perc: bool = False
74
+ WARNING_no_perc_splines: bool = False
75
+ WARNING_betaH_small: bool = False
76
+ WARNING_not_T0_global_min: bool = False
77
+
78
+
79
+ @dataclass
80
+ class TransitionContext:
81
+ """Collect shared inputs and outputs for a single transition."""
82
+
83
+ tr: TransitionInfo
84
+ pot: generic_potential
85
+ phases: list[PhaseInfo]
86
+ GWconfig: config.GWConf
87
+ PercolationConf: config.PercolationConf
88
+ derived_param_names: list[str]
89
+ verbose: bool
90
+ derived_params: dict
91
+ Tnuc: float
92
+ outdict: dict
93
+ phase_symmetric: PhaseInfo
94
+ phase_broken: PhaseInfo
95
+
96
+
97
+ @dataclass
98
+ class PercolationResult:
99
+ """Hold percolation-related outputs and splines when available."""
100
+
101
+ Tperc: float
102
+ TSYM: np.ndarray | None
103
+ H: np.ndarray | None
104
+ P: np.ndarray | None
105
+ TBRO: np.ndarray | None
106
+ S: np.ndarray | None
107
+ Pint: interpolate.interp1d | None
108
+ Sint: interpolate.interp1d | None
109
+ Hint: interpolate.interp1d | None
110
+ Pexpint: interpolate.interp1d | None
111
+ soundSpSqint: interpolate.interp1d | None
112
+ scalef_r_int: interpolate.interp1d | None
113
+ TBROint: interpolate.interp1d | None
114
+ successful: bool
115
+
116
+
117
+ class TransitionObservables:
118
+ """Evaluate derived observables for every transition of a potential."""
119
+
120
+ def __init__(self, pot: generic_potential, phases: list[PhaseInfo],
121
+ transitions: list[TransitionInfo], verbose=False):
122
+ """Store inputs and immediately compute all derived quantities."""
123
+ self.pot = pot
124
+ self.phases = phases
125
+ self.GWconfig = pot.config.gwConf
126
+ self.PercolationConf = pot.config.percolationConf
127
+ self.transitions = transitions
128
+ self.verbose = verbose
129
+ self.derived_param_names = [name for name in config.all_observables.keys()]
130
+
131
+ if verbose:
132
+ console.print(
133
+ "[green bold]\nStart percolation computation...[/]",
134
+ )
135
+
136
+ self.transitionObservables = self.computeTransitionObservables()
137
+ self.check_T0_global_min()
138
+
139
+ def check_T0_global_min(self):
140
+ """Check whether the global minimum at T = Tmin is the last minimum.
141
+ If not, raise an error.
142
+ """
143
+ T0 = self.pot.Tmin
144
+ last_vev = self.phases[self.transitions[-1].low_phase].valAt(self.pot.Tmin)
145
+
146
+ if not np.allclose(last_vev, self.pot.X0):
147
+ # The global minimum at T = 0 is not the last minimum!
148
+
149
+ for i, tr in enumerate(self.transitions):
150
+ if tr is not None and tr.type == 1:
151
+ self.transitions[i].derived_params["WARNING:not_T0_global_min"] = True
152
+ self.transitionObservables[i]["WARNING:not_T0_global_min"] = True
153
+
154
+ msg = (
155
+ "The global minimum at T = {} is not the last minimum found "
156
+ "by the phase tracing algorithm. This may be unwanted, for "
157
+ "example if the potential is expected to have a unique "
158
+ "global minimum at T = {} or when identifying the last phase "
159
+ "with the electroweak vacuum."
160
+ ).format(T0 * self.pot.conversionFactor, T0 * self.pot.conversionFactor)
161
+
162
+ if self.GWconfig.check_if_T0_global_min:
163
+ console.print(f"[bold red]ERROR:[/bold red] {msg}")
164
+ raise errors.GlobalMinimumError(msg)
165
+ elif self.verbose:
166
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
167
+ console.print(
168
+ "[bold yellow]WARNING:[/bold yellow] "
169
+ "Continuing, because the check_if_T0_global_min flag is "
170
+ "set to False."
171
+ )
172
+ else:
173
+ # The global minimum at T = 0 is the last minimum.
174
+ for i, tr in enumerate(self.transitions):
175
+ if tr is not None and tr.type == 1:
176
+ self.transitions[i].derived_params["WARNING:not_T0_global_min"] = False
177
+ self.transitionObservables[i]["WARNING:not_T0_global_min"] = False
178
+
179
+ if self.verbose:
180
+ msg = (
181
+ "The global minimum at T_min is the last minimum found "
182
+ "by the phase tracing algorithm."
183
+ )
184
+ console.print(
185
+ f"[bold cyan]INFO:[/bold cyan] {msg}"
186
+ )
187
+
188
+ def computeTransitionObservables(self):
189
+ """
190
+ Calculate the GW observables for each transition in the
191
+ transitions_dict. This is done by calling the
192
+ `computeTransitionObservables_one_transition`
193
+ function from the `bubbledynamics` module.
194
+
195
+ Raises
196
+ ------
197
+ err
198
+ Raises an error if GW observables cannot be calculated.
199
+
200
+ Returns
201
+ -------
202
+ transitionObservables: dict
203
+ A dictionary with the GW observables for each transition.
204
+ """
205
+ self.transitionObservables = {i: {} for i in range(len(self.transitions))}
206
+ self.percolationResults = {i: None for i in range(len(self.transitions))}
207
+ self.outdicts = {i: None for i in range(len(self.transitions))}
208
+
209
+ for i, tr in enumerate(self.transitions):
210
+ if tr is not None and tr.type == 1:
211
+ try:
212
+ derived_params_dict_tr, percol, outdict = self.computeTransitionObservables_one_transition(
213
+ tr, nAction=self.PercolationConf.n_action
214
+ )
215
+ self.transitions[i].derived_params = derived_params_dict_tr
216
+ self.percolationResults[i] = percol
217
+ self.outdicts[i] = outdict
218
+ self.transitionObservables[i] = derived_params_dict_tr
219
+ except Exception as err:
220
+ if self.verbose:
221
+ print("Error calculating GW parameters: ", err)
222
+ raise err
223
+ return self.transitionObservables
224
+
225
+ def _initialize_transition_context(self, tr: TransitionInfo) -> TransitionContext:
226
+ """Prepare reusable data structures for a single transition."""
227
+ derived_params = {
228
+ "WARNING:too_weak_to_compute_perc": False,
229
+ "WARNING:no_perc_splines": False,
230
+ "WARNING:betaH_small": False,
231
+ }
232
+ return TransitionContext(
233
+ tr=tr,
234
+ pot=self.pot,
235
+ phases=self.phases,
236
+ GWconfig=self.GWconfig,
237
+ PercolationConf=self.PercolationConf,
238
+ derived_param_names=self.derived_param_names,
239
+ verbose=self.verbose,
240
+ derived_params=derived_params,
241
+ Tnuc=tr.Tnuc,
242
+ outdict=tr.full_tunneling_info,
243
+ phase_symmetric=self.phases[tr.high_phase],
244
+ phase_broken=self.phases[tr.low_phase],
245
+ )
246
+
247
+ def _ensure_wall_velocity(self, ctx: TransitionContext, T: float) -> None:
248
+ """Populate v_wall if requested by configuration."""
249
+ if "v_wall" not in ctx.derived_param_names:
250
+ return
251
+ GWconfig = ctx.GWconfig
252
+ derived = ctx.derived_params
253
+ if GWconfig.wall_velocity == "LTE":
254
+ hydr = Hydrodynamics(ctx.pot, ctx.phase_symmetric, ctx.phase_broken, ctx.verbose)
255
+ derived["v_wall"] = hydr.calcWallVelocityLTE(T)
256
+ elif GWconfig.wall_velocity == "c":
257
+ derived["v_wall"] = 1.0
258
+ elif GWconfig.wall_velocity == "WallGo":
259
+ raise NotImplementedError("WallGo is not implemented yet.")
260
+ else:
261
+ vw = 0.0
262
+ try:
263
+ vw = float(GWconfig.wall_velocity)
264
+ except:
265
+ raise ValueError("wall_velocity must be 'LTE' or a float. " +
266
+ "Not: ", GWconfig.wall_velocity, "!")
267
+ if vw <= 0.0 or vw > 1.0:
268
+ raise ValueError("Wall velocity must be 0 < vw <= 1.0." +
269
+ "Not ", vw, "!")
270
+ else:
271
+ derived["v_wall"] = vw
272
+
273
+ def _ensure_sound_speed(self, ctx: TransitionContext, T: float) -> None:
274
+ """Populate sound speed values when requested."""
275
+ if "c_s" not in ctx.derived_param_names:
276
+ return
277
+ derived = ctx.derived_params
278
+ GWconfig = ctx.GWconfig
279
+ if ctx.verbose:
280
+ print("Calculating sound speed...")
281
+ if GWconfig.sound_speed == "compute":
282
+ hydr = Hydrodynamics(ctx.pot, ctx.phase_symmetric, ctx.phase_broken, ctx.verbose)
283
+ c_sb = hydr.calc_cs(T, sym=False)
284
+ c_ss = hydr.calc_cs(T, sym=True)
285
+ derived["c_s"] = c_sb
286
+ derived["c_s_sym"] = c_ss
287
+ derived["c_s_bro"] = c_sb
288
+ else:
289
+ cs = 0.0
290
+ try:
291
+ cs = float(GWconfig.sound_speed)
292
+ except:
293
+ raise NotImplementedError(
294
+ "The GWconfig.sound_speed option is not implemented yet: "
295
+ + GWconfig.sound_speed
296
+ )
297
+ if cs <= 1.0 and cs > 0:
298
+ derived["c_s"] = cs
299
+ derived["c_s_sym"] = cs
300
+ derived["c_s_bro"] = cs
301
+ else:
302
+ raise ValueError("Speed of sound must be 0 < cs <= 1." +
303
+ " Not ", cs, "!")
304
+
305
+
306
+ def _compute_transition_strength(self, ctx: TransitionContext) -> float:
307
+ """Return alpha_theta at nucleation and handle logging."""
308
+ weak_threshold = ctx.PercolationConf.weak_threshold
309
+ _, alpha_theta_nuc, _, _, _, _ = calcAlphas(
310
+ ctx.Tnuc, ctx.pot, ctx.phase_symmetric, ctx.phase_broken,
311
+ verbose=ctx.verbose
312
+ )
313
+ if ctx.verbose:
314
+ print("alpha at nucleation =", np.squeeze(alpha_theta_nuc))
315
+ return np.squeeze(alpha_theta_nuc)
316
+
317
+ def _compute_percolation(
318
+ self,
319
+ ctx: TransitionContext,
320
+ alpha_theta_nuc: float,
321
+ rtol: float,
322
+ nAction: int,
323
+ ) -> PercolationResult:
324
+ """Either reuse nucleation data or perform the percolation computation."""
325
+ pot = ctx.pot
326
+ derived = ctx.derived_params
327
+ verbose = ctx.verbose
328
+ GWconfig = ctx.GWconfig
329
+ weak_threshold = ctx.PercolationConf.weak_threshold
330
+
331
+ if alpha_theta_nuc < weak_threshold:
332
+ if verbose:
333
+ print(
334
+ "alpha at nucleation is too small (alpha =",
335
+ alpha_theta_nuc,
336
+ "<",
337
+ weak_threshold,
338
+ "), using Tnuc as Treh",
339
+ )
340
+ derived["Treh_SM_GeV"] = ctx.Tnuc * pot.conversionFactor
341
+ derived["WARNING:too_weak_to_compute_perc"] = True
342
+ derived["WARNING:no_perc_splines"] = True
343
+ return PercolationResult(
344
+ Tperc=ctx.Tnuc,
345
+ TSYM=None,
346
+ H=None,
347
+ P=None,
348
+ TBRO=None,
349
+ S=None,
350
+ Pint=None,
351
+ Sint=None,
352
+ Hint=None,
353
+ Pexpint=None,
354
+ soundSpSqint=None,
355
+ scalef_r_int=None,
356
+ TBROint=None,
357
+ successful=False,
358
+ )
359
+
360
+ if verbose:
361
+ msg = (
362
+ f"alpha at nucleation is large enough "
363
+ f"(alpha = {alpha_theta_nuc:.3e} > {weak_threshold:.3e}). "
364
+ "Continue with percolation computation."
365
+ )
366
+ print(msg)
367
+
368
+ try:
369
+ Tperc, TSYM, H, P, TBRO, S, P_exp, scalef_r, soundSpSq = calcPercAndEvolve(
370
+ ctx.outdict,
371
+ ctx.Tnuc,
372
+ ctx.phase_symmetric,
373
+ ctx.phase_broken,
374
+ pot,
375
+ rtol=rtol,
376
+ nAction=nAction,
377
+ verbose=verbose,
378
+ vw=ctx.derived_params.get("v_wall", 1.0),
379
+ )
380
+ except Exception as err:
381
+ if verbose:
382
+ print("Fatal error in calcPercAndEvolve: ", err)
383
+ raise
384
+
385
+ try:
386
+ if verbose:
387
+ print("Calculating percolation splines...")
388
+ finite_mask = np.isfinite(P)
389
+ Pint = interpolate.interp1d(
390
+ TSYM[finite_mask],
391
+ P[finite_mask],
392
+ kind="cubic",
393
+ fill_value=(0, 1),
394
+ bounds_error=False,
395
+ )
396
+ Pexpint = interpolate.interp1d(
397
+ TSYM[finite_mask],
398
+ P_exp[finite_mask],
399
+ kind="cubic",
400
+ fill_value=(0, 1),
401
+ bounds_error=False,
402
+ )
403
+ soundSqSqint = interpolate.interp1d(
404
+ TSYM[finite_mask],
405
+ soundSpSq[finite_mask],
406
+ kind="cubic",
407
+ bounds_error=False,
408
+ )
409
+ scalef_r_int = interpolate.interp1d(
410
+ TSYM[finite_mask],
411
+ scalef_r[finite_mask],
412
+ kind="cubic",
413
+ bounds_error=False,
414
+ )
415
+ Sint = interpolate.interp1d(
416
+ TSYM[np.isfinite(S)],
417
+ S[np.isfinite(S)],
418
+ kind="cubic",
419
+ fill_value=(-np.inf, np.inf),
420
+ bounds_error=False,
421
+ )
422
+ Hint = interpolate.interp1d(TSYM, H, kind="cubic")
423
+ TBROint = interpolate.interp1d(TSYM, TBRO, kind="cubic")
424
+ except Exception as err:
425
+ derived["WARNING:no_perc_splines"] = True
426
+ if verbose:
427
+ print("Error in computing percolation splines: ", err)
428
+ msg = (
429
+ "WARNING: Cannot calculate the splines. Continue with the "
430
+ "percolation temperature, but no splines will be available "
431
+ "for further calculations."
432
+ )
433
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
434
+ Pint = Sint = Hint = TBROint = Pexpint = soundSqSqint = scalef_r_int = None
435
+
436
+ return PercolationResult(
437
+ Tperc=Tperc,
438
+ TSYM=TSYM,
439
+ H=H,
440
+ P=P,
441
+ TBRO=TBRO,
442
+ S=S,
443
+ Pint=Pint,
444
+ Sint=Sint,
445
+ Hint=Hint,
446
+ Pexpint=Pexpint,
447
+ soundSpSqint=soundSqSqint,
448
+ scalef_r_int=scalef_r_int,
449
+ TBROint=TBROint,
450
+ successful=True,
451
+ )
452
+
453
+ def _populate_temperature_observables(
454
+ self,
455
+ ctx: TransitionContext,
456
+ percolation: PercolationResult,
457
+ ) -> None:
458
+ """Fill temperature-related entries in the derived parameters."""
459
+ derived = ctx.derived_params
460
+ pot = ctx.pot
461
+
462
+ if "Tperc_SM_GeV" in ctx.derived_param_names:
463
+ if ctx.verbose:
464
+ print("Calculating percolation temperature...")
465
+ derived["Tperc"] = percolation.Tperc
466
+ derived["Tperc_SM_GeV"] = percolation.Tperc * pot.conversionFactor
467
+
468
+ if "Tcrit_SM_GeV" in ctx.derived_param_names:
469
+ if ctx.verbose:
470
+ print("Calculating critical temperature...")
471
+ derived["Tcrit_SM_GeV"] = ctx.tr.Tcrit * pot.conversionFactor
472
+
473
+ if "xi_crit" in ctx.derived_param_names:
474
+ if ctx.verbose:
475
+ print("Calculating critical vev over temperature...")
476
+ T_crit = ctx.tr.Tcrit
477
+ vev_crit = np.sqrt(np.sum(ctx.phase_broken.valAt(T_crit) ** 2))
478
+ derived["xi_crit"] = vev_crit / T_crit
479
+
480
+ if "Tnuc_SM_GeV" in ctx.derived_param_names:
481
+ if ctx.verbose:
482
+ print("Calculating nucleation temperature...")
483
+ derived["Tnuc_SM_GeV"] = ctx.tr.Tnuc * pot.conversionFactor
484
+
485
+ if "Treh_SM_GeV" in ctx.derived_param_names:
486
+ if ctx.verbose:
487
+ print("Calculating reheating temperature...")
488
+ if derived.get("WARNING:no_perc_splines", False):
489
+ if ctx.verbose:
490
+ msg = (
491
+ "WARNING: Set Treh_SM_GeV to Tperc_SM_GeV "
492
+ "because no percolation splines were found."
493
+ )
494
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
495
+ Treh = percolation.Tperc
496
+ else:
497
+ Treh = percolation.TBROint(percolation.Tperc) if percolation.TBROint else percolation.Tperc
498
+ derived["Treh"] = Treh
499
+ derived["Treh_SM_GeV"] = Treh * pot.conversionFactor
500
+
501
+ def _compute_alpha_parameters(
502
+ self,
503
+ ctx: TransitionContext,
504
+ percolation: PercolationResult,
505
+ ) -> None:
506
+ """Compute alpha-related quantities if requested."""
507
+ if "alpha" not in ctx.derived_param_names:
508
+ return
509
+ if ctx.verbose:
510
+ print("Calculating alpha parameters...")
511
+ alpha_p, alpha_theta, alpha_e, alpha_hyd, alpha_inf, alpha_eq = calcAlphas(
512
+ percolation.Tperc, ctx.pot, ctx.phase_symmetric, ctx.phase_broken,
513
+ verbose=ctx.verbose
514
+ )
515
+ derived = ctx.derived_params
516
+ derived["alpha"] = np.squeeze(alpha_theta)
517
+ derived["alpha_theta"] = np.squeeze(alpha_theta)
518
+ derived["alpha_p"] = np.squeeze(alpha_p)
519
+ derived["alpha_e"] = np.squeeze(alpha_e)
520
+ derived["alpha_hyd"] = np.squeeze(alpha_hyd)
521
+ derived["alpha_inf"] = np.squeeze(alpha_inf)
522
+ derived["alpha_eq"] = np.squeeze(alpha_eq)
523
+
524
+ def _compute_beta_and_separation(
525
+ self,
526
+ ctx: TransitionContext,
527
+ percolation: PercolationResult,
528
+ tmin: float,
529
+ tmax: float,
530
+ ) -> None:
531
+ """Populate beta/H variants, RH and Tf if configured."""
532
+ derived = ctx.derived_params
533
+ pot = ctx.pot
534
+ verbose = ctx.verbose
535
+ outdict = ctx.outdict
536
+ phase_symmetric = ctx.phase_symmetric
537
+ phase_broken = ctx.phase_broken
538
+
539
+ if "betaH_S3_approx" in ctx.derived_param_names:
540
+ if verbose:
541
+ print("Calculating beta/H (approximation through derivative of bounce action)...")
542
+ derived["betaH_S3_approx"] = calc_betaH_S3_approx(
543
+ percolation.Tperc, outdict, pot, phase_symmetric, phase_broken, tmin, tmax, verbose=verbose
544
+ )
545
+
546
+ if "RH" in ctx.derived_param_names:
547
+ if verbose:
548
+ print("Calculating mean bubble separation...")
549
+ if (
550
+ derived.get("WARNING:no_perc_splines", False)
551
+ or percolation.Sint is None
552
+ or percolation.TSYM is None
553
+ ):
554
+ if verbose:
555
+ msg = (
556
+ "WARNING: Set RH to Nan "
557
+ "because no percolation splines were found."
558
+ )
559
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
560
+ derived["RH"] = np.nan
561
+ else:
562
+ Rsep = calcMeanBubbleSeparation(
563
+ percolation.Tperc,
564
+ percolation.TSYM[0],
565
+ percolation.Sint,
566
+ percolation.Pint,
567
+ percolation.Hint,
568
+ )
569
+ Rsep_exp = calcMeanBubbleSeparationwExp(
570
+ percolation.Tperc,
571
+ percolation.TSYM[0],
572
+ percolation.Sint,
573
+ percolation.Pexpint,
574
+ percolation.Hint,
575
+ percolation.soundSpSqint,
576
+ percolation.scalef_r_int,
577
+ )
578
+ RsepHperc = Rsep * percolation.Hint(percolation.Tperc)
579
+ derived["RH"] = RsepHperc
580
+
581
+ # This combines the dark-sector contribution with the SM bath and
582
+ # therefore assumes a DS + SM setup.
583
+ if "g_eff_tot_reh" in ctx.derived_param_names:
584
+ if verbose:
585
+ print("Calculating g_eff_tot_reh...")
586
+ bosons = pot.boson_massSq(ctx.phase_broken.valAt(percolation.Tperc), percolation.Tperc)
587
+ fermions = pot.fermion_massSq(ctx.phase_broken.valAt(percolation.Tperc))
588
+ gefftotreh = td.e_geffDS(
589
+ bosons*(~pot.mass_spectrum.is_SM_bosons),
590
+ fermions*(~pot.mass_spectrum.is_SM_fermions),
591
+ percolation.Tperc,
592
+ ) + td.e_geffSM(percolation.Tperc, pot.conversionFactor)
593
+ derived["g_eff_tot_reh"] = gefftotreh
594
+
595
+ # This combines the dark-sector contribution with the SM bath and
596
+ # therefore assumes a DS + SM setup.
597
+ if "h_eff_tot_reh" in ctx.derived_param_names:
598
+ if verbose:
599
+ print("Calculating h_eff_tot_reh...")
600
+ bosons = pot.boson_massSq(ctx.phase_broken.valAt(percolation.Tperc), percolation.Tperc)
601
+ fermions = pot.fermion_massSq(ctx.phase_broken.valAt(percolation.Tperc))
602
+ hefftotreh = td.s_geffDS(
603
+ bosons*(~pot.mass_spectrum.is_SM_bosons),
604
+ fermions*(~pot.mass_spectrum.is_SM_fermions),
605
+ percolation.Tperc,
606
+ ) + td.s_geffSM(percolation.Tperc, pot.conversionFactor)
607
+ derived["h_eff_tot_reh"] = hefftotreh
608
+
609
+ if "g0" in ctx.derived_param_names:
610
+ derived["g0"] = 2
611
+
612
+ if "h0" in ctx.derived_param_names:
613
+ derived["h0"] = 3.91
614
+
615
+ if (
616
+ "betaH_S3" in ctx.derived_param_names
617
+ or "betaH_RH" in ctx.derived_param_names
618
+ or "Tf_SM_GeV" in ctx.derived_param_names
619
+ ):
620
+ self._populate_beta_related(
621
+ ctx,
622
+ percolation,
623
+ tmin,
624
+ tmax,
625
+ )
626
+
627
+ def _populate_beta_related(
628
+ self,
629
+ ctx: TransitionContext,
630
+ percolation: PercolationResult,
631
+ tmin: float,
632
+ tmax: float,
633
+ ) -> None:
634
+ """Gather betaH variants and Tf once percolation data is available."""
635
+ derived = ctx.derived_params
636
+ verbose = ctx.verbose
637
+ pot = ctx.pot
638
+
639
+ if "betaH_S3" in ctx.derived_param_names:
640
+ if verbose:
641
+ print("Calculating betaH_S3 (through the percolation splines)...")
642
+ if derived.get("WARNING:no_perc_splines", False) or percolation.Sint is None:
643
+ if verbose:
644
+ msg = (
645
+ "WARNING: Approximate betaH_S3 only by using calc_betaH_S3_approx "
646
+ "because no percolation splines were found."
647
+ )
648
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
649
+
650
+ derived["betaH_S3"] = calc_betaH_S3_approx(
651
+ percolation.Tperc,
652
+ ctx.outdict,
653
+ ctx.pot,
654
+ ctx.phase_symmetric,
655
+ ctx.phase_broken,
656
+ tmin,
657
+ tmax,
658
+ verbose=verbose,
659
+ )
660
+ else:
661
+ derived["betaH_S3"] = calc_betaH_S3(
662
+ percolation.Tperc,
663
+ percolation.Sint,
664
+ ctx.outdict,
665
+ ctx.pot,
666
+ ctx.phase_symmetric,
667
+ ctx.phase_broken,
668
+ verbose,
669
+ )
670
+
671
+ if "betaH_RH" in ctx.derived_param_names:
672
+ if derived.get("WARNING:no_perc_splines", False):
673
+ if verbose:
674
+ msg = (
675
+ "Set betaH_RH to np.nan "
676
+ "because no percolation splines were found."
677
+ )
678
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
679
+ derived["betaH_RH"] = np.nan
680
+ else:
681
+ f_perc = ctx.PercolationConf.f_perc
682
+ derived["betaH_RH"] = (
683
+ (8 * np.pi) ** (1 / 3)
684
+ / derived["RH"]
685
+ * max(derived.get("v_wall", 1.0), derived.get("c_s", 1 / np.sqrt(3)))
686
+ * (1 / f_perc) ** (1 / 3)
687
+ )
688
+
689
+ if "Tf_SM_GeV" in ctx.derived_param_names:
690
+ if verbose:
691
+ print("Calculating Tf_SM_GeV...")
692
+ if (
693
+ derived.get("WARNING:no_perc_splines", False)
694
+ or percolation.Pint is None
695
+ or percolation.TSYM is None
696
+ ):
697
+ if verbose:
698
+ msg = (
699
+ "Set Tf_SM_GeV to np.nan because no "
700
+ "percolation splines were found."
701
+ )
702
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
703
+ Tf_val = np.nan
704
+ else:
705
+ try:
706
+ Tf_val = calcTf(
707
+ percolation.Tperc,
708
+ percolation.TSYM[-1],
709
+ percolation.Pint,
710
+ pot,
711
+ verbose=verbose,
712
+ )
713
+ except errors.PercolationError as err:
714
+ if verbose:
715
+ msg = (
716
+ "Could not compute the temperature at which the "
717
+ "transition ends (Tf_SM_GeV). Set it to np.nan instead."
718
+ )
719
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
720
+ console.print("Error message: ", err)
721
+ Tf_val = np.nan
722
+ derived["Tf_SM_GeV"] = Tf_val * pot.conversionFactor if np.isfinite(Tf_val) else np.nan
723
+
724
+ def _compute_kappa_parameters(
725
+ self,
726
+ ctx: TransitionContext,
727
+ percolation: PercolationResult
728
+ ) -> None:
729
+ """Calculate kappa parameters if required.
730
+
731
+ Parameters
732
+ ----------
733
+ ctx : TransitionContext
734
+ percolation : PercolationResult
735
+
736
+ Returns
737
+ -------
738
+ None.
739
+ """
740
+ names = ctx.derived_param_names
741
+ if not {"kappa_phi", "kappa_sw", "kappa_turb"} & set(names):
742
+ return
743
+ if ctx.verbose:
744
+ print("Calculating kappa parameters...")
745
+ _, alpha_thetaP, _, alpha_hydP, alpha_infP, alpha_eqP = calcAlphas(
746
+ percolation.Tperc, ctx.pot, ctx.phase_symmetric, ctx.phase_broken,
747
+ verbose=ctx.verbose
748
+ )
749
+ # Compute the initial bubble radius R0. We take it to be
750
+ # the radius of the bubble at the nucleation temperature,
751
+ # which is the radius at which the field is halfway
752
+ # between the start and endpoint of the transition. Note
753
+ # that the endpoint of the transition is not necessarily
754
+ # the broken phase minimum, rather the point from which on
755
+ # the field just rolls down to the broken phase minimum, i.e.
756
+ # the endpoint in the instanton solution.
757
+
758
+ # this is the dictionary with the transition
759
+ # parameters at the nucleation temperature
760
+ instanton = ctx.tr.instanton
761
+ R_, phi_ = instanton.profile1D.R, instanton.profile1D.Phi
762
+ phi_middle = (phi_[0] + phi_[-1]) / 2
763
+ idx = np.argmin(np.abs(phi_ - phi_middle))
764
+
765
+ # this is the radius at which the field is halfway between the
766
+ # start and endpoint of the transition
767
+ R0 = R_[idx]
768
+
769
+ RH = ctx.derived_params.get("RH")
770
+ if RH is None:
771
+ overlap_tmin = max(float(ctx.phase_symmetric.Tmin), float(ctx.phase_broken.Tmin))
772
+ overlap_tmax = min(float(ctx.phase_symmetric.Tmax), float(ctx.phase_broken.Tmax))
773
+ betaH_approx = calcBetaHapprox(
774
+ percolation.Tperc,
775
+ ctx.outdict,
776
+ ctx.pot,
777
+ ctx.phase_symmetric,
778
+ ctx.phase_broken,
779
+ overlap_tmin,
780
+ overlap_tmax,
781
+ verbose=ctx.verbose,
782
+ )
783
+ f_perc = ctx.PercolationConf.f_perc
784
+ RH = (8 * np.pi / f_perc) ** (1 / 3) / betaH_approx * max(
785
+ ctx.derived_params.get("v_wall", 1.0),
786
+ ctx.derived_params.get("c_s", 1 / np.sqrt(3))
787
+ )
788
+
789
+ # Bubble size at time of collision (percolation)
790
+ Hstar = HubbleParameter(
791
+ energyDensity(ctx.pot, ctx.phase_symmetric, percolation.Tperc),
792
+ ctx.pot.conversionFactor
793
+ )
794
+ Rstar = RH / Hstar
795
+
796
+ kappa_phi, kappa_sw, kappa_turb = calc_kappas(
797
+ alpha_hydP,
798
+ alpha_infP,
799
+ alpha_eqP,
800
+ ctx.derived_params.get("v_wall", 1.0),
801
+ ctx.derived_params.get("c_s", 1 / np.sqrt(3)),
802
+ Rstar,
803
+ R0,
804
+ ctx.GWconfig,
805
+ )
806
+ ctx.derived_params["kappa_phi"] = kappa_phi
807
+ ctx.derived_params["kappa_sw"] = kappa_sw
808
+ ctx.derived_params["kappa_turb"] = kappa_turb
809
+
810
+ def _finalize_results(self, ctx: TransitionContext) -> dict:
811
+ """Ensure all requested parameters are present and return dict."""
812
+ derived = ctx.derived_params
813
+ beta_S3 = derived.get("betaH_S3", np.nan)
814
+ beta_RH = derived.get("betaH_RH", np.nan)
815
+
816
+ if "WARNING:betaH_small" in ctx.derived_param_names:
817
+ derived["WARNING:betaH_small"] = derived.get("betaH_RH", np.inf) < 10
818
+
819
+ if "WARNING:betaH_very_small" in ctx.derived_param_names:
820
+ derived["WARNING:betaH_very_small"] = derived.get("betaH_RH", np.inf) < 3
821
+
822
+ if "WARNING:nucleationRate_nonexponential" in ctx.derived_param_names:
823
+ derived["WARNING:nucleationRate_nonexponential"] = (
824
+ np.isfinite(beta_S3) and beta_S3 < 0
825
+ )
826
+
827
+ if "WARNING:betaH_nonfinite" in ctx.derived_param_names:
828
+ derived["WARNING:betaH_nonfinite"] = (
829
+ not np.isfinite(beta_S3) or not np.isfinite(beta_RH)
830
+ )
831
+
832
+ if "WARNING:betaH_mismatch" in ctx.derived_param_names:
833
+ mismatch = False
834
+ if np.isfinite(beta_S3) and np.isfinite(beta_RH):
835
+ abs_beta_S3 = abs(beta_S3)
836
+ abs_beta_RH = abs(beta_RH)
837
+ if abs_beta_S3 == 0 or abs_beta_RH == 0:
838
+ mismatch = not np.isclose(abs_beta_S3, abs_beta_RH)
839
+ else:
840
+ mismatch = max(
841
+ abs_beta_RH / abs_beta_S3,
842
+ abs_beta_S3 / abs_beta_RH,
843
+ ) >= 10
844
+ derived["WARNING:betaH_mismatch"] = mismatch
845
+
846
+ if "WARNING:not_T0_global_min" in ctx.derived_param_names:
847
+ # This flag is set in the check_T0_global_min function,
848
+ # when checking all transitions after initialization.
849
+ derived["WARNING:not_T0_global_min"] = False
850
+
851
+ # Check if there are any names in derived_param_names which are not
852
+ # in the derived_params_dict
853
+ for name in ctx.derived_param_names:
854
+ if name not in derived:
855
+ msg = (
856
+ f"{name} not found in derived_params_dict. "
857
+ "It might be that this variable still needs to be implemented. "
858
+ "It is added to the dictionary with value NaN."
859
+ )
860
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {msg}")
861
+ derived[name] = np.nan
862
+
863
+ return derived
864
+
865
+ def computeTransitionObservables_one_transition(self, tr : TransitionInfo,
866
+ rtol=1e-4, nAction=50):
867
+ """Calculate the GW observables after the phase tracing and the
868
+ computation of the nucleation temperature have been performed.
869
+ This function assumes kinetic equilibrium between the dark sector and
870
+ the SM.
871
+
872
+ Parameters
873
+ ----------
874
+ tr : TransitionInfo
875
+ The transition information for which to compute observables.
876
+ rtol : float, optional
877
+ Relative tolerance for the computation. Default is 1e-4.
878
+ nAction : int, optional
879
+ Number of action points to consider. Default is 50.
880
+ Returns
881
+ ----------
882
+ GWparams : tuple (float)
883
+ Dictionary with the GW parameters."""
884
+
885
+ ctx = self._initialize_transition_context(tr)
886
+ if ctx.verbose:
887
+ print(
888
+ "\nStart calculating GW observables for transition from phase",
889
+ tr.high_phase,
890
+ "to phase",
891
+ tr.low_phase,
892
+ "at Tnuc =",
893
+ tr.Tnuc * self.pot.conversionFactor,
894
+ "GeV",
895
+ )
896
+
897
+ tmin = max(float(ctx.phase_symmetric.Tmin), float(ctx.phase_broken.Tmin))
898
+ tmax = min(float(ctx.phase_symmetric.Tmax), float(ctx.phase_broken.Tmax))
899
+ if tmin >= tmax:
900
+ raise errors.PercolationError(
901
+ "No overlapping temperature interval between traced phases: "
902
+ f"Tsym in [{ctx.phase_symmetric.Tmin:.8g}, {ctx.phase_symmetric.Tmax:.8g}], "
903
+ f"Tbro in [{ctx.phase_broken.Tmin:.8g}, {ctx.phase_broken.Tmax:.8g}]."
904
+ )
905
+
906
+ alpha_theta_nuc = self._compute_transition_strength(ctx)
907
+ percolation = self._compute_percolation(ctx, alpha_theta_nuc, rtol, nAction)
908
+
909
+ self._ensure_wall_velocity(ctx, percolation.Tperc)
910
+ self._ensure_sound_speed(ctx, percolation.Tperc)
911
+
912
+ self._populate_temperature_observables(ctx, percolation)
913
+ self._compute_alpha_parameters(ctx, percolation)
914
+ self._compute_beta_and_separation(ctx, percolation, tmin, tmax)
915
+ self._compute_kappa_parameters(ctx, percolation)
916
+
917
+ derived = ctx.derived_params
918
+ if "D" in ctx.derived_param_names:
919
+ derived["D"] = 1
920
+ if "step" in ctx.derived_param_names:
921
+ derived["step"] = ctx.tr.step
922
+ if "total_steps" in ctx.derived_param_names:
923
+ derived["total_steps"] = ctx.tr.total_steps
924
+
925
+ return self._finalize_results(ctx), percolation, ctx.outdict
926
+
927
+ def __iter__(self):
928
+ """Enable iteration over the derived observables dictionary."""
929
+ return iter(self.transitionObservables)
930
+
931
+ def __getitem__(self, key: str):
932
+ """Allow dictionary-style access to an individual transition."""
933
+ return self.transitionObservables[key]
934
+
935
+ def __len__(self):
936
+ """Return the number of transitions for which observables were computed."""
937
+ return len(self.transitionObservables)
938
+
939
+ def keys(self):
940
+ """Expose the transition keys."""
941
+ return self.transitionObservables.keys()
942
+
943
+ def items(self):
944
+ """Return key/value pairs of the transition observables."""
945
+ return self.transitionObservables.items()
946
+
947
+ def printDerivedParams(self, derived_param_names):
948
+ """Print a rich-formatted table of the derived parameters per transition."""
949
+ nTransitions = len(self.transitions)
950
+ print("\nComputed thermodynamic quantities for", nTransitions, "transition(s):\n")
951
+ for i, tr in enumerate(self.transitions):
952
+ if tr.type == 1:
953
+ derived_params_dict = self.transitionObservables[i]
954
+ table = rich.table.Table(
955
+ title=f"Transition {i+1}: First-order phase transition",
956
+ title_justify="left",
957
+ box=rich.box.ROUNDED,
958
+ )
959
+ table.add_column("Parameter", style="cyan", no_wrap=True)
960
+ table.add_column("Value", style="orange1")
961
+ for j, name in enumerate(derived_param_names):
962
+ table.add_row(name, f"{derived_params_dict[name]:.10e}")
963
+ console.print(table)
964
+
965
+ if tr.type == 2:
966
+ table = rich.table.Table(
967
+ title=f"Transition {i+1}: Second-order phase transition",
968
+ title_justify="left",
969
+ box=rich.box.ROUNDED,
970
+ )
971
+ table.add_column("Parameter", style="cyan", no_wrap=True)
972
+ table.add_column("Value", style="orange1")
973
+ table.add_row("Start phase", str(tr.high_phase))
974
+ table.add_row("End phase", str(tr.low_phase))
975
+ table.add_row("Tcrit / GeV", str(tr.Tcrit * self.pot.conversionFactor))
976
+ console.print(table)