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,1632 @@
|
|
|
1
|
+
"""Seedless dynamic zoom-window percolation (adaptive step size) algorithm.
|
|
2
|
+
|
|
3
|
+
Grid-construction helpers (step-1 scouting, jump/range/anchor-remesh builders,
|
|
4
|
+
adaptive cold-boundary picker, jump-underresolution guards) live in
|
|
5
|
+
:mod:`transitionlistener.percolation_adaptive_gridbuilders`. This module keeps the
|
|
6
|
+
top-level scan controllers and their :class:`PercolationState` bookkeeping.
|
|
7
|
+
|
|
8
|
+
Part of TransitionListener v2.0
|
|
9
|
+
Documentation: https://tasillo.de/TransitionListener/
|
|
10
|
+
|
|
11
|
+
Authors:
|
|
12
|
+
Jonas Matuszak <jonas.matuszak@kit.edu>
|
|
13
|
+
Carlo Tasillo <carlo.tasillo@ific.uv.es>
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import math
|
|
17
|
+
import numpy as np
|
|
18
|
+
from scipy import interpolate
|
|
19
|
+
from scipy import optimize
|
|
20
|
+
|
|
21
|
+
from transitionlistener import constants as cn
|
|
22
|
+
from transitionlistener import errors
|
|
23
|
+
from transitionlistener.bubbledynamics import (
|
|
24
|
+
PercolationGrid,
|
|
25
|
+
PercolationState,
|
|
26
|
+
_temperature_grid,
|
|
27
|
+
)
|
|
28
|
+
from transitionlistener.percolation_adaptive_rate import (
|
|
29
|
+
_build_dynamiczoomwindow_hot_head_grid,
|
|
30
|
+
_build_dynamiczoomwindow_hot_shoulder_grid,
|
|
31
|
+
_build_dynamiczoomwindow_rate_peak_grid,
|
|
32
|
+
_find_value_crossing,
|
|
33
|
+
_hot_head_underresolved,
|
|
34
|
+
_temperature_present,
|
|
35
|
+
)
|
|
36
|
+
from transitionlistener.percolation_adaptive_gridbuilders import (
|
|
37
|
+
_adaptive_cold_boundary,
|
|
38
|
+
_build_dynamiczoomwindow_anchor_remesh_grid,
|
|
39
|
+
_build_dynamiczoomwindow_jump_grid,
|
|
40
|
+
_build_dynamiczoomwindow_range_grid,
|
|
41
|
+
_build_dynamiczoomwindow_unresolved_jump_grid,
|
|
42
|
+
_cached_promotion_bounds,
|
|
43
|
+
_initial_temperature_grid,
|
|
44
|
+
_raise_if_hot_head_underresolved,
|
|
45
|
+
_raise_if_large_delta_p_underresolved,
|
|
46
|
+
_range_expand_batch_size,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _controller_added_support_points(
|
|
51
|
+
support_bank: np.ndarray | None,
|
|
52
|
+
free_support_bank: np.ndarray | None,
|
|
53
|
+
) -> int:
|
|
54
|
+
"""Count support points that require new controller-requested actions."""
|
|
55
|
+
|
|
56
|
+
bank = _temperature_grid(support_bank)
|
|
57
|
+
free_bank = _temperature_grid(free_support_bank)
|
|
58
|
+
if bank.size == 0:
|
|
59
|
+
return 0
|
|
60
|
+
if free_bank.size == 0:
|
|
61
|
+
return int(bank.size)
|
|
62
|
+
count = 0
|
|
63
|
+
for value in bank:
|
|
64
|
+
scale = max(np.max(np.abs(free_bank)), abs(float(value)), 1.0)
|
|
65
|
+
if not np.any(np.isclose(free_bank, float(value), atol=1e-10 * scale, rtol=0.0)):
|
|
66
|
+
count += 1
|
|
67
|
+
return int(count)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _log_support_rebuild(
|
|
71
|
+
state: PercolationState,
|
|
72
|
+
stage: str,
|
|
73
|
+
reason: str,
|
|
74
|
+
old_grid: np.ndarray,
|
|
75
|
+
new_grid: np.ndarray,
|
|
76
|
+
verbose: bool,
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Bump ``state.rebuild_count`` and print a support-rebuild line when verbose."""
|
|
79
|
+
state.rebuild_count = int(state.rebuild_count or 0) + 1
|
|
80
|
+
if verbose:
|
|
81
|
+
print(
|
|
82
|
+
"Support rebuild:",
|
|
83
|
+
f"stage={stage}, reason={reason}, "
|
|
84
|
+
f"old=[{float(old_grid[-1]):2.5g}, {float(old_grid[0]):2.5g}], "
|
|
85
|
+
f"new=[{float(new_grid[-1]):2.5g}, {float(new_grid[0]):2.5g}], "
|
|
86
|
+
f"n={len(new_grid)}",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _apply_dynamiczoomwindow_support_update(
|
|
91
|
+
current_active_grid: np.ndarray,
|
|
92
|
+
candidate_grid: np.ndarray,
|
|
93
|
+
support_bank: np.ndarray | None,
|
|
94
|
+
free_support_bank: np.ndarray | None,
|
|
95
|
+
settings,
|
|
96
|
+
*,
|
|
97
|
+
max_new_points: int | None = None,
|
|
98
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
99
|
+
"""Apply one support-grid update while keeping dormant scout points inactive."""
|
|
100
|
+
current_active = _temperature_grid(current_active_grid)
|
|
101
|
+
bank = _temperature_grid(support_bank)
|
|
102
|
+
free_bank = _temperature_grid(free_support_bank)
|
|
103
|
+
if bank.size == 0:
|
|
104
|
+
bank = current_active
|
|
105
|
+
else:
|
|
106
|
+
bank = _temperature_grid(np.concatenate((bank, current_active)))
|
|
107
|
+
|
|
108
|
+
known_active = _temperature_grid(np.concatenate((bank, current_active)))
|
|
109
|
+
new_candidates: list[float] = []
|
|
110
|
+
for value in _temperature_grid(candidate_grid):
|
|
111
|
+
numeric = float(value)
|
|
112
|
+
if _temperature_present(known_active, numeric) or _temperature_present(new_candidates, numeric):
|
|
113
|
+
continue
|
|
114
|
+
new_candidates.append(numeric)
|
|
115
|
+
|
|
116
|
+
controller_added = _controller_added_support_points(bank, free_bank)
|
|
117
|
+
updated_bank = bank
|
|
118
|
+
dropped_new_points: list[float] = []
|
|
119
|
+
limit = int(settings.n_action_max if max_new_points is None else max_new_points)
|
|
120
|
+
admitted_candidates: list[float] = []
|
|
121
|
+
for value in new_candidates:
|
|
122
|
+
if _temperature_present(free_bank, float(value)):
|
|
123
|
+
updated_bank = _temperature_grid(np.append(updated_bank, float(value)))
|
|
124
|
+
admitted_candidates.append(float(value))
|
|
125
|
+
continue
|
|
126
|
+
if controller_added >= limit:
|
|
127
|
+
dropped_new_points.append(float(value))
|
|
128
|
+
continue
|
|
129
|
+
updated_bank = _temperature_grid(np.append(updated_bank, float(value)))
|
|
130
|
+
admitted_candidates.append(float(value))
|
|
131
|
+
controller_added += 1
|
|
132
|
+
|
|
133
|
+
active = current_active
|
|
134
|
+
if admitted_candidates:
|
|
135
|
+
active = _temperature_grid(np.concatenate((active, np.asarray(admitted_candidates, dtype=float))))
|
|
136
|
+
if active.size == 0:
|
|
137
|
+
active = current_active
|
|
138
|
+
return active, updated_bank, np.asarray(dropped_new_points, dtype=float)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _estimate_dynamiczoomwindow_accuracy(
|
|
142
|
+
temperatures: np.ndarray,
|
|
143
|
+
hubble: np.ndarray,
|
|
144
|
+
probabilities: np.ndarray,
|
|
145
|
+
actions: np.ndarray,
|
|
146
|
+
settings,
|
|
147
|
+
pot,
|
|
148
|
+
*,
|
|
149
|
+
previous: dict[str, float | None] | None = None,
|
|
150
|
+
) -> dict[str, float | None]:
|
|
151
|
+
"""Estimate Tperc, Tf and RH together with conservative convergence indicators."""
|
|
152
|
+
from transitionlistener import bubbledynamics as bd
|
|
153
|
+
|
|
154
|
+
estimate: dict[str, float | None] = {
|
|
155
|
+
"Tperc": math.nan,
|
|
156
|
+
"Tperc_uncertainty": math.inf,
|
|
157
|
+
"Tf": math.nan,
|
|
158
|
+
"Tf_uncertainty": math.inf,
|
|
159
|
+
"RH": math.nan,
|
|
160
|
+
"RH_uncertainty": math.inf,
|
|
161
|
+
"Tperc_bracket_width": math.inf,
|
|
162
|
+
"Tf_bracket_width": math.inf,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
Tperc, Tperc_width, _ = _find_value_crossing(temperatures, probabilities, settings.f_perc)
|
|
166
|
+
estimate["Tperc"] = math.nan if Tperc is None else float(Tperc)
|
|
167
|
+
estimate["Tperc_bracket_width"] = math.inf if Tperc_width is None else float(Tperc_width)
|
|
168
|
+
|
|
169
|
+
Tf_cross, Tf_width, _ = _find_value_crossing(temperatures, probabilities, settings.f_final)
|
|
170
|
+
estimate["Tf_bracket_width"] = math.inf if Tf_width is None else float(Tf_width)
|
|
171
|
+
|
|
172
|
+
temps = np.asarray(temperatures, dtype=float)
|
|
173
|
+
hubble_values = np.asarray(hubble, dtype=float)
|
|
174
|
+
probability_values = np.asarray(probabilities, dtype=float)
|
|
175
|
+
action_values = np.asarray(actions, dtype=float)
|
|
176
|
+
|
|
177
|
+
finite_prob = np.isfinite(probability_values)
|
|
178
|
+
finite_action = np.isfinite(action_values)
|
|
179
|
+
finite_hubble = np.isfinite(hubble_values)
|
|
180
|
+
if (
|
|
181
|
+
np.count_nonzero(finite_prob) >= 4
|
|
182
|
+
and np.count_nonzero(finite_action) >= 4
|
|
183
|
+
and np.count_nonzero(finite_hubble) >= 4
|
|
184
|
+
):
|
|
185
|
+
try:
|
|
186
|
+
Pint = interpolate.interp1d(
|
|
187
|
+
temps[finite_prob],
|
|
188
|
+
probability_values[finite_prob],
|
|
189
|
+
kind="cubic",
|
|
190
|
+
fill_value=(0.0, 1.0),
|
|
191
|
+
bounds_error=False,
|
|
192
|
+
)
|
|
193
|
+
Sint = interpolate.interp1d(
|
|
194
|
+
temps[finite_action],
|
|
195
|
+
action_values[finite_action],
|
|
196
|
+
kind="cubic",
|
|
197
|
+
fill_value=(-np.inf, np.inf),
|
|
198
|
+
bounds_error=False,
|
|
199
|
+
)
|
|
200
|
+
Hint = interpolate.interp1d(
|
|
201
|
+
temps[finite_hubble],
|
|
202
|
+
hubble_values[finite_hubble],
|
|
203
|
+
kind="cubic",
|
|
204
|
+
fill_value="extrapolate",
|
|
205
|
+
bounds_error=False,
|
|
206
|
+
)
|
|
207
|
+
except Exception:
|
|
208
|
+
Pint = Sint = Hint = None
|
|
209
|
+
else:
|
|
210
|
+
Pint = Sint = Hint = None
|
|
211
|
+
|
|
212
|
+
if Tf_cross is not None:
|
|
213
|
+
estimate["Tf"] = float(Tf_cross)
|
|
214
|
+
if Pint is not None and np.isfinite(float(estimate["Tperc"])):
|
|
215
|
+
try:
|
|
216
|
+
estimate["Tf"] = bd.calcTf(
|
|
217
|
+
float(estimate["Tperc"]),
|
|
218
|
+
float(temps[-1]),
|
|
219
|
+
Pint,
|
|
220
|
+
pot,
|
|
221
|
+
verbose=False,
|
|
222
|
+
)
|
|
223
|
+
except Exception:
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
if (
|
|
227
|
+
Pint is not None
|
|
228
|
+
and Sint is not None
|
|
229
|
+
and Hint is not None
|
|
230
|
+
and np.isfinite(float(estimate["Tperc"]))
|
|
231
|
+
):
|
|
232
|
+
try:
|
|
233
|
+
bubble_separation = bd.calcMeanBubbleSeparation(
|
|
234
|
+
float(estimate["Tperc"]),
|
|
235
|
+
float(temps[0]),
|
|
236
|
+
Sint,
|
|
237
|
+
Pint,
|
|
238
|
+
Hint,
|
|
239
|
+
None,
|
|
240
|
+
None,
|
|
241
|
+
verbose=False,
|
|
242
|
+
)
|
|
243
|
+
rh_indicator = float(bubble_separation * float(Hint(float(estimate["Tperc"]))))
|
|
244
|
+
if np.isfinite(rh_indicator) and rh_indicator > 0.0:
|
|
245
|
+
estimate["RH"] = rh_indicator
|
|
246
|
+
except Exception:
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
previous_Tperc = None if previous is None else previous.get("Tperc")
|
|
250
|
+
previous_Tf = None if previous is None else previous.get("Tf")
|
|
251
|
+
previous_RH = None if previous is None else previous.get("RH")
|
|
252
|
+
|
|
253
|
+
if np.isfinite(float(estimate["Tperc"])):
|
|
254
|
+
bracket_uncertainty = float(estimate["Tperc_bracket_width"]) / max(abs(float(estimate["Tperc"])), 1e-30)
|
|
255
|
+
if previous_Tperc is None or not np.isfinite(previous_Tperc) or not np.isfinite(float(estimate["Tperc"])):
|
|
256
|
+
estimate["Tperc_uncertainty"] = bracket_uncertainty
|
|
257
|
+
else:
|
|
258
|
+
estimate["Tperc_uncertainty"] = max(
|
|
259
|
+
bracket_uncertainty,
|
|
260
|
+
abs(float(estimate["Tperc"]) - float(previous_Tperc))
|
|
261
|
+
/ max(abs(float(estimate["Tperc"])), 1e-30),
|
|
262
|
+
)
|
|
263
|
+
if np.isfinite(float(estimate["Tf"])):
|
|
264
|
+
bracket_uncertainty = float(estimate["Tf_bracket_width"]) / max(abs(float(estimate["Tf"])), 1e-30)
|
|
265
|
+
if previous_Tf is None or not np.isfinite(previous_Tf) or not np.isfinite(float(estimate["Tf"])):
|
|
266
|
+
estimate["Tf_uncertainty"] = bracket_uncertainty
|
|
267
|
+
else:
|
|
268
|
+
estimate["Tf_uncertainty"] = max(
|
|
269
|
+
bracket_uncertainty,
|
|
270
|
+
abs(float(estimate["Tf"]) - float(previous_Tf))
|
|
271
|
+
/ max(abs(float(estimate["Tf"])), 1e-30),
|
|
272
|
+
)
|
|
273
|
+
if np.isfinite(float(estimate["RH"])):
|
|
274
|
+
estimate["RH_uncertainty"] = (
|
|
275
|
+
0.0
|
|
276
|
+
if previous_RH is None
|
|
277
|
+
or not np.isfinite(previous_RH)
|
|
278
|
+
or not np.isfinite(float(estimate["RH"]))
|
|
279
|
+
else abs(float(estimate["RH"]) - float(previous_RH))
|
|
280
|
+
/ max(abs(float(estimate["RH"])), 1e-30)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
return estimate
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _dynamiczoomwindow_accuracy_reached(
|
|
287
|
+
estimate: dict[str, float | None],
|
|
288
|
+
settings,
|
|
289
|
+
) -> bool:
|
|
290
|
+
"""Return whether all requested default-path accuracy targets were met."""
|
|
291
|
+
return (
|
|
292
|
+
np.isfinite(float(estimate["Tperc"]))
|
|
293
|
+
and np.isfinite(float(estimate["Tf"]))
|
|
294
|
+
and np.isfinite(float(estimate["RH"]))
|
|
295
|
+
and float(estimate["Tperc_uncertainty"]) <= settings.acc_tperc
|
|
296
|
+
and float(estimate["Tf_uncertainty"]) <= settings.acc_tfinal
|
|
297
|
+
and float(estimate["RH_uncertainty"]) <= settings.acc_rh
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _dynamiczoomwindow_accuracy_warning_message(
|
|
302
|
+
stage: str,
|
|
303
|
+
estimate: dict[str, float | None],
|
|
304
|
+
settings,
|
|
305
|
+
) -> str:
|
|
306
|
+
"""Describe which accuracy goals prevented further early stopping."""
|
|
307
|
+
reasons: list[str] = []
|
|
308
|
+
if not np.isfinite(float(estimate["Tperc"])):
|
|
309
|
+
reasons.append("Tperc could not be bracketed")
|
|
310
|
+
elif float(estimate["Tperc_uncertainty"]) > settings.acc_tperc:
|
|
311
|
+
reasons.append(
|
|
312
|
+
f"Tperc uncertainty {100.0 * float(estimate['Tperc_uncertainty']):2.3g}% > "
|
|
313
|
+
f"{100.0 * settings.acc_tperc:2.3g}%"
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if not np.isfinite(float(estimate["Tf"])):
|
|
317
|
+
reasons.append("Tf could not be determined")
|
|
318
|
+
elif float(estimate["Tf_uncertainty"]) > settings.acc_tfinal:
|
|
319
|
+
reasons.append(
|
|
320
|
+
f"Tf uncertainty {100.0 * float(estimate['Tf_uncertainty']):2.3g}% > "
|
|
321
|
+
f"{100.0 * settings.acc_tfinal:2.3g}%"
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
if not np.isfinite(float(estimate["RH"])):
|
|
325
|
+
reasons.append("RH could not be determined")
|
|
326
|
+
elif float(estimate["RH_uncertainty"]) > settings.acc_rh:
|
|
327
|
+
reasons.append(
|
|
328
|
+
f"RH uncertainty {100.0 * float(estimate['RH_uncertainty']):2.3g}% > "
|
|
329
|
+
f"{100.0 * settings.acc_rh:2.3g}%"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
if not reasons:
|
|
333
|
+
reasons.append("the requested accuracy could not be certified")
|
|
334
|
+
return (
|
|
335
|
+
f"Warning: {stage} stopped before reaching all requested accuracy goals. "
|
|
336
|
+
f"Increasing n_action_max above {settings.n_action_max} might help. "
|
|
337
|
+
"Remaining issue(s): " + "; ".join(reasons) + "."
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _try_dynamiczoomwindow_local_refinement(
|
|
342
|
+
temperatures: np.ndarray,
|
|
343
|
+
hubble: np.ndarray,
|
|
344
|
+
probabilities: np.ndarray,
|
|
345
|
+
actions: np.ndarray,
|
|
346
|
+
settings,
|
|
347
|
+
*,
|
|
348
|
+
remaining_budget: int,
|
|
349
|
+
vw: float,
|
|
350
|
+
apply_candidate_grid,
|
|
351
|
+
) -> tuple[bool, int]:
|
|
352
|
+
"""Spend one refinement batch on the most local unresolved feature."""
|
|
353
|
+
if remaining_budget <= 0:
|
|
354
|
+
return False, int(remaining_budget)
|
|
355
|
+
|
|
356
|
+
batch = min(int(settings.n_action_increment), int(remaining_budget))
|
|
357
|
+
hot_shoulder_grid, _ = _build_dynamiczoomwindow_hot_shoulder_grid(
|
|
358
|
+
temperatures,
|
|
359
|
+
probabilities,
|
|
360
|
+
actions,
|
|
361
|
+
hubble,
|
|
362
|
+
settings,
|
|
363
|
+
max_new_points=batch,
|
|
364
|
+
)
|
|
365
|
+
if hot_shoulder_grid is not None and apply_candidate_grid(hot_shoulder_grid, "hot_shoulder_refine"):
|
|
366
|
+
return True, int(remaining_budget)
|
|
367
|
+
|
|
368
|
+
jump_grid = _build_dynamiczoomwindow_unresolved_jump_grid(
|
|
369
|
+
temperatures,
|
|
370
|
+
probabilities,
|
|
371
|
+
settings,
|
|
372
|
+
max_new_points=int(remaining_budget),
|
|
373
|
+
actions=actions,
|
|
374
|
+
hubble=hubble,
|
|
375
|
+
vw=vw,
|
|
376
|
+
)
|
|
377
|
+
if jump_grid is not None and apply_candidate_grid(jump_grid, "jump_refine"):
|
|
378
|
+
return True, int(remaining_budget)
|
|
379
|
+
|
|
380
|
+
return False, int(remaining_budget)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _try_dynamiczoomwindow_completed_refinement(
|
|
384
|
+
temperatures: np.ndarray,
|
|
385
|
+
hubble: np.ndarray,
|
|
386
|
+
probabilities: np.ndarray,
|
|
387
|
+
actions: np.ndarray,
|
|
388
|
+
settings,
|
|
389
|
+
pot,
|
|
390
|
+
*,
|
|
391
|
+
remaining_budget: int,
|
|
392
|
+
previous_estimate: dict[str, float | None] | None,
|
|
393
|
+
iterator: int,
|
|
394
|
+
stage: str,
|
|
395
|
+
vw: float,
|
|
396
|
+
apply_candidate_grid,
|
|
397
|
+
promote_cached_action_points,
|
|
398
|
+
) -> tuple[bool, dict[str, float | None] | None]:
|
|
399
|
+
"""Run the simplified remeshing order once the current range brackets the transition.
|
|
400
|
+
|
|
401
|
+
The simplified controller still gives priority to the most obviously
|
|
402
|
+
untrustworthy local feature before rebuilding the general anchor mesh:
|
|
403
|
+
|
|
404
|
+
1. repair a still-underresolved hot-shoulder interval directly,
|
|
405
|
+
2. repair a still-large neighbouring ``Delta P`` jump directly,
|
|
406
|
+
3. otherwise rebuild the next support batch from fixed probability anchors
|
|
407
|
+
plus the largest remaining neighbouring ``Delta P`` violations.
|
|
408
|
+
"""
|
|
409
|
+
if iterator < settings.maxit:
|
|
410
|
+
refined, remaining_budget = _try_dynamiczoomwindow_local_refinement(
|
|
411
|
+
temperatures,
|
|
412
|
+
hubble,
|
|
413
|
+
probabilities,
|
|
414
|
+
actions,
|
|
415
|
+
settings,
|
|
416
|
+
remaining_budget=remaining_budget,
|
|
417
|
+
vw=vw,
|
|
418
|
+
apply_candidate_grid=apply_candidate_grid,
|
|
419
|
+
)
|
|
420
|
+
if refined:
|
|
421
|
+
return True, None
|
|
422
|
+
|
|
423
|
+
remesh_grid, remesh_reason = _build_dynamiczoomwindow_anchor_remesh_grid(
|
|
424
|
+
temperatures,
|
|
425
|
+
probabilities,
|
|
426
|
+
settings,
|
|
427
|
+
max_new_points=min(int(settings.n_action_increment), int(remaining_budget)) if remaining_budget > 0 else 0,
|
|
428
|
+
actions=actions,
|
|
429
|
+
hubble=hubble,
|
|
430
|
+
vw=vw,
|
|
431
|
+
)
|
|
432
|
+
if remesh_grid is not None and remesh_reason is not None and iterator < settings.maxit and remaining_budget > 0:
|
|
433
|
+
if apply_candidate_grid(remesh_grid, "anchor_remesh"):
|
|
434
|
+
return True, None
|
|
435
|
+
|
|
436
|
+
estimate = _estimate_dynamiczoomwindow_accuracy(
|
|
437
|
+
temperatures,
|
|
438
|
+
hubble,
|
|
439
|
+
probabilities,
|
|
440
|
+
actions,
|
|
441
|
+
settings,
|
|
442
|
+
pot,
|
|
443
|
+
previous=previous_estimate,
|
|
444
|
+
)
|
|
445
|
+
if promote_cached_action_points("cached_action_points"):
|
|
446
|
+
return True, estimate
|
|
447
|
+
if previous_estimate is not None and _dynamiczoomwindow_accuracy_reached(estimate, settings):
|
|
448
|
+
return False, estimate
|
|
449
|
+
|
|
450
|
+
if previous_estimate is not None or remaining_budget <= 0:
|
|
451
|
+
print(_dynamiczoomwindow_accuracy_warning_message(stage, estimate, settings))
|
|
452
|
+
return False, estimate
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def _try_dynamiczoomwindow_ordered_support_refinement(
|
|
456
|
+
temperatures: np.ndarray,
|
|
457
|
+
hubble: np.ndarray,
|
|
458
|
+
probabilities: np.ndarray,
|
|
459
|
+
actions: np.ndarray,
|
|
460
|
+
settings,
|
|
461
|
+
*,
|
|
462
|
+
remaining_budget: int,
|
|
463
|
+
vw: float,
|
|
464
|
+
hot_bound: float,
|
|
465
|
+
hot_head_needs_support: bool,
|
|
466
|
+
apply_candidate_grid,
|
|
467
|
+
) -> tuple[bool, int]:
|
|
468
|
+
"""Try the shared adaptive step size support-refinement order before range expansion."""
|
|
469
|
+
|
|
470
|
+
refined_locally, remaining_budget = _try_dynamiczoomwindow_local_refinement(
|
|
471
|
+
temperatures,
|
|
472
|
+
hubble,
|
|
473
|
+
probabilities,
|
|
474
|
+
actions,
|
|
475
|
+
settings,
|
|
476
|
+
remaining_budget=remaining_budget,
|
|
477
|
+
vw=vw,
|
|
478
|
+
apply_candidate_grid=apply_candidate_grid,
|
|
479
|
+
)
|
|
480
|
+
if refined_locally or remaining_budget <= 0:
|
|
481
|
+
return bool(refined_locally), int(remaining_budget)
|
|
482
|
+
|
|
483
|
+
if hot_head_needs_support:
|
|
484
|
+
hot_extend_high = float(temperatures[0]) + (float(hot_bound) - float(temperatures[0])) * max(
|
|
485
|
+
float(settings.rel_increment),
|
|
486
|
+
0.35,
|
|
487
|
+
)
|
|
488
|
+
hot_head_grid, _ = _build_dynamiczoomwindow_hot_head_grid(
|
|
489
|
+
temperatures,
|
|
490
|
+
probabilities,
|
|
491
|
+
actions,
|
|
492
|
+
hubble,
|
|
493
|
+
settings,
|
|
494
|
+
new_high=min(float(hot_bound), float(hot_extend_high)),
|
|
495
|
+
max_new_points=min(int(settings.n_action_increment), int(remaining_budget)),
|
|
496
|
+
)
|
|
497
|
+
if hot_head_grid is not None and apply_candidate_grid(hot_head_grid, "hot_head_refine"):
|
|
498
|
+
return True, int(remaining_budget)
|
|
499
|
+
|
|
500
|
+
cold_tail_unresolved = bool(probabilities[-1] < settings.f_final)
|
|
501
|
+
if cold_tail_unresolved and remaining_budget > 0:
|
|
502
|
+
rate_peak_grid, _ = _build_dynamiczoomwindow_rate_peak_grid(
|
|
503
|
+
temperatures,
|
|
504
|
+
actions,
|
|
505
|
+
hubble,
|
|
506
|
+
settings,
|
|
507
|
+
max_new_points=min(int(settings.n_action_increment), int(remaining_budget)),
|
|
508
|
+
)
|
|
509
|
+
if rate_peak_grid is not None and apply_candidate_grid(rate_peak_grid, "rate_peak_refine"):
|
|
510
|
+
return True, int(remaining_budget)
|
|
511
|
+
|
|
512
|
+
if probabilities[0] <= settings.f_start and probabilities[-1] >= settings.f_perc and not hot_head_needs_support:
|
|
513
|
+
remesh_grid, _ = _build_dynamiczoomwindow_anchor_remesh_grid(
|
|
514
|
+
temperatures,
|
|
515
|
+
probabilities,
|
|
516
|
+
settings,
|
|
517
|
+
max_new_points=min(int(settings.n_action_increment), int(remaining_budget)),
|
|
518
|
+
actions=actions,
|
|
519
|
+
hubble=hubble,
|
|
520
|
+
vw=vw,
|
|
521
|
+
)
|
|
522
|
+
if remesh_grid is not None and apply_candidate_grid(remesh_grid, "anchor_remesh"):
|
|
523
|
+
return True, int(remaining_budget)
|
|
524
|
+
|
|
525
|
+
return False, int(remaining_budget)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def _dynamiczoomwindow_post_sweep_controller(
|
|
529
|
+
temperatures: np.ndarray,
|
|
530
|
+
hubble: np.ndarray,
|
|
531
|
+
probabilities: np.ndarray,
|
|
532
|
+
actions: np.ndarray,
|
|
533
|
+
settings,
|
|
534
|
+
pot,
|
|
535
|
+
*,
|
|
536
|
+
stage: str,
|
|
537
|
+
iterator: int,
|
|
538
|
+
remaining_budget: int,
|
|
539
|
+
previous_estimate: dict[str, float | None] | None,
|
|
540
|
+
vw: float,
|
|
541
|
+
hot_bound: float,
|
|
542
|
+
apply_candidate_grid,
|
|
543
|
+
promote_cached_action_points,
|
|
544
|
+
promote_hot_head_cached_points,
|
|
545
|
+
profile_residual: float = 0.0,
|
|
546
|
+
profile_rho_tol: float = math.inf,
|
|
547
|
+
verbose: bool = False,
|
|
548
|
+
) -> tuple[str, dict[str, float | None] | None, int, bool]:
|
|
549
|
+
"""Shared adaptive step size controller after one computed P(T) sweep.
|
|
550
|
+
|
|
551
|
+
The stage-specific code is responsible for computing the profile. Once the
|
|
552
|
+
arrays exist, both step 2 and step 3 follow the same support-policy order:
|
|
553
|
+
repair hot onset, accept/refine completed profiles, stop on budget/iteration
|
|
554
|
+
limits, try local support refinement, otherwise ask the caller to range
|
|
555
|
+
expand.
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
hot_head_needs_support, _, _ = _hot_head_underresolved(
|
|
559
|
+
temperatures,
|
|
560
|
+
probabilities,
|
|
561
|
+
actions,
|
|
562
|
+
hubble,
|
|
563
|
+
settings,
|
|
564
|
+
for_acceptance=True,
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
if hot_head_needs_support and promote_hot_head_cached_points():
|
|
568
|
+
return "retry", None, int(remaining_budget), hot_head_needs_support
|
|
569
|
+
if promote_cached_action_points("cached_action_points"):
|
|
570
|
+
return "retry", None, int(remaining_budget), hot_head_needs_support
|
|
571
|
+
|
|
572
|
+
completed = (
|
|
573
|
+
float(probabilities[0]) <= float(settings.f_start)
|
|
574
|
+
and float(probabilities[-1]) >= float(settings.f_final)
|
|
575
|
+
and not hot_head_needs_support
|
|
576
|
+
)
|
|
577
|
+
if completed:
|
|
578
|
+
refinement_previous_estimate = previous_estimate if profile_residual <= profile_rho_tol else None
|
|
579
|
+
if profile_residual > profile_rho_tol and verbose:
|
|
580
|
+
print(
|
|
581
|
+
f"{stage} profile residual remains above tolerance even after same-grid "
|
|
582
|
+
"Picard sweeps. Spending any remaining controller budget on support "
|
|
583
|
+
"refinement before accepting the profile."
|
|
584
|
+
)
|
|
585
|
+
should_retry, previous_estimate = _try_dynamiczoomwindow_completed_refinement(
|
|
586
|
+
temperatures,
|
|
587
|
+
hubble,
|
|
588
|
+
probabilities,
|
|
589
|
+
actions,
|
|
590
|
+
settings,
|
|
591
|
+
pot,
|
|
592
|
+
remaining_budget=remaining_budget,
|
|
593
|
+
previous_estimate=refinement_previous_estimate,
|
|
594
|
+
iterator=iterator,
|
|
595
|
+
stage=stage,
|
|
596
|
+
vw=vw,
|
|
597
|
+
apply_candidate_grid=apply_candidate_grid,
|
|
598
|
+
promote_cached_action_points=promote_cached_action_points,
|
|
599
|
+
)
|
|
600
|
+
if should_retry:
|
|
601
|
+
return "retry", previous_estimate, int(remaining_budget), hot_head_needs_support
|
|
602
|
+
return "accept", previous_estimate, int(remaining_budget), hot_head_needs_support
|
|
603
|
+
|
|
604
|
+
if iterator >= settings.maxit or remaining_budget <= 0:
|
|
605
|
+
print(
|
|
606
|
+
f"Warning: {stage} reached the support-update limit before the full "
|
|
607
|
+
"completion range was resolved. Continuing with the best available grid."
|
|
608
|
+
)
|
|
609
|
+
return "limit", previous_estimate, int(remaining_budget), hot_head_needs_support
|
|
610
|
+
|
|
611
|
+
refined, remaining_budget = _try_dynamiczoomwindow_ordered_support_refinement(
|
|
612
|
+
temperatures,
|
|
613
|
+
hubble,
|
|
614
|
+
probabilities,
|
|
615
|
+
actions,
|
|
616
|
+
settings,
|
|
617
|
+
remaining_budget=remaining_budget,
|
|
618
|
+
vw=vw,
|
|
619
|
+
hot_bound=hot_bound,
|
|
620
|
+
hot_head_needs_support=hot_head_needs_support,
|
|
621
|
+
apply_candidate_grid=apply_candidate_grid,
|
|
622
|
+
)
|
|
623
|
+
if refined:
|
|
624
|
+
return "retry", None, int(remaining_budget), hot_head_needs_support
|
|
625
|
+
|
|
626
|
+
return "range_expand", previous_estimate, int(remaining_budget), hot_head_needs_support
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def _dynamiczoomwindow_range_targets(
|
|
630
|
+
temperatures: np.ndarray,
|
|
631
|
+
probabilities: np.ndarray,
|
|
632
|
+
actions: np.ndarray,
|
|
633
|
+
hubble: np.ndarray,
|
|
634
|
+
settings,
|
|
635
|
+
*,
|
|
636
|
+
target_low: float,
|
|
637
|
+
target_high: float,
|
|
638
|
+
iterator: int,
|
|
639
|
+
hot_head_needs_support: bool,
|
|
640
|
+
maxit_margin: int,
|
|
641
|
+
cold_tail_last_temperature: float | None,
|
|
642
|
+
cold_tail_last_probability: float | None,
|
|
643
|
+
cold_tail_last_integral: float | None,
|
|
644
|
+
cold_tail_last_log_gamma_h4: float | None,
|
|
645
|
+
cold_tail_post_peak_streak: int,
|
|
646
|
+
) -> tuple[float, float, float | None, float | None, float | None, float | None, int, bool]:
|
|
647
|
+
"""Choose the next range-expansion bounds for the shared adaptive step size controller."""
|
|
648
|
+
|
|
649
|
+
temperatures = np.asarray(temperatures, dtype=float)
|
|
650
|
+
probabilities = np.asarray(probabilities, dtype=float)
|
|
651
|
+
actions = np.asarray(actions, dtype=float)
|
|
652
|
+
hubble = np.asarray(hubble, dtype=float)
|
|
653
|
+
cold_tail_unresolved = bool(probabilities[-1] < settings.f_final)
|
|
654
|
+
|
|
655
|
+
if (probabilities[0] > settings.f_start or hot_head_needs_support) and not cold_tail_unresolved:
|
|
656
|
+
high = float(temperatures[0]) + (float(target_high) - float(temperatures[0])) * settings.rel_increment
|
|
657
|
+
else:
|
|
658
|
+
high = float(temperatures[0])
|
|
659
|
+
|
|
660
|
+
if not cold_tail_unresolved:
|
|
661
|
+
return (
|
|
662
|
+
high,
|
|
663
|
+
float(temperatures[-1]),
|
|
664
|
+
cold_tail_last_temperature,
|
|
665
|
+
cold_tail_last_probability,
|
|
666
|
+
cold_tail_last_integral,
|
|
667
|
+
cold_tail_last_log_gamma_h4,
|
|
668
|
+
int(cold_tail_post_peak_streak),
|
|
669
|
+
False,
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
if iterator >= settings.maxit - int(maxit_margin):
|
|
673
|
+
low = float(target_low)
|
|
674
|
+
else:
|
|
675
|
+
low, current_integral, current_log_gamma_h4, cold_tail_post_peak_streak = _adaptive_cold_boundary(
|
|
676
|
+
current_low=float(temperatures[-1]),
|
|
677
|
+
target_low=float(target_low),
|
|
678
|
+
current_probability=float(probabilities[-1]),
|
|
679
|
+
current_action=float(actions[-1]),
|
|
680
|
+
current_hubble=float(hubble[-1]),
|
|
681
|
+
settings=settings,
|
|
682
|
+
last_temperature=cold_tail_last_temperature,
|
|
683
|
+
last_probability=cold_tail_last_probability,
|
|
684
|
+
last_integral=cold_tail_last_integral,
|
|
685
|
+
last_log_gamma_h4=cold_tail_last_log_gamma_h4,
|
|
686
|
+
post_peak_streak=cold_tail_post_peak_streak,
|
|
687
|
+
)
|
|
688
|
+
cold_tail_last_temperature = float(temperatures[-1])
|
|
689
|
+
cold_tail_last_probability = float(probabilities[-1])
|
|
690
|
+
cold_tail_last_integral = float(current_integral)
|
|
691
|
+
cold_tail_last_log_gamma_h4 = current_log_gamma_h4
|
|
692
|
+
low = min(float(low), float(temperatures[-1]))
|
|
693
|
+
|
|
694
|
+
return (
|
|
695
|
+
high,
|
|
696
|
+
low,
|
|
697
|
+
cold_tail_last_temperature,
|
|
698
|
+
cold_tail_last_probability,
|
|
699
|
+
cold_tail_last_integral,
|
|
700
|
+
cold_tail_last_log_gamma_h4,
|
|
701
|
+
int(cold_tail_post_peak_streak),
|
|
702
|
+
True,
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def _apply_dynamiczoomwindow_candidate_grid(
|
|
707
|
+
active_grid: np.ndarray,
|
|
708
|
+
candidate_grid: np.ndarray,
|
|
709
|
+
support_bank: np.ndarray,
|
|
710
|
+
free_support_bank: np.ndarray,
|
|
711
|
+
settings,
|
|
712
|
+
outdict: dict,
|
|
713
|
+
phase_symmetric,
|
|
714
|
+
phase_broken,
|
|
715
|
+
*,
|
|
716
|
+
tmin: float,
|
|
717
|
+
tmax: float,
|
|
718
|
+
stage: str,
|
|
719
|
+
reset_arrays,
|
|
720
|
+
note_rebuild,
|
|
721
|
+
extra_budget: int = 0,
|
|
722
|
+
) -> tuple[bool, np.ndarray, np.ndarray, np.ndarray]:
|
|
723
|
+
"""Refresh the cached support bank and merge one candidate refinement grid."""
|
|
724
|
+
from transitionlistener import bubbledynamics as bd
|
|
725
|
+
|
|
726
|
+
old_grid = np.asarray(active_grid, dtype=float).copy()
|
|
727
|
+
cached_points = bd._transition_action_outdict_temperatures(
|
|
728
|
+
outdict,
|
|
729
|
+
phase_symmetric,
|
|
730
|
+
phase_broken,
|
|
731
|
+
tmin=tmin,
|
|
732
|
+
tmax=tmax,
|
|
733
|
+
)
|
|
734
|
+
if cached_points.size > 0:
|
|
735
|
+
free_support_bank = _temperature_grid(
|
|
736
|
+
np.concatenate((free_support_bank, cached_points))
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
new_active, updated_bank, dropped_new_points = _apply_dynamiczoomwindow_support_update(
|
|
740
|
+
active_grid,
|
|
741
|
+
candidate_grid,
|
|
742
|
+
support_bank,
|
|
743
|
+
free_support_bank,
|
|
744
|
+
settings,
|
|
745
|
+
max_new_points=int(settings.n_action_max) + max(int(extra_budget), 0),
|
|
746
|
+
)
|
|
747
|
+
if dropped_new_points.size > 0:
|
|
748
|
+
print(
|
|
749
|
+
f"Warning: n_action_max = {settings.n_action_max} blocked "
|
|
750
|
+
f"{len(dropped_new_points)} new controller-added support points during {stage}."
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
if (
|
|
754
|
+
np.asarray(new_active, dtype=float).shape == np.asarray(active_grid, dtype=float).shape
|
|
755
|
+
and (
|
|
756
|
+
np.asarray(new_active, dtype=float).size == 0
|
|
757
|
+
or np.allclose(
|
|
758
|
+
np.asarray(new_active, dtype=float),
|
|
759
|
+
np.asarray(active_grid, dtype=float),
|
|
760
|
+
rtol=0.0,
|
|
761
|
+
atol=1e-14,
|
|
762
|
+
)
|
|
763
|
+
)
|
|
764
|
+
):
|
|
765
|
+
return False, active_grid, updated_bank, free_support_bank
|
|
766
|
+
|
|
767
|
+
reset_arrays(new_active)
|
|
768
|
+
note_rebuild(old_grid, new_active)
|
|
769
|
+
return True, new_active, updated_bank, free_support_bank
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def _promote_dynamiczoomwindow_cached_points(
|
|
773
|
+
active_grid: np.ndarray,
|
|
774
|
+
support_bank: np.ndarray,
|
|
775
|
+
free_support_bank: np.ndarray,
|
|
776
|
+
settings,
|
|
777
|
+
outdict: dict,
|
|
778
|
+
phase_symmetric,
|
|
779
|
+
phase_broken,
|
|
780
|
+
*,
|
|
781
|
+
tmin: float,
|
|
782
|
+
tmax: float,
|
|
783
|
+
promotion_high: float | None = None,
|
|
784
|
+
promotion_low: float | None = None,
|
|
785
|
+
stage: str,
|
|
786
|
+
reason: str,
|
|
787
|
+
reset_arrays,
|
|
788
|
+
note_rebuild,
|
|
789
|
+
) -> tuple[bool, np.ndarray, np.ndarray, np.ndarray]:
|
|
790
|
+
"""Promote newly cached action temperatures into the active support bank."""
|
|
791
|
+
from transitionlistener import bubbledynamics as bd
|
|
792
|
+
|
|
793
|
+
cached_points = bd._transition_action_outdict_temperatures(
|
|
794
|
+
outdict,
|
|
795
|
+
phase_symmetric,
|
|
796
|
+
phase_broken,
|
|
797
|
+
tmin=tmin,
|
|
798
|
+
tmax=tmax,
|
|
799
|
+
)
|
|
800
|
+
pending_points = np.asarray(
|
|
801
|
+
[
|
|
802
|
+
float(value)
|
|
803
|
+
for value in _temperature_grid(cached_points)
|
|
804
|
+
if not _temperature_present(support_bank, float(value))
|
|
805
|
+
],
|
|
806
|
+
dtype=float,
|
|
807
|
+
)
|
|
808
|
+
if (
|
|
809
|
+
pending_points.size > 0
|
|
810
|
+
and promotion_high is not None
|
|
811
|
+
and promotion_low is not None
|
|
812
|
+
and np.isfinite(float(promotion_high))
|
|
813
|
+
and np.isfinite(float(promotion_low))
|
|
814
|
+
):
|
|
815
|
+
high = float(max(promotion_high, promotion_low))
|
|
816
|
+
low = float(min(promotion_high, promotion_low))
|
|
817
|
+
scale = max(abs(high), abs(low), 1.0)
|
|
818
|
+
atol = 1e-10 * scale
|
|
819
|
+
pending_points = np.asarray(
|
|
820
|
+
[
|
|
821
|
+
float(value)
|
|
822
|
+
for value in np.asarray(pending_points, dtype=float)
|
|
823
|
+
if low - atol <= float(value) <= high + atol
|
|
824
|
+
],
|
|
825
|
+
dtype=float,
|
|
826
|
+
)
|
|
827
|
+
pending_points = _temperature_grid(pending_points)
|
|
828
|
+
if pending_points.size == 0:
|
|
829
|
+
return False, active_grid, support_bank, free_support_bank
|
|
830
|
+
|
|
831
|
+
return _apply_dynamiczoomwindow_candidate_grid(
|
|
832
|
+
active_grid,
|
|
833
|
+
pending_points,
|
|
834
|
+
support_bank,
|
|
835
|
+
free_support_bank,
|
|
836
|
+
settings,
|
|
837
|
+
outdict,
|
|
838
|
+
phase_symmetric,
|
|
839
|
+
phase_broken,
|
|
840
|
+
tmin=tmin,
|
|
841
|
+
tmax=tmax,
|
|
842
|
+
stage=stage,
|
|
843
|
+
reset_arrays=reset_arrays,
|
|
844
|
+
note_rebuild=lambda old_grid, new_grid: note_rebuild(reason, old_grid, new_grid),
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def _compute_step2_profile(
|
|
849
|
+
state: PercolationState,
|
|
850
|
+
TSYM: np.ndarray,
|
|
851
|
+
previous_probability: np.ndarray,
|
|
852
|
+
settings,
|
|
853
|
+
outdict: dict,
|
|
854
|
+
pot,
|
|
855
|
+
phase_symmetric,
|
|
856
|
+
phase_broken,
|
|
857
|
+
CF: float,
|
|
858
|
+
vw: float,
|
|
859
|
+
verbose: bool,
|
|
860
|
+
) -> None:
|
|
861
|
+
"""Compute the P=0-Hubble profile used by adaptive step size, percolation step 2."""
|
|
862
|
+
from transitionlistener import bubbledynamics as bd
|
|
863
|
+
|
|
864
|
+
TSYM = np.asarray(TSYM, dtype=float)
|
|
865
|
+
previous_probability = np.asarray(previous_probability, dtype=float)
|
|
866
|
+
state.Sr.fill(0)
|
|
867
|
+
state.Hr.fill(0)
|
|
868
|
+
state.Pr.fill(0)
|
|
869
|
+
state.Tb.fill(0)
|
|
870
|
+
action_tail_saturated = False
|
|
871
|
+
|
|
872
|
+
for i, T in enumerate(TSYM):
|
|
873
|
+
rho = bd.energyDensity(pot, phase_symmetric, T)
|
|
874
|
+
state.Hr[i] = bd.HubbleParameter(rho, CF)
|
|
875
|
+
state.Sr[i] = (
|
|
876
|
+
bd.calcAction(pot, T, phase_symmetric, phase_broken, outdict)
|
|
877
|
+
if not action_tail_saturated
|
|
878
|
+
else np.nan
|
|
879
|
+
)
|
|
880
|
+
if (
|
|
881
|
+
i >= 1
|
|
882
|
+
and previous_probability[i] > settings.f_final
|
|
883
|
+
and previous_probability[i - 1] > settings.f_final
|
|
884
|
+
):
|
|
885
|
+
if verbose and not action_tail_saturated:
|
|
886
|
+
print(
|
|
887
|
+
"WARNING: P > ",
|
|
888
|
+
settings.f_final,
|
|
889
|
+
" at T = ",
|
|
890
|
+
TSYM[i] * CF,
|
|
891
|
+
" GeV detected. Stop computation of action from here on "
|
|
892
|
+
"and only fill in the values for the Hubble rate.",
|
|
893
|
+
)
|
|
894
|
+
action_tail_saturated = True
|
|
895
|
+
|
|
896
|
+
_, Pr_ode = bd.percIntegralODE_full_sweep(
|
|
897
|
+
TSYM,
|
|
898
|
+
state.Hr,
|
|
899
|
+
state.Sr,
|
|
900
|
+
vw=vw,
|
|
901
|
+
pot=pot,
|
|
902
|
+
phase_symmetric=phase_symmetric,
|
|
903
|
+
time_temperature_mode=settings.time_temperature_mode,
|
|
904
|
+
integral_method=settings.integral_method,
|
|
905
|
+
)
|
|
906
|
+
state.Pr[:] = Pr_ode
|
|
907
|
+
|
|
908
|
+
if verbose:
|
|
909
|
+
for i, T in enumerate(TSYM):
|
|
910
|
+
print(
|
|
911
|
+
f"Step {i+1}/{len(TSYM)}, T = {TSYM[i] * CF:2.8g} GeV, "
|
|
912
|
+
f"P_true = {state.Pr[i]:2.5g}, S_3 = {state.Sr[i] * CF:2.5g} GeV, "
|
|
913
|
+
f"S_3/T = {state.Sr[i] / T:2.5g}"
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def _compute_step3_profile(
|
|
918
|
+
state: PercolationState,
|
|
919
|
+
TSYM: np.ndarray,
|
|
920
|
+
previous_probability: np.ndarray,
|
|
921
|
+
settings,
|
|
922
|
+
outdict: dict,
|
|
923
|
+
pot,
|
|
924
|
+
phase_symmetric,
|
|
925
|
+
phase_broken,
|
|
926
|
+
CF: float,
|
|
927
|
+
vw: float,
|
|
928
|
+
TBROmin: float,
|
|
929
|
+
TBROmax: float,
|
|
930
|
+
verbose: bool,
|
|
931
|
+
) -> None:
|
|
932
|
+
"""Compute the P-dependent Hubble/Tb profile used by adaptive step size, percolation step 3."""
|
|
933
|
+
from transitionlistener import bubbledynamics as bd
|
|
934
|
+
|
|
935
|
+
previous_probability = np.asarray(previous_probability, dtype=float)
|
|
936
|
+
state.Hr.fill(0)
|
|
937
|
+
state.Pr.fill(0)
|
|
938
|
+
state.Tb.fill(np.nan)
|
|
939
|
+
state.Sr.fill(0)
|
|
940
|
+
profile_saturated = False
|
|
941
|
+
|
|
942
|
+
for i, T in enumerate(TSYM):
|
|
943
|
+
if not profile_saturated:
|
|
944
|
+
eSYM = bd.energyDensity(pot, phase_symmetric, T)
|
|
945
|
+
state.Sr[i] = bd.calcAction(pot, T, phase_symmetric, phase_broken, outdict)
|
|
946
|
+
P = float(previous_probability[i]) if np.isfinite(previous_probability[i]) else 0.0
|
|
947
|
+
|
|
948
|
+
if P <= 1e-12:
|
|
949
|
+
state.Hr[i] = bd.HubbleParameter(eSYM, CF)
|
|
950
|
+
state.Tb[i] = np.nan
|
|
951
|
+
elif i == 0:
|
|
952
|
+
try:
|
|
953
|
+
TBRO = optimize.brentq(
|
|
954
|
+
bd.Tb_criterion,
|
|
955
|
+
TBROmin,
|
|
956
|
+
TBROmax,
|
|
957
|
+
args=(T, phase_symmetric, phase_broken, pot),
|
|
958
|
+
)
|
|
959
|
+
except ValueError as err:
|
|
960
|
+
raise errors.PercolationError(
|
|
961
|
+
"Error in Tb_criterion: "
|
|
962
|
+
f"{err}. The phase tracing or tunneling precision may be too low."
|
|
963
|
+
)
|
|
964
|
+
state.Tb[i] = TBRO
|
|
965
|
+
eBRO = bd.energyDensity(pot, phase_broken, TBRO)
|
|
966
|
+
state.Hr[i] = bd.HubbleParameter(P * eBRO + (1.0 - P) * eSYM, CF)
|
|
967
|
+
else:
|
|
968
|
+
P_prev = float(previous_probability[i - 1]) if np.isfinite(previous_probability[i - 1]) else 0.0
|
|
969
|
+
previous_tb = float(state.Tb[i - 1]) if np.isfinite(state.Tb[i - 1]) else np.nan
|
|
970
|
+
if P_prev < 1e-6 or not np.isfinite(previous_tb):
|
|
971
|
+
try:
|
|
972
|
+
TBRO = optimize.brentq(
|
|
973
|
+
bd.Tb_criterion,
|
|
974
|
+
TBROmin,
|
|
975
|
+
TBROmax,
|
|
976
|
+
args=(T, phase_symmetric, phase_broken, pot),
|
|
977
|
+
)
|
|
978
|
+
except ValueError as err:
|
|
979
|
+
if not np.isfinite(previous_tb):
|
|
980
|
+
raise errors.PercolationError(
|
|
981
|
+
"Error in Tb_criterion for the first nonzero-P support point: "
|
|
982
|
+
f"{err}. The hot pure-symmetric tail may be too close to the "
|
|
983
|
+
"broken-branch tracing boundary."
|
|
984
|
+
)
|
|
985
|
+
TBRO = optimize.brentq(
|
|
986
|
+
bd.entropy_criterion_SYM_BRO,
|
|
987
|
+
TBROmin,
|
|
988
|
+
TBROmax,
|
|
989
|
+
args=(previous_tb, T, TSYM[i - 1], phase_broken, pot),
|
|
990
|
+
)
|
|
991
|
+
else:
|
|
992
|
+
try:
|
|
993
|
+
TBRO = optimize.brentq(
|
|
994
|
+
bd.entropy_criterion_SYM_BRO,
|
|
995
|
+
TBROmin,
|
|
996
|
+
TBROmax,
|
|
997
|
+
args=(previous_tb, T, TSYM[i - 1], phase_broken, pot),
|
|
998
|
+
)
|
|
999
|
+
except ValueError:
|
|
1000
|
+
TBRO = optimize.brentq(
|
|
1001
|
+
bd.Tb_criterion,
|
|
1002
|
+
TBROmin,
|
|
1003
|
+
TBROmax,
|
|
1004
|
+
args=(T, phase_symmetric, phase_broken, pot),
|
|
1005
|
+
)
|
|
1006
|
+
eBRO = bd.energyDensity(pot, phase_broken, TBRO)
|
|
1007
|
+
|
|
1008
|
+
dP = P - P_prev
|
|
1009
|
+
energy_release = eBRO * (P - dP) + dP * eSYM
|
|
1010
|
+
if energy_release / eBRO < 1e-50:
|
|
1011
|
+
state.Tb[i] = TBRO
|
|
1012
|
+
else:
|
|
1013
|
+
try:
|
|
1014
|
+
TBRO = optimize.brentq(
|
|
1015
|
+
bd.energy_criterion_BRO,
|
|
1016
|
+
TBROmin,
|
|
1017
|
+
TBROmax,
|
|
1018
|
+
args=(eSYM, eBRO, P, dP, phase_broken, pot),
|
|
1019
|
+
)
|
|
1020
|
+
state.Tb[i] = TBRO
|
|
1021
|
+
except ValueError:
|
|
1022
|
+
state.Tb[i] = state.Tb[i - 1]
|
|
1023
|
+
TBRO = state.Tb[i]
|
|
1024
|
+
eBRO = bd.energyDensity(pot, phase_broken, TBRO)
|
|
1025
|
+
|
|
1026
|
+
if state.Tb[i] < TSYM[i]:
|
|
1027
|
+
raise ValueError(
|
|
1028
|
+
"There was a problem with the T_bro calculation, "
|
|
1029
|
+
"T_bro < T_sym. This is usually not allowed."
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1032
|
+
state.Hr[i] = bd.HubbleParameter(P * eBRO + (1.0 - P) * eSYM, CF)
|
|
1033
|
+
else:
|
|
1034
|
+
state.Sr[i] = np.nan
|
|
1035
|
+
state.Hr[i] = bd.HubbleParameter(bd.energyDensity(pot, phase_symmetric, T), CF)
|
|
1036
|
+
|
|
1037
|
+
if i >= 1 and previous_probability[i] > settings.f_final and previous_probability[i - 1] > settings.f_final:
|
|
1038
|
+
if verbose and not profile_saturated:
|
|
1039
|
+
print(
|
|
1040
|
+
"WARNING: P > ",
|
|
1041
|
+
settings.f_final,
|
|
1042
|
+
" at T = ",
|
|
1043
|
+
TSYM[i] * CF,
|
|
1044
|
+
" GeV detected. Stop computation of action from here on "
|
|
1045
|
+
"and only fill in the values for the Hubble rate.",
|
|
1046
|
+
)
|
|
1047
|
+
profile_saturated = True
|
|
1048
|
+
|
|
1049
|
+
_, Pr_ode = bd.percIntegralODE_full_sweep(
|
|
1050
|
+
TSYM,
|
|
1051
|
+
state.Hr,
|
|
1052
|
+
state.Sr,
|
|
1053
|
+
vw=vw,
|
|
1054
|
+
pot=pot,
|
|
1055
|
+
phase_symmetric=phase_symmetric,
|
|
1056
|
+
time_temperature_mode=settings.time_temperature_mode,
|
|
1057
|
+
integral_method=settings.integral_method,
|
|
1058
|
+
)
|
|
1059
|
+
state.Pr[:] = Pr_ode
|
|
1060
|
+
|
|
1061
|
+
if verbose:
|
|
1062
|
+
for i, T in enumerate(TSYM):
|
|
1063
|
+
print(
|
|
1064
|
+
f"Step {i+1}/{len(TSYM)}, T = {T * CF:2.8g} GeV, "
|
|
1065
|
+
f"P_true = {state.Pr[i]:2.5g}, S_3 = {state.Sr[i] * CF:2.5g} GeV, "
|
|
1066
|
+
f"S_3/T = {state.Sr[i] / T:2.5g}"
|
|
1067
|
+
)
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
def _initial_percolation_scan_dynamiczoomwindow(
|
|
1071
|
+
grid: PercolationGrid,
|
|
1072
|
+
settings,
|
|
1073
|
+
outdict: dict,
|
|
1074
|
+
pot,
|
|
1075
|
+
phase_symmetric,
|
|
1076
|
+
phase_broken,
|
|
1077
|
+
CF: float,
|
|
1078
|
+
vw: float,
|
|
1079
|
+
verbose: bool,
|
|
1080
|
+
) -> PercolationState:
|
|
1081
|
+
"""Dynamic zoom-window step 2 with a coarse scan and local window refinement."""
|
|
1082
|
+
from transitionlistener import bubbledynamics as bd
|
|
1083
|
+
|
|
1084
|
+
original_grid = _temperature_grid(grid.TSYM)
|
|
1085
|
+
TSYM = original_grid.copy()
|
|
1086
|
+
# Free support points are cached/scout action temperatures. They may be
|
|
1087
|
+
# promoted into the active grid later, but they do not consume the
|
|
1088
|
+
# controller's new-action budget.
|
|
1089
|
+
free_support_bank = (
|
|
1090
|
+
_temperature_grid(grid.free_support_bank)
|
|
1091
|
+
if grid.free_support_bank is not None
|
|
1092
|
+
else bd._transition_action_outdict_temperatures(
|
|
1093
|
+
outdict,
|
|
1094
|
+
phase_symmetric,
|
|
1095
|
+
phase_broken,
|
|
1096
|
+
tmin=grid.tmin,
|
|
1097
|
+
tmax=grid.tmax,
|
|
1098
|
+
)
|
|
1099
|
+
)
|
|
1100
|
+
state = PercolationState(
|
|
1101
|
+
TSYM=TSYM,
|
|
1102
|
+
Sr=np.zeros_like(TSYM),
|
|
1103
|
+
Hr=np.zeros_like(TSYM),
|
|
1104
|
+
Pr=np.zeros_like(TSYM),
|
|
1105
|
+
Tb=np.zeros_like(TSYM),
|
|
1106
|
+
explored_tmin=float(TSYM[-1]),
|
|
1107
|
+
rebuild_count=0,
|
|
1108
|
+
support_bank=_temperature_grid(TSYM),
|
|
1109
|
+
free_support_bank=free_support_bank,
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
iterator = 1
|
|
1113
|
+
previous_estimate = None
|
|
1114
|
+
|
|
1115
|
+
def _note_rebuild(reason: str, old_grid: np.ndarray, new_grid: np.ndarray) -> None:
|
|
1116
|
+
_log_support_rebuild(state, "step2", reason, old_grid, new_grid, verbose)
|
|
1117
|
+
|
|
1118
|
+
def _reset_active_arrays(new_grid: np.ndarray) -> None:
|
|
1119
|
+
nonlocal TSYM
|
|
1120
|
+
TSYM = np.asarray(new_grid, dtype=float)
|
|
1121
|
+
state.TSYM = TSYM
|
|
1122
|
+
state.Sr = np.zeros_like(TSYM)
|
|
1123
|
+
state.Hr = np.zeros_like(TSYM)
|
|
1124
|
+
state.Pr = np.zeros_like(TSYM)
|
|
1125
|
+
state.Tb = np.zeros_like(TSYM)
|
|
1126
|
+
|
|
1127
|
+
def _apply_candidate_grid(candidate_grid: np.ndarray, reason: str, *, extra_budget: int = 0) -> bool:
|
|
1128
|
+
nonlocal TSYM
|
|
1129
|
+
changed, TSYM, state.support_bank, state.free_support_bank = _apply_dynamiczoomwindow_candidate_grid(
|
|
1130
|
+
TSYM,
|
|
1131
|
+
candidate_grid,
|
|
1132
|
+
state.support_bank,
|
|
1133
|
+
state.free_support_bank,
|
|
1134
|
+
settings,
|
|
1135
|
+
outdict,
|
|
1136
|
+
phase_symmetric,
|
|
1137
|
+
phase_broken,
|
|
1138
|
+
tmin=grid.tmin,
|
|
1139
|
+
tmax=grid.tmax,
|
|
1140
|
+
stage="step 2",
|
|
1141
|
+
reset_arrays=_reset_active_arrays,
|
|
1142
|
+
note_rebuild=lambda old_grid, new_grid: _note_rebuild(reason, old_grid, new_grid),
|
|
1143
|
+
extra_budget=extra_budget,
|
|
1144
|
+
)
|
|
1145
|
+
return changed
|
|
1146
|
+
|
|
1147
|
+
def _promote_cached_action_points(reason: str) -> bool:
|
|
1148
|
+
nonlocal TSYM
|
|
1149
|
+
promotion_bounds = _cached_promotion_bounds(TSYM)
|
|
1150
|
+
changed, TSYM, state.support_bank, state.free_support_bank = _promote_dynamiczoomwindow_cached_points(
|
|
1151
|
+
TSYM,
|
|
1152
|
+
state.support_bank,
|
|
1153
|
+
state.free_support_bank,
|
|
1154
|
+
settings,
|
|
1155
|
+
outdict,
|
|
1156
|
+
phase_symmetric,
|
|
1157
|
+
phase_broken,
|
|
1158
|
+
tmin=grid.tmin,
|
|
1159
|
+
tmax=grid.tmax,
|
|
1160
|
+
promotion_high=None if promotion_bounds is None else promotion_bounds[0],
|
|
1161
|
+
promotion_low=None if promotion_bounds is None else promotion_bounds[1],
|
|
1162
|
+
stage="step 2",
|
|
1163
|
+
reason=reason,
|
|
1164
|
+
reset_arrays=_reset_active_arrays,
|
|
1165
|
+
note_rebuild=_note_rebuild,
|
|
1166
|
+
)
|
|
1167
|
+
return changed
|
|
1168
|
+
|
|
1169
|
+
def _promote_hot_head_cached_points() -> bool:
|
|
1170
|
+
nonlocal TSYM
|
|
1171
|
+
changed, TSYM, state.support_bank, state.free_support_bank = _promote_dynamiczoomwindow_cached_points(
|
|
1172
|
+
TSYM,
|
|
1173
|
+
state.support_bank,
|
|
1174
|
+
state.free_support_bank,
|
|
1175
|
+
settings,
|
|
1176
|
+
outdict,
|
|
1177
|
+
phase_symmetric,
|
|
1178
|
+
phase_broken,
|
|
1179
|
+
tmin=grid.tmin,
|
|
1180
|
+
tmax=grid.tmax,
|
|
1181
|
+
promotion_high=float(grid.tmax),
|
|
1182
|
+
promotion_low=float(TSYM[0]),
|
|
1183
|
+
stage="step 2",
|
|
1184
|
+
reason="hot_head_cached_points",
|
|
1185
|
+
reset_arrays=_reset_active_arrays,
|
|
1186
|
+
note_rebuild=_note_rebuild,
|
|
1187
|
+
)
|
|
1188
|
+
return changed
|
|
1189
|
+
|
|
1190
|
+
if verbose:
|
|
1191
|
+
print("\nPercolation step 2: calculating Tperc assuming P = 0 in the Hubble rate")
|
|
1192
|
+
|
|
1193
|
+
while True:
|
|
1194
|
+
state.explored_tmin = min(float(state.explored_tmin), float(TSYM[-1]))
|
|
1195
|
+
_compute_step2_profile(
|
|
1196
|
+
state,
|
|
1197
|
+
TSYM,
|
|
1198
|
+
np.zeros_like(TSYM),
|
|
1199
|
+
settings,
|
|
1200
|
+
outdict,
|
|
1201
|
+
pot=pot,
|
|
1202
|
+
phase_symmetric=phase_symmetric,
|
|
1203
|
+
phase_broken=phase_broken,
|
|
1204
|
+
CF=CF,
|
|
1205
|
+
vw=vw,
|
|
1206
|
+
verbose=verbose,
|
|
1207
|
+
)
|
|
1208
|
+
|
|
1209
|
+
remaining_budget = max(
|
|
1210
|
+
int(settings.n_action_max) - _controller_added_support_points(state.support_bank, state.free_support_bank),
|
|
1211
|
+
0,
|
|
1212
|
+
)
|
|
1213
|
+
decision, previous_estimate, remaining_budget, hot_head_needs_support = (
|
|
1214
|
+
_dynamiczoomwindow_post_sweep_controller(
|
|
1215
|
+
TSYM,
|
|
1216
|
+
state.Hr,
|
|
1217
|
+
state.Pr,
|
|
1218
|
+
state.Sr,
|
|
1219
|
+
settings,
|
|
1220
|
+
pot,
|
|
1221
|
+
stage="step 2",
|
|
1222
|
+
iterator=iterator,
|
|
1223
|
+
remaining_budget=remaining_budget,
|
|
1224
|
+
previous_estimate=previous_estimate,
|
|
1225
|
+
vw=vw,
|
|
1226
|
+
hot_bound=grid.tmax,
|
|
1227
|
+
apply_candidate_grid=_apply_candidate_grid,
|
|
1228
|
+
promote_cached_action_points=_promote_cached_action_points,
|
|
1229
|
+
promote_hot_head_cached_points=_promote_hot_head_cached_points,
|
|
1230
|
+
verbose=verbose,
|
|
1231
|
+
)
|
|
1232
|
+
)
|
|
1233
|
+
if decision == "retry":
|
|
1234
|
+
iterator += 1
|
|
1235
|
+
continue
|
|
1236
|
+
if decision in {"accept", "limit"}:
|
|
1237
|
+
break
|
|
1238
|
+
|
|
1239
|
+
(
|
|
1240
|
+
Tmax_new,
|
|
1241
|
+
Tmin_new,
|
|
1242
|
+
state.cold_tail_last_temperature,
|
|
1243
|
+
state.cold_tail_last_probability,
|
|
1244
|
+
state.cold_tail_last_integral,
|
|
1245
|
+
state.cold_tail_last_log_gamma_h4,
|
|
1246
|
+
state.cold_tail_post_peak_streak,
|
|
1247
|
+
_,
|
|
1248
|
+
) = _dynamiczoomwindow_range_targets(
|
|
1249
|
+
TSYM,
|
|
1250
|
+
state.Pr,
|
|
1251
|
+
state.Sr,
|
|
1252
|
+
state.Hr,
|
|
1253
|
+
settings,
|
|
1254
|
+
target_low=grid.tmin,
|
|
1255
|
+
target_high=grid.tmax,
|
|
1256
|
+
iterator=iterator,
|
|
1257
|
+
hot_head_needs_support=hot_head_needs_support,
|
|
1258
|
+
maxit_margin=2,
|
|
1259
|
+
cold_tail_last_temperature=state.cold_tail_last_temperature,
|
|
1260
|
+
cold_tail_last_probability=state.cold_tail_last_probability,
|
|
1261
|
+
cold_tail_last_integral=state.cold_tail_last_integral,
|
|
1262
|
+
cold_tail_last_log_gamma_h4=state.cold_tail_last_log_gamma_h4,
|
|
1263
|
+
cold_tail_post_peak_streak=state.cold_tail_post_peak_streak,
|
|
1264
|
+
)
|
|
1265
|
+
|
|
1266
|
+
candidate_grid = _build_dynamiczoomwindow_range_grid(
|
|
1267
|
+
TSYM,
|
|
1268
|
+
new_high=Tmax_new,
|
|
1269
|
+
new_low=Tmin_new,
|
|
1270
|
+
max_new_points=_range_expand_batch_size(settings, int(remaining_budget)),
|
|
1271
|
+
)
|
|
1272
|
+
if candidate_grid is None or not _apply_candidate_grid(candidate_grid, "range_expand"):
|
|
1273
|
+
break
|
|
1274
|
+
previous_estimate = None
|
|
1275
|
+
iterator += 1
|
|
1276
|
+
|
|
1277
|
+
return state
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
def _refine_percolation_temperature_dynamiczoomwindow(
|
|
1281
|
+
state: PercolationState,
|
|
1282
|
+
grid: PercolationGrid,
|
|
1283
|
+
settings,
|
|
1284
|
+
outdict: dict,
|
|
1285
|
+
pot,
|
|
1286
|
+
phase_symmetric,
|
|
1287
|
+
phase_broken,
|
|
1288
|
+
CF: float,
|
|
1289
|
+
vw: float,
|
|
1290
|
+
Tperc_prev: float,
|
|
1291
|
+
rtol: float,
|
|
1292
|
+
verbose: bool,
|
|
1293
|
+
) -> tuple[float, PercolationState]:
|
|
1294
|
+
"""Dynamic zoom-window step 3 with the same focused refinement as step 2."""
|
|
1295
|
+
from transitionlistener import bubbledynamics as bd
|
|
1296
|
+
|
|
1297
|
+
TBROmin = grid.TBROmin
|
|
1298
|
+
TBROmax = grid.TBROmax
|
|
1299
|
+
TSYM = np.asarray(state.TSYM, dtype=float)
|
|
1300
|
+
state.support_bank = _temperature_grid(state.support_bank)
|
|
1301
|
+
state.free_support_bank = _temperature_grid(state.free_support_bank)
|
|
1302
|
+
state.rebuild_count = int(state.rebuild_count or 0)
|
|
1303
|
+
state.cold_tail_post_peak_streak = int(state.cold_tail_post_peak_streak or 0)
|
|
1304
|
+
previous_estimate = None
|
|
1305
|
+
iterator = 1
|
|
1306
|
+
profile_picard_retries = 0
|
|
1307
|
+
profile_rho_tol = max(5.0 * float(settings.acc_rh), 0.02)
|
|
1308
|
+
|
|
1309
|
+
def _note_rebuild(reason: str, old_grid: np.ndarray, new_grid: np.ndarray) -> None:
|
|
1310
|
+
_log_support_rebuild(state, "step3", reason, old_grid, new_grid, verbose)
|
|
1311
|
+
|
|
1312
|
+
def _reset_state_arrays(new_grid: np.ndarray) -> None:
|
|
1313
|
+
nonlocal TSYM, profile_picard_retries
|
|
1314
|
+
TSYM = np.asarray(new_grid, dtype=float)
|
|
1315
|
+
state.TSYM = TSYM
|
|
1316
|
+
state.Sr = np.zeros_like(TSYM)
|
|
1317
|
+
state.Hr = np.zeros_like(TSYM)
|
|
1318
|
+
state.Pr = np.zeros_like(TSYM)
|
|
1319
|
+
state.Tb = np.zeros_like(TSYM)
|
|
1320
|
+
profile_picard_retries = 0
|
|
1321
|
+
|
|
1322
|
+
def _profile_self_consistency_residual() -> float:
|
|
1323
|
+
"""Return the current step-3 fixed-point residual on the active broken branch.
|
|
1324
|
+
|
|
1325
|
+
Step 3 updates H(T) from the previous Picard iterate Pprev(T). Once the
|
|
1326
|
+
current iterate P(T) is known, a fully self-consistent solution should
|
|
1327
|
+
satisfy rho(H) ~= P * eBRO(TBRO) + (1 - P) * eSYM(TSYM) over the region
|
|
1328
|
+
where TBRO is still actively evolved. Large deviations mean that Tperc
|
|
1329
|
+
may already look stable while the underlying P/H/TBRO profile has not
|
|
1330
|
+
yet caught up.
|
|
1331
|
+
"""
|
|
1332
|
+
active_mask = (
|
|
1333
|
+
np.isfinite(state.Tb)
|
|
1334
|
+
& (state.Tb > 0.0)
|
|
1335
|
+
& np.isfinite(state.Pr)
|
|
1336
|
+
& np.isfinite(state.Hr)
|
|
1337
|
+
)
|
|
1338
|
+
if not np.any(active_mask):
|
|
1339
|
+
return 0.0
|
|
1340
|
+
|
|
1341
|
+
rho_from_h = 3.0 * np.asarray(state.Hr[active_mask], dtype=float) ** 2
|
|
1342
|
+
rho_from_h *= (cn.Mpl_GeV / CF) ** 2 / (8.0 * np.pi)
|
|
1343
|
+
|
|
1344
|
+
tsym_active = np.asarray(TSYM[active_mask], dtype=float)
|
|
1345
|
+
tbro_active = np.asarray(state.Tb[active_mask], dtype=float)
|
|
1346
|
+
e_sym = np.asarray(
|
|
1347
|
+
[bd.energyDensity(pot, phase_symmetric, float(temp)) for temp in tsym_active],
|
|
1348
|
+
dtype=float,
|
|
1349
|
+
)
|
|
1350
|
+
e_bro = np.asarray(
|
|
1351
|
+
[bd.energyDensity(pot, phase_broken, float(temp)) for temp in tbro_active],
|
|
1352
|
+
dtype=float,
|
|
1353
|
+
)
|
|
1354
|
+
rho_target = np.asarray(state.Pr[active_mask], dtype=float) * e_bro
|
|
1355
|
+
rho_target += (1.0 - np.asarray(state.Pr[active_mask], dtype=float)) * e_sym
|
|
1356
|
+
scale = np.maximum(np.maximum(np.abs(rho_target), np.abs(rho_from_h)), 1.0e-30)
|
|
1357
|
+
return float(np.nanmax(np.abs(rho_from_h - rho_target) / scale))
|
|
1358
|
+
|
|
1359
|
+
def _apply_candidate_grid(candidate_grid: np.ndarray, reason: str, *, extra_budget: int = 0) -> bool:
|
|
1360
|
+
nonlocal TSYM
|
|
1361
|
+
changed, TSYM, state.support_bank, state.free_support_bank = _apply_dynamiczoomwindow_candidate_grid(
|
|
1362
|
+
TSYM,
|
|
1363
|
+
candidate_grid,
|
|
1364
|
+
state.support_bank,
|
|
1365
|
+
state.free_support_bank,
|
|
1366
|
+
settings,
|
|
1367
|
+
outdict,
|
|
1368
|
+
phase_symmetric,
|
|
1369
|
+
phase_broken,
|
|
1370
|
+
tmin=grid.tmin,
|
|
1371
|
+
tmax=grid.tmax,
|
|
1372
|
+
stage="step 3",
|
|
1373
|
+
reset_arrays=_reset_state_arrays,
|
|
1374
|
+
note_rebuild=lambda old_grid, new_grid: _note_rebuild(reason, old_grid, new_grid),
|
|
1375
|
+
extra_budget=extra_budget,
|
|
1376
|
+
)
|
|
1377
|
+
return changed
|
|
1378
|
+
|
|
1379
|
+
def _promote_cached_action_points(reason: str) -> bool:
|
|
1380
|
+
nonlocal TSYM
|
|
1381
|
+
promotion_bounds = _cached_promotion_bounds(TSYM)
|
|
1382
|
+
changed, TSYM, state.support_bank, state.free_support_bank = _promote_dynamiczoomwindow_cached_points(
|
|
1383
|
+
TSYM,
|
|
1384
|
+
state.support_bank,
|
|
1385
|
+
state.free_support_bank,
|
|
1386
|
+
settings,
|
|
1387
|
+
outdict,
|
|
1388
|
+
phase_symmetric,
|
|
1389
|
+
phase_broken,
|
|
1390
|
+
tmin=grid.tmin,
|
|
1391
|
+
tmax=grid.tmax,
|
|
1392
|
+
promotion_high=None if promotion_bounds is None else promotion_bounds[0],
|
|
1393
|
+
promotion_low=None if promotion_bounds is None else promotion_bounds[1],
|
|
1394
|
+
stage="step 3",
|
|
1395
|
+
reason=reason,
|
|
1396
|
+
reset_arrays=_reset_state_arrays,
|
|
1397
|
+
note_rebuild=_note_rebuild,
|
|
1398
|
+
)
|
|
1399
|
+
return changed
|
|
1400
|
+
|
|
1401
|
+
def _promote_hot_head_cached_points() -> bool:
|
|
1402
|
+
nonlocal TSYM
|
|
1403
|
+
changed, TSYM, state.support_bank, state.free_support_bank = _promote_dynamiczoomwindow_cached_points(
|
|
1404
|
+
TSYM,
|
|
1405
|
+
state.support_bank,
|
|
1406
|
+
state.free_support_bank,
|
|
1407
|
+
settings,
|
|
1408
|
+
outdict,
|
|
1409
|
+
phase_symmetric,
|
|
1410
|
+
phase_broken,
|
|
1411
|
+
tmin=grid.tmin,
|
|
1412
|
+
tmax=grid.tmax,
|
|
1413
|
+
promotion_high=float(grid.tmax),
|
|
1414
|
+
promotion_low=float(TSYM[0]),
|
|
1415
|
+
stage="step 3",
|
|
1416
|
+
reason="hot_head_cached_points",
|
|
1417
|
+
reset_arrays=_reset_state_arrays,
|
|
1418
|
+
note_rebuild=_note_rebuild,
|
|
1419
|
+
)
|
|
1420
|
+
return changed
|
|
1421
|
+
|
|
1422
|
+
if verbose:
|
|
1423
|
+
print("\nPercolation step 3: calculating Tperc with P-dependent Hubble rate")
|
|
1424
|
+
|
|
1425
|
+
while True:
|
|
1426
|
+
if state.explored_tmin is None:
|
|
1427
|
+
state.explored_tmin = float(TSYM[-1])
|
|
1428
|
+
else:
|
|
1429
|
+
state.explored_tmin = min(float(state.explored_tmin), float(TSYM[-1]))
|
|
1430
|
+
|
|
1431
|
+
if verbose:
|
|
1432
|
+
print(f"Iteration {iterator}, T_perc = {Tperc_prev * CF:2.8g} GeV")
|
|
1433
|
+
|
|
1434
|
+
Pprev = np.asarray(state.Pr, dtype=float).copy()
|
|
1435
|
+
_compute_step3_profile(
|
|
1436
|
+
state,
|
|
1437
|
+
TSYM,
|
|
1438
|
+
Pprev,
|
|
1439
|
+
settings,
|
|
1440
|
+
outdict,
|
|
1441
|
+
pot,
|
|
1442
|
+
phase_symmetric,
|
|
1443
|
+
phase_broken,
|
|
1444
|
+
CF,
|
|
1445
|
+
vw,
|
|
1446
|
+
TBROmin,
|
|
1447
|
+
TBROmax,
|
|
1448
|
+
verbose,
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
Pint = interpolate.interp1d(TSYM, state.Pr)
|
|
1452
|
+
try:
|
|
1453
|
+
Tperc = optimize.brentq(lambda T: Pint(T) - settings.f_perc, TSYM[0], TSYM[-1])
|
|
1454
|
+
except ValueError as err:
|
|
1455
|
+
msg = err.args[0]
|
|
1456
|
+
if msg.startswith("f(a) and f(b) must have different signs") and Pint(TSYM[-1]) < settings.f_perc:
|
|
1457
|
+
raise errors.TooMuchSupercoolingError(
|
|
1458
|
+
"The percolation temperature could not be found because the "
|
|
1459
|
+
"true vacuum fraction only reaches "
|
|
1460
|
+
f"{Pint(TSYM[-1])} < {settings.f_perc} at Tmin = "
|
|
1461
|
+
f"{TSYM[-1] * CF} GeV."
|
|
1462
|
+
)
|
|
1463
|
+
raise errors.PercolationError(err)
|
|
1464
|
+
|
|
1465
|
+
# First demand consistency of the P-dependent Hubble update itself: if
|
|
1466
|
+
# Tperc still moves noticeably between successive step-3 iterations,
|
|
1467
|
+
# continue iterating before judging the support accuracy.
|
|
1468
|
+
if np.isfinite(Tperc) and abs(Tperc - Tperc_prev) / max(abs(Tperc), 1e-30) >= rtol:
|
|
1469
|
+
if iterator >= settings.maxit:
|
|
1470
|
+
print(
|
|
1471
|
+
"Warning: step 3 reached the iteration limit before Tperc fully "
|
|
1472
|
+
"stopped moving under the P-dependent Hubble update. Continuing."
|
|
1473
|
+
)
|
|
1474
|
+
else:
|
|
1475
|
+
Tperc_prev = Tperc
|
|
1476
|
+
iterator += 1
|
|
1477
|
+
continue
|
|
1478
|
+
|
|
1479
|
+
profile_residual = _profile_self_consistency_residual()
|
|
1480
|
+
if profile_residual > profile_rho_tol and profile_picard_retries < 3 and iterator < settings.maxit:
|
|
1481
|
+
if verbose:
|
|
1482
|
+
print(
|
|
1483
|
+
"Step-3 profile not yet self-consistent on the current support grid: "
|
|
1484
|
+
f"max |rho(H) - rho_mix| / rho = {profile_residual:2.5g} > {profile_rho_tol:2.5g}. "
|
|
1485
|
+
"Recomputing P/H/TBRO once more with the same cached actions."
|
|
1486
|
+
)
|
|
1487
|
+
Tperc_prev = Tperc
|
|
1488
|
+
iterator += 1
|
|
1489
|
+
profile_picard_retries += 1
|
|
1490
|
+
continue
|
|
1491
|
+
if profile_residual <= profile_rho_tol:
|
|
1492
|
+
profile_picard_retries = 0
|
|
1493
|
+
|
|
1494
|
+
remaining_budget = max(
|
|
1495
|
+
int(settings.n_action_max) - _controller_added_support_points(state.support_bank, state.free_support_bank),
|
|
1496
|
+
0,
|
|
1497
|
+
)
|
|
1498
|
+
decision, previous_estimate, remaining_budget, hot_head_needs_support = (
|
|
1499
|
+
_dynamiczoomwindow_post_sweep_controller(
|
|
1500
|
+
TSYM,
|
|
1501
|
+
state.Hr,
|
|
1502
|
+
state.Pr,
|
|
1503
|
+
state.Sr,
|
|
1504
|
+
settings,
|
|
1505
|
+
pot,
|
|
1506
|
+
stage="step 3",
|
|
1507
|
+
iterator=iterator,
|
|
1508
|
+
remaining_budget=remaining_budget,
|
|
1509
|
+
previous_estimate=previous_estimate,
|
|
1510
|
+
vw=vw,
|
|
1511
|
+
hot_bound=grid.tmax,
|
|
1512
|
+
apply_candidate_grid=_apply_candidate_grid,
|
|
1513
|
+
promote_cached_action_points=_promote_cached_action_points,
|
|
1514
|
+
promote_hot_head_cached_points=_promote_hot_head_cached_points,
|
|
1515
|
+
profile_residual=profile_residual,
|
|
1516
|
+
profile_rho_tol=profile_rho_tol,
|
|
1517
|
+
verbose=verbose,
|
|
1518
|
+
)
|
|
1519
|
+
)
|
|
1520
|
+
if decision == "retry":
|
|
1521
|
+
Tperc_prev = Tperc
|
|
1522
|
+
iterator += 1
|
|
1523
|
+
continue
|
|
1524
|
+
if decision == "accept":
|
|
1525
|
+
_raise_if_hot_head_underresolved(
|
|
1526
|
+
TSYM,
|
|
1527
|
+
state.Pr,
|
|
1528
|
+
state.Sr,
|
|
1529
|
+
state.Hr,
|
|
1530
|
+
settings,
|
|
1531
|
+
stage="step 3",
|
|
1532
|
+
outcome="successful",
|
|
1533
|
+
)
|
|
1534
|
+
_raise_if_large_delta_p_underresolved(
|
|
1535
|
+
TSYM,
|
|
1536
|
+
state.Pr,
|
|
1537
|
+
settings,
|
|
1538
|
+
stage="step 3",
|
|
1539
|
+
outcome="successful",
|
|
1540
|
+
)
|
|
1541
|
+
return float(Tperc), state
|
|
1542
|
+
|
|
1543
|
+
if decision == "limit":
|
|
1544
|
+
_raise_if_hot_head_underresolved(
|
|
1545
|
+
TSYM,
|
|
1546
|
+
state.Pr,
|
|
1547
|
+
state.Sr,
|
|
1548
|
+
state.Hr,
|
|
1549
|
+
settings,
|
|
1550
|
+
stage="step 3",
|
|
1551
|
+
outcome="best-available unresolved-tail",
|
|
1552
|
+
)
|
|
1553
|
+
_raise_if_large_delta_p_underresolved(
|
|
1554
|
+
TSYM,
|
|
1555
|
+
state.Pr,
|
|
1556
|
+
settings,
|
|
1557
|
+
stage="step 3",
|
|
1558
|
+
outcome="best-available unresolved-tail",
|
|
1559
|
+
)
|
|
1560
|
+
return float(Tperc), state
|
|
1561
|
+
|
|
1562
|
+
cold_tail_unresolved = bool(state.Pr[-1] < settings.f_final)
|
|
1563
|
+
if cold_tail_unresolved:
|
|
1564
|
+
lower_bound_reached = abs(float(TSYM[-1]) - float(grid.tmin)) <= 1e-12 * max(abs(float(grid.tmin)), 1.0)
|
|
1565
|
+
if lower_bound_reached:
|
|
1566
|
+
_raise_if_hot_head_underresolved(
|
|
1567
|
+
TSYM,
|
|
1568
|
+
state.Pr,
|
|
1569
|
+
state.Sr,
|
|
1570
|
+
state.Hr,
|
|
1571
|
+
settings,
|
|
1572
|
+
stage="step 3",
|
|
1573
|
+
outcome="eternal-inflation",
|
|
1574
|
+
)
|
|
1575
|
+
_raise_if_large_delta_p_underresolved(
|
|
1576
|
+
TSYM,
|
|
1577
|
+
state.Pr,
|
|
1578
|
+
settings,
|
|
1579
|
+
stage="step 3",
|
|
1580
|
+
outcome="eternal-inflation",
|
|
1581
|
+
)
|
|
1582
|
+
raise errors.EternalInflationError(
|
|
1583
|
+
"The transition cannot completely finish because the true vacuum "
|
|
1584
|
+
"fraction at the lowest evaluated temperature stays below f_final: "
|
|
1585
|
+
f"P(Tmin) = {state.Pr[-1]:2.5g} < {settings.f_final:2.5g} at "
|
|
1586
|
+
f"Tmin = {TSYM[-1] * CF:2.5g} GeV."
|
|
1587
|
+
)
|
|
1588
|
+
|
|
1589
|
+
(
|
|
1590
|
+
Tmax_new,
|
|
1591
|
+
Tmin_new,
|
|
1592
|
+
state.cold_tail_last_temperature,
|
|
1593
|
+
state.cold_tail_last_probability,
|
|
1594
|
+
state.cold_tail_last_integral,
|
|
1595
|
+
state.cold_tail_last_log_gamma_h4,
|
|
1596
|
+
state.cold_tail_post_peak_streak,
|
|
1597
|
+
_,
|
|
1598
|
+
) = _dynamiczoomwindow_range_targets(
|
|
1599
|
+
TSYM,
|
|
1600
|
+
state.Pr,
|
|
1601
|
+
state.Sr,
|
|
1602
|
+
state.Hr,
|
|
1603
|
+
settings,
|
|
1604
|
+
target_low=grid.tmin,
|
|
1605
|
+
target_high=grid.tmax,
|
|
1606
|
+
iterator=iterator,
|
|
1607
|
+
hot_head_needs_support=hot_head_needs_support,
|
|
1608
|
+
maxit_margin=1,
|
|
1609
|
+
cold_tail_last_temperature=state.cold_tail_last_temperature,
|
|
1610
|
+
cold_tail_last_probability=state.cold_tail_last_probability,
|
|
1611
|
+
cold_tail_last_integral=state.cold_tail_last_integral,
|
|
1612
|
+
cold_tail_last_log_gamma_h4=state.cold_tail_last_log_gamma_h4,
|
|
1613
|
+
cold_tail_post_peak_streak=state.cold_tail_post_peak_streak,
|
|
1614
|
+
)
|
|
1615
|
+
|
|
1616
|
+
candidate_grid = _build_dynamiczoomwindow_range_grid(
|
|
1617
|
+
TSYM,
|
|
1618
|
+
new_high=Tmax_new,
|
|
1619
|
+
new_low=Tmin_new,
|
|
1620
|
+
max_new_points=_range_expand_batch_size(settings, int(remaining_budget)),
|
|
1621
|
+
)
|
|
1622
|
+
if candidate_grid is None or not _apply_candidate_grid(candidate_grid, "range_expand"):
|
|
1623
|
+
_raise_if_large_delta_p_underresolved(
|
|
1624
|
+
TSYM,
|
|
1625
|
+
state.Pr,
|
|
1626
|
+
settings,
|
|
1627
|
+
stage="step 3",
|
|
1628
|
+
outcome="best-available unresolved-tail",
|
|
1629
|
+
)
|
|
1630
|
+
return float(Tperc), state
|
|
1631
|
+
Tperc_prev = Tperc
|
|
1632
|
+
iterator += 1
|