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,647 @@
1
+ """This module contains routines to compute the hydrodynamics
2
+ of the expanding bubbles.
3
+
4
+ Based on the code snippet provided in `arxiv:2303.10171`.
5
+
6
+ Part of TransitionListener v2.0
7
+ Documentation: https://tasillo.de/TransitionListener/
8
+
9
+ Authors:
10
+ Jonas Matuszak <jonas.matuszak@kit.edu>
11
+ Carlo Tasillo <carlo.tasillo@ific.uv.es>
12
+ """
13
+
14
+ import numpy as np
15
+ from scipy.integrate import solve_ivp, simpson
16
+ from scipy.optimize import root_scalar
17
+
18
+ from transitionlistener import generic_potential
19
+ from transitionlistener.phases import PhaseInfo
20
+
21
+ from . import console
22
+ from rich import print
23
+ from rich.panel import Panel
24
+
25
+
26
+ def _local_temperature_step(pot, T: float) -> float:
27
+ """Finite-difference temperature step that keeps the stencil at T > 0."""
28
+ T_abs = abs(float(T))
29
+ dT = max(float(getattr(pot, "T_eps", 1e-3)), T_abs * 1.0e-4)
30
+ if T_abs > 0.0:
31
+ dT = min(dT, 0.25 * T_abs)
32
+ return dT
33
+
34
+
35
+ def kappa_sw(alpha : float, vw: float, cs: float) -> float:
36
+ """Approximate the sound-wave efficiency factor using fit formulas from
37
+ 1004.4187 by Jose R. Espinosa, Thomas Konstandin, Jose M. No &
38
+ Geraldine Servant
39
+
40
+ Parameters
41
+ ----------
42
+ alpha : float
43
+ The strength of the phase transition at nucleation.
44
+ vw : float
45
+ The bubble wall velocity.
46
+ cs : float
47
+ The speed of sound in the broken phase.
48
+
49
+ Returns
50
+ -------
51
+ float
52
+ The sound-wave efficiency factor.
53
+ """
54
+ # Intermediate kappas, Jouguet velocity
55
+ kA = vw**(6/5) * 6.9 * alpha / (1.36 - 0.037 * np.sqrt(alpha) + alpha)
56
+ kB = alpha**(2/5) / (0.017 + (0.997 + alpha)**(2/5))
57
+ kC = np.sqrt(alpha) / (0.135 + np.sqrt(0.98 + alpha))
58
+ kD = alpha / (0.73 + 0.083 * np.sqrt(alpha) + alpha)
59
+ dK = -0.9 * np.log(np.sqrt(alpha) / (1 + np.sqrt(alpha)))
60
+ vJ = (cs + np.sqrt(alpha**2 + 2 * alpha / 3)) / (1 + alpha)
61
+ lK = kC - kB - (vJ - cs) * dK
62
+
63
+ if vw <= cs: # Choose regime
64
+ num = (cs**(11/5) * kA * kB)
65
+ denom = ((cs**(11/5) - vw**(11/5)) * kB + vw * cs**(6/5) * kA)
66
+ return num / denom
67
+ elif vw < vJ:
68
+ return kB + (vw - cs) * dK + ((vw - cs)**3 / (vJ - cs)**3) * lK
69
+ else:
70
+ num = (vJ - 1)**3 * vJ**(5/2) * vw**(-5/2) * kC * kD
71
+ denom = ((vJ - 1)**3 - (vw - 1)**3) * vJ**(5/2) * kC + (vw - 1)**3 * kD
72
+ return num / denom
73
+
74
+
75
+ class Hydrodynamics():
76
+ """Provide hydrodynamic solutions for expanding first-order phase transition bubbles."""
77
+
78
+ def __init__(self, pot : generic_potential, high_phase : PhaseInfo,
79
+ low_phase : PhaseInfo, verbose=False):
80
+ """Store the potential and phase data used to solve the hydrodynamics problem.
81
+
82
+ Parameters
83
+ ----------
84
+ pot : generic_potential
85
+ The effective potential describing the plasma.
86
+ high_phase : PhaseInfo
87
+ Phase outside the bubble wall.
88
+ low_phase : PhaseInfo
89
+ Phase inside the bubble wall.
90
+ verbose : bool, optional
91
+ Toggle diagnostic logging for root finding and integration steps.
92
+ """
93
+ self.pot = pot
94
+ self.high_phase = high_phase
95
+ self.low_phase = low_phase
96
+ self.verbose = verbose
97
+
98
+ def calc_cs(self, T: float, sym: bool) -> float:
99
+ """Calculate the speed of sound in the broken phase.
100
+
101
+ Parameters
102
+ ----------
103
+ T : float
104
+ Temperature, typically at nucleation
105
+ sym : bool
106
+ Whether to use symmetric or broken phase
107
+
108
+ Returns
109
+ ----------
110
+ float
111
+ speed of sound in the broken phase.
112
+ """
113
+ phase = self.high_phase if sym else self.low_phase
114
+ dT = _local_temperature_step(self.pot, T)
115
+ phi = phase.valAt(T)
116
+ dVdT = self.pot.dVdT(phi, T, dT=dT, include_decoupled=False)
117
+ ddVdT = self.pot.d2VdT2(phi, T, dT=dT, include_decoupled=False)
118
+ cs = np.sqrt(dVdT/(T*ddVdT))
119
+ return cs
120
+
121
+ def calcWallVelocityLTE(self, Tn: float) -> float:
122
+ """Calculate the wall velocity in local thermal equilibrium (LTE).
123
+ This is done by solving the matching conditions across the bubble wall.
124
+ It is based on the method described in `arxiv:2303.10171`.
125
+
126
+ Whether the species directly coupled to the scalar undergoing the
127
+ phase transition and the rest of the heat bath behave as a single
128
+ fluid or as two fluids is read from
129
+ ``pot.config.gwConf.coupled_hydrodynamics``. The bubble wall always
130
+ does work on the species coupled to the transitioning scalar; the
131
+ rest of the heat bath is only directly coupled to the wall when this
132
+ flag is ``True``. (When it is ``False``, the two sectors can still be
133
+ in thermal contact through indirect, slower processes; this switch
134
+ only controls whether they are treated as a single hydrodynamic
135
+ fluid in the matching conditions.)
136
+
137
+ Parameters
138
+ ----------
139
+ Tn : float
140
+ Temperature, typically at nucleation
141
+
142
+ Returns
143
+ ----------
144
+ float
145
+ wall velocity in local thermal equilibrium.
146
+ """
147
+
148
+ coupled = bool(self.pot.config.gwConf.coupled_hydrodynamics)
149
+ dT = _local_temperature_step(self.pot, Tn)
150
+ phi_s = self.high_phase.valAt(Tn)
151
+ phi_b = self.low_phase.valAt(Tn)
152
+ dVdT_s = self.pot.dVdT(phi_s, Tn, dT=dT, include_decoupled=coupled)
153
+ dVdT_b = self.pot.dVdT(phi_b, Tn, dT=dT, include_decoupled=coupled)
154
+ ddVdT_s = self.pot.d2VdT2(phi_s, Tn, dT=dT, include_decoupled=coupled)
155
+ ddVdT_b = self.pot.d2VdT2(phi_b, Tn, dT=dT, include_decoupled=coupled)
156
+
157
+ w_s = - Tn * dVdT_s
158
+ w_b = - Tn * dVdT_b
159
+ psi_n = w_b/w_s
160
+ cs2 = dVdT_s/(Tn*ddVdT_s)
161
+ cb2 = dVdT_b/(Tn*ddVdT_b)
162
+
163
+ # Calculate the alpha definition from the paper
164
+ # eq. (19) in `arxiv:2303.10171`
165
+ p_s = - self.pot.Vtot(phi_s, Tn, include_decoupled=coupled)
166
+ p_b = - self.pot.Vtot(phi_b, Tn, include_decoupled=coupled)
167
+
168
+ e_s = w_s - p_s
169
+ e_b = w_b - p_b
170
+
171
+ DTheta = (e_s - p_s/cb2 - (e_b - p_b/cb2))
172
+ alpha = DTheta / (3*w_s)
173
+
174
+ vw = self.find_vw(alpha, cb2, cs2, psi_n)
175
+ return vw
176
+
177
+ def find_vJ(self, alN: float, cb2: float) -> float:
178
+ """Calculate the Jouguet velocity, i.e. the smallest wall velocity a
179
+ physically consistent detonation solution is allowed to have from the
180
+ PT strength α and sound speed.
181
+
182
+ Parameters
183
+ ----------
184
+ alN : float
185
+ Strength of the phase transition at nucleation.
186
+ cb2 : float
187
+ Sound speed squared in the broken phase.
188
+
189
+ Returns
190
+ -------
191
+ float
192
+ The Jouguet velocity.
193
+ """
194
+ return np.sqrt(cb2) * (1 + np.sqrt(3 * alN * (1 - cb2 + 3 * cb2 * alN))) \
195
+ / (1 + 3 * cb2 * alN)
196
+
197
+ def get_vp(self, vm: float, al: float, cb2: float, branch=-1) -> float:
198
+ """Find the veloity outside the bubble using the
199
+ matching conditions.
200
+
201
+ Parameters
202
+ ----------
203
+ vm : float
204
+ The plasma velocity in the wall frame inside the bubble.
205
+ al : float
206
+ Strength of the phase transition at nucleation.
207
+ cb2 : float
208
+ The speed of sound squared in the broken phase.
209
+ branch : int
210
+ Choose the branch of the solution, either -1 or +1.
211
+ Returns
212
+ -------
213
+ float
214
+ The plasma velocity in the wall frame outside the bubble.
215
+ """
216
+ disc = vm**4 - 2*cb2 * vm**2*(1 - 6*al) + cb2**2*(1 - 12*vm**2 * al * (1 - 3*al))
217
+ return 0.5 * (cb2 + vm**2 + branch * np.sqrt(disc))/(vm + 3*cb2 * vm * al)
218
+
219
+ def w_from_alpha(self, al: float, alN: float, nu: float, mu: float) -> float:
220
+ """Compute the enthalpy from alpha
221
+
222
+ Parameters
223
+ ----------
224
+ al : float
225
+ Strength of the phase transition at the wall.
226
+ alN : float
227
+ Strength of the phase transition at nucleation.
228
+ nu : float
229
+ Parameter depending on the speed of sound in the broken phase.
230
+ mu : float
231
+ Parameter depending on the speed of sound in the symmetric phase.
232
+
233
+ Returns
234
+ -------
235
+ float
236
+ The enthalpy in the wall frame.
237
+ """
238
+ return (abs((1 - 3 * alN) * mu - nu) +1e-100) / (abs ((1 -3* al) * mu - nu) +1e-100)
239
+
240
+ def eqWall(self, al : float, alN: float, vm: float, nu: float,
241
+ mu: float, psiN: float, solution: int = -1):
242
+ """
243
+
244
+ Parameters
245
+ ----------
246
+ al : float
247
+ Strength of the phase transition at the wall.
248
+ alN : float
249
+ Strength of the phase transition at nucleation.
250
+ vm : float
251
+ Velocity of the fluid at the wall.
252
+ nu : float
253
+ Parameter depending on the speed of sound in the broken phase.
254
+ mu : float
255
+ Parameter depending on the speed of sound in the symmetric phase.
256
+ psiN : float
257
+ Ratio of enthalpies at nucleation.
258
+ solution : int
259
+ Choose the branch of the solution, either -1 or +1.
260
+
261
+ Returns
262
+ -------
263
+ float
264
+ The value of the matching condition equation.
265
+
266
+ """
267
+ vp = self.get_vp(vm, al ,1/(nu -1), solution)
268
+ ga2m, ga2p = 1/(1 - vm**2) ,1/(1 - vp**2)
269
+ psi = psiN * self.w_from_alpha(al, alN, nu, mu)**(nu / mu -1)
270
+ return vp * vm * al /(1 -(nu -1) * vp * vm) -(1 -3* al -(ga2p / ga2m)**(nu /2) *
271
+ psi) /(3* nu)
272
+
273
+ def solve_alpha(self, vw, alN, cb2, cs2, psiN):
274
+ """Find the strength of the phase transition at the wall.
275
+
276
+ Parameters
277
+ ----------
278
+ vw : float
279
+ Velocity of the fluid at the wall.
280
+ alN : float
281
+ Strength of the phase transition at nucleation.
282
+ cb2 : float
283
+ Speed of sound squared in the broken phase.
284
+ cs2 : float
285
+ Speed of sound squared in the symmetric phase.
286
+ psiN : float
287
+ Ratio of enthalpies at nucleation.
288
+
289
+ Returns
290
+ -------
291
+ float
292
+ The strength of the phase transition at the wall.
293
+ """
294
+ nu, mu = 1+1/ cb2 ,1+1/ cs2
295
+ vm = min(np.sqrt(cb2), vw)
296
+ vp_max = min(cs2 / vw, vw)
297
+ al_min = max ((vm - vp_max) *(cb2 - vm * vp_max) /(3* cb2 * vm *(1 - vp_max**2)),
298
+ (mu - nu) /(3* mu))
299
+ al_max = 1/3
300
+ branch = -1
301
+ if self.eqWall(al_min, alN, vm, nu, mu, psiN) * \
302
+ self.eqWall(al_max, alN, vm, nu, mu, psiN) > 0:
303
+ branch = 1
304
+
305
+ sol = root_scalar(self.eqWall, (alN, vm, nu, mu, psiN, branch),
306
+ bracket=(al_min, al_max), rtol=1e-10, xtol=1e-10)
307
+
308
+ if not sol.converged and self.verbose:
309
+ print("WARNING: desired precision not reached in 'solve_alpha'")
310
+ return sol.root
311
+
312
+ def dfdv(self, v : float, X : np.ndarray, cs2 : float) -> list[float]:
313
+ """
314
+ Compute the derivatives for the hydrodynamic equations.
315
+
316
+ Parameters
317
+ ----------
318
+ v : float
319
+ Velocity of the fluid.
320
+ X : np.ndarray
321
+ Array containing the variables [xi, w].
322
+ cs2 : float
323
+ Speed of sound squared.
324
+
325
+ Returns
326
+ -------
327
+ list[float]
328
+ The derivatives [dXidv, dwdv].
329
+ """
330
+ xi, w = X
331
+ with np.errstate(divide='ignore', invalid='ignore'):
332
+ mu_xiv = (xi - v) / (1 - xi * v)
333
+ dxidv = xi * (1 - v * xi) * (mu_xiv**2 / cs2 - 1) / (2 * v * (1 - v**2))
334
+ dwdv = w * (1 + 1 / cs2) * mu_xiv / (1 - v**2)
335
+ return [dxidv, dwdv]
336
+
337
+ def integrate_plasma(self, v0, vw, w0, c2, shock_wave=True):
338
+ """
339
+ Integrate the hydrodynamic equations for the plasma profile.
340
+
341
+ Parameters
342
+ ----------
343
+ v0 : float
344
+ Initial velocity of the fluid.
345
+ vw : float
346
+ Velocity of the fluid at the wall.
347
+ w0 : float
348
+ Initial value of w.
349
+ c2 : float
350
+ Speed of sound squared.
351
+ shock_wave : bool
352
+ Whether to include shock wave effects.
353
+
354
+ Returns
355
+ ----------
356
+ OdeResult
357
+ The result of the integration containing the plasma profile.
358
+ """
359
+ def event (v ,X, cs2):
360
+ xi, w = X
361
+ return xi*(xi - v) / (1 - xi*v) - cs2
362
+
363
+ event.terminal = True
364
+ sol = None
365
+ if shock_wave :
366
+ sol = solve_ivp(self.dfdv, (v0 ,1e-20), [ vw, w0] ,
367
+ events= event, args=(c2,), rtol=1e-10, atol=1e-10)
368
+ else:
369
+ sol = solve_ivp(self.dfdv,(v0 ,1e-20), [vw, w0] ,
370
+ args=(c2,), rtol=1e-10, atol=1e-10)
371
+ if not sol.success and self.verbose:
372
+ print(" WARNING : desired precision not reached in 'integrate_plasma'")
373
+
374
+ return sol
375
+
376
+ def shooting(self, vw : float, alN : float, cb2 : float, cs2 : float, psiN : float) -> float:
377
+ """ Shooting method to find the wall velocity consistent with matching conditions.
378
+
379
+ Parameters
380
+ ----------
381
+ vw : float
382
+ Velocity of the fluid at the wall.
383
+ alN : float
384
+ Strength of the phase transition at nucleation.
385
+ cb2 : float
386
+ Speed of sound squared in the broken phase.
387
+ cs2 : float
388
+ Speed of sound squared in the symmetric phase.
389
+ psiN : float
390
+ Ratio of enthalpies at nucleation.
391
+
392
+ Returns
393
+ -------
394
+ float
395
+ The value of the shooting function.
396
+ """
397
+ nu, mu = 1+1/cb2 ,1+1/ cs2
398
+ vm = min(np.sqrt(cb2), vw)
399
+ al = self.solve_alpha(vw, alN, cb2, cs2, psiN)
400
+ vp = self.get_vp(vm, al, cb2)
401
+ wp = self.w_from_alpha(al, alN, nu, mu)
402
+ sol = self.integrate_plasma ((vw - vp) /(1 - vw * vp), vw, wp, cs2)
403
+ vp_sw = sol.y[0, -1]
404
+ vm_sw = (vp_sw - sol.t[-1]) /(1 - vp_sw * sol.t[-1])
405
+ wm_sw = sol.y[1, -1]
406
+ return vp_sw / vm_sw - ((mu -1) * wm_sw +1) /((mu -1) + wm_sw)
407
+
408
+ def max_al(self, cb2 : float, cs2 : float, psiN : float,
409
+ upper_limit : float =1) -> float:
410
+ """Return the maximal nucleation-strength compatible with
411
+ subsonic solutions.
412
+
413
+ Parameters
414
+ ----------
415
+ cb2 : float
416
+ Speed of sound squared in the broken phase.
417
+ cs2 : float
418
+ Speed of sound squared in the symmetric phase.
419
+ psiN : float
420
+ Ratio of enthalpies at nucleation.
421
+ upper_limit : float
422
+ Upper limit for the search of maximal alpha.
423
+
424
+ Returns
425
+ -------
426
+ float
427
+ The maximal nucleation-strength compatible with subsonic solutions.
428
+
429
+ """
430
+ nu, mu = 1+1/ cb2 ,1+1/ cs2
431
+ vm = np.sqrt(cb2)
432
+
433
+ def func(alN):
434
+ vw = self.find_vJ(alN, cb2)
435
+ vp = cs2 / vw
436
+ ga2p, ga2m = 1/(1 - vp**2) ,1/(1 - vm**2)
437
+ wp = (vp + vw - vw * mu) /(vp + vw - vp * mu)
438
+ psi = psiN * wp**(nu / mu -1)
439
+ al = (mu - nu) /(3* mu) +(alN -(mu - nu) /(3* mu)) / wp
440
+ return vp * vm * al /(1 -(nu -1) * vp * vm) -(
441
+ 1 -3* al -(ga2p / ga2m)**(nu /2) * psi) /(3*nu)
442
+
443
+ if func(upper_limit) < 0:
444
+ return upper_limit
445
+ sol = root_scalar(func, bracket =((1 - psiN) /3, upper_limit),
446
+ rtol =1e-10, xtol =1e-10)
447
+ return sol.root
448
+
449
+ def find_vw(self, alN : float, cb2 : float, cs2 : float,
450
+ psiN : float) -> float:
451
+ """Solve for the wall velocity consistent with matching conditions.
452
+
453
+ Parameters
454
+ ----------
455
+ alN : float
456
+ Strength of the phase transition at nucleation.
457
+ cb2 : float
458
+ Speed of sound squared in the broken phase.
459
+ cs2 : float
460
+ Speed of sound squared in the symmetric phase.
461
+ psiN : float
462
+ Ratio of enthalpies at nucleation.
463
+
464
+ Returns
465
+ -------
466
+ float
467
+ The wall velocity consistent with matching conditions.
468
+
469
+ """
470
+ nu, mu = 1+1/ cb2 ,1+1/ cs2
471
+ vJ = self.find_vJ(alN, cb2)
472
+ if alN < (1 - psiN) /3 or alN <=(mu - nu) /(3* mu):
473
+ if self.verbose:
474
+ print("alpha_N is too small to find a solution for the wall velocity, set it to 0.")
475
+ return 0
476
+ if alN > self.max_al(cb2, cs2, psiN ,100) or self.shooting(vJ, alN, cb2, cs2, psiN) < 0:
477
+ if self.verbose:
478
+ print("alpha_N too large to find a solution for the wall velocity, set it to 1.")
479
+ return 1
480
+ sol = root_scalar(self.shooting, (alN, cb2, cs2, psiN) ,
481
+ bracket =[1e-3, vJ], rtol =1e-10, xtol =1e-10)
482
+ return sol.root
483
+
484
+ def detonation(self, alN : float, cb2 : float, psiN : float) -> float:
485
+ """Return the detonation wall velocity if a solution exists.
486
+
487
+ Parameters
488
+ ----------
489
+ alN : float
490
+ Strength of the phase transition at nucleation.
491
+ cb2 : float
492
+ Speed of sound squared in the broken phase.
493
+ psiN : float
494
+ Ratio of enthalpies at nucleation.
495
+
496
+ Returns
497
+ -------
498
+ float
499
+ The detonation wall velocity if a solution exists, otherwise 0.
500
+ """
501
+ nu = 1+1/cb2
502
+ vJ = self.find_vJ(alN, cb2)
503
+
504
+ def matching_eq(vw):
505
+ A = vw**2+ cb2 *(1 -3* alN *(1 - vw**2))
506
+ vm =(A + np.sqrt(A**2 -4* vw**2* cb2)) /(2* vw)
507
+ ga2w, ga2m = 1/(1 - vw**2) ,1/(1 - vm**2)
508
+ return vw * vm * alN /(1 -(nu -1) * vw * vm) \
509
+ -(1 -3* alN -(ga2w / ga2m)**(nu /2) * psiN)/(3* nu)
510
+
511
+ if matching_eq(vJ +1e-10) * matching_eq (1 -1e-10) > 0:
512
+ if self.verbose:
513
+ print('No detonation solution')
514
+ return 0
515
+
516
+ sol = root_scalar(matching_eq, bracket =(vJ +1e-10 ,1 -1e-10),
517
+ rtol =1e-10, xtol =1e-10)
518
+ return sol.root
519
+
520
+ def find_kappa(self, alN : float, cb2 : float, cs2 : float,
521
+ psiN : float, vw : float = None) -> float:
522
+ """Compute the hydrodynamic efficiency factor for given plasma parameters.
523
+
524
+ Parameters
525
+ ----------
526
+ alN : float
527
+ Strength of the phase transition at nucleation.
528
+ cb2 : float
529
+ Speed of sound squared in the broken phase.
530
+ cs2 : float
531
+ Speed of sound squared in the symmetric phase.
532
+ psiN : float
533
+ Ratio of enthalpies at nucleation.
534
+ vw : float, optional
535
+ Wall velocity. If not provided, it will be computed.
536
+
537
+ Returns
538
+ -------
539
+ float
540
+ The hydrodynamic efficiency factor.
541
+ """
542
+ if vw is None:
543
+ vw = self.find_vw(alN, cb2, cs2, psiN)
544
+ nu, mu = 1 + 1/cb2, 1 + 1/cs2
545
+ kappa, wp, vm, vp = 0, 1 ,0 ,0
546
+ if vw < 1:
547
+ vm = min(np.sqrt(cb2), vw)
548
+ al = self.solve_alpha(vw, alN, cb2, cs2, psiN)
549
+ vp = self.get_vp(vm, al, cb2)
550
+ wp = self.w_from_alpha(al, alN, nu, mu)
551
+ sol = self.integrate_plasma ((vw - vp) /(1 - vw * vp) ,vw, wp, cs2)
552
+ v, xi, w = sol.t, sol.y[0], sol.y [1]
553
+ kappa += 4 * simpson((xi * v)**2* w /(1 - v**2), xi) /(vw**3* alN)
554
+ if vw**2 > cb2:
555
+ w0 = psiN * wp**(nu / mu) * ((1 - vm**2)/(1 - vp**2))**(nu/2) \
556
+ if vw < 1 else 1+6*alN/(nu-2)
557
+ v0 = (vw - vm) / (1 - vw * vm) if vw < 1 else 3 * alN / (nu-2+3*alN)
558
+ sol = self.integrate_plasma(v0, vw, w0, cb2, False)
559
+ v, xi, w = np.flip(sol.t), np.flip(sol.y [0]), np.flip(sol.y [1])
560
+ mask = np.append(xi[1:] > xi [: -1], True)
561
+ kappa += 4 * simpson(((xi * v)**2* w /(1 - v**2))[mask], xi[mask]) \
562
+ /(vw**3* alN)
563
+ return kappa
564
+
565
+
566
+ def calc_kappas(alphaN: float, alpha_inf: float, alpha_eq: float,
567
+ vw: float, cs: float, Rsep: float, R0: float, config) -> tuple:
568
+ """
569
+ Calculate the kappa parameters kappa_phi, kappa_sw and kappa_turb.
570
+ These parameters quantify the amount of energy that is available
571
+ for gravitational wave production in the broken phase due to
572
+ bubble collisions, sound waves and turbulence.
573
+
574
+ Parameters
575
+ ----------
576
+ alphaN : float
577
+ The strength of the phase transition at nucleation.
578
+ alpha_inf : float
579
+ The critical strength of the phase transition at which bubbles
580
+ would run away if there was only leading order friction.
581
+ alpha_eq : float
582
+ The critical strength of the phase transition at which
583
+ the next-to-leading order friction is compensated by a
584
+ high bubble wall velocity.
585
+ vw : float
586
+ The bubble wall velocity.
587
+ cs : float
588
+ The speed of sound in the broken phase.
589
+ Rsep : float
590
+ The mean bubble separation at the time of percolation.
591
+ R0 : float
592
+ The critical bubble radius at which the phase transition
593
+ starts at nucleation.
594
+ config : object
595
+ The configuration object containing for instance the
596
+ epsilon parameter determining the importance of
597
+ turbulence in the broken phase.
598
+
599
+ Returns
600
+ ----------
601
+ kappa_bw : float
602
+ The kappa parameter for bubble wall collisions.
603
+ kappa_sw : float
604
+ The kappa parameter for sound waves.
605
+ kappa_turb : float
606
+ The kappa parameter for turbulence.
607
+ """
608
+ # unknown parameter epsilon is to represent the fraction of overall kinetic energy
609
+ # in bulk motion that is converted to MHD turbulence, see arXiv:2403.03723 eq. (2.13)
610
+ eps = config.epsilon_turbulence
611
+
612
+ # Step 1:
613
+ # Computation of kappa_phi
614
+ # based on 1903.09642 by John Ellis, Marek
615
+ # Lewicki, José Miguel No & Ville Vaskonen
616
+ gamma_eq = np.inf if alpha_eq == 0 else (alphaN - alpha_inf) / alpha_eq
617
+ gamma_star = 2 * Rsep / (3 * R0)
618
+
619
+ def kappa_col(gamma_star, gamma_eq, alpha_inf, alpha):
620
+ if alpha < alpha_inf:
621
+ # If the leading-order friction is so large that the bubbles would not even reach
622
+ # relativistic terminal velocity, we set kappa_BW = 0.
623
+ return 0
624
+ if gamma_star > gamma_eq:
625
+ return (gamma_eq / gamma_star) * (1 - (alpha_inf / alpha) * (gamma_eq / gamma_star)**2)
626
+ else:
627
+ return 1 - alpha_inf / alpha
628
+
629
+ kappa_BW = 0
630
+ if config.bw_collisions == "off":
631
+ kappa_BW = 0
632
+ elif config.bw_collisions == "full":
633
+ kappa_BW = 1
634
+ elif config.bw_collisions == "NLO":
635
+ kappa_BW = kappa_col(gamma_star, gamma_eq, alpha_inf, alphaN)
636
+ else:
637
+ raise ValueError("Wrong config input: bw_collisions must be 'off', 'full' or 'NLO'.")
638
+
639
+ # Step 2:
640
+ # Computation of kappa_sw (assuming no kappa_phi)
641
+ # based on 1004.4187 by Jose R. Espinosa, Thomas Konstandin,
642
+ # Jose M. No & Geraldine Servant
643
+
644
+ alpha_eff = alphaN * (1 - kappa_BW)
645
+ kappa_SW = (1 - kappa_BW) * kappa_sw(alpha_eff, vw, cs)
646
+ kappa_TURB = eps * kappa_SW
647
+ return kappa_BW, kappa_SW, kappa_TURB