TransitionListener 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. transitionlistener/__init__.py +21 -0
  2. transitionlistener/bubbledynamics.py +2250 -0
  3. transitionlistener/bubbledynamics_fixedstep.py +2929 -0
  4. transitionlistener/check_installation.py +309 -0
  5. transitionlistener/ckm.py +91 -0
  6. transitionlistener/colors.py +288 -0
  7. transitionlistener/config.py +185 -0
  8. transitionlistener/constants.py +64 -0
  9. transitionlistener/counterterms/__init__.py +230 -0
  10. transitionlistener/counterterms/cw.py +298 -0
  11. transitionlistener/counterterms/tensors.py +343 -0
  12. transitionlistener/counterterms/twohdm.py +520 -0
  13. transitionlistener/counterterms/twohdm_solver.py +374 -0
  14. transitionlistener/errors.py +315 -0
  15. transitionlistener/examples/bp_et.yaml +64 -0
  16. transitionlistener/examples/bp_lisa.yaml +70 -0
  17. transitionlistener/examples/bp_muares.yaml +69 -0
  18. transitionlistener/examples/bp_pta.yaml +64 -0
  19. transitionlistener/examples/example_dark_photon.yaml +73 -0
  20. transitionlistener/examples/example_grid.yaml +55 -0
  21. transitionlistener/examples/example_line.yaml +51 -0
  22. transitionlistener/examples/example_point.yaml +73 -0
  23. transitionlistener/examples/example_random_conformal.yaml +42 -0
  24. transitionlistener/examples/example_ultranest.yaml +39 -0
  25. transitionlistener/finiteT.py +468 -0
  26. transitionlistener/generic_potential.py +1521 -0
  27. transitionlistener/gridplots.py +592 -0
  28. transitionlistener/gwfopt.py +604 -0
  29. transitionlistener/helper_functions.py +843 -0
  30. transitionlistener/hydrodynamics.py +647 -0
  31. transitionlistener/interface/__init__.py +95 -0
  32. transitionlistener/interface/check.py +51 -0
  33. transitionlistener/interface/cli.py +211 -0
  34. transitionlistener/interface/config_loader.py +264 -0
  35. transitionlistener/interface/logging_utils.py +30 -0
  36. transitionlistener/interface/output_schema.py +296 -0
  37. transitionlistener/interface/pipeline.py +300 -0
  38. transitionlistener/interface/samplers.py +565 -0
  39. transitionlistener/interface/scans.py +468 -0
  40. transitionlistener/interface/single_point.py +581 -0
  41. transitionlistener/interface/state.py +61 -0
  42. transitionlistener/lineplots.py +597 -0
  43. transitionlistener/logo/TL-logo_large.png +0 -0
  44. transitionlistener/logo/TL-logo_small.png +0 -0
  45. transitionlistener/models/TL_2HDM.py +512 -0
  46. transitionlistener/models/TL_conformal_dark_u1.py +202 -0
  47. transitionlistener/models/TL_dark_U1.py +198 -0
  48. transitionlistener/models/TL_dark_U1_g_parameterization.py +198 -0
  49. transitionlistener/models/TL_dark_flipflop.py +274 -0
  50. transitionlistener/models/templatePotential.py +260 -0
  51. transitionlistener/nucleation.py +412 -0
  52. transitionlistener/observability.py +790 -0
  53. transitionlistener/particles.py +436 -0
  54. transitionlistener/pathDeformation.py +1462 -0
  55. transitionlistener/percolation_adaptive_gridbuilders.py +1385 -0
  56. transitionlistener/percolation_adaptive_rate.py +1049 -0
  57. transitionlistener/percolation_adaptivestepsize.py +1632 -0
  58. transitionlistener/percolation_fixedstepsize.py +618 -0
  59. transitionlistener/phases.py +1499 -0
  60. transitionlistener/plot_settings.py +121 -0
  61. transitionlistener/plots.py +1781 -0
  62. transitionlistener/runtime_options.py +278 -0
  63. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/bandwidths.npy +0 -0
  64. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/density.npy +0 -0
  65. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/freqs.npy +0 -0
  66. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log.txt +16 -0
  67. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log10rhogrid.npy +0 -0
  68. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/log10rholabels.txt +30 -0
  69. transitionlistener/tab_data/30f_fs{hd}_100fDMGP_ceffyl/pulsar_list.txt +1 -0
  70. transitionlistener/tab_data/HLV-O2-PLI.csv +82 -0
  71. transitionlistener/tab_data/HLV-O2-noise.csv +83 -0
  72. transitionlistener/tab_data/HLVK-design-PLI.csv +61 -0
  73. transitionlistener/tab_data/HLVK-design-noise.csv +83 -0
  74. transitionlistener/tab_data/NG15_PLI_hc.csv +31 -0
  75. transitionlistener/tab_data/NG15_noise_hc.csv +56 -0
  76. transitionlistener/tab_data/PLI-dataset.csv +327 -0
  77. transitionlistener/tab_data/energydensities.dat +1001 -0
  78. transitionlistener/tab_data/finiteT_b.dat.txt +1999 -0
  79. transitionlistener/tab_data/finiteT_f.dat.txt +1000 -0
  80. transitionlistener/tab_data/geff_SM.dat +10009 -0
  81. transitionlistener/tab_data/muAres_f_Hz_PLI_h2OmegaGW.csv +131 -0
  82. transitionlistener/tab_data/muAres_f_Hz_h2Omega_noise.csv +99 -0
  83. transitionlistener/tab_data/noise-dataset.csv +327 -0
  84. transitionlistener/tab_data/numberdensities.dat +1001 -0
  85. transitionlistener/tab_data/pressures.dat +1001 -0
  86. transitionlistener/thermodynamics.py +686 -0
  87. transitionlistener/transitionObservables.py +1199 -0
  88. transitionlistener/transitionObservables_fixedstep.py +976 -0
  89. transitionlistener/transitions.py +1000 -0
  90. transitionlistener/tunneling1D.py +910 -0
  91. transitionlistener-2.0.0.dist-info/METADATA +817 -0
  92. transitionlistener-2.0.0.dist-info/RECORD +95 -0
  93. transitionlistener-2.0.0.dist-info/WHEEL +4 -0
  94. transitionlistener-2.0.0.dist-info/entry_points.txt +2 -0
  95. transitionlistener-2.0.0.dist-info/licenses/LICENSE.txt +674 -0
@@ -0,0 +1,520 @@
1
+ """
2
+ SymPy curvature tensor generator for the CP-conserving 2HDM.
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 __future__ import annotations
13
+
14
+ from itertools import product
15
+ from typing import Dict, Tuple
16
+
17
+ import sympy as sp
18
+
19
+ from . import CountertermGenerator, register_generator
20
+
21
+
22
+ class TwoHDMCountertermGenerator(CountertermGenerator):
23
+ r"""Generate symbolic curvature tensors for the CP-conserving 2HDM.
24
+
25
+ The generator rewrites the two Higgs doublets in an eight-component real
26
+ basis and differentiates the tree-level potential
27
+
28
+ .. math::
29
+ V = m_{11}^2\,\Phi_1^\dagger\Phi_1
30
+ + m_{22}^2\,\Phi_2^\dagger\Phi_2
31
+ - m_{12}^2\,(\Phi_1^\dagger\Phi_2 + \Phi_2^\dagger\Phi_1)
32
+ + \frac{\lambda_1}{2}(\Phi_1^\dagger\Phi_1)^2
33
+ + \frac{\lambda_2}{2}(\Phi_2^\dagger\Phi_2)^2
34
+ + \lambda_3(\Phi_1^\dagger\Phi_1)(\Phi_2^\dagger\Phi_2)
35
+ + \lambda_4(\Phi_1^\dagger\Phi_2)(\Phi_2^\dagger\Phi_1)
36
+ + \frac{\lambda_5}{2}\!\left[(\Phi_1^\dagger\Phi_2)^2 + (\Phi_2^\dagger\Phi_1)^2\right]
37
+
38
+ to obtain the Higgs, gauge, and Yukawa tensors consumed by the runtime
39
+ Coleman-Weinberg solver.
40
+ """
41
+
42
+ model_key = "models.TL_2HDM"
43
+ alias = "TL_2HDM"
44
+ output_filename = "2hdm_curvature_tensors.json"
45
+
46
+ def _field_basis(self) -> Tuple[list[sp.Symbol], Dict[str, sp.Symbol]]:
47
+ """Return the eight real scalar fields spanning the 2HDM gauge basis."""
48
+ phi1p_R, phi1p_I, phi2p_R, phi2p_I, phi1_R, phi1_I, phi2_R, phi2_I = sp.symbols(
49
+ "phi1p_R phi1p_I phi2p_R phi2p_I phi1_R phi1_I phi2_R phi2_I", real=True
50
+ )
51
+ fields = [
52
+ phi1p_R,
53
+ phi1p_I,
54
+ phi2p_R,
55
+ phi2p_I,
56
+ phi1_R,
57
+ phi1_I,
58
+ phi2_R,
59
+ phi2_I,
60
+ ]
61
+ return fields, {str(sym): sym for sym in fields}
62
+
63
+ def _parameters(self) -> Dict[str, sp.Symbol]:
64
+ """Create the symbolic model, CKM, and gauge parameters appearing in the tensors."""
65
+ lambda1, lambda2, lambda3, lambda4, lambda5 = sp.symbols(
66
+ "lambda1 lambda2 lambda3 lambda4 lambda5", real=True
67
+ )
68
+ m11sq, m22sq, m12sq = sp.symbols("m11sq m22sq m12sq", real=True)
69
+ tan_beta = sp.symbols("tan_beta", real=True)
70
+ v1, v2 = sp.symbols("v1 v2", real=True)
71
+ yuk_type = sp.symbols("yukawa_type", integer=True)
72
+ params: Dict[str, sp.Symbol] = {
73
+ "lambda1": lambda1,
74
+ "lambda2": lambda2,
75
+ "lambda3": lambda3,
76
+ "lambda4": lambda4,
77
+ "lambda5": lambda5,
78
+ "m11sq": m11sq,
79
+ "m22sq": m22sq,
80
+ "m12sq": m12sq,
81
+ "tan_beta": tan_beta,
82
+ "v1": v1,
83
+ "v2": v2,
84
+ "yukawa_type": yuk_type,
85
+ }
86
+ Cg, Cgs = sp.symbols("C_g C_gs", real=True)
87
+ params["Cg"] = Cg
88
+ params["Cgs"] = Cgs
89
+
90
+ params.update(
91
+ {
92
+ "m_e": sp.symbols("C_MassElectron", real=True),
93
+ "m_mu": sp.symbols("C_MassMu", real=True),
94
+ "m_tau": sp.symbols("C_MassTau", real=True),
95
+ "m_u": sp.symbols("C_MassUp", real=True),
96
+ "m_c": sp.symbols("C_MassCharm", real=True),
97
+ "m_t": sp.symbols("C_MassTop", real=True),
98
+ "m_d": sp.symbols("C_MassDown", real=True),
99
+ "m_s": sp.symbols("C_MassStrange", real=True),
100
+ "m_b": sp.symbols("C_MassBottom", real=True),
101
+ }
102
+ )
103
+
104
+ Vud, Vus, Vub, Vcd, Vcs, Vcb, Vtd, Vts, Vtb = sp.symbols("Vud Vus Vub Vcd Vcs Vcb Vtd Vts Vtb")
105
+ params.update(
106
+ {
107
+ "Vud": Vud,
108
+ "Vus": Vus,
109
+ "Vub": Vub,
110
+ "Vcd": Vcd,
111
+ "Vcs": Vcs,
112
+ "Vcb": Vcb,
113
+ "Vtd": Vtd,
114
+ "Vts": Vts,
115
+ "Vtb": Vtb,
116
+ }
117
+ )
118
+ return params
119
+
120
+ def _higgs_potential(self, fields, params):
121
+ r"""Construct the CP-conserving 2HDM tree-level scalar potential.
122
+
123
+ The returned SymPy expression implements
124
+
125
+ .. math::
126
+ V(\Phi_1,\Phi_2)
127
+ = m_{11}^2\,\Phi_1^\dagger\Phi_1
128
+ + m_{22}^2\,\Phi_2^\dagger\Phi_2
129
+ - m_{12}^2(\Phi_1^\dagger\Phi_2 + \Phi_2^\dagger\Phi_1)
130
+ + \frac{\lambda_1}{2}(\Phi_1^\dagger\Phi_1)^2
131
+ + \frac{\lambda_2}{2}(\Phi_2^\dagger\Phi_2)^2
132
+ + \lambda_3(\Phi_1^\dagger\Phi_1)(\Phi_2^\dagger\Phi_2)
133
+ + \lambda_4(\Phi_1^\dagger\Phi_2)(\Phi_2^\dagger\Phi_1)
134
+ + \frac{\lambda_5}{2}\!\left[(\Phi_1^\dagger\Phi_2)^2 + (\Phi_2^\dagger\Phi_1)^2\right].
135
+ """
136
+ (
137
+ phi1p_R,
138
+ phi1p_I,
139
+ phi2p_R,
140
+ phi2p_I,
141
+ phi1_R,
142
+ phi1_I,
143
+ phi2_R,
144
+ phi2_I,
145
+ ) = fields
146
+
147
+ phi1 = sp.Matrix(
148
+ [
149
+ (phi1p_R + sp.I * phi1p_I) / sp.sqrt(2),
150
+ (phi1_R + sp.I * phi1_I) / sp.sqrt(2),
151
+ ]
152
+ )
153
+ phi2 = sp.Matrix(
154
+ [
155
+ (phi2p_R + sp.I * phi2p_I) / sp.sqrt(2),
156
+ (phi2_R + sp.I * phi2_I) / sp.sqrt(2),
157
+ ]
158
+ )
159
+ phi1_sq = sp.simplify((phi1.conjugate().T * phi1)[0])
160
+ phi2_sq = sp.simplify((phi2.conjugate().T * phi2)[0])
161
+ phi12 = sp.simplify((phi1.conjugate().T * phi2)[0])
162
+ phi21 = sp.simplify((phi2.conjugate().T * phi1)[0])
163
+
164
+ lam1 = params["lambda1"]
165
+ lam2 = params["lambda2"]
166
+ lam3 = params["lambda3"]
167
+ lam4 = params["lambda4"]
168
+ lam5 = params["lambda5"]
169
+ m11sq = params["m11sq"]
170
+ m22sq = params["m22sq"]
171
+ m12sq = params["m12sq"]
172
+
173
+ return sp.simplify(
174
+ m11sq * phi1_sq
175
+ + m22sq * phi2_sq
176
+ - m12sq * (phi12 + phi21)
177
+ + (lam1 / 2) * phi1_sq**2
178
+ + (lam2 / 2) * phi2_sq**2
179
+ + lam3 * phi1_sq * phi2_sq
180
+ + lam4 * phi12 * phi21
181
+ + (lam5 / 2) * (phi12**2 + phi21**2)
182
+ )
183
+
184
+ def _build_gauge_curvature(self, params):
185
+ r"""Build the gauge tensor :math:`G_{abij}` in the real Higgs basis.
186
+
187
+ The tensor is defined so that the gauge-boson mass matrix can be written
188
+ as
189
+
190
+ .. math::
191
+ (M_G^2)_{ab} = \frac{1}{2} G_{abij}\, \phi_i \phi_j.
192
+ """
193
+ Cg = params["Cg"]
194
+ Cgs = params["Cgs"]
195
+ half_cg_sq = sp.Rational(1, 2) * Cg * Cg
196
+ half_cgs_sq = sp.Rational(1, 2) * Cgs * Cgs
197
+ half_cg_cgs = sp.Rational(1, 2) * Cg * Cgs
198
+
199
+ tensor = sp.MutableDenseNDimArray.zeros(4, 4, 8, 8)
200
+
201
+ for a in range(3):
202
+ for i in range(8):
203
+ tensor[a, a, i, i] = half_cg_sq
204
+
205
+ for i in range(8):
206
+ tensor[3, 3, i, i] = half_cgs_sq
207
+
208
+ def set_pair(a: int, b: int, i: int, j: int, value: sp.Expr) -> None:
209
+ tensor[a, b, i, j] = value
210
+ tensor[b, a, j, i] = value
211
+
212
+ positive_pairs = [
213
+ (0, 3, [(0, 4), (1, 5), (2, 6), (3, 7), (4, 0), (5, 1), (6, 2), (7, 3)]),
214
+ (2, 3, [(0, 0), (1, 1), (2, 2), (3, 3)]),
215
+ ]
216
+ for a, b, entries in positive_pairs:
217
+ for i, j in entries:
218
+ set_pair(a, b, i, j, half_cg_cgs)
219
+
220
+ negative_pairs = [
221
+ (2, 3, [(4, 4), (5, 5), (6, 6), (7, 7)]),
222
+ ]
223
+ for a, b, entries in negative_pairs:
224
+ for i, j in entries:
225
+ set_pair(a, b, i, j, -half_cg_cgs)
226
+
227
+ entries_13 = [
228
+ ((0, 5), 1),
229
+ ((1, 4), -1),
230
+ ((2, 7), 1),
231
+ ((3, 6), -1),
232
+ ((4, 1), -1),
233
+ ((5, 0), 1),
234
+ ((6, 3), -1),
235
+ ((7, 2), 1),
236
+ ]
237
+ for (i, j), sign in entries_13:
238
+ set_pair(1, 3, i, j, sign * half_cg_cgs)
239
+
240
+ entries_30 = [
241
+ (0, 4),
242
+ (1, 5),
243
+ (2, 6),
244
+ (3, 7),
245
+ (4, 0),
246
+ (5, 1),
247
+ (6, 2),
248
+ (7, 3),
249
+ ]
250
+ for i, j in entries_30:
251
+ set_pair(3, 0, i, j, half_cg_cgs)
252
+
253
+ entries_31 = [
254
+ ((0, 5), 1),
255
+ ((1, 4), -1),
256
+ ((2, 7), 1),
257
+ ((3, 6), -1),
258
+ ((4, 1), -1),
259
+ ((5, 0), 1),
260
+ ((6, 3), -1),
261
+ ((7, 2), 1),
262
+ ]
263
+ for (i, j), sign in entries_31:
264
+ set_pair(3, 1, i, j, sign * half_cg_cgs)
265
+
266
+ result = {}
267
+ for a in range(4):
268
+ for b in range(4):
269
+ for i in range(8):
270
+ for j in range(8):
271
+ value = sp.simplify(tensor[a, b, i, j])
272
+ if value != 0:
273
+ result[(a, b, i, j)] = value
274
+ return result
275
+
276
+ def _lepton_curvature(self, params, yukawa_type: str):
277
+ r"""Build the lepton Yukawa tensor :math:`Y^{(\ell)}_{ab i}` for a given 2HDM type."""
278
+ NH = 8
279
+ NL = 9
280
+
281
+ v1 = params["v1"]
282
+ v2 = params["v2"]
283
+ m_e = params["m_e"]
284
+ m_mu = params["m_mu"]
285
+ m_tau = params["m_tau"]
286
+
287
+ if yukawa_type in ("TypeI", "TypeFlipped"):
288
+ vL = v2
289
+ elif yukawa_type in ("TypeII", "TypeLeptonSpecific"):
290
+ vL = v1
291
+ else:
292
+ raise ValueError(f"Unknown Yukawa type {yukawa_type}")
293
+
294
+ YIJRL = sp.Matrix.zeros(NL, NL)
295
+ YIJSL = sp.Matrix.zeros(NL, NL)
296
+ YIJEL = sp.Matrix.zeros(NL, NL)
297
+ YIJPL = sp.Matrix.zeros(NL, NL)
298
+
299
+ masses = [m_e, m_mu, m_tau]
300
+ lepton_pairs = [(1, 6), (3, 7), (5, 8)]
301
+ for (i, j), mass in zip(lepton_pairs, masses):
302
+ YIJRL[i, j] = mass / vL
303
+ for i in range(NL):
304
+ for j in range(i):
305
+ YIJRL[i, j] = YIJRL[j, i]
306
+
307
+ YIJSL[0, 1] = m_e / vL
308
+ YIJSL[2, 3] = m_mu / vL
309
+ YIJSL[4, 5] = m_tau / vL
310
+ for i in range(NL):
311
+ for j in range(i):
312
+ YIJSL[i, j] = YIJSL[j, i]
313
+
314
+ YIJPL = sp.I * YIJSL
315
+ YIJEL = sp.I * YIJRL
316
+
317
+ tensor = sp.MutableDenseNDimArray.zeros(NL, NL, NH)
318
+
319
+ if yukawa_type in ("TypeI", "TypeFlipped"):
320
+ for i in range(NL):
321
+ for j in range(NL):
322
+ if YIJRL[i, j] != 0:
323
+ tensor[i, j, 2] = YIJRL[i, j]
324
+ tensor[i, j, 3] = YIJEL[i, j]
325
+ if YIJSL[i, j] != 0:
326
+ tensor[i, j, 6] = YIJSL[i, j]
327
+ tensor[i, j, 7] = YIJPL[i, j]
328
+ else:
329
+ for i in range(NL):
330
+ for j in range(NL):
331
+ if YIJRL[i, j] != 0:
332
+ tensor[i, j, 0] = YIJRL[i, j]
333
+ tensor[i, j, 1] = YIJEL[i, j]
334
+ if YIJSL[i, j] != 0:
335
+ tensor[i, j, 4] = YIJSL[i, j]
336
+ tensor[i, j, 5] = YIJPL[i, j]
337
+
338
+ result = {}
339
+ for a in range(NL):
340
+ for b in range(NL):
341
+ for i in range(NH):
342
+ val = sp.simplify(tensor[a, b, i])
343
+ if val != 0:
344
+ result[(a, b, i)] = val
345
+ return result
346
+
347
+ def _quark_curvature(self, params, type_label: str):
348
+ r"""Build the quark Yukawa tensor :math:`Y^{(q)}_{ab i}` for a given 2HDM type."""
349
+ NH = 8
350
+ NQ = 12
351
+
352
+ v1 = params["v1"]
353
+ v2 = params["v2"]
354
+ VCKM = sp.Matrix(
355
+ [
356
+ [params["Vud"], params["Vus"], params["Vub"]],
357
+ [params["Vcd"], params["Vcs"], params["Vcb"]],
358
+ [params["Vtd"], params["Vts"], params["Vtb"]],
359
+ ]
360
+ )
361
+
362
+ up_masses = [params["m_u"], params["m_c"], params["m_t"]]
363
+ down_masses = [params["m_d"], params["m_s"], params["m_b"]]
364
+
365
+ if type_label in ("TypeI", "TypeLeptonSpecific"):
366
+ vD = v2
367
+ elif type_label in ("TypeII", "TypeFlipped"):
368
+ vD = v1
369
+ else:
370
+ raise ValueError(f"Unknown Yukawa type {type_label}")
371
+
372
+ YIJR2 = sp.Matrix.zeros(NQ, NQ)
373
+ YIJE2 = sp.Matrix.zeros(NQ, NQ)
374
+ YIJS2 = sp.Matrix.zeros(NQ, NQ)
375
+ YIJP2 = sp.Matrix.zeros(NQ, NQ)
376
+ YIJRD = sp.Matrix.zeros(NQ, NQ)
377
+ YIJED = sp.Matrix.zeros(NQ, NQ)
378
+ YIJSD = sp.Matrix.zeros(NQ, NQ)
379
+ YIJPD = sp.Matrix.zeros(NQ, NQ)
380
+
381
+ for up_idx, mass in enumerate(up_masses):
382
+ for down_idx in range(3):
383
+ V_elem = VCKM[up_idx, down_idx]
384
+ YIJR2[up_idx, 9 + down_idx] = -sp.conjugate(V_elem) * mass / v2
385
+ YIJS2[up_idx, 6 + up_idx] = mass / v2
386
+
387
+ for down_idx, mass in enumerate(down_masses):
388
+ YIJSD[3 + down_idx, 9 + down_idx] = mass / vD
389
+
390
+ for down_idx, mass in enumerate(down_masses):
391
+ for up_idx in range(3):
392
+ V_elem = VCKM[up_idx, down_idx]
393
+ YIJRD[3 + down_idx, 6 + up_idx] = V_elem * mass / vD
394
+
395
+ for i in range(NQ):
396
+ for j in range(i):
397
+ YIJR2[i, j] = YIJR2[j, i]
398
+ YIJS2[i, j] = YIJS2[j, i]
399
+ YIJRD[i, j] = YIJRD[j, i]
400
+ YIJSD[i, j] = YIJSD[j, i]
401
+
402
+ YIJP2 = -sp.I * YIJS2
403
+ YIJE2 = -sp.I * YIJR2
404
+ YIJPD = sp.I * YIJSD
405
+ YIJED = sp.I * YIJRD
406
+
407
+ tensor = sp.MutableDenseNDimArray.zeros(NQ, NQ, NH)
408
+ for i in range(NQ):
409
+ for j in range(NQ):
410
+ tensor[i, j, 2] = YIJR2[i, j]
411
+ tensor[i, j, 3] = YIJE2[i, j]
412
+ tensor[i, j, 6] = YIJS2[i, j]
413
+ tensor[i, j, 7] = YIJP2[i, j]
414
+ if type_label in ("TypeI", "TypeLeptonSpecific"):
415
+ tensor[i, j, 2] += YIJRD[i, j]
416
+ tensor[i, j, 3] += YIJED[i, j]
417
+ tensor[i, j, 6] += YIJSD[i, j]
418
+ tensor[i, j, 7] += YIJPD[i, j]
419
+ else:
420
+ tensor[i, j, 0] += YIJRD[i, j]
421
+ tensor[i, j, 1] += YIJED[i, j]
422
+ tensor[i, j, 4] += YIJSD[i, j]
423
+ tensor[i, j, 5] += YIJPD[i, j]
424
+
425
+ result = {}
426
+ for a in range(NQ):
427
+ for b in range(NQ):
428
+ for i in range(NH):
429
+ val = sp.simplify(tensor[a, b, i])
430
+ if val != 0:
431
+ result[(a, b, i)] = val
432
+ return result
433
+
434
+ def _curvature_tensor(self, expr: sp.Expr, fields, order: int):
435
+ r"""Differentiate ``expr`` and store the non-vanishing Taylor coefficients.
436
+
437
+ For derivative order :math:`n`, the returned dictionary contains the
438
+ tensors
439
+
440
+ .. math::
441
+ \Lambda_{i_1\cdots i_n}
442
+ = \left.\frac{\partial^n V}{\partial \phi_{i_1}\cdots\partial \phi_{i_n}}\right|_{\phi=0}.
443
+ """
444
+ zero_subs = {f: 0 for f in fields}
445
+
446
+ def simplify(val: sp.Expr) -> sp.Expr:
447
+ if isinstance(val, sp.Basic):
448
+ return sp.simplify(val)
449
+ return sp.simplify(sp.sympify(val))
450
+
451
+ if order == 1:
452
+ data = {}
453
+ for i, fi in enumerate(fields):
454
+ val = simplify(sp.diff(expr, fi).subs(zero_subs))
455
+ if val != 0:
456
+ data[(i,)] = val
457
+ return data
458
+
459
+ if order == 2:
460
+ data = {}
461
+ for i, fi in enumerate(fields):
462
+ for j, fj in enumerate(fields[i:], start=i):
463
+ val = simplify(sp.diff(expr, fi, fj).subs(zero_subs))
464
+ if val != 0:
465
+ data[(i, j)] = val
466
+ return data
467
+
468
+ if order == 3:
469
+ data = {}
470
+ for i, fi in enumerate(fields):
471
+ for j, fj in enumerate(fields[i:], start=i):
472
+ for k, fk in enumerate(fields[j:], start=j):
473
+ val = simplify(sp.diff(expr, fi, fj, fk).subs(zero_subs))
474
+ if val != 0:
475
+ data[(i, j, k)] = val
476
+ return data
477
+
478
+ if order == 4:
479
+ data = {}
480
+ for i, fi in enumerate(fields):
481
+ for j, fj in enumerate(fields[i:], start=i):
482
+ for k, fk in enumerate(fields[j:], start=j):
483
+ for l, fl in enumerate(fields[k:], start=k):
484
+ val = simplify(sp.diff(expr, fi, fj, fk, fl).subs(zero_subs))
485
+ if val != 0:
486
+ data[(i, j, k, l)] = val
487
+ return data
488
+
489
+ raise ValueError("Supported derivative order is 1..4")
490
+
491
+ def build_dataset(self):
492
+ """Assemble the full 2HDM tensor payload and accompanying metadata."""
493
+ fields, field_map = self._field_basis()
494
+ params = self._parameters()
495
+ potential = self._higgs_potential(fields, params)
496
+
497
+ curvature = {
498
+ "Curvature_Higgs_L1": self._curvature_tensor(potential, fields, order=1),
499
+ "Curvature_Higgs_L2": self._curvature_tensor(potential, fields, order=2),
500
+ "Curvature_Higgs_L3": self._curvature_tensor(potential, fields, order=3),
501
+ "Curvature_Higgs_L4": self._curvature_tensor(potential, fields, order=4),
502
+ "Curvature_Gauge_G2H2": self._build_gauge_curvature(params),
503
+ }
504
+
505
+ type_labels = ("TypeI", "TypeII", "TypeLeptonSpecific", "TypeFlipped")
506
+ curvature["Curvature_Lepton_F2H1"] = {label: self._lepton_curvature(params, label) for label in type_labels}
507
+ curvature["Curvature_Quark_F2H1"] = {label: self._quark_curvature(params, label) for label in type_labels}
508
+
509
+ metadata = {
510
+ "generated_by": f"{__name__}.{self.__class__.__name__}",
511
+ "sympy_version": sp.__version__,
512
+ "field_basis": list(field_map.keys()),
513
+ "parameters": list(params.keys()),
514
+ "yukawa_types": list(type_labels),
515
+ "parameter_symbol_names": {name: str(sym) for name, sym in params.items()},
516
+ }
517
+ return metadata, curvature
518
+
519
+
520
+ register_generator(TwoHDMCountertermGenerator)