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,604 @@
1
+ """
2
+ Module to calculate the GW energy spectrum from the phase transition parameters.
3
+
4
+ Part of TransitionListener v2.0
5
+ Documentation: https://tasillo.de/TransitionListener/
6
+
7
+ Authors:
8
+ Jonas Matuszak <jonas.matuszak@kit.edu>
9
+ Carlo Tasillo <carlo.tasillo@ific.uv.es>
10
+ """
11
+
12
+ from collections import OrderedDict
13
+
14
+ import numpy as np
15
+ from scipy.special import gamma
16
+ import transitionlistener.constants as c
17
+ from . import errors
18
+ from scipy.optimize import minimize_scalar
19
+
20
+ from . import console, print
21
+ import rich
22
+
23
+
24
+ class FOPTspectrum():
25
+ """
26
+ Class to compute the gravitational wave spectrum from a first order phase transition.
27
+ """
28
+
29
+ def __init__(self, gwparams_dict: dict, astroparams_dict: dict=
30
+ {"A": 0.0, "gamma": 0.0}, verbose: bool = False):
31
+ """Validate inputs, cache parameters and pre-compute spectra grids."""
32
+ self.gwparams_dict = gwparams_dict
33
+ self.astroparams_dict = astroparams_dict
34
+ if verbose:
35
+ self.print_params()
36
+
37
+ try:
38
+ self.alpha = gwparams_dict["alpha"]
39
+ self.RH = gwparams_dict["RH"]
40
+ self.Treh_SM_GeV = gwparams_dict["Treh_SM_GeV"]
41
+ self.g_eff_tot_reh = gwparams_dict["g_eff_tot_reh"]
42
+ self.h_eff_tot_reh = gwparams_dict["h_eff_tot_reh"]
43
+ self.kappa_phi = gwparams_dict["kappa_phi"]
44
+ self.kappa_sw = gwparams_dict["kappa_sw"]
45
+ self.kappa_turb = gwparams_dict["kappa_turb"]
46
+ self.v_wall = gwparams_dict.get("v_wall", 1.0) # Default to 1.0 if not provided
47
+ self.c_s = gwparams_dict.get("c_s", 1/np.sqrt(3)) # Default to 1/sqrt(3) if not provided
48
+ self.D = gwparams_dict.get("D", 1.0) # Default to 1.0 if not provided
49
+ self.alpha_hyd = gwparams_dict.get("alpha_hyd", self.alpha) # Default to alpha if not provided
50
+ self.g0 = gwparams_dict.get("g0", 2) # Default to 2 if not provided
51
+ self.h0 = gwparams_dict.get("h0", 3.9309363) # Default to h0 from LISA update paper
52
+ self.betaH = gwparams_dict.get(
53
+ "betaH",
54
+ (8 * np.pi / 0.28957) ** (1 / 3) / self.RH * max(self.v_wall, self.c_s),
55
+ ) # Default to betaH_RH if not provided
56
+ except Exception as err:
57
+ raise errors.GWParamsError(f"Missing necessary GW parameters in gwparams_dict. Error: {err}")
58
+
59
+ self.verbose = verbose
60
+ self.sources = ["Wall collisions", "Sound waves", "Turbulence"]
61
+
62
+ self.h2OmegaGW = self.h2Omega_0_sum
63
+ self.h2OmegaGW_with_astrobg = self.h2Omega_PT_and_astro
64
+ self.my_additional_spec_info = self.additional_spec_info(source="all")
65
+
66
+ f_Hz = np.logspace(-10, 1, 1000) # Frequency range in Hz for the spectrum
67
+ self.gwspec_dict = {
68
+ "h2OmegaGW_fun": self.h2OmegaGW,
69
+ "h2OmegaGW_with_astrobg_fun": self.h2OmegaGW_with_astrobg,
70
+ "f_Hz": f_Hz,
71
+ "h2OmegaGW": self.h2OmegaGW(f_Hz),
72
+ "h2OmegaGW_with_astrobg": self.h2OmegaGW_with_astrobg(f_Hz),
73
+ "additional_spec_info": self.my_additional_spec_info,
74
+ }
75
+
76
+
77
+ def __iter__(self):
78
+ """Allow iteration over the underlying spectrum dictionary."""
79
+ return iter(self.gwspec_dict)
80
+
81
+ def __getitem__(self, key: str):
82
+ """Expose dictionary-style access to precomputed spectrum components."""
83
+ return self.gwspec_dict[key]
84
+
85
+ def __len__(self):
86
+ """Return the number of entries stored in the spectrum dictionary."""
87
+ return len(self.gwspec_dict)
88
+
89
+ def keys(self):
90
+ """Return the keys of the spectrum dictionary."""
91
+ return self.gwspec_dict.keys()
92
+
93
+ def print_params(self):
94
+ """Render a table with the gravitational wave and astrophysical parameters."""
95
+ table = rich.table.Table(title="FULL GW PARAMETERS", title_justify="left", box=rich.box.ROUNDED)
96
+ table.add_column("Parameter", style="cyan", no_wrap=True)
97
+ table.add_column("Value", style="orange1")
98
+
99
+ def _format_numeric_value(key: str, value) -> str:
100
+ if isinstance(value, (bool, np.bool_)):
101
+ return str(bool(value))
102
+ if isinstance(value, (int, np.integer)):
103
+ return str(int(value))
104
+ if isinstance(value, (float, np.floating)):
105
+ if not np.isfinite(value):
106
+ return "-"
107
+ if key in {"step", "total_steps"}:
108
+ return str(int(round(value)))
109
+ return f"{float(value):.4e}"
110
+ return str(value)
111
+
112
+ def _format_warning_value(value) -> str:
113
+ if isinstance(value, (float, np.floating)) and not np.isfinite(value):
114
+ return "False"
115
+ if isinstance(value, (int, np.integer, bool, np.bool_)):
116
+ return str(bool(value))
117
+ return str(bool(value))
118
+
119
+ regular_items = []
120
+ warning_items = []
121
+ for key, value in self.gwparams_dict.items():
122
+ if isinstance(key, str) and key.upper().startswith("WARNING"):
123
+ warning_items.append((key, value))
124
+ else:
125
+ regular_items.append((key, value))
126
+
127
+ for key, value in sorted(regular_items, key=lambda kv: (kv[0].lower(), kv[0])):
128
+ table.add_row(key, _format_numeric_value(key, value))
129
+
130
+ if warning_items:
131
+ table.add_section()
132
+ table.add_row("Warnings", "")
133
+ sorted_warnings = sorted(warning_items, key=lambda kv: (kv[0].lower(), kv[0]))
134
+ warning_labels = [
135
+ key.split("WARNING:", 1)[1] if ":" in key else key
136
+ for key, _ in sorted_warnings
137
+ ]
138
+ max_warning_len = max(len(label) for label in warning_labels)
139
+ for label, (_, value) in zip(warning_labels, sorted_warnings):
140
+ table.add_row(f" {label:<{max_warning_len}}", _format_warning_value(value))
141
+
142
+ console.print("\n", table)
143
+
144
+ def BPL(self, f : float, fb: float, Omegab: float, n1: float, n2: float, a1: float) -> float:
145
+ """
146
+ A broken power law function, see 2403.03723 eq. (2.4)
147
+
148
+ Parameters
149
+ ----------
150
+ f : float
151
+ Frequency in Hz
152
+ fb : float
153
+ Break frequency in Hz
154
+ Omegab : float
155
+ Amplitude at fb
156
+ n1 : float
157
+ Exponent before the break
158
+ n2 : float
159
+ Exponent after the break
160
+ a1 : float
161
+ Transition parameter at the break
162
+
163
+ Returns
164
+ -------
165
+ float
166
+ Broken power law function value at frequency ``f``.
167
+ """
168
+ return Omegab * (f / fb)**n1 * (1/2 + 1/2 * (f / fb)**a1)**((n2 - n1) / a1)
169
+
170
+ def DBPL(self, f : float, f1: float, f2: float, Omega2: float,
171
+ n1: float, n2: float, n3: float, a1: float, a2: float) -> float:
172
+ """
173
+ A double broken power law function, see 2403.03723 eq. (2.8)
174
+
175
+ Parameters
176
+ ----------
177
+ f : float
178
+ Frequency in Hz
179
+ f1 : float
180
+ First break frequency in Hz
181
+ f2 : float
182
+ Second break frequency in Hz
183
+ Omega2 : float
184
+ Amplitude at f2
185
+ n1 : float
186
+ Exponent before the first break
187
+ n2 : float
188
+ Exponent between the first and second break
189
+ n3 : float
190
+ Exponent after the second break
191
+ a1 : float
192
+ Transition parameter at the first break
193
+ a2 : float
194
+ Transition parameter at the second break
195
+
196
+ Returns
197
+ -------
198
+ float
199
+ Double broken power law function value at frequency ``f``.
200
+ """
201
+ S = (f / f1)**n1 * (1 + (f / f1)**a1)**((n2 - n1) / a1) * (1 + (f / f2)**a2)**((n3 - n2) / a2)
202
+ S_at_f2 = (f2 / f1)**n1 * (1 + (f2 / f1)**a1)**((n2 - n1) / a1) * 2**((n3 - n2) / a2)
203
+ S2 = S / S_at_f2 # both are normalized to the same arbitrary constant which drops out here
204
+ return Omega2 * S2
205
+
206
+ def additional_spec_info(
207
+ self,
208
+ source: str = "all",
209
+ f_pivot_Hz: float = 1e-8,
210
+ ) -> OrderedDict[str, dict[str, float | str]]:
211
+ """Calculate summary information about the GW spectrum.
212
+
213
+ Parameters
214
+ ----------
215
+ source : str
216
+ Source to consider for the GW spectrum. Options are ``"all"``,
217
+ ``"Wall collisions"``, ``"Sound waves"``, ``"Turbulence"``.
218
+ f_pivot_Hz : float
219
+ Pivot frequency in Hz to evaluate the spectral amplitude.
220
+
221
+ Returns
222
+ -------
223
+ OrderedDict[str, dict[str, float | str]]
224
+ Ordered dictionary containing linear, logarithmic and LaTeX labelled
225
+ representations of each quantity describing the spectrum.
226
+ """
227
+
228
+ # Takes log10 frequency in Hertz as input and returns minus the total h2Omega_GW amplitude
229
+ minush2O = lambda log10f: -self.h2Omega_0_sum(10**log10f, source)
230
+
231
+ # Use log10 of the frequency for better numerical stability
232
+ res = minimize_scalar(minush2O, method="brent", options={'maxiter': 1000, 'xtol': 1e-6})
233
+ peak_freq_Hz = 10**res.x
234
+ peak_amplitude = -res.fun
235
+
236
+ # Compute the spectral amplitude at pivot frequency
237
+ h2Omega_at_pivot = self.h2Omega_0_sum(f_pivot_Hz, source)
238
+
239
+ def _safe_log10(value: float) -> float:
240
+ try:
241
+ if value > 0:
242
+ return float(np.log10(value))
243
+ except Exception:
244
+ pass
245
+ return float("nan")
246
+
247
+ def _make_entry(value: float, latex: str, label: str) -> dict[str, float | str]:
248
+ lin_value = float(value)
249
+ return {
250
+ "lin": lin_value,
251
+ "log10": _safe_log10(lin_value),
252
+ "latex": latex,
253
+ "label": label,
254
+ }
255
+
256
+ add_info: OrderedDict[str, dict[str, float | str]] = OrderedDict()
257
+ add_info["f_peak_Hz"] = _make_entry(
258
+ peak_freq_Hz,
259
+ r"$f_\mathrm{peak} / \mathrm{Hz}$",
260
+ "f_peak_Hz",
261
+ )
262
+ add_info["h2OmegaGW_peak"] = _make_entry(
263
+ peak_amplitude,
264
+ r"$h^2 \Omega_\mathrm{GW}(f_\mathrm{peak})$",
265
+ "h2OmegaGW_peak",
266
+ )
267
+ add_info["f_pivot_Hz"] = _make_entry(
268
+ f_pivot_Hz,
269
+ r"$f_\mathrm{pivot} / \mathrm{Hz}$",
270
+ "f_pivot_Hz",
271
+ )
272
+ add_info["h2OmegaGW_at_pivot"] = _make_entry(
273
+ h2Omega_at_pivot,
274
+ r"$h^2 \Omega_\mathrm{GW}(f_\mathrm{pivot})$",
275
+ "h2OmegaGW_at_pivot",
276
+ )
277
+ return add_info
278
+
279
+ def h2Omega_PT_and_astro(self, f_Hz : float) -> float:
280
+ """Calculate the total GW energy density spectrum from a first order
281
+ phase transition and an astrophysical background.
282
+
283
+ Parameters
284
+ ----------
285
+ f_Hz : float
286
+ Frequency in Hz
287
+
288
+ Returns
289
+ -------
290
+ float
291
+ Total GW energy density spectrum value.
292
+ """
293
+ return self.h2Omega_0_sum(f_Hz) + self.h2Omega_astro_noise(f_Hz)
294
+
295
+ def h2Omega_astro_noise(self, f_Hz : float) -> float:
296
+ """
297
+ Calculate the astrophysical background noise spectrum in the power-law approximation, e.g. for
298
+ supermassive black hole mergers in the nHz frequency range. Note that the below parameterization
299
+ is specific to the PTA band and should not be used for other frequencies.
300
+
301
+ Parameters
302
+ ----------
303
+ f_Hz : float
304
+ Frequency in Hz
305
+
306
+ Returns
307
+ -------
308
+ float
309
+ Astrophysical noise spectrum value.
310
+ """
311
+ A = self.astroparams_dict.get("A", 1e-15) # Default to 1e-15 if not provided
312
+ gamma = self.astroparams_dict.get("gamma", 13/3) # Default to 13/3 if not provided
313
+ return 2 * np.pi**2 / (3 * c.H100_Hz**2) * A**2 * (f_Hz/c.fyr_Hz)**(5-gamma) * c.fyr_Hz**2
314
+
315
+ def h2Omega_0_sum(self, f_Hz : float, source : str ="all") -> float:
316
+ """State-of-the-art model for the GW spectrum from a FOPT.
317
+
318
+ Parameters
319
+ ----------
320
+ f_Hz : float
321
+ Frequency in Hz
322
+ source : str
323
+ Source to consider for the GW spectrum. Options are "all",
324
+ "Wall collisions", "Sound waves", "Turbulence".
325
+
326
+ Returns
327
+ -------
328
+ float
329
+ GW energy density spectrum value at frequency ``f_Hz``.
330
+ """
331
+
332
+ self.sources = ["Wall collisions", "Sound waves", "Turbulence"]
333
+
334
+ # Amplitude prefactor due to redshift
335
+ Rh2 = c.Omega_gamma_h2 / self.D**(4./3.) * (self.h0 / self.h_eff_tot_reh)**(4./3.) \
336
+ * self.g_eff_tot_reh / self.g0
337
+
338
+ # Hubble parameter at the time of the phase transition redshifted to today in Hertz
339
+ H_p0_Hz = 1.65e-5 * self.D**(-4/3) * (self.Treh_SM_GeV / 100)
340
+ H_p0_Hz *= (self.g_eff_tot_reh / 100)**(1/2)
341
+ H_p0_Hz *= (100 / self.h_eff_tot_reh)**(1/3)
342
+
343
+ # Kinetic energy fractions.
344
+ # Note: The factor 0.6 accounts for the efficiency in producing kinetic energy
345
+ # in the bulk fluid motion with respect to the single bubble case, as found in Ref.
346
+ # 2209.04369 (Higgsless simulations) by Jinno, Konstandin, Rubira, Stomberg
347
+ _k = self.alpha / (1 + self.alpha)
348
+ Kphi = self.kappa_phi * _k
349
+ Ksw = 0.6 * self.kappa_sw * _k
350
+ Kturb = 0.6 * self.kappa_turb * _k
351
+
352
+ # Peak frequency of the different GW sources today. _f0 is the redshifted Horizon scale at the time of the PT.
353
+ fp = 0.28957
354
+ self.betaH = (8 * np.pi/fp)**(1/3) * max(self.v_wall, self.c_s) / self.RH
355
+
356
+ # Scalar field source
357
+ fb_phi_Hz = H_p0_Hz * 0.11 * self.betaH
358
+ A_phi = 0.05
359
+ n1_phi = 2.4
360
+ n2_phi = -2.4
361
+ a1_phi = 1.2
362
+ h2Omega_phi_peak = Rh2 * A_phi * Kphi**2 / self.betaH**2 # eq. (2.7) of 2403.03723
363
+ h2Omega_phi = self.BPL(f_Hz, fb_phi_Hz, h2Omega_phi_peak, n1_phi, n2_phi, a1_phi)
364
+
365
+ # Sound wave source
366
+ A_sw = 0.11
367
+ n1_sw = 3.0
368
+ n2_sw = 1.0
369
+ n3_sw = -3.0
370
+ a1_sw = 2
371
+ a2_sw = 4
372
+ # This shell-thickness estimate assumes detonations or deflagrations.
373
+ # A full hydrodynamic profile would be needed for more general solutions.
374
+ xi_shell = np.abs(self.v_wall - self.c_s) # dimensionless sound shell thickness
375
+
376
+ Delta_w = xi_shell / max(self.v_wall, self.c_s) # relative frequency separation between f1 and f2
377
+ f1_sw_Hz = H_p0_Hz * 0.2 / self.RH
378
+ f2_sw_Hz = H_p0_Hz * 0.5 / self.RH / Delta_w
379
+
380
+ # Compute the lifetime only from the relevant alpha
381
+ Htau_shell = self.RH / np.sqrt(0.6 * self.kappa_sw * self.alpha_hyd/(1 + self.alpha_hyd) / (4/3))
382
+
383
+ Htau_sw = min(1, Htau_shell)
384
+ h2Omega_int_sw = Rh2 * A_sw * Ksw**2 * Htau_sw * self.RH # eq. (2.10) of 2403.03723
385
+ h2Omega_sw_peak_at_f2 = 1 / np.pi * (np.sqrt(2) + 2 * (f2_sw_Hz / f1_sw_Hz) / (1 + f2_sw_Hz**2 / f1_sw_Hz**2)) \
386
+ * h2Omega_int_sw
387
+ h2Omega_sw = self.DBPL(f_Hz, f1_sw_Hz, f2_sw_Hz, h2Omega_sw_peak_at_f2, n1_sw, n2_sw, n3_sw, a1_sw, a2_sw)
388
+
389
+ # Turbulence source as in eq. (2.15) of 2403.03723, not the approximation in eq. (2.25)
390
+ if Kturb != 0:
391
+ Omega_s = Kturb
392
+ v_Alfven = np.sqrt(Omega_s / (4/3))
393
+ N = 2 # number of eddy turnovers
394
+ N_Hp_tau_e = N * self.RH / (2 * np.pi * v_Alfven)
395
+ # Duration of the turbulence source in units of Hubble time
396
+ A_turb = 0.085
397
+ a2_turb = 2.15
398
+ f2_turb_Hz = 2.2 * H_p0_Hz / self.RH
399
+ p_aniso = (1 + (f_Hz / f2_turb_Hz)**a2_turb)**(-11/(3 * a2_turb))
400
+ T_turb = np.where(
401
+ f_Hz <= H_p0_Hz / (2 * np.pi) / N_Hp_tau_e,
402
+ np.log(1 + N_Hp_tau_e)**2,
403
+ np.log(1 + H_p0_Hz / (2 * np.pi * f_Hz))**2
404
+ )
405
+ h2Omega_turb = 3 * A_turb * Omega_s**2 * Rh2 * self.RH**3 * (f_Hz / H_p0_Hz)**3 * p_aniso * T_turb
406
+ else:
407
+ h2Omega_turb = f_Hz * 0.0
408
+
409
+ # Total GW spectrum
410
+ if source == "all":
411
+ res = h2Omega_phi + h2Omega_sw + h2Omega_turb
412
+ elif source == "Wall collisions":
413
+ res = h2Omega_phi
414
+ elif source == "Sound waves":
415
+ res = h2Omega_sw
416
+ elif source == "Turbulence":
417
+ res = h2Omega_turb
418
+ else:
419
+ print("ERROR: Unknown source")
420
+ exit()
421
+ return res
422
+
423
+ def h2Omega_0_sum_old(self, f_Hz : float, source : str ="all") -> float:
424
+ '''
425
+ Alternative SW model as used in sub-gev paper and presented in LISA update paper. The remaining
426
+ sources are described as in 1903.09642.
427
+
428
+ Parameters
429
+ ----------
430
+ f_Hz : float
431
+ Frequency in Hz
432
+ source : str
433
+ Source to consider for the GW spectrum. Options are "all",
434
+ "Wall collisions", "Sound waves", "Turbulence".
435
+
436
+ Returns
437
+ -------
438
+ float
439
+ GW energy density spectrum value at frequency f_Hz.
440
+ '''
441
+
442
+ self.sources = ["Bubble collisions", "Sound waves", "Turbulence"]
443
+ self.h0 = 3.9309363
444
+ self.kappa_sw = self.alphaDS / (0.73 + 0.083 * np.sqrt(self.alphaDS) + self.alphaDS)
445
+
446
+ # Kinetic energy fractions. KswDS is used for the computation of the sound wave source lifetime
447
+ _k = self.alpha / (1 + self.alpha)
448
+ Kphi = self.kappa_phi * _k
449
+ Ksw = self.kappa_sw * _k
450
+ Kturb = self.kappa_turb * _k
451
+ KswDS = self.kappa_sw * self.alphaDS / (1 + self.alphaDS) if self.alphaDS is not np.nan else Ksw
452
+
453
+ # Peak frequency of the different GW sources today. _f0 is the redshifted Horizon scale at the time of the PT.
454
+ _f0 = 1.65e-5 * self.D**(-1/3) * (self.Treh_SM_GeV / 100)
455
+ _f0 *= (self.g_eff_tot_reh / 100)**(1/2)
456
+ _f0 *= (100 / self.h_eff_tot_reh)**(1/3)
457
+ f0phi_Hz = _f0 * 0.51 / self.RH
458
+ f0sw_Hz = _f0 * 3.4 / self.RH / (self.v_wall - self.c_s)
459
+ f0turb_Hz = _f0 * 3.9 / self.RH / (self.v_wall - self.c_s)
460
+
461
+ f0sw_Hz = 8.87619e-6 * self.D**(-1/3) * (8*np.pi)**(1/3.) * self.v_wall / self.RH
462
+ f0sw_Hz *= (self.Treh_SM_GeV / 100)
463
+ f0sw_Hz *= (self.g_eff_tot_reh/100)**(1/2) * (100/self.h_eff_tot_reh)**(1/3)
464
+
465
+ # Amplutude prefactor due to redshift
466
+ Rh2 = (
467
+ c.Omega_gamma_h2
468
+ / self.D**(4.0 / 3.0)
469
+ * (self.h0 / self.h_eff_tot_reh) ** (4.0 / 3.0)
470
+ * self.g_eff_tot_reh
471
+ / self.g0
472
+ )
473
+
474
+ # Spectral functions
475
+ def s_phi(x : float) -> float:
476
+ # Based on 1802.05712: Cutting et al., Gravitational waves from vacuum
477
+ # first-order phase transitions: from the envelope to the lattice.
478
+ return 0.024 * x**3 * (1 + 2 * x**2.07)**(-2.18)
479
+
480
+ def s_sw(x : float, p : float=3, q : float=4, n : float=2) -> float:
481
+ # Normalization of the (p,q,n) paramterized spectrum
482
+ # Without specifying the normalization, the spectrum is normalized to 1 at x=1
483
+ # In that case: norm = 1/0.687.
484
+ norm = (q/p)**(p/n) * ((p+q)/q)**((p+q)/n)
485
+ norm *= gamma(p/n) * gamma(q/n) / n / gamma((p+q)/n)
486
+
487
+ S_tilde = x**p / (q/(p+q) + p / (p+q) * x**n)**((p+q)/n)
488
+ return S_tilde / norm
489
+
490
+ def s_turb(x : float) -> float:
491
+ # Based on 0909.0622: Caprini, Durrer and Servant, the stochastic GW
492
+ # background from turbulence and magnetic fields generated by a FOPT.
493
+ turb_correction = 3.9 / (self.v_wall - self.c_s) / self.RH
494
+ # Ratio of f_turb at emission with respect to H at emission
495
+ return 6.8 * x**3 * (1 + x)**(-11/3) / (1 + 8 * np.pi * x * turb_correction)
496
+
497
+ # Lifetime of the sound wave source
498
+ Htau_sw = min(1, self.RH / np.sqrt(KswDS / (4/3)))
499
+
500
+
501
+ # Peak amplitudes
502
+ Omega_phi_peak = Kphi**2 * self.RH**2
503
+ # Keep the normalization used in the current implementation, which
504
+ # differs from the legacy expression by a factor of sqrt(3).
505
+ Omega_sw_peak = Ksw**2 * self.RH * Htau_sw * 0.012 * 3 * np.sqrt(3)
506
+ Omega_turb_peak = Kturb**(3/2) * self.RH * (1 - Htau_sw)
507
+
508
+ # Total GW spectrum
509
+ if source == "all":
510
+ res = Rh2 * (Omega_phi_peak * s_phi(f_Hz / f0phi_Hz)
511
+ + Omega_sw_peak * s_sw(f_Hz / f0sw_Hz)
512
+ + Omega_turb_peak * s_turb(f_Hz / f0turb_Hz))
513
+ elif source == "Bubble collisions":
514
+ res = Rh2 * Omega_phi_peak * s_phi(f_Hz / f0phi_Hz)
515
+ elif source == "Sound waves":
516
+ res = Rh2 * Omega_sw_peak * s_sw(f_Hz / f0sw_Hz)
517
+ elif source == "Turbulence":
518
+ res = Rh2 * Omega_turb_peak * s_turb(f_Hz / f0turb_Hz)
519
+ else:
520
+ print("ERROR: Unknown source")
521
+ exit()
522
+ return res
523
+
524
+
525
+
526
+ """
527
+ def h2Omega_0_sum_older(self, f_Hz, source="all"):
528
+ '''
529
+ Calculate the GW energy density spectrum from a FOPT.
530
+ Follows 1903.09642, with adaptation for DS phase transition: dilution D
531
+ due to DS entropy production is included; alpha_DS is the strength of the
532
+ DS PT normalized on the DS radiation bath.
533
+ Assume v_wall = 1 and instantaneous reheating after the PT (at Treh_SM_GeV).
534
+ '''
535
+
536
+ # Kinetic energy fractions. KswDS is used for the computation of the sound wave source lifetime
537
+ _k = self.alpha / (1 + self.alpha)
538
+ Kphi = self.kappa_phi * _k
539
+ Ksw = self.kappa_sw * _k
540
+ Kturb = self.kappa_turb * _k
541
+ KswDS = self.kappa_sw * self.alphaDS / (1 + self.alphaDS) if self.alphaDS is not np.nan else Ksw
542
+
543
+ # Peak frequency of the different GW sources today. _f0 is the redshifted Horizon scale at the time of the PT.
544
+ _f0 = 1.65e-5 * self.D**(-1/3) * (self.Treh_SM_GeV / 100)
545
+ _f0 *= (self.g_eff_tot_reh / 100)**(1/2)
546
+ _f0 *= (100 / self.h_eff_tot_reh)**(1/3)
547
+ f0phi_Hz = _f0 * 0.51 / self.RH
548
+ f0sw_Hz = _f0 * 3.4 / self.RH / (self.v_wall - self.c_s)
549
+ f0turb_Hz = _f0 * 3.9 / self.RH / (self.v_wall - self.c_s)
550
+
551
+ # Amplutude prefactor due to redshift
552
+ Rh2 = (
553
+ Omega_gamma_h2
554
+ / self.D**(4.0 / 3.0)
555
+ * (self.g0 / self.g_eff_tot_reh) ** (4.0 / 3.0)
556
+ * self.h_eff_tot_reh
557
+ / self.h0
558
+ )
559
+
560
+ # Spectral functions
561
+ def s_phi(x):
562
+ # Based on 1802.05712: Cutting et al., gravitational waves from
563
+ # vacuum first-order phase transitions: from the envelope to the lattice.
564
+ return 0.024 * x**3 * (1 + 2 * x**2.07)**(-2.18)
565
+
566
+ def s_sw(x):
567
+ # Based on 1512.06239: Caprini et al., Science with the space-based
568
+ # interferometer eLISA. II: Gravitational waves from cosmological
569
+ # phase transitions.
570
+ return 0.38 * x**3 * (1 + 0.75 * x**2)**(-3.5)
571
+
572
+ def s_turb(x):
573
+ # Based on 0909.0622: Caprini, Durrer and Servant, the stochastic
574
+ # gravitational wave background from turbulence and magnetic fields
575
+ # generated by a first-order phase transition.
576
+ turb_correction = 3.9 / (self.v_wall - self.c_s) / self.RH
577
+ # Ratio of f_turb at emission with respect to H at emission
578
+ return 6.8 * x**3 * (1 + x)**(-11/3) / (1 + 8 * np.pi * x * turb_correction)
579
+
580
+ # Lifetime of the sound wave source
581
+ Htau_sw = min(1, self.RH / np.sqrt(KswDS / (4/3)))
582
+
583
+ # Peak amplitudes
584
+ Omega_phi_peak = Kphi**2 * self.RH**2
585
+ Omega_sw_peak = Ksw**2 * self.RH * Htau_sw
586
+ Omega_turb_peak = Kturb**(3/2) * self.RH * (1 - Htau_sw)
587
+
588
+ # Total GW spectrum
589
+ if source == "all":
590
+ res = Rh2 * (Omega_phi_peak * s_phi(f_Hz / f0phi_Hz)
591
+ + Omega_sw_peak * s_sw(f_Hz / f0sw_Hz)
592
+ + Omega_turb_peak * s_turb(f_Hz / f0turb_Hz))
593
+ elif source == "Bubble collisions":
594
+ res = Rh2 * Omega_phi_peak * s_phi(f_Hz / f0phi_Hz)
595
+ elif source == "Sound waves":
596
+ res = Rh2 * Omega_sw_peak * s_sw(f_Hz / f0sw_Hz)
597
+ elif source == "Turbulence":
598
+ res = Rh2 * Omega_turb_peak * s_turb(f_Hz / f0turb_Hz)
599
+ else:
600
+ print("ERROR: Unknown source")
601
+ exit()
602
+ return res
603
+
604
+ """