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,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
|
+
"""
|