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,1000 @@
|
|
|
1
|
+
"""This module contains routines to compute the possible transitions
|
|
2
|
+
between phases.
|
|
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 dataclasses import dataclass
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from scipy import optimize
|
|
17
|
+
|
|
18
|
+
from . import pathDeformation
|
|
19
|
+
from . import errors
|
|
20
|
+
|
|
21
|
+
from . import console, print
|
|
22
|
+
|
|
23
|
+
from rich.table import Table
|
|
24
|
+
from rich.panel import Panel
|
|
25
|
+
from rich.columns import Columns as RichColumns
|
|
26
|
+
from rich.box import ROUNDED
|
|
27
|
+
|
|
28
|
+
from transitionlistener.nucleation import computeNucleationTemperature
|
|
29
|
+
from transitionlistener.generic_potential import generic_potential
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from transitionlistener.phases import PhaseInfo
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class TransitionInfo():
|
|
37
|
+
"""Object containing the information about one possible phase transition."""
|
|
38
|
+
Tcrit: float
|
|
39
|
+
"""Critical temperature where the two minima are degenerate"""
|
|
40
|
+
high_vev: np.ndarray
|
|
41
|
+
"""Field values in the high phase"""
|
|
42
|
+
high_phase: int
|
|
43
|
+
"""Key of the high phase"""
|
|
44
|
+
low_vev: np.ndarray
|
|
45
|
+
"""Field values in the low phase"""
|
|
46
|
+
low_phase: int
|
|
47
|
+
"""Key of the low phase"""
|
|
48
|
+
type: int
|
|
49
|
+
"""Type of transition: 1 for first order, 2 fo second order (continuous)"""
|
|
50
|
+
Tnuc: float = 0.0
|
|
51
|
+
"""Nucleation temperature."""
|
|
52
|
+
Tperc: float = 0.0
|
|
53
|
+
"""Percolation temperature."""
|
|
54
|
+
full_tunneling_info = {}
|
|
55
|
+
"""Save the temperatures and corresponding actions from the nucleation
|
|
56
|
+
computation."""
|
|
57
|
+
action_Tnuc: float = 0.0
|
|
58
|
+
"""Action at the nucleation."""
|
|
59
|
+
instanton = {}
|
|
60
|
+
"""Instanton solution at the nucleation temperature."""
|
|
61
|
+
step: int = 0
|
|
62
|
+
"""Ordering number of the possible transitions"""
|
|
63
|
+
total_steps: int = 0
|
|
64
|
+
"""Total number of transitions."""
|
|
65
|
+
derived_params = {}
|
|
66
|
+
"""Computed observables."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _transition_sort_temperature(transition: TransitionInfo) -> tuple[float, float]:
|
|
70
|
+
"""Sort transitions by descending nucleation temperature, falling back to ``Tcrit``.
|
|
71
|
+
|
|
72
|
+
Seedless percolation can keep first-order transition candidates with
|
|
73
|
+
``Tnuc=None``. Those transitions still need a stable ordering, so we fall
|
|
74
|
+
back to ``Tcrit`` when no finite ``Tnuc`` is available.
|
|
75
|
+
"""
|
|
76
|
+
tnuc = transition.Tnuc
|
|
77
|
+
tcrit = transition.Tcrit
|
|
78
|
+
tnuc_key = float(tnuc) if tnuc is not None and np.isfinite(tnuc) else float("-inf")
|
|
79
|
+
tcrit_key = float(tcrit) if tcrit is not None and np.isfinite(tcrit) else float("-inf")
|
|
80
|
+
return tnuc_key, tcrit_key
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _format_temperature_gev(temperature: float | None, conversion_factor: float) -> str:
|
|
84
|
+
"""Return a readable GeV string for finite temperatures and ``None`` otherwise."""
|
|
85
|
+
if temperature is None or not np.isfinite(temperature):
|
|
86
|
+
return "None"
|
|
87
|
+
return str(temperature * conversion_factor)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Transitions():
|
|
91
|
+
"""Compute the transition path from the high-T phase to the 0-T phase
|
|
92
|
+
for a given set of phases.
|
|
93
|
+
|
|
94
|
+
Class containing the routines to find the transitions between
|
|
95
|
+
all possible pairs of phases.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
phases : list["PhaseInfo"]
|
|
100
|
+
List of the phases as PhaseInfo objects.
|
|
101
|
+
pot : generic_potential
|
|
102
|
+
The potential.
|
|
103
|
+
verbose : bool, optional
|
|
104
|
+
Set the outputlevel.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, phases: list["PhaseInfo"], pot: generic_potential,
|
|
108
|
+
verbose: bool = False):
|
|
109
|
+
"""Trace the transition network and determine nucleation temperatures.
|
|
110
|
+
|
|
111
|
+
Parameters
|
|
112
|
+
----------
|
|
113
|
+
phases : list[PhaseInfo]
|
|
114
|
+
List of the phases as PhaseInfo objects.
|
|
115
|
+
pot : generic_potential
|
|
116
|
+
The potential.
|
|
117
|
+
verbose : bool, optional
|
|
118
|
+
Set the outputlevel.
|
|
119
|
+
"""
|
|
120
|
+
self.phases = phases
|
|
121
|
+
self.pot = pot
|
|
122
|
+
self.V = pot.Vtot
|
|
123
|
+
self.dV = pot.gradV
|
|
124
|
+
self.d2V = pot.d2V
|
|
125
|
+
self.verbose = verbose
|
|
126
|
+
|
|
127
|
+
if self.phases is None or len(self.phases) == 0:
|
|
128
|
+
msg = "Cannot find phase transitions, because no phases were found."
|
|
129
|
+
raise errors.NoPhases(msg)
|
|
130
|
+
if len(self.phases) == 1:
|
|
131
|
+
msg = "Cannot find phase transitions, because only one phase was found."
|
|
132
|
+
raise errors.OnlyOnePhase(msg)
|
|
133
|
+
|
|
134
|
+
# assemble the tunneling parameters:
|
|
135
|
+
self.tunnelFromPhase_args = {"Ttol": pot.config.tracingConf.nucleation_Ttol,
|
|
136
|
+
"approximate_strength_threshold": pot.config.tracingConf.approx_strength_threshold,
|
|
137
|
+
"nuclCriterion": pot.nucleationCriterion,
|
|
138
|
+
"fullTunneling_params": pot.config.tracingConf.tunneling_params}
|
|
139
|
+
|
|
140
|
+
self.conversionFactor = pot.conversionFactor
|
|
141
|
+
|
|
142
|
+
if verbose:
|
|
143
|
+
console.print("\n[bold green]Finding phase transitions...[/bold green]")
|
|
144
|
+
|
|
145
|
+
transitionsTnuc = self.findTransitionPathFromTnuc(verbose=verbose)
|
|
146
|
+
self.transitions = transitionsTnuc
|
|
147
|
+
|
|
148
|
+
if verbose:
|
|
149
|
+
msg = (
|
|
150
|
+
f"\n[bold green]Found {len(transitionsTnuc)} possible "
|
|
151
|
+
f"phase transition(s) ...[/bold green]"
|
|
152
|
+
)
|
|
153
|
+
console.print(msg)
|
|
154
|
+
|
|
155
|
+
self.printTransitions(transitionsTnuc)
|
|
156
|
+
|
|
157
|
+
if len(transitionsTnuc) == 0:
|
|
158
|
+
raise errors.NoTransitionFound("No transition found.")
|
|
159
|
+
|
|
160
|
+
if all(tr.type == 2 for tr in transitionsTnuc):
|
|
161
|
+
raise errors.OnlySecondOrderTransitionsError(
|
|
162
|
+
"Only second-order transition(s) found. No first-order transitions.")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def __str__(self):
|
|
166
|
+
"""Pretty-print the discovered transitions and return an empty string."""
|
|
167
|
+
self.printTransitions(self.transitions)
|
|
168
|
+
return ""
|
|
169
|
+
|
|
170
|
+
def __iter__(self):
|
|
171
|
+
"""Enable iteration over the list of transitions."""
|
|
172
|
+
return iter(self.transitions)
|
|
173
|
+
|
|
174
|
+
def __getitem__(self, key: str):
|
|
175
|
+
"""Allow dictionary-style access via the transition index."""
|
|
176
|
+
return self.transitions[key]
|
|
177
|
+
|
|
178
|
+
def __len__(self):
|
|
179
|
+
"""Return the number of transitions that were found."""
|
|
180
|
+
return len(self.transitions)
|
|
181
|
+
|
|
182
|
+
def keys(self):
|
|
183
|
+
"""Expose the valid indices for the transition list."""
|
|
184
|
+
return self.transitions.keys()
|
|
185
|
+
|
|
186
|
+
def findAllTransitions(self) -> dict[TransitionInfo]:
|
|
187
|
+
"""Find all the possible transitions between coexisting phases
|
|
188
|
+
by computing the nucleation temperatures.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
phases : list[PhaseInfo]
|
|
193
|
+
V : func
|
|
194
|
+
dV : func
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
dict[TransitionInfo]
|
|
199
|
+
List with TransitionInfo objects.
|
|
200
|
+
"""
|
|
201
|
+
pot = self.pot
|
|
202
|
+
phases = self.phases
|
|
203
|
+
verbose = self.verbose
|
|
204
|
+
tunnelFromPhase_args = self.tunnelFromPhase_args
|
|
205
|
+
seedless = pot.config.percolationConf.algorithm_mode == "adaptive_step_size"
|
|
206
|
+
|
|
207
|
+
# This below could miss transitions in 2D cases where there is no critical
|
|
208
|
+
# temperature but the phases still coexist!
|
|
209
|
+
transitions = self.findTransitionPathFromTnuc(verbose=verbose)
|
|
210
|
+
|
|
211
|
+
if transitions is None:
|
|
212
|
+
raise errors.NoTransitionFound("No transition found.")
|
|
213
|
+
|
|
214
|
+
if verbose:
|
|
215
|
+
message = f"[bold green]Found {len(transitions)} possible transition(s):[/bold green]"
|
|
216
|
+
for tr in transitions:
|
|
217
|
+
message += f"\n{tr.high_phase} -> {tr.low_phase} at Tcrit = {tr.Tcrit * self.conversionFactor} GeV"
|
|
218
|
+
console.print()
|
|
219
|
+
console.print(Panel.fit(message,border_style="green"))
|
|
220
|
+
|
|
221
|
+
transitions_dict = []
|
|
222
|
+
TooMuchSupercooling = False
|
|
223
|
+
for tr in transitions:
|
|
224
|
+
if tr.type != 1:
|
|
225
|
+
# This is a second order transition. Still add it to the list
|
|
226
|
+
# of transitions, but don't try to find the tunneling solution.
|
|
227
|
+
second_order_dict = self.makeSecondOrderDict(tr)
|
|
228
|
+
transitions_dict.append(second_order_dict)
|
|
229
|
+
continue
|
|
230
|
+
Tmax = tr.Tcrit
|
|
231
|
+
if verbose:
|
|
232
|
+
print("\n"+"#"*50)
|
|
233
|
+
print(f"Investigating transition with Tcrit = {tr.Tcrit * self.conversionFactor} GeV" + \
|
|
234
|
+
f" from phase {tr.high_phase:} to phase {tr.low_phase:}")
|
|
235
|
+
high_phase = phases[tr.high_phase]
|
|
236
|
+
low_phase = phases[tr.low_phase]
|
|
237
|
+
try:
|
|
238
|
+
tunn = computeNucleationTemperature(self.V, self.dV, self.d2V, high_phase, low_phase,
|
|
239
|
+
self.conversionFactor, verbose=verbose,
|
|
240
|
+
allow_second_order_fallback=not seedless,
|
|
241
|
+
**tunnelFromPhase_args)
|
|
242
|
+
# sometimes a second order transition slips through the phase tracing
|
|
243
|
+
if tunn['trantype'] == 2:
|
|
244
|
+
tr.type = 2
|
|
245
|
+
tr.action = np.nan
|
|
246
|
+
transitions_dict.append(tr)
|
|
247
|
+
else:
|
|
248
|
+
tr.full_tunneling_info = tunn["full_tunneling_info"]
|
|
249
|
+
tr.Tnuc = tunn["Tnuc"]
|
|
250
|
+
tr.low_phase = tunn["low_phase"]
|
|
251
|
+
tr.high_phase = tunn["high_phase"]
|
|
252
|
+
tr.instanton = tunn["instanton"]
|
|
253
|
+
tr.type = tunn["trantype"]
|
|
254
|
+
tr.action = tunn["action"]
|
|
255
|
+
transitions_dict.append(tr)
|
|
256
|
+
except errors.NucleationError as err:
|
|
257
|
+
if verbose:
|
|
258
|
+
print("Nucleation error: ", err)
|
|
259
|
+
if seedless:
|
|
260
|
+
tr.Tnuc = None
|
|
261
|
+
tr.action = np.nan
|
|
262
|
+
tr.full_tunneling_info = {}
|
|
263
|
+
tr.instanton = {}
|
|
264
|
+
transitions_dict.append(tr)
|
|
265
|
+
else:
|
|
266
|
+
TooMuchSupercooling = True
|
|
267
|
+
continue
|
|
268
|
+
except Exception as err:
|
|
269
|
+
raise errors.NucleationError("An unexpected error occurred " +
|
|
270
|
+
"while trying to find the tunneling solution: "
|
|
271
|
+
+ str(err))
|
|
272
|
+
|
|
273
|
+
# Check if we have found any transitions
|
|
274
|
+
if len(transitions_dict) == 0:
|
|
275
|
+
if TooMuchSupercooling:
|
|
276
|
+
raise errors.TooMuchSupercoolingError()
|
|
277
|
+
raise errors.NoTransitionFound("No transition found.")
|
|
278
|
+
|
|
279
|
+
# Check if only second-order transitions were found
|
|
280
|
+
if all(tr.type == 2 for tr in transitions_dict):
|
|
281
|
+
raise errors.OnlySecondOrderTransitionsError(
|
|
282
|
+
"Only second-order transition(s) found. No first-order transitions.")
|
|
283
|
+
|
|
284
|
+
# Check if there are transitions in the list, that are not possible
|
|
285
|
+
# because they already happened through a (series of) previous transition(s).
|
|
286
|
+
# For instance, if nucleation temperatures for the transitions 0 -> 1, 1 -> 2,
|
|
287
|
+
# and 0 -> 2 are found, but 0 -> 1 has already happened when 0 -> 2 would nucleate,
|
|
288
|
+
# 0 -> 2 cannot happen and we can remove the 0 -> 2 transition from the list.
|
|
289
|
+
# This is done by going through the list of transitions, starting from
|
|
290
|
+
# the highest Tnuc and checking if a given transition is would start
|
|
291
|
+
# from a phase, that has already disappeared:
|
|
292
|
+
disappeared_phases = set()
|
|
293
|
+
# order the transitions by Tnuc: the highest Tnuc first
|
|
294
|
+
transitions_dict = sorted(transitions_dict, key=_transition_sort_temperature, reverse=True)
|
|
295
|
+
|
|
296
|
+
# Have to check if the first transition is second order, if yes ->
|
|
297
|
+
# replace highT_phase_key with the one it second order transitions to!
|
|
298
|
+
highT_phase_key = [key for key in phases.keys() if phases[key].T[-1] == pot.Tmax][0]
|
|
299
|
+
for tr in transitions_dict:
|
|
300
|
+
if tr.high_phase == highT_phase_key and tr.type == 2:
|
|
301
|
+
highT_phase_key = tr.low_phase
|
|
302
|
+
|
|
303
|
+
for i, tr in enumerate(transitions_dict):
|
|
304
|
+
# Check if the first FOPT in the dictionary actually starts from
|
|
305
|
+
# the high-T phase of the model. If not, we throw an error.
|
|
306
|
+
|
|
307
|
+
# Note: this is only a hotfix for a specific physics bug in the vev flip flop model.
|
|
308
|
+
# There are cases in which this check here leads to the wrong results. What should be
|
|
309
|
+
# done instead is to only check one transition at a time, starting from the high-T phase.
|
|
310
|
+
# This will need some restructuring of the code, so for now we just
|
|
311
|
+
# check the first transition in the dictionary.
|
|
312
|
+
if pot.config.tracingConf.do_high_T_phase_check:
|
|
313
|
+
if i == 0:
|
|
314
|
+
if tr.high_phase != highT_phase_key and tr.type == 1:
|
|
315
|
+
raise errors.WrongHighTPhaseError(
|
|
316
|
+
"The first transition in the transition dictionary " +
|
|
317
|
+
"does not start from the high-T phase of the model. " +
|
|
318
|
+
"This is unexpected and indicates a bug in the code.")
|
|
319
|
+
|
|
320
|
+
# If the starting (high) phase of the transition is in the set of
|
|
321
|
+
# disappeared phases, we can remove this transition from the list
|
|
322
|
+
if tr.high_phase in disappeared_phases:
|
|
323
|
+
if verbose:
|
|
324
|
+
print(
|
|
325
|
+
"\n WARNING: Removing the transition "
|
|
326
|
+
f"{tr.high_phase} -> {tr.low_phase} at Tnuc = "
|
|
327
|
+
f"{_format_temperature_gev(tr.Tnuc, self.conversionFactor)} GeV from the dictionary "
|
|
328
|
+
"because the high phase has already disappeared in a "
|
|
329
|
+
"previous transition."
|
|
330
|
+
)
|
|
331
|
+
# transitions_dict[i] = None
|
|
332
|
+
transitions_dict.remove(transitions_dict[i])
|
|
333
|
+
else:
|
|
334
|
+
# If the high phase is not in the set of disappeared phases, we can add it to the set
|
|
335
|
+
disappeared_phases.add(tr.high_phase)
|
|
336
|
+
|
|
337
|
+
# Enumerate the transitions and save the order in the dictionary
|
|
338
|
+
for i, tr in enumerate(transitions_dict):
|
|
339
|
+
tr.step = i + 1
|
|
340
|
+
tr.total_steps = len(transitions_dict)
|
|
341
|
+
|
|
342
|
+
# Output the transitions
|
|
343
|
+
if verbose:
|
|
344
|
+
print("\nFound %d transition(s): \n" % len(transitions_dict))
|
|
345
|
+
self.printTransitions(transitions_dict)
|
|
346
|
+
|
|
347
|
+
return transitions_dict
|
|
348
|
+
|
|
349
|
+
def printTransitions(self, transitions: dict[TransitionInfo]) -> None:
|
|
350
|
+
"""Pretty-printing of the transitions
|
|
351
|
+
|
|
352
|
+
Parameters
|
|
353
|
+
----------
|
|
354
|
+
transitions : dict[TransitionInfo]
|
|
355
|
+
List with TransitionInfo objects.
|
|
356
|
+
"""
|
|
357
|
+
tables = []
|
|
358
|
+
for tr in transitions:
|
|
359
|
+
if tr.type == 1:
|
|
360
|
+
table = Table(title="First-order phase transition", title_justify="left", box=ROUNDED)
|
|
361
|
+
table.add_column("Parameter", style="cyan", no_wrap=True)
|
|
362
|
+
table.add_column("Value", style="orange1")
|
|
363
|
+
table.add_row("Tnuc / GeV", _format_temperature_gev(tr.Tnuc, self.pot.conversionFactor))
|
|
364
|
+
table.add_row("high phase", str(tr.high_phase))
|
|
365
|
+
table.add_row("low phase", str(tr.low_phase))
|
|
366
|
+
table.add_row("high vev / GeV", str(tr.high_vev * self.pot.conversionFactor))
|
|
367
|
+
table.add_row("low vev / GeV", str(tr.low_vev * self.pot.conversionFactor))
|
|
368
|
+
table.add_row("action / GeV", str(tr.action_Tnuc * self.pot.conversionFactor))
|
|
369
|
+
tables.append(table)
|
|
370
|
+
elif tr.type == 2:
|
|
371
|
+
# This is a second-order transition
|
|
372
|
+
table = Table(title="Second-order phase transition", title_justify="left", box=ROUNDED)
|
|
373
|
+
table.add_column("Parameter", style="cyan", no_wrap=True)
|
|
374
|
+
table.add_column("Value", style="orange1")
|
|
375
|
+
table.add_row("Tcrit / GeV", _format_temperature_gev(tr.Tcrit, self.pot.conversionFactor))
|
|
376
|
+
table.add_row("high phase", str(tr.high_phase))
|
|
377
|
+
table.add_row("low phase", str(tr.low_phase))
|
|
378
|
+
table.add_row("high vev / GeV", str(tr.high_vev * self.pot.conversionFactor))
|
|
379
|
+
table.add_row("low vev / GeV", str(tr.low_vev * self.pot.conversionFactor))
|
|
380
|
+
tables.append(table)
|
|
381
|
+
console.print(RichColumns(tables))
|
|
382
|
+
|
|
383
|
+
def secondOrderTrans(self, V: callable, high_phase: "PhaseInfo",
|
|
384
|
+
low_phase: "PhaseInfo", verbose=False,
|
|
385
|
+
degeneracyTest=True) -> TransitionInfo:
|
|
386
|
+
"""Create the TransitionInfo object for a second order transition.
|
|
387
|
+
|
|
388
|
+
Parameters
|
|
389
|
+
----------
|
|
390
|
+
V : callable
|
|
391
|
+
The effective potential function.
|
|
392
|
+
high_phase : PhaseInfo
|
|
393
|
+
The phase information for the high-temperature phase
|
|
394
|
+
low_phase : PhaseInfo
|
|
395
|
+
The phase information for the low-temperature phase
|
|
396
|
+
verbose : bool
|
|
397
|
+
Set the output level.
|
|
398
|
+
degeneracyTest : bool, optional
|
|
399
|
+
If true, search for Tcrit, where the to minima are degenerate.
|
|
400
|
+
Note, that this causes errors for hidden second order transitions
|
|
401
|
+
|
|
402
|
+
Returns
|
|
403
|
+
-------
|
|
404
|
+
TransitionInfo:
|
|
405
|
+
The transition information.
|
|
406
|
+
|
|
407
|
+
Notes
|
|
408
|
+
-----
|
|
409
|
+
A second order transition is identified by finding the critical temperature
|
|
410
|
+
Tcrit where the two phases are degenerate in energy. At this temperature, the potential
|
|
411
|
+
values of the two phases are equal. If such a temperature is found and the
|
|
412
|
+
degeneracy check passes, a TransitionInfo object is created to represent the
|
|
413
|
+
second order transition.
|
|
414
|
+
"""
|
|
415
|
+
# Calculate the critical temperature Tcrit, where the two phases are
|
|
416
|
+
# degenerate. This is done by finding the temperature where the
|
|
417
|
+
# potential is equal for the two phases. This is done by using
|
|
418
|
+
# optimize.brentq to find the root of the function
|
|
419
|
+
def f(T): return V(high_phase.valAt(T), T) - V(low_phase.valAt(T), T)
|
|
420
|
+
|
|
421
|
+
Tcrit = np.nan
|
|
422
|
+
|
|
423
|
+
try:
|
|
424
|
+
Tcrit = optimize.brentq(f, high_phase.T[0], low_phase.T[-1], disp=False)
|
|
425
|
+
if verbose:
|
|
426
|
+
print("Second order transition between phases %s and %s at Tcrit = %g GeV" \
|
|
427
|
+
% (high_phase.key, low_phase.key, Tcrit * self.conversionFactor))
|
|
428
|
+
print("high_phase.valAt(Tcrit) = ", high_phase.valAt(Tcrit) * self.conversionFactor, "GeV")
|
|
429
|
+
print("low_phase.valAt(Tcrit) = ", low_phase.valAt(Tcrit) * self.conversionFactor, "GeV")
|
|
430
|
+
print("V(high_phase.valAt(Tcrit), Tcrit) = ", V(high_phase.valAt(Tcrit), Tcrit) \
|
|
431
|
+
* self.conversionFactor**4, "GeV^4")
|
|
432
|
+
print("V(low_phase.valAt(Tcrit), Tcrit) = ", V(low_phase.valAt(Tcrit), Tcrit) \
|
|
433
|
+
* self.conversionFactor**4, "GeV^4\n")
|
|
434
|
+
except ValueError as err:
|
|
435
|
+
if verbose:
|
|
436
|
+
lower = high_phase.T[0] * self.conversionFactor
|
|
437
|
+
upper = low_phase.T[-1] * self.conversionFactor
|
|
438
|
+
print(
|
|
439
|
+
"The critical temperature could not be found in the range "
|
|
440
|
+
f"({lower}, {upper}) GeV between the phases "
|
|
441
|
+
f"{high_phase.key} and {low_phase.key}. This is likely a "
|
|
442
|
+
"problem due to the numerical precision used in phase tracing."
|
|
443
|
+
)
|
|
444
|
+
print("ValueError: ", err)
|
|
445
|
+
Tcrit = np.nan
|
|
446
|
+
|
|
447
|
+
# no Tcrit found:
|
|
448
|
+
if np.isnan(Tcrit):
|
|
449
|
+
Tcrit = low_phase.Tmax
|
|
450
|
+
transition = TransitionInfo(
|
|
451
|
+
Tcrit=Tcrit,
|
|
452
|
+
high_vev=high_phase.X[0],
|
|
453
|
+
high_phase=high_phase.key,
|
|
454
|
+
low_vev=low_phase.X[0],
|
|
455
|
+
low_phase=low_phase.key,
|
|
456
|
+
type=2,
|
|
457
|
+
Tnuc=Tcrit,
|
|
458
|
+
Tperc=Tcrit)
|
|
459
|
+
elif np.abs(V(high_phase.valAt(Tcrit), Tcrit) / V(low_phase.valAt(Tcrit), Tcrit) - 1) < 1e-4:
|
|
460
|
+
# Check if the two phases are actually degenerate at Tcrit
|
|
461
|
+
# This is a second order transition
|
|
462
|
+
if verbose:
|
|
463
|
+
print("The check for degeneracy at Tcrit = ", Tcrit * self.conversionFactor,
|
|
464
|
+
"GeV succeeded. Create crossover transition object now.")
|
|
465
|
+
transition = TransitionInfo(
|
|
466
|
+
Tcrit=Tcrit,
|
|
467
|
+
high_vev=high_phase.X[0],
|
|
468
|
+
high_phase=high_phase.key,
|
|
469
|
+
low_vev=low_phase.X[0],
|
|
470
|
+
low_phase=low_phase.key,
|
|
471
|
+
type=2,
|
|
472
|
+
Tnuc=Tcrit, # this part is technically wrong, but we need a value to sort the transitions
|
|
473
|
+
Tperc=Tcrit)
|
|
474
|
+
else:
|
|
475
|
+
if verbose:
|
|
476
|
+
print("Degeneracy check failed at Tcrit = ", Tcrit * self.conversionFactor, "GeV")
|
|
477
|
+
Tcrit = np.nan
|
|
478
|
+
transition = TransitionInfo(
|
|
479
|
+
Tcrit=np.nan,
|
|
480
|
+
high_vev=high_phase.X[0],
|
|
481
|
+
high_phase=high_phase.key,
|
|
482
|
+
low_vev=low_phase.X[0],
|
|
483
|
+
low_phase=low_phase.key,
|
|
484
|
+
type=2,
|
|
485
|
+
Tnuc=np.nan,
|
|
486
|
+
Tperc=np.nan)
|
|
487
|
+
return transition
|
|
488
|
+
|
|
489
|
+
def makeSecondOrderDict(self, transition: TransitionInfo) -> dict:
|
|
490
|
+
"""
|
|
491
|
+
Create a dictionary describing a second-order phase transition.
|
|
492
|
+
"""
|
|
493
|
+
transition_dict = TransitionInfo(
|
|
494
|
+
Tcrit=transition.Tcrit,
|
|
495
|
+
high_vev=transition.high_vev,
|
|
496
|
+
high_phase=transition.high_phase,
|
|
497
|
+
low_vev=transition.low_vev,
|
|
498
|
+
low_phase=transition.low_phase,
|
|
499
|
+
type=2,
|
|
500
|
+
Tnuc=np.nan,
|
|
501
|
+
Tperc=np.nan)
|
|
502
|
+
return transition_dict
|
|
503
|
+
|
|
504
|
+
def sortForSmallestAction(self, V, phases, startPhase, childPhases) -> list:
|
|
505
|
+
"""Estimate the action by computing the action for the linearised
|
|
506
|
+
potential between the start phase and the child phases.
|
|
507
|
+
Return a sorted list of the child phases after the
|
|
508
|
+
smallest action approximation
|
|
509
|
+
|
|
510
|
+
Parameters
|
|
511
|
+
----------
|
|
512
|
+
|
|
513
|
+
Returns
|
|
514
|
+
-------
|
|
515
|
+
|
|
516
|
+
.. todo:: implement
|
|
517
|
+
|
|
518
|
+
"""
|
|
519
|
+
pass
|
|
520
|
+
|
|
521
|
+
def getUniqueTunnelingPhases(self, V : callable, phases : list["PhaseInfo"],
|
|
522
|
+
startPhase : "PhaseInfo",
|
|
523
|
+
childPhases : list["PhaseInfo"]) -> list["PhaseInfo"]:
|
|
524
|
+
"""Check if there are equivalent tunneling paths to different phases.
|
|
525
|
+
|
|
526
|
+
This functions checks there are two phases which have the same distance
|
|
527
|
+
to the start phase and the same potential values. If so the
|
|
528
|
+
tunneling paths are considered equivalent and only one phase is returned.
|
|
529
|
+
|
|
530
|
+
Parameters
|
|
531
|
+
----------
|
|
532
|
+
V : callable
|
|
533
|
+
The effective potential V(X, T)
|
|
534
|
+
phases : list[PhaseInfo]
|
|
535
|
+
childPhases : list
|
|
536
|
+
List of phase keys
|
|
537
|
+
|
|
538
|
+
Returns
|
|
539
|
+
-------
|
|
540
|
+
list :
|
|
541
|
+
List with phase keys.
|
|
542
|
+
"""
|
|
543
|
+
xtol = 1e-3
|
|
544
|
+
Vtol = 1e-3
|
|
545
|
+
uniqueTunnelingPhases = []
|
|
546
|
+
while childPhases != []:
|
|
547
|
+
p1 = childPhases.pop()
|
|
548
|
+
DX1min = np.sqrt(np.sum(phases[p1].X[0] - phases[startPhase].X[0])**2)
|
|
549
|
+
DV1min = np.abs(V(phases[p1].X[0], phases[p1].T[0]) -
|
|
550
|
+
V(phases[startPhase].X[0], phases[startPhase].T[0]))
|
|
551
|
+
DX1max = np.sqrt(np.sum(phases[p1].X[-1] - phases[startPhase].X[-1])**2)
|
|
552
|
+
DV1max = np.abs(V(phases[p1].X[-1], phases[p1].T[-1]) -
|
|
553
|
+
V(phases[startPhase].X[-1], phases[startPhase].T[-1]))
|
|
554
|
+
equiv = False
|
|
555
|
+
for p2 in childPhases:
|
|
556
|
+
DX2min = np.sqrt(np.sum(phases[p2].X[0] - phases[startPhase].X[0])**2)
|
|
557
|
+
DX2max = np.sqrt(np.sum(phases[p2].X[-1] - phases[startPhase].X[-1])**2)
|
|
558
|
+
DV2min = np.abs(V(phases[p2].X[0], phases[p2].T[0]) -
|
|
559
|
+
V(phases[startPhase].X[0], phases[startPhase].T[0]))
|
|
560
|
+
DV2max = np.abs(V(phases[p2].X[-1], phases[p2].T[-1]) -
|
|
561
|
+
V(phases[startPhase].X[-1], phases[startPhase].T[-1]))
|
|
562
|
+
if ((abs(DV1min - DV2min) < Vtol and abs(DX1min - DX2min) < xtol) and
|
|
563
|
+
(abs(DV1max - DV2max) < Vtol and abs(DX1max - DX2max) < xtol)):
|
|
564
|
+
equiv = True
|
|
565
|
+
print("WARNING: Encountered 2 equivalent child phases. " +
|
|
566
|
+
"This implies domain walls!")
|
|
567
|
+
break
|
|
568
|
+
if not equiv:
|
|
569
|
+
uniqueTunnelingPhases.append(p1)
|
|
570
|
+
|
|
571
|
+
return uniqueTunnelingPhases
|
|
572
|
+
|
|
573
|
+
def findTransitionPathFromTnuc(self, verbose=False) -> list[TransitionInfo]:
|
|
574
|
+
"""Find the transition path starting from the high-T phase.
|
|
575
|
+
|
|
576
|
+
Calculate the nucleation temperature for the transitions
|
|
577
|
+
starting from the high-T phase. The function constructs
|
|
578
|
+
a list of transitions with the highest possible nucleation
|
|
579
|
+
temperatures.
|
|
580
|
+
|
|
581
|
+
Parameters
|
|
582
|
+
----------
|
|
583
|
+
verbose : bool
|
|
584
|
+
Set the output level.
|
|
585
|
+
|
|
586
|
+
Returns
|
|
587
|
+
-------
|
|
588
|
+
list[TranstionInfo]:
|
|
589
|
+
A list of of transitions sorted in decreasing temperature.
|
|
590
|
+
"""
|
|
591
|
+
transitions = []
|
|
592
|
+
tunnelFromPhase_args = self.tunnelFromPhase_args
|
|
593
|
+
phases = self.phases
|
|
594
|
+
V = self.V
|
|
595
|
+
startPhase = phases[phases.rootPhase]
|
|
596
|
+
seedless = self.pot.config.percolationConf.algorithm_mode == "adaptive_step_size"
|
|
597
|
+
|
|
598
|
+
while True:
|
|
599
|
+
# check if we are in the lowest minimum
|
|
600
|
+
if startPhase.childPhases == []:
|
|
601
|
+
if startPhase.key == self.phases.T0Phase:
|
|
602
|
+
break
|
|
603
|
+
else:
|
|
604
|
+
raise errors.WrongT0MinimumError
|
|
605
|
+
|
|
606
|
+
childTransitions = []
|
|
607
|
+
# exclude equivalent tunnelings to mirror phases
|
|
608
|
+
childPhases = self.getUniqueTunnelingPhases(V, phases, startPhase.key, startPhase.childPhases)
|
|
609
|
+
for i in childPhases:
|
|
610
|
+
endPhase = phases[i]
|
|
611
|
+
|
|
612
|
+
# Check for overlap between phases
|
|
613
|
+
tmax = min(startPhase.Tmax, endPhase.Tmax)
|
|
614
|
+
tmin = max(startPhase.Tmin, endPhase.Tmin)
|
|
615
|
+
|
|
616
|
+
if tmin >= tmax:
|
|
617
|
+
if endPhase.key in startPhase.low_trans:
|
|
618
|
+
childTransitions.append(
|
|
619
|
+
self.secondOrderTrans(V, startPhase, endPhase, verbose))
|
|
620
|
+
startPhase = endPhase
|
|
621
|
+
continue # There could be other second order transitions (at slightly high temps)
|
|
622
|
+
else:
|
|
623
|
+
pass
|
|
624
|
+
continue
|
|
625
|
+
|
|
626
|
+
if seedless:
|
|
627
|
+
transition = self.seedlessFirstOrderCandidate(
|
|
628
|
+
V,
|
|
629
|
+
startPhase,
|
|
630
|
+
endPhase,
|
|
631
|
+
tmin,
|
|
632
|
+
tmax,
|
|
633
|
+
verbose,
|
|
634
|
+
)
|
|
635
|
+
if transition is not None:
|
|
636
|
+
childTransitions.append(transition)
|
|
637
|
+
continue
|
|
638
|
+
|
|
639
|
+
try:
|
|
640
|
+
if verbose:
|
|
641
|
+
print(f"\nTry tunneling from phase {startPhase.key} to phase {endPhase.key}")
|
|
642
|
+
tunn = computeNucleationTemperature(self.V, self.dV, self.d2V,
|
|
643
|
+
startPhase,
|
|
644
|
+
endPhase, self.conversionFactor,
|
|
645
|
+
verbose=verbose,
|
|
646
|
+
allow_second_order_fallback=not seedless,
|
|
647
|
+
**tunnelFromPhase_args)
|
|
648
|
+
|
|
649
|
+
# create TunnelingInfo object
|
|
650
|
+
tr = TransitionInfo(Tcrit=tunn['Tcrit'], Tnuc=tunn['Tnuc'],
|
|
651
|
+
low_phase=tunn['low_phase'],
|
|
652
|
+
high_phase=tunn['high_phase'],
|
|
653
|
+
low_vev=tunn['low_vev'],
|
|
654
|
+
high_vev=tunn['high_vev'],
|
|
655
|
+
action_Tnuc=tunn['action'],
|
|
656
|
+
type=tunn['trantype'])
|
|
657
|
+
if tr.type != 2: # Sometimes a second order transitions slips through here
|
|
658
|
+
tr.instanton = tunn['instanton']
|
|
659
|
+
childTransitions.append(tr)
|
|
660
|
+
except errors.NucleationError as err:
|
|
661
|
+
# Too much supercooling, cannot tunnel.
|
|
662
|
+
if verbose:
|
|
663
|
+
print("Nucleation error: ", err)
|
|
664
|
+
if seedless:
|
|
665
|
+
transition = TransitionInfo(
|
|
666
|
+
Tcrit=tmax,
|
|
667
|
+
Tnuc=None,
|
|
668
|
+
low_phase=endPhase.key,
|
|
669
|
+
high_phase=startPhase.key,
|
|
670
|
+
low_vev=endPhase.valAt(tmax),
|
|
671
|
+
high_vev=startPhase.valAt(tmax),
|
|
672
|
+
action_Tnuc=np.nan,
|
|
673
|
+
type=1,
|
|
674
|
+
)
|
|
675
|
+
transition.full_tunneling_info = {}
|
|
676
|
+
transition.instanton = {}
|
|
677
|
+
childTransitions.append(transition)
|
|
678
|
+
else:
|
|
679
|
+
continue
|
|
680
|
+
except Exception as err:
|
|
681
|
+
raise errors.NucleationError("Unexpected nucleation error:\n" +
|
|
682
|
+
str(err))
|
|
683
|
+
|
|
684
|
+
if childTransitions == []:
|
|
685
|
+
raise errors.TooMuchSupercoolingError
|
|
686
|
+
|
|
687
|
+
childTransitions = sorted(childTransitions, key=_transition_sort_temperature, reverse=True)
|
|
688
|
+
# new start phase:
|
|
689
|
+
startPhase = phases[childTransitions[0].low_phase]
|
|
690
|
+
transitions.append(childTransitions[0])
|
|
691
|
+
|
|
692
|
+
for i, tr in enumerate(transitions):
|
|
693
|
+
tr.step = i + 1
|
|
694
|
+
tr.total_steps = len(transitions)
|
|
695
|
+
|
|
696
|
+
return transitions
|
|
697
|
+
|
|
698
|
+
def seedlessFirstOrderCandidate(
|
|
699
|
+
self,
|
|
700
|
+
V: callable,
|
|
701
|
+
high_phase: "PhaseInfo",
|
|
702
|
+
low_phase: "PhaseInfo",
|
|
703
|
+
tmin: float,
|
|
704
|
+
tmax: float,
|
|
705
|
+
verbose: bool = False,
|
|
706
|
+
) -> TransitionInfo | None:
|
|
707
|
+
"""Build a first-order transition candidate without computing Tnuc.
|
|
708
|
+
|
|
709
|
+
Seedless adaptive step size should not spend CPU finding a nucleation-temperature seed
|
|
710
|
+
before percolation. It only needs a plausible high/low phase pair and
|
|
711
|
+
a finite overlap interval; the dynamic support-bank scouts then decide
|
|
712
|
+
where action evaluations are physically relevant.
|
|
713
|
+
"""
|
|
714
|
+
|
|
715
|
+
def DV(T):
|
|
716
|
+
return V(high_phase.valAt(T), T) - V(low_phase.valAt(T), T)
|
|
717
|
+
|
|
718
|
+
try:
|
|
719
|
+
dv_min = DV(tmin)
|
|
720
|
+
dv_max = DV(tmax)
|
|
721
|
+
except Exception as err:
|
|
722
|
+
if verbose:
|
|
723
|
+
print(
|
|
724
|
+
"Skipping seedless candidate because the phase-overlap "
|
|
725
|
+
"potential difference could not be evaluated:",
|
|
726
|
+
err,
|
|
727
|
+
)
|
|
728
|
+
return None
|
|
729
|
+
|
|
730
|
+
if dv_min < 0:
|
|
731
|
+
if verbose:
|
|
732
|
+
print(
|
|
733
|
+
"Skipping seedless candidate because the starting phase is "
|
|
734
|
+
"already lower than the target phase at Tmin."
|
|
735
|
+
)
|
|
736
|
+
return None
|
|
737
|
+
|
|
738
|
+
candidate_temperature = tmax
|
|
739
|
+
if dv_max <= 0:
|
|
740
|
+
try:
|
|
741
|
+
candidate_temperature = optimize.brentq(DV, tmin, tmax, disp=False)
|
|
742
|
+
except ValueError:
|
|
743
|
+
candidate_temperature = tmax
|
|
744
|
+
elif high_phase.Tmax <= low_phase.Tmax:
|
|
745
|
+
if verbose:
|
|
746
|
+
print(
|
|
747
|
+
"Skipping seedless candidate because the starting phase is "
|
|
748
|
+
"higher at Tmax and also exists to at least as high a "
|
|
749
|
+
"temperature as the target phase."
|
|
750
|
+
)
|
|
751
|
+
return None
|
|
752
|
+
|
|
753
|
+
if verbose:
|
|
754
|
+
print(
|
|
755
|
+
"Adding seedless first-order candidate from phase",
|
|
756
|
+
high_phase.key,
|
|
757
|
+
"to phase",
|
|
758
|
+
low_phase.key,
|
|
759
|
+
"with reference T =",
|
|
760
|
+
candidate_temperature * self.conversionFactor,
|
|
761
|
+
"GeV",
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
transition = TransitionInfo(
|
|
765
|
+
Tcrit=candidate_temperature,
|
|
766
|
+
Tnuc=None,
|
|
767
|
+
low_phase=low_phase.key,
|
|
768
|
+
high_phase=high_phase.key,
|
|
769
|
+
low_vev=low_phase.valAt(candidate_temperature),
|
|
770
|
+
high_vev=high_phase.valAt(candidate_temperature),
|
|
771
|
+
action_Tnuc=np.nan,
|
|
772
|
+
type=1,
|
|
773
|
+
)
|
|
774
|
+
transition.full_tunneling_info = {}
|
|
775
|
+
transition.instanton = {}
|
|
776
|
+
return transition
|
|
777
|
+
|
|
778
|
+
def findPossibleTransitionsFromPhases(self, verbose=False) -> list[TransitionInfo]:
|
|
779
|
+
"""Find all possible transitions between coexisting phases.
|
|
780
|
+
|
|
781
|
+
Parameters
|
|
782
|
+
----------
|
|
783
|
+
verbose : bool
|
|
784
|
+
Set the output level.
|
|
785
|
+
|
|
786
|
+
Returns
|
|
787
|
+
-------
|
|
788
|
+
list[TranstionInfo]:
|
|
789
|
+
A list of of transitions sorted in decreasing temperature.
|
|
790
|
+
"""
|
|
791
|
+
|
|
792
|
+
phases = self.phases
|
|
793
|
+
V = self.V
|
|
794
|
+
start_high = False
|
|
795
|
+
verbose = self.verbose
|
|
796
|
+
|
|
797
|
+
if verbose:
|
|
798
|
+
print("\nFind all possible transitions based on the existence of a critical temperature:")
|
|
799
|
+
transitions = []
|
|
800
|
+
for i in phases.keys():
|
|
801
|
+
for j in phases.keys():
|
|
802
|
+
if i == j:
|
|
803
|
+
continue
|
|
804
|
+
# Try going from i to j (phase1 -> phase2)
|
|
805
|
+
phase1, phase2 = phases[i], phases[j]
|
|
806
|
+
tmax = min(phase1.Tmax, phase2.Tmax)
|
|
807
|
+
tmin = max(phase1.Tmin, phase2.Tmin)
|
|
808
|
+
|
|
809
|
+
if verbose:
|
|
810
|
+
print("\nChecking transition from phase", i, "to phase", j,
|
|
811
|
+
"which coexists in the range [", tmin * self.conversionFactor,
|
|
812
|
+
",", tmax * self.conversionFactor, "] GeV")
|
|
813
|
+
|
|
814
|
+
if tmin >= tmax:
|
|
815
|
+
# There is no overlap between the two phases, so no transition can occur. Still, if previously
|
|
816
|
+
# the low_trans dictionary was filled, indicating the possibility of a second-order transition,
|
|
817
|
+
# we can still check for that.
|
|
818
|
+
if verbose:
|
|
819
|
+
print("There's no overlap between the phases. A crossover transition might "
|
|
820
|
+
+ "be possible if there is a link between the two.")
|
|
821
|
+
if phase2.key in phase1.low_trans:
|
|
822
|
+
transitions.append(
|
|
823
|
+
self.secondOrderTrans(V, phase1, phase2, verbose))
|
|
824
|
+
else:
|
|
825
|
+
pass
|
|
826
|
+
continue
|
|
827
|
+
|
|
828
|
+
def DV(T):
|
|
829
|
+
return V(phase1.valAt(T), T) - V(phase2.valAt(T), T)
|
|
830
|
+
|
|
831
|
+
DVmin, DVmax = DV(tmin), DV(tmax)
|
|
832
|
+
|
|
833
|
+
if DVmin < 0:
|
|
834
|
+
# phase1 is lower at tmin, so no tunneling can occur
|
|
835
|
+
if verbose:
|
|
836
|
+
print("Tunneling cannot occur, because phase", i,
|
|
837
|
+
"is lower than phase", j, "at tmin: DV(tmin) =",
|
|
838
|
+
DVmin * self.conversionFactor**4, "GeV^4")
|
|
839
|
+
continue
|
|
840
|
+
|
|
841
|
+
if DVmax > 0:
|
|
842
|
+
# phase1 is higher even at tmax. This can be weird, because a critical
|
|
843
|
+
# temperature in 1d potential needs to exist and be below Tmax. In 2d
|
|
844
|
+
# scenarios, however, this can happen without a problem. In the latter
|
|
845
|
+
# case, we need to make sure that this is not an unphysical transition
|
|
846
|
+
# 1 -> 0 though, from a phase 1 that will only become relevant at
|
|
847
|
+
# lower temperatures and might allow for 0 -> 1, assuming that the
|
|
848
|
+
# univere is in phase 0 at high temperatures. To check this, we
|
|
849
|
+
# require that the maximal temperature at which phase1 (the starting
|
|
850
|
+
# phase) is defined is higher than that of phase2 (the ending phase)
|
|
851
|
+
if phase1.Tmax > phase2.Tmax:
|
|
852
|
+
if verbose:
|
|
853
|
+
print("Possible transition without a critical temp. found." +
|
|
854
|
+
"This can happen for multidimensional potentials.")
|
|
855
|
+
# Note: Tcrit here is not an actual critical temperature, but
|
|
856
|
+
# the largest temperature where the two phases coexist.
|
|
857
|
+
transition = TransitionInfo(
|
|
858
|
+
Tcrit=tmax,
|
|
859
|
+
high_vev=phase1.valAt(tmax),
|
|
860
|
+
high_phase=phase1.key,
|
|
861
|
+
low_vev=phase2.valAt(tmax),
|
|
862
|
+
low_phase=phase2.key,
|
|
863
|
+
type=1)
|
|
864
|
+
transitions.append(transition)
|
|
865
|
+
else:
|
|
866
|
+
if verbose:
|
|
867
|
+
print("Tunneling cannot occur, because phase", i,
|
|
868
|
+
"is higher than phase", j, "at tmax: DV(tmax) =",
|
|
869
|
+
DVmax * self.conversionFactor**4,
|
|
870
|
+
"GeV^4 and the initial phase exists since higher temperatures.")
|
|
871
|
+
continue
|
|
872
|
+
|
|
873
|
+
Tcrit = optimize.brentq(DV, tmin, tmax, disp=False)
|
|
874
|
+
if verbose:
|
|
875
|
+
print("Found a possible transition from phase", i,
|
|
876
|
+
"to phase", j, "with Tcrit =",
|
|
877
|
+
Tcrit * self.conversionFactor, "GeV")
|
|
878
|
+
transition = TransitionInfo(
|
|
879
|
+
Tcrit=Tcrit,
|
|
880
|
+
high_vev=phase1.valAt(Tcrit),
|
|
881
|
+
high_phase=phase1.key,
|
|
882
|
+
low_vev=phase2.valAt(Tcrit),
|
|
883
|
+
low_phase=phase2.key,
|
|
884
|
+
type=1)
|
|
885
|
+
transitions.append(transition)
|
|
886
|
+
|
|
887
|
+
if start_high:
|
|
888
|
+
raise NotImplementedError("start_high=True not yet supported")
|
|
889
|
+
|
|
890
|
+
return sorted(transitions, key=lambda x: x.Tcrit)[::-1]
|
|
891
|
+
|
|
892
|
+
def getStartPhase(self, phases, V=None):
|
|
893
|
+
"""
|
|
894
|
+
Find the key for the high-T phase.
|
|
895
|
+
|
|
896
|
+
Parameters
|
|
897
|
+
----------
|
|
898
|
+
phases : dict
|
|
899
|
+
Output from :func:`traceMultiMin`.
|
|
900
|
+
V : callable
|
|
901
|
+
The potential V(x,T). Only necessary if there are
|
|
902
|
+
multiple phases with the same Tmax.
|
|
903
|
+
"""
|
|
904
|
+
startPhases = []
|
|
905
|
+
startPhase = None
|
|
906
|
+
Tmax = None
|
|
907
|
+
assert len(phases) > 0
|
|
908
|
+
for i in list(phases.keys()):
|
|
909
|
+
if phases[i].T[-1] == Tmax:
|
|
910
|
+
# add this to the startPhases list.
|
|
911
|
+
startPhases.append(i)
|
|
912
|
+
elif Tmax is None or phases[i].T[-1] > Tmax:
|
|
913
|
+
startPhases = [i]
|
|
914
|
+
Tmax = phases[i].T[-1]
|
|
915
|
+
if len(startPhases) == 1 or V is None:
|
|
916
|
+
startPhase = startPhases[0]
|
|
917
|
+
else:
|
|
918
|
+
# more than one phase have the same maximum temperature
|
|
919
|
+
# Pick the stable one at high temp.
|
|
920
|
+
Vmin = None
|
|
921
|
+
for i in startPhases:
|
|
922
|
+
V_ = V(phases[i].X[-1], phases[i].T[-1])
|
|
923
|
+
if Vmin is None or V_ < Vmin:
|
|
924
|
+
Vmin = V_
|
|
925
|
+
startPhase = i
|
|
926
|
+
assert startPhase in phases
|
|
927
|
+
return startPhase
|
|
928
|
+
|
|
929
|
+
def evalNucleationCriterion(self, T: float, start_phase: "PhaseInfo",
|
|
930
|
+
end_phase: "PhaseInfo", V: callable, dV: callable,
|
|
931
|
+
phitol: float, overlapAngle: float,
|
|
932
|
+
nuclCriterion: callable,
|
|
933
|
+
fullTunneling_params: dict, verbose: bool,
|
|
934
|
+
outdict: dict) -> float:
|
|
935
|
+
""" Find the lowest action tunneling solution.
|
|
936
|
+
|
|
937
|
+
This functions stores the results in the dict `outdict` with
|
|
938
|
+
the temperature as the key.
|
|
939
|
+
|
|
940
|
+
Parameters
|
|
941
|
+
----------
|
|
942
|
+
T : float
|
|
943
|
+
The temperature.
|
|
944
|
+
start_phase : PhaseInfo
|
|
945
|
+
The starting phase
|
|
946
|
+
end_phase : PhaseInfo
|
|
947
|
+
The end phase
|
|
948
|
+
V : callable
|
|
949
|
+
The effective potential
|
|
950
|
+
dV : callable
|
|
951
|
+
The gradient of the effective potential.
|
|
952
|
+
|
|
953
|
+
Returns
|
|
954
|
+
-------
|
|
955
|
+
float :
|
|
956
|
+
Evaluation of nuclCriterion(S,T)."""
|
|
957
|
+
try:
|
|
958
|
+
T = T[0] # need this when the function is run from optimize.fmin
|
|
959
|
+
except:
|
|
960
|
+
pass
|
|
961
|
+
|
|
962
|
+
# Loop through all the phases, adding acceptable minima
|
|
963
|
+
x0 = start_phase.valAt(T)
|
|
964
|
+
|
|
965
|
+
if T in outdict:
|
|
966
|
+
return nuclCriterion(outdict[T]['action'], T, start_phase, end_phase)
|
|
967
|
+
|
|
968
|
+
V0 = V(x0, T)
|
|
969
|
+
p = end_phase
|
|
970
|
+
|
|
971
|
+
if p.Tmin > T:
|
|
972
|
+
if verbose:
|
|
973
|
+
print("Phase %s not valid at T = %g" % (p.key, T * self.conversionFactor),
|
|
974
|
+
"GeV < Tmin = ", p.Tmin * self.conversionFactor, "GeV")
|
|
975
|
+
return np.inf
|
|
976
|
+
|
|
977
|
+
if p.Tmax < T:
|
|
978
|
+
if verbose:
|
|
979
|
+
print("Phase %s not valid at T = %g" % (p.key, T * self.conversionFactor),
|
|
980
|
+
"GeV > Tmax = ", p.Tmax * self.conversionFactor, "GeV")
|
|
981
|
+
return -np.inf
|
|
982
|
+
|
|
983
|
+
x1 = p.valAt(T)
|
|
984
|
+
V1 = V(x1, T)
|
|
985
|
+
if V1 >= V0:
|
|
986
|
+
if verbose:
|
|
987
|
+
print("Tunneling energetically not possible. Tmax might be too" +
|
|
988
|
+
" high or the numerical accuracy is not sufficient. This is not a problem, just a warning.")
|
|
989
|
+
|
|
990
|
+
tdict = dict(low_vev=x1, high_vev=x0, Tnuc=T,
|
|
991
|
+
low_phase=end_phase.key, high_phase=start_phase.key)
|
|
992
|
+
# Get rid of the T parameter for V and dV
|
|
993
|
+
|
|
994
|
+
outdict = pathDeformation.bounceAction(
|
|
995
|
+
T, V, dV, outdict, tdict, verbose=verbose,
|
|
996
|
+
conversionFactor=self.conversionFactor,
|
|
997
|
+
**fullTunneling_params)
|
|
998
|
+
|
|
999
|
+
S = outdict[T]['action']
|
|
1000
|
+
return nuclCriterion(S, T, start_phase, end_phase)
|