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,468 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides the functions for the one-loop finite
|
|
3
|
+
temperature corrections to a potential in QFT. The two basic
|
|
4
|
+
functions are:
|
|
5
|
+
|
|
6
|
+
Jb(x) = int[0->inf] dy +y^2 log( 1 - exp(-sqrt(x^2 + y^2)) )
|
|
7
|
+
|
|
8
|
+
Jf(x) = int[0->inf] dy -y^2 log( 1 + exp(-sqrt(x^2 + y^2)) )
|
|
9
|
+
|
|
10
|
+
Call them by:
|
|
11
|
+
|
|
12
|
+
Jb(x, approx='high', deriv=0, n = 8)
|
|
13
|
+
|
|
14
|
+
Here, approx can either be 'exact', 'spline', 'high', or 'low'.
|
|
15
|
+
``Exact`` calculates the integral numerically, while ``high``
|
|
16
|
+
and ``low`` calculate the high and low x expansions of J to order n.
|
|
17
|
+
Specify the derivative with the ``deriv`` parameter.
|
|
18
|
+
|
|
19
|
+
Part of TransitionListener v2.0
|
|
20
|
+
Documentation: https://tasillo.de/TransitionListener/
|
|
21
|
+
|
|
22
|
+
Authors:
|
|
23
|
+
Jonas Matuszak <jonas.matuszak@kit.edu>
|
|
24
|
+
Carlo Tasillo <carlo.tasillo@ific.uv.es>
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
from typing import Callable
|
|
29
|
+
|
|
30
|
+
import numpy
|
|
31
|
+
from scipy import integrate, interpolate
|
|
32
|
+
from scipy import special
|
|
33
|
+
from scipy.special import factorial as fac
|
|
34
|
+
|
|
35
|
+
pi = numpy.pi
|
|
36
|
+
euler_gamma = 0.577215661901532
|
|
37
|
+
log, exp, sqrt = numpy.log, numpy.exp, numpy.sqrt
|
|
38
|
+
array = numpy.array
|
|
39
|
+
|
|
40
|
+
spline_data_path = os.path.dirname(__file__) + "/tab_data"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# The following are the exact integrals:
|
|
44
|
+
|
|
45
|
+
def _Jf_exact(x : complex | float) -> complex:
|
|
46
|
+
"""Evaluate the fermionic thermal integral for a single scalar argument.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
x : complex | float
|
|
51
|
+
Dimensionless mass-to-temperature ratio. Complex values are handled via
|
|
52
|
+
analytic continuation as described in the original CosmoTransitions
|
|
53
|
+
implementation.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
complex
|
|
58
|
+
Exact value of the integral ``J_f(x)``.
|
|
59
|
+
"""
|
|
60
|
+
f = lambda y: -y*y*log(1+exp(-sqrt(y*y+x*x)))
|
|
61
|
+
if(x.imag == 0):
|
|
62
|
+
x = abs(x)
|
|
63
|
+
return integrate.quad(f, 0, numpy.inf)[0]
|
|
64
|
+
else:
|
|
65
|
+
f1 = lambda y: -y*y*log(2*abs(numpy.cos(sqrt(abs(x*x)-y*y)/2)))
|
|
66
|
+
return (
|
|
67
|
+
integrate.quad(f1,0,abs(x))[0] +
|
|
68
|
+
integrate.quad(f,abs(x),numpy.inf)[0]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _Jf_exact2(theta : float) -> float:
|
|
73
|
+
"""Evaluate the fermionic thermal integral as a function of ``theta = x^2``.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
theta : float
|
|
78
|
+
Squared mass-to-temperature ratio. Negative values are supported through
|
|
79
|
+
analytic continuation.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
float
|
|
84
|
+
Exact value of the integral ``J_f(x)`` with ``x^2 = theta``.
|
|
85
|
+
"""
|
|
86
|
+
# Note that this is a function of theta so that you can get negative values
|
|
87
|
+
f = lambda y: -y*y*log(1+exp(-sqrt(y*y+theta))).real
|
|
88
|
+
if theta >= 0:
|
|
89
|
+
return integrate.quad(f, 0, numpy.inf)[0]
|
|
90
|
+
else:
|
|
91
|
+
f1 = lambda y: -y*y*log(2*abs(numpy.cos(sqrt(-theta-y*y)/2)))
|
|
92
|
+
return (
|
|
93
|
+
integrate.quad(f, abs(theta)**.5, numpy.inf)[0] +
|
|
94
|
+
integrate.quad(f1, 0, abs(theta)**.5)[0]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _Jb_exact(x : complex | float) -> complex:
|
|
99
|
+
"""Evaluate the bosonic thermal integral for a single scalar argument.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
x : complex | float
|
|
104
|
+
Dimensionless mass-to-temperature ratio. Complex values are handled via
|
|
105
|
+
analytic continuation.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
complex
|
|
110
|
+
Exact value of the integral ``J_b(x)``.
|
|
111
|
+
"""
|
|
112
|
+
f = lambda y: y*y*log(1-exp(-sqrt(y*y+x*x)))
|
|
113
|
+
if(x.imag == 0):
|
|
114
|
+
x = abs(x)
|
|
115
|
+
return integrate.quad(f, 0, numpy.inf)[0]
|
|
116
|
+
else:
|
|
117
|
+
f1 = lambda y: y*y*log(2*abs(numpy.sin(sqrt(abs(x*x)-y*y)/2)))
|
|
118
|
+
return (
|
|
119
|
+
integrate.quad(f1,0,abs(x))[0] +
|
|
120
|
+
integrate.quad(f,abs(x),numpy.inf)[0]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _Jb_exact2(theta : float) -> float:
|
|
125
|
+
"""Evaluate the bosonic thermal integral as a function of ``theta = x^2``.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
theta : float
|
|
130
|
+
Squared mass-to-temperature ratio. Negative values are supported through
|
|
131
|
+
analytic continuation.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
float
|
|
136
|
+
Exact value of the integral ``J_b(x)`` with ``x^2 = theta``.
|
|
137
|
+
"""
|
|
138
|
+
# Note that this is a function of theta so that you can get negative values
|
|
139
|
+
f = lambda y: y*y*log(1-exp(-sqrt(y*y+theta))).real
|
|
140
|
+
if theta >= 0:
|
|
141
|
+
return integrate.quad(f, 0, numpy.inf)[0]
|
|
142
|
+
else:
|
|
143
|
+
f1 = lambda y: y*y*log(2*abs(numpy.sin(sqrt(-theta-y*y)/2)))
|
|
144
|
+
return (
|
|
145
|
+
integrate.quad(f, abs(theta)**.5, numpy.inf)[0] +
|
|
146
|
+
integrate.quad(f1, 0, abs(theta)**.5)[0]
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _dJf_exact(x : complex | float) -> complex:
|
|
151
|
+
"""Derivative of the fermionic thermal integral with respect to ``x``."""
|
|
152
|
+
f = lambda y: y*y*(exp(sqrt(y*y+x*x))+1)**-1*x/sqrt(y*y+x*x)
|
|
153
|
+
return integrate.quad(f, 0, numpy.inf)[0]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _dJb_exact(x : complex | float) -> complex:
|
|
157
|
+
"""Derivative of the bosonic thermal integral with respect to ``x``."""
|
|
158
|
+
f = lambda y: y*y*(exp(sqrt(y*y+x*x))-1)**-1*x/sqrt(y*y+x*x)
|
|
159
|
+
return integrate.quad(f, 0, numpy.inf)[0]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def arrayFunc(f : Callable[[float], float], x : float | numpy.ndarray, typ=float) -> numpy.ndarray | float:
|
|
163
|
+
"""Vectorise a scalar integrand helper so it accepts scalar or 1D arrays.
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
f : Callable[[float], float]
|
|
168
|
+
Function that accepts and returns scalar values.
|
|
169
|
+
x : float | array-like
|
|
170
|
+
Input argument. Arrays are handled element-wise, scalars are forwarded
|
|
171
|
+
directly.
|
|
172
|
+
typ : type, optional
|
|
173
|
+
Data type of the output array. Defaults to ``float``.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
numpy.ndarray | float
|
|
178
|
+
Array of evaluated values or the scalar return of ``f(x)``.
|
|
179
|
+
"""
|
|
180
|
+
i = 0
|
|
181
|
+
try:
|
|
182
|
+
n = len(x)
|
|
183
|
+
except:
|
|
184
|
+
return f(x) # x isn't an array
|
|
185
|
+
s = numpy.empty(n, typ)
|
|
186
|
+
while(i < n):
|
|
187
|
+
try:
|
|
188
|
+
s[i] = f(x[i])
|
|
189
|
+
except:
|
|
190
|
+
s[i] = numpy.NaN
|
|
191
|
+
i += 1
|
|
192
|
+
return s
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def Jf_exact(x : complex | float) -> complex | float:
|
|
196
|
+
"""Jf calculated directly from the integral."""
|
|
197
|
+
return arrayFunc(_Jf_exact, x, complex)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def Jf_exact2(theta : float) -> float:
|
|
201
|
+
"""Jf calculated directly from the integral; input is theta = x^2."""
|
|
202
|
+
return arrayFunc(_Jf_exact2, theta)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def Jb_exact(x : complex | float) -> complex | float:
|
|
206
|
+
"""Jb calculated directly from the integral."""
|
|
207
|
+
return arrayFunc(_Jb_exact, x)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def Jb_exact2(theta : float) -> float:
|
|
211
|
+
"""Jb calculated directly from the integral; input is theta = x^2."""
|
|
212
|
+
return arrayFunc(_Jb_exact2, theta)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def dJf_exact(x : complex | float) -> complex | float:
|
|
216
|
+
"""dJf/dx calculated directly from the integral."""
|
|
217
|
+
return arrayFunc(_dJf_exact, x)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def dJb_exact(x : complex | float) -> complex | float:
|
|
221
|
+
"""dJb/dx calculated directly from the integral."""
|
|
222
|
+
return arrayFunc(_dJb_exact, x)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# Spline fitting, Jf
|
|
226
|
+
_xfmin = -6.82200203 # -11.2403168
|
|
227
|
+
_xfmax = 1.35e3
|
|
228
|
+
_Jf_dat_path = spline_data_path+"/finiteT_f.dat.txt"
|
|
229
|
+
if os.path.exists(_Jf_dat_path):
|
|
230
|
+
_xf, _yf = numpy.loadtxt(_Jf_dat_path).T
|
|
231
|
+
else:
|
|
232
|
+
# x = |xmin|*sinh(y), where y in linear
|
|
233
|
+
# (so that we're not overpopulating the uniteresting region)
|
|
234
|
+
_xf = numpy.linspace(numpy.arcsinh(-1.3*20),
|
|
235
|
+
numpy.arcsinh(-20*_xfmax/_xfmin), 1000)
|
|
236
|
+
_xf = abs(_xfmin)*numpy.sinh(_xf)/20
|
|
237
|
+
_yf = Jf_exact2(_xf)
|
|
238
|
+
numpy.savetxt(_Jf_dat_path, numpy.array([_xf, _yf]).T)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
_tckf = interpolate.splrep(_xf, _yf)
|
|
242
|
+
def Jf_spline(X,n=0):
|
|
243
|
+
"""Jf interpolated from a saved spline. Input is (m/T)^2."""
|
|
244
|
+
X = numpy.array(X, copy=False)
|
|
245
|
+
x = X.ravel()
|
|
246
|
+
y = interpolate.splev(x,_tckf, der=n).ravel()
|
|
247
|
+
y[x < _xfmin] = interpolate.splev(_xfmin,_tckf, der=n)
|
|
248
|
+
y[x > _xfmax] = 0
|
|
249
|
+
return y.reshape(X.shape)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# Spline fitting, Jb
|
|
253
|
+
_xbmin = -2999.9999999999995 # This is new, see BSMPTv3 appendix
|
|
254
|
+
# We're setting the lower acceptable bound as the point where it's a minimum
|
|
255
|
+
# This guarantees that it's a monatonically increasing function, and the first
|
|
256
|
+
# deriv is continuous.
|
|
257
|
+
_xbmax = 1.41e3
|
|
258
|
+
_Jb_dat_path = spline_data_path+"/finiteT_b.dat.txt"
|
|
259
|
+
if os.path.exists(_Jb_dat_path):
|
|
260
|
+
_xb, _yb = numpy.loadtxt(_Jb_dat_path).T
|
|
261
|
+
else:
|
|
262
|
+
# x = |xmin|*sinh(y), where y in linear
|
|
263
|
+
# (so that we're not overpopulating the uniteresting region)
|
|
264
|
+
_xb = numpy.linspace(numpy.arcsinh(-1.3*20),
|
|
265
|
+
numpy.arcsinh(-20*_xbmax/_xbmin), 1000)
|
|
266
|
+
_xb = abs(_xbmin)*numpy.sinh(_xb)/20
|
|
267
|
+
_yb = Jb_exact2(_xb)
|
|
268
|
+
numpy.savetxt(_Jb_dat_path, numpy.array([_xb, _yb]).T)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
_tckb = interpolate.splrep(_xb, _yb)
|
|
272
|
+
def Jb_spline(X : float | numpy.ndarray, n : int = 0) -> float | numpy.ndarray:
|
|
273
|
+
"""Jb interpolated from a saved spline. Input is (m/T)^2."""
|
|
274
|
+
X = numpy.array(X, copy=False)
|
|
275
|
+
x = X.ravel()
|
|
276
|
+
y = interpolate.splev(x,_tckb, der=n).ravel()
|
|
277
|
+
y[x < _xbmin] = interpolate.splev(_xbmin,_tckb, der=n)
|
|
278
|
+
y[x > _xbmax] = 0
|
|
279
|
+
return y.reshape(X.shape)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# Now for the low x expansion (require that n <= 50)
|
|
283
|
+
a,b,c,d = -pi**4/45, pi*pi/12, -pi/6, -1/32.
|
|
284
|
+
logab = 1.5 - 2*euler_gamma + 2*log(4*pi)
|
|
285
|
+
l = numpy.arange(50)+1
|
|
286
|
+
g = (-2*pi**3.5 * (-1)**l*(1+special.zetac(2*l+1)) *
|
|
287
|
+
special.gamma(l+.5)/(fac(l+2)*(2*pi)**(2*l+4)))
|
|
288
|
+
lowCoef_b = (a,b,c,d,logab,l,g)
|
|
289
|
+
del (a,b,c,d,logab,l,g) # clean up name space
|
|
290
|
+
|
|
291
|
+
a,b,d = -7*pi**4/360, pi*pi/24, 1/32.
|
|
292
|
+
logaf = 1.5 - 2*euler_gamma + 2*log(pi)
|
|
293
|
+
l = numpy.arange(50)+1
|
|
294
|
+
g = (.25*pi**3.5 * (-1)**l*(1+special.zetac(2*l+1)) *
|
|
295
|
+
special.gamma(l+.5)*(1-.5**(2*l+1))/(fac(l+2)*pi**(2*l+4)))
|
|
296
|
+
lowCoef_f = (a,b,d,logaf,l,g)
|
|
297
|
+
del (a,b,d,logaf,l,g) # clean up name space
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def Jb_low(x : complex | float, n : int = 20) -> complex | float:
|
|
301
|
+
"""Jb calculated using the low-x (high-T) expansion."""
|
|
302
|
+
(a,b,c,d,logab,l,g) = lowCoef_b
|
|
303
|
+
y = a + x*x*(b + x*(c + d*x*(numpy.nan_to_num(log(x*x)) - logab)))
|
|
304
|
+
i = 1
|
|
305
|
+
while i <= n:
|
|
306
|
+
y += g[i-1]*x**(2*i+4)
|
|
307
|
+
i += 1
|
|
308
|
+
return y
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def Jf_low(x : complex | float, n : int = 20) -> complex | float:
|
|
312
|
+
"""Jf calculated using the low-x (high-T) expansion."""
|
|
313
|
+
(a,b,d,logaf,l,g) = lowCoef_f
|
|
314
|
+
y = a + x*x*(b + d*x*x*(numpy.nan_to_num(log(x*x)) - logaf))
|
|
315
|
+
i = 1
|
|
316
|
+
while i <= n:
|
|
317
|
+
y += g[i-1]*x**(2*i+4)
|
|
318
|
+
i += 1
|
|
319
|
+
return y
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# The next few functions are all for the high-T approximation
|
|
323
|
+
|
|
324
|
+
def x2K2(k : int, x : float | numpy.ndarray) -> float | numpy.ndarray:
|
|
325
|
+
"""Return ``-x^2 K_2(k x) / k^2`` with analytic behaviour enforced at the origin,
|
|
326
|
+
where ``K_2`` is the modified Bessel function of the second kind.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
k : int
|
|
331
|
+
Positive integer
|
|
332
|
+
x : float | numpy.ndarray
|
|
333
|
+
Dimensionless argument. Arrays are handled element-wise.
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
float | numpy.ndarray
|
|
338
|
+
Evaluated expression with the ``x -> 0`` limit patched in.
|
|
339
|
+
"""
|
|
340
|
+
y = -x*x*special.kn(2, k*x)/(k*k)
|
|
341
|
+
if(isinstance(x, numpy.ndarray)):
|
|
342
|
+
y[x == 0] = numpy.ones(len(y[x == 0]))*-2.0/k**4
|
|
343
|
+
elif(x == 0):
|
|
344
|
+
return -2.0/k**4
|
|
345
|
+
return y
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def dx2K2(k : int, x : float | numpy.ndarray) -> float | numpy.ndarray:
|
|
349
|
+
"""Return the first derivative of :func:`x2K2` with respect to ``x``."""
|
|
350
|
+
y = abs(x)
|
|
351
|
+
return numpy.nan_to_num(x*y*special.kn(1,k*y)/k)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def d2x2K2(k : int, x : float | numpy.ndarray) -> float | numpy.ndarray:
|
|
355
|
+
"""Return the second derivative of :func:`x2K2` with respect to ``x``."""
|
|
356
|
+
x = abs(x)
|
|
357
|
+
y = numpy.nan_to_num(x*(special.kn(1,k*x)/k - x*special.kn(0,k*x)))
|
|
358
|
+
if(isinstance(x, numpy.ndarray)):
|
|
359
|
+
y[x == 0] = numpy.ones(len(y[x == 0]))*1.0/k**2
|
|
360
|
+
elif(x == 0):
|
|
361
|
+
return 1.0/k**2
|
|
362
|
+
return y
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def d3x2K2(k : int, x : float | numpy.ndarray) -> float | numpy.ndarray:
|
|
366
|
+
"""Return the third derivative of :func:`x2K2` with respect to ``x``."""
|
|
367
|
+
y = abs(x)
|
|
368
|
+
return numpy.nan_to_num(x*(y*k*special.kn(1,k*y) - 3*special.kn(0,k*y)))
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def Jb_high(x : float | numpy.ndarray, deriv=0, n=8) -> float | numpy.ndarray:
|
|
372
|
+
"""Jb calculated using the high-x (low-T) expansion."""
|
|
373
|
+
K = (x2K2, dx2K2, d2x2K2, d3x2K2)[deriv]
|
|
374
|
+
y, k = 0.0, 1
|
|
375
|
+
while k <= n:
|
|
376
|
+
y += K(k,x)
|
|
377
|
+
k += 1
|
|
378
|
+
return y
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def Jf_high(x : float | numpy.ndarray, deriv=0, n=8) -> float | numpy.ndarray:
|
|
382
|
+
"""Jf calculated using the high-x (low-T) expansion."""
|
|
383
|
+
K = (x2K2, dx2K2, d2x2K2, d3x2K2)[deriv]
|
|
384
|
+
y, k, i = 0.0, 1, 1
|
|
385
|
+
while k <= n:
|
|
386
|
+
y += i*K(k,x)
|
|
387
|
+
i *= -1
|
|
388
|
+
k += 1
|
|
389
|
+
return y
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# And here are the final functions:
|
|
393
|
+
# Note that if approx = 'spline', the function called is
|
|
394
|
+
# J(theta) (x^2 -> theta so you can get negative mass squared)
|
|
395
|
+
def Jb(x : float | numpy.ndarray, approx : str ='high', deriv : int =0, n : int =8) -> float | numpy.ndarray:
|
|
396
|
+
"""
|
|
397
|
+
A shorthand for calling one of the Jb functions above.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
approx : str, optional
|
|
402
|
+
One of 'exact', 'high', 'low', or 'spline'.
|
|
403
|
+
deriv : int, optional
|
|
404
|
+
The order of the derivative (0 for no derivative).
|
|
405
|
+
Must be <= (1, 3, 0, 3) for approx = (exact, high, low, spline).
|
|
406
|
+
n : int, optional
|
|
407
|
+
Number of terms to use in the low and high-T approximations.
|
|
408
|
+
"""
|
|
409
|
+
if(approx == 'exact'):
|
|
410
|
+
if(deriv == 0):
|
|
411
|
+
return Jb_exact(x)
|
|
412
|
+
elif(deriv == 1):
|
|
413
|
+
return dJb_exact(x)
|
|
414
|
+
else:
|
|
415
|
+
raise ValueError("For approx=='exact', deriv must be 0 or 1.")
|
|
416
|
+
elif(approx == 'spline'):
|
|
417
|
+
return Jb_spline(x, deriv)
|
|
418
|
+
elif(approx == 'low'):
|
|
419
|
+
if(n > 100):
|
|
420
|
+
raise ValueError("Must have n <= 100")
|
|
421
|
+
if(deriv == 0):
|
|
422
|
+
return Jb_low(x,n)
|
|
423
|
+
else:
|
|
424
|
+
raise ValueError("For approx=='low', deriv must be 0.")
|
|
425
|
+
elif(approx == 'high'):
|
|
426
|
+
if(deriv > 3):
|
|
427
|
+
raise ValueError("For approx=='high', deriv must be 3 or less.")
|
|
428
|
+
else:
|
|
429
|
+
return Jb_high(x, deriv, n)
|
|
430
|
+
raise ValueError("Unexpected value for 'approx'.")
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def Jf(x : float | numpy.ndarray, approx : str ='high', deriv : int =0, n : int =8) -> float | numpy.ndarray:
|
|
434
|
+
"""
|
|
435
|
+
A shorthand for calling one of the Jf functions above.
|
|
436
|
+
|
|
437
|
+
Parameters
|
|
438
|
+
----------
|
|
439
|
+
approx : str, optional
|
|
440
|
+
One of 'exact', 'high', 'low', or 'spline'.
|
|
441
|
+
deriv : int, optional
|
|
442
|
+
The order of the derivative (0 for no derivative).
|
|
443
|
+
Must be <= (1, 3, 0, 3) for approx = (exact, high, low, spline).
|
|
444
|
+
n : int, optional
|
|
445
|
+
Number of terms to use in the low and high-T approximations.
|
|
446
|
+
"""
|
|
447
|
+
if(approx == 'exact'):
|
|
448
|
+
if(deriv == 0):
|
|
449
|
+
return Jf_exact(x)
|
|
450
|
+
elif(deriv == 1):
|
|
451
|
+
return dJf_exact(x)
|
|
452
|
+
else:
|
|
453
|
+
raise ValueError("For approx=='exact', deriv must be 0 or 1.")
|
|
454
|
+
elif(approx == 'spline'):
|
|
455
|
+
return Jf_spline(x, deriv)
|
|
456
|
+
elif(approx == 'low'):
|
|
457
|
+
if(n > 100):
|
|
458
|
+
raise ValueError("Must have n <= 100")
|
|
459
|
+
if(deriv == 0):
|
|
460
|
+
return Jf_low(x,n)
|
|
461
|
+
else:
|
|
462
|
+
raise ValueError("For approx=='low', deriv must be 0.")
|
|
463
|
+
elif(approx == 'high'):
|
|
464
|
+
if(deriv > 3):
|
|
465
|
+
raise ValueError("For approx=='high', deriv must be 3 or less.")
|
|
466
|
+
else:
|
|
467
|
+
return Jf_high(x, deriv, n)
|
|
468
|
+
raise ValueError("Unexpected value for 'approx'.")
|