pybhpt 0.9.4__cp39-cp39-macosx_14_0_arm64.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.
Potentially problematic release.
This version of pybhpt might be problematic. Click here for more details.
- cybhpt_full.cpython-39-darwin.so +0 -0
- pybhpt/.dylibs/libgsl.28.dylib +0 -0
- pybhpt/.dylibs/libgslcblas.0.dylib +0 -0
- pybhpt/__init__.py +0 -0
- pybhpt/flux.py +124 -0
- pybhpt/geo.py +407 -0
- pybhpt/hertz.py +402 -0
- pybhpt/metric.py +327 -0
- pybhpt/radial.py +381 -0
- pybhpt/redshift.py +20 -0
- pybhpt/swsh.py +347 -0
- pybhpt/teuk.py +245 -0
- pybhpt-0.9.4.dist-info/METADATA +135 -0
- pybhpt-0.9.4.dist-info/RECORD +16 -0
- pybhpt-0.9.4.dist-info/WHEEL +6 -0
- pybhpt-0.9.4.dist-info/licenses/LICENSE +674 -0
pybhpt/redshift.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from cybhpt_full import RedshiftCoefficients as RedshiftCoefficientsCython
|
|
2
|
+
from cybhpt_full import SphericalHarmonicCoupling as SphericalHarmonicCouplingCython
|
|
3
|
+
|
|
4
|
+
class RedshiftCoefficients:
|
|
5
|
+
def __init__(self, gauge, geo):
|
|
6
|
+
self.base = RedshiftCoefficientsCython(gauge, geo.base)
|
|
7
|
+
|
|
8
|
+
def __call__(self, Ni, ai, bi, ci, di, jr = 0, jz = 0):
|
|
9
|
+
return self.base(Ni, ai, bi, ci, di, jr, jz)
|
|
10
|
+
|
|
11
|
+
class SphericalHarmonicCoupling:
|
|
12
|
+
def __init__(self, lmax, m):
|
|
13
|
+
self.base = SphericalHarmonicCouplingCython(lmax, m)
|
|
14
|
+
self.azimuthalmode = self.base.azimuthalmode
|
|
15
|
+
|
|
16
|
+
def zcouplingcoefficient(self, n, i, l):
|
|
17
|
+
return self.base.zcouplingcoefficient(n, i, l)
|
|
18
|
+
|
|
19
|
+
def dzcouplingcoefficient(self, n, i, l):
|
|
20
|
+
return self.base.dzcouplingcoefficient(n, i, l)
|
pybhpt/swsh.py
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import scipy.sparse
|
|
2
|
+
import scipy.sparse.linalg
|
|
3
|
+
from scipy.special import sph_harm
|
|
4
|
+
from scipy.special import binom
|
|
5
|
+
from scipy.special import factorial
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Wigner 3j-symbol and Clebsch-Gordon coefficients
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def fac(n):
|
|
13
|
+
if n < 0:
|
|
14
|
+
return 0
|
|
15
|
+
return float(np.math.factorial(n))
|
|
16
|
+
|
|
17
|
+
def Yslm(s, l, m, th):
|
|
18
|
+
if np.abs(s) > l:
|
|
19
|
+
return 0.*th
|
|
20
|
+
if s == 0:
|
|
21
|
+
return np.real(sph_harm(m, l, 0., th))
|
|
22
|
+
elif s + m < 0:
|
|
23
|
+
return (-1.)**(s+m)*YslmBase(-s, l, -m, np.cos(th))
|
|
24
|
+
# elif th > np.pi/2.:
|
|
25
|
+
# return (-1.)**(l + m)*YslmBase(-s, l, m, -np.cos(th))
|
|
26
|
+
else:
|
|
27
|
+
return YslmBase(s, l, m, np.cos(th))
|
|
28
|
+
|
|
29
|
+
def YslmBase(s, l, m, z):
|
|
30
|
+
rmax = l - s
|
|
31
|
+
pref = (0.5)**(l)*(-1.)**m*np.sqrt(factorial(l+m)/factorial(l+s)*factorial(l-m)/factorial(l-s)*(2*l+1)/(4.*np.pi))*np.sqrt(1. - z)**(s + m)*np.sqrt(1. + z)**(s - m)
|
|
32
|
+
|
|
33
|
+
yslm = 0.*pref
|
|
34
|
+
for r in range(0, rmax + 1):
|
|
35
|
+
yslm += binom(l - s, r)*binom(l + s, r + s - m)*(z - 1.)**(rmax - r)*(z + 1.)**(r)
|
|
36
|
+
|
|
37
|
+
return pref*yslm
|
|
38
|
+
|
|
39
|
+
def clebsch(l1, l2, l3, m1, m2, m3):
|
|
40
|
+
return (-1)**(l1 - l2 + m3)*np.sqrt(2*l3 + 1)*w3j(l1, l2, l3, m1, m2, -m3);
|
|
41
|
+
|
|
42
|
+
def w3j(l1, l2, l3, m1, m2, m3):
|
|
43
|
+
if m1 + m2 + m3 != 0:
|
|
44
|
+
return 0
|
|
45
|
+
elif abs(l1 - l2) > l3:
|
|
46
|
+
return 0
|
|
47
|
+
elif l1 + l2 < l3:
|
|
48
|
+
return 0
|
|
49
|
+
|
|
50
|
+
if abs(m1) > l1:
|
|
51
|
+
return 0
|
|
52
|
+
elif abs(m2) > l2:
|
|
53
|
+
return 0
|
|
54
|
+
elif abs(m3) > l3:
|
|
55
|
+
return 0
|
|
56
|
+
|
|
57
|
+
sumTerm = w3j_tsum(l1, l2, l3, m1, m2, m3)
|
|
58
|
+
if sumTerm == 0:
|
|
59
|
+
return 0
|
|
60
|
+
sumSign = np.sign(sumTerm)
|
|
61
|
+
tempLog = 0.5*(np.log(fac(l1 + m1)) + np.log(fac(l2 + m2)) + np.log(fac(l3 + m3)))
|
|
62
|
+
tempLog += 0.5*(np.log(fac(l1 - m1)) + np.log(fac(l2 - m2)) + np.log(fac(l3 - m3)))
|
|
63
|
+
tempLog += np.log(triangle_coeff(l1, l2, l3))
|
|
64
|
+
tempLog += np.log(abs(sumTerm))
|
|
65
|
+
|
|
66
|
+
temp = sumSign*np.exp(tempLog)
|
|
67
|
+
temp *= (-1)**(l1-l2-m3)
|
|
68
|
+
|
|
69
|
+
return temp
|
|
70
|
+
|
|
71
|
+
def w3j_tsum(l1, l2, l3, m1, m2, m3):
|
|
72
|
+
t_min_num = w3j_t_min(l1, l2, l3, m1, m2, m3)
|
|
73
|
+
t_max_num = w3j_t_max(l1, l2, l3, m1, m2, m3)
|
|
74
|
+
x = 0
|
|
75
|
+
if t_max_num < t_min_num:
|
|
76
|
+
t_max_num = t_min_num
|
|
77
|
+
|
|
78
|
+
for t in range(t_min_num - 1, t_max_num + 2):
|
|
79
|
+
term = (fac(t)*fac(l3 - l2 + m1 + t)*fac(l3 - l1 - m2 + t)
|
|
80
|
+
*fac(l1 + l2 - l3 - t)*fac(l1 - t - m1)*fac(l2 - t + m2))
|
|
81
|
+
if term > 0:
|
|
82
|
+
x += (-1)**t/term
|
|
83
|
+
|
|
84
|
+
return x
|
|
85
|
+
|
|
86
|
+
def w3j_t_min(l1, l2, l3, m1, m2, m3):
|
|
87
|
+
temp = 0
|
|
88
|
+
|
|
89
|
+
comp = l3 - l2 + m1
|
|
90
|
+
if temp + comp < 0:
|
|
91
|
+
temp = -comp
|
|
92
|
+
comp = l3 - l1 - m2
|
|
93
|
+
if temp + comp < 0:
|
|
94
|
+
temp = -comp
|
|
95
|
+
|
|
96
|
+
return temp
|
|
97
|
+
|
|
98
|
+
def w3j_t_max(l1, l2, l3, m1, m2, m3):
|
|
99
|
+
temp = 1
|
|
100
|
+
comp = l1 + l2 - l3
|
|
101
|
+
if comp - temp > 0:
|
|
102
|
+
temp = comp
|
|
103
|
+
comp = l1 - m1
|
|
104
|
+
if comp - temp > 0:
|
|
105
|
+
temp = comp
|
|
106
|
+
comp = l2 + m2
|
|
107
|
+
if comp - temp > 0:
|
|
108
|
+
temp = comp
|
|
109
|
+
|
|
110
|
+
return temp;
|
|
111
|
+
|
|
112
|
+
def triangle_coeff(l1, l2, l3):
|
|
113
|
+
return np.sqrt(fac(l1 + l2 - l3)*fac(l3 + l1 - l2)*fac(l2 + l3 - l1)/fac(l1 + l2 + l3 + 1))
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
SWSH Eigenvalue Functions
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def k1(s, l, j, m):
|
|
120
|
+
return np.sqrt((2*l + 1)/(2*j + 1))*clebsch(l, 1, j, m, 0, m)*clebsch(l, 1, j, -s, 0, -s);
|
|
121
|
+
|
|
122
|
+
def k2(s, l, j, m):
|
|
123
|
+
ktemp = 2./3.*np.sqrt((2*l + 1)/(2*j + 1))*clebsch(l, 2, j, m, 0, m)*clebsch(l, 2, j, -s, 0, -s);
|
|
124
|
+
if j == l:
|
|
125
|
+
ktemp += 1/3.
|
|
126
|
+
return ktemp
|
|
127
|
+
|
|
128
|
+
def k2m2(s, l, m):
|
|
129
|
+
temp = (l - m - 1.)/(l - 1.)
|
|
130
|
+
temp *= (l + m - 1.)/(l - 1.)
|
|
131
|
+
temp *= np.float64(l - m)/l
|
|
132
|
+
temp *= np.float64(l + m)/l
|
|
133
|
+
temp *= (l - s)/(2.*l - 1.)
|
|
134
|
+
temp *= (l + s)/(2.*l - 1.)
|
|
135
|
+
temp *= (l - s - 1.)/(2.*l + 1.)
|
|
136
|
+
temp *= (l + s - 1.)/(2.*l - 3.)
|
|
137
|
+
return np.sqrt(temp)
|
|
138
|
+
|
|
139
|
+
def k2m1(s, l, m):
|
|
140
|
+
temp = np.float64(l - m)*np.float64(l + m)/(2.*l - 1.)
|
|
141
|
+
temp *= np.float64(l - s)*np.float64(l + s)/(2.*l + 1.)
|
|
142
|
+
return -2.*m*s*np.sqrt(temp)/l/(l - 1.)/(l + 1.)
|
|
143
|
+
|
|
144
|
+
def k2p0(s, l, m):
|
|
145
|
+
temp = np.float64(l*(l + 1.) - 3.*m*m)/(2.*l - 1.)/l
|
|
146
|
+
temp *= np.float64(l*(l + 1.) - 3.*s*s)/(2.*l + 3.)/(l + 1.)
|
|
147
|
+
return 1./3.*(1. + 2.*temp)
|
|
148
|
+
|
|
149
|
+
def k2p1(s, l, m):
|
|
150
|
+
temp = np.float64(l - m + 1.)*np.float64(l + m + 1.)/(2.*l + 1.)
|
|
151
|
+
temp *= np.float64(l - s + 1.)*np.float64(l + s + 1.)/(2.*l + 3.)
|
|
152
|
+
return -2.*m*s*np.sqrt(temp)/l/(l + 1.)/(l + 2.)
|
|
153
|
+
|
|
154
|
+
def k2p2(s, l, m):
|
|
155
|
+
temp = (l - m + 1.)/(l + 1.)
|
|
156
|
+
temp *= (l + m + 1.)/(l + 1.)
|
|
157
|
+
temp *= (l - m + 2.)/(l + 2.)
|
|
158
|
+
temp *= (l + m + 2.)/(l + 2.)
|
|
159
|
+
temp *= (l - s + 2.)/(2.*l + 3.)
|
|
160
|
+
temp *= (l + s + 2.)/(2.*l + 3.)
|
|
161
|
+
temp *= (l - s + 1.)/(2.*l + 1.)
|
|
162
|
+
temp *= (l + s + 1.)/(2.*l + 5.)
|
|
163
|
+
return np.sqrt(temp)
|
|
164
|
+
|
|
165
|
+
def k1m1(s, l, m):
|
|
166
|
+
temp = np.float64(l - m)*np.float64(l + m)/(2.*l - 1.)
|
|
167
|
+
temp *= np.float64(l - s)*np.float64(l + s)/(2.*l + 1.)
|
|
168
|
+
return np.sqrt(temp)/l
|
|
169
|
+
|
|
170
|
+
def k1p0(s, l, m):
|
|
171
|
+
return -np.float64(m*s)/l/(l + 1.)
|
|
172
|
+
|
|
173
|
+
def k1p1(s, l, m):
|
|
174
|
+
temp = np.float64(l - m + 1.)*np.float64(l + m + 1.)/(2.*l + 3.)
|
|
175
|
+
temp *= np.float64(l - s + 1.)*np.float64(l + s + 1.)/(2.*l + 1.)
|
|
176
|
+
return np.sqrt(temp)/(l + 1.)
|
|
177
|
+
|
|
178
|
+
def akm2(s, l, m, g):
|
|
179
|
+
return -g*g*k2m2(s, l, m)
|
|
180
|
+
|
|
181
|
+
def akm1(s, l, m, g):
|
|
182
|
+
return -g*g*k2m1(s, l, m) + 2.*s*g*k1m1(s, l, m)
|
|
183
|
+
|
|
184
|
+
def akp0(s, l, m, g):
|
|
185
|
+
return -g*g*k2p0(s, l, m) + 2.*s*g*k1p0(s, l, m) + l*(l + 1.) - s*(s + 1.) - 2.*m*g + g*g
|
|
186
|
+
|
|
187
|
+
def akp1(s, l, m, g):
|
|
188
|
+
return -g*g*k2p1(s, l, m) + 2.*s*g*k1p1(s, l, m)
|
|
189
|
+
|
|
190
|
+
def akp2(s, l, m, g):
|
|
191
|
+
return -g*g*k2p2(s, l, m)
|
|
192
|
+
|
|
193
|
+
def spectral_sparse_matrix(s, m, g, nmax):
|
|
194
|
+
lmin = max(abs(s), abs(m))
|
|
195
|
+
larray = np.arange(lmin, lmin + nmax)
|
|
196
|
+
return scipy.sparse.diags([akm2(s, larray[2:], m, g), akm1(s, larray[1:], m, g), akp0(s, larray, m, g), akp1(s, larray[:-1], m, g), akp2(s, larray[:-2], m, g)], [-2, -1, 0, 1, 2])
|
|
197
|
+
|
|
198
|
+
def swsh_eigs(s, l, m, g, nmax=None, return_eigenvectors=True):
|
|
199
|
+
lmin = max(abs(s), abs(m))
|
|
200
|
+
kval = l - lmin
|
|
201
|
+
|
|
202
|
+
if nmax is None:
|
|
203
|
+
buffer = round(20 + 2*g)
|
|
204
|
+
Nmax = kval + buffer + 2
|
|
205
|
+
else:
|
|
206
|
+
if nmax < kval:
|
|
207
|
+
Nmax = kval + 5
|
|
208
|
+
else:
|
|
209
|
+
Nmax = nmax
|
|
210
|
+
|
|
211
|
+
mat = spectral_sparse_matrix(s, m, g, Nmax)
|
|
212
|
+
out = scipy.sparse.linalg.eigs(mat, k=Nmax-2, which='SM', return_eigenvectors=return_eigenvectors)
|
|
213
|
+
|
|
214
|
+
return out
|
|
215
|
+
|
|
216
|
+
def Yslm_eigenvalue(s, l, *args):
|
|
217
|
+
return l*(l + 1.) - s*(s + 1.)
|
|
218
|
+
|
|
219
|
+
def swsh_eigenvalue(s, l, m, g, nmax=None):
|
|
220
|
+
if g == 0.:
|
|
221
|
+
return Yslm_eigenvalue(s, l)
|
|
222
|
+
|
|
223
|
+
las = swsh_eigs(s, l, m, g, nmax=nmax, return_eigenvectors=False)
|
|
224
|
+
|
|
225
|
+
return np.real(las[::-1][l - max(abs(s), abs(m))])
|
|
226
|
+
|
|
227
|
+
def swsh_coeffs(s, l, m, g, th):
|
|
228
|
+
if g == 0.:
|
|
229
|
+
return Yslm(s, l, m, th)
|
|
230
|
+
|
|
231
|
+
_, eig = swsh_eigs(s, l, m, g, nmax=None, return_eigenvectors=True)
|
|
232
|
+
coeffs = np.real(eig[l - max(abs(s), abs(m))])
|
|
233
|
+
|
|
234
|
+
return np.real(eig[l - max(abs(s), abs(m))])
|
|
235
|
+
|
|
236
|
+
class SWSHBase:
|
|
237
|
+
def __init__(self, *args):
|
|
238
|
+
arg_num = np.array(args).shape[0]
|
|
239
|
+
if arg_num < 3:
|
|
240
|
+
print('Error. Not enough arguments to create class')
|
|
241
|
+
pass
|
|
242
|
+
|
|
243
|
+
self.s = args[0]
|
|
244
|
+
self.l = args[1]
|
|
245
|
+
self.m = args[2]
|
|
246
|
+
self.lmin = max(abs(self.s), abs(self.m))
|
|
247
|
+
|
|
248
|
+
if arg_num > 3:
|
|
249
|
+
self.spheroidicity = args[3]
|
|
250
|
+
|
|
251
|
+
class SWSHSeriesBase(SWSHBase):
|
|
252
|
+
def __init__(self, s, l, m, g):
|
|
253
|
+
SWSHBase.__init__(self, s, l, m, g)
|
|
254
|
+
|
|
255
|
+
def sparse_matrix(self, nmax):
|
|
256
|
+
return spectral_sparse_matrix(self.s, self.m, self.spheroidicity, nmax)
|
|
257
|
+
|
|
258
|
+
def eigs(self, nmax = None, **kwargs):
|
|
259
|
+
kval = self.l - self.lmin
|
|
260
|
+
|
|
261
|
+
if nmax is None:
|
|
262
|
+
buffer = round(20 + 2*self.spheroidicity)
|
|
263
|
+
Nmax = kval + buffer + 2
|
|
264
|
+
else:
|
|
265
|
+
if nmax < kval:
|
|
266
|
+
Nmax = kval + 5
|
|
267
|
+
else:
|
|
268
|
+
Nmax = nmax
|
|
269
|
+
|
|
270
|
+
if "k" not in kwargs.keys():
|
|
271
|
+
kwargs["k"] = Nmax - 2
|
|
272
|
+
|
|
273
|
+
if "which" not in kwargs.keys():
|
|
274
|
+
kwargs["which"] = 'SM'
|
|
275
|
+
|
|
276
|
+
# if "sigma" not in kwargs.keys():
|
|
277
|
+
# kwargs["sigma"] = (self.l + Nmax)*(self.l + Nmax + 1) - self.s*(self.s + 1)
|
|
278
|
+
|
|
279
|
+
if "return_eigenvectors" not in kwargs.keys():
|
|
280
|
+
kwargs["return_eigenvectors"] = True
|
|
281
|
+
|
|
282
|
+
mat = self.sparse_matrix(Nmax)
|
|
283
|
+
return scipy.sparse.linalg.eigs(mat, **kwargs)
|
|
284
|
+
|
|
285
|
+
def generate_eigenvalue(self):
|
|
286
|
+
las = np.real(self.eigs(return_eigenvectors=False))
|
|
287
|
+
pos = np.argsort(las)[self.l - self.lmin]
|
|
288
|
+
return las[pos]
|
|
289
|
+
|
|
290
|
+
def generate_eigs(self):
|
|
291
|
+
las, eigs = self.eigs()
|
|
292
|
+
pos = np.argsort(np.real(las))[self.l - self.lmin]
|
|
293
|
+
eigs_temp = np.real(eigs[:, pos])
|
|
294
|
+
eigs_return = np.sign(eigs_temp[self.l - self.lmin])*eigs_temp
|
|
295
|
+
return (np.real(las[pos]), eigs_return)
|
|
296
|
+
|
|
297
|
+
class SWSH(SWSHSeriesBase):
|
|
298
|
+
def __init__(self, s, l, m, g):
|
|
299
|
+
SWSHSeriesBase.__init__(self, s, l, m, g)
|
|
300
|
+
if self.spheroidicity == 0.:
|
|
301
|
+
self.eval = self.Yslm
|
|
302
|
+
self.eigenvalue = Yslm_eigenvalue(self.s, self.l)
|
|
303
|
+
self.coeffs = np.zeros(self.l - self.lmin)
|
|
304
|
+
self.coeffs[-1] = 1.
|
|
305
|
+
else:
|
|
306
|
+
self.eval = self.Sslm
|
|
307
|
+
self.eigenvalue, self.coeffs = self.generate_eigs()
|
|
308
|
+
|
|
309
|
+
def Yslm(self, l, th):
|
|
310
|
+
return Yslm(self.s, l, self.m, th)
|
|
311
|
+
|
|
312
|
+
def Sslm(self, *args):
|
|
313
|
+
th = args[-1]
|
|
314
|
+
term_num = self.coeffs.shape[0]
|
|
315
|
+
pts_num = th.shape[0]
|
|
316
|
+
Yslm_array = np.empty((term_num, pts_num))
|
|
317
|
+
for i in range(term_num):
|
|
318
|
+
Yslm_array[i] = self.Yslm(self.lmin + i, th)
|
|
319
|
+
|
|
320
|
+
return np.dot(self.coeffs, Yslm_array)
|
|
321
|
+
|
|
322
|
+
def __call__(self, th):
|
|
323
|
+
return self.eval(self.l, th)
|
|
324
|
+
|
|
325
|
+
def muCoupling(s, l):
|
|
326
|
+
"""
|
|
327
|
+
Eigenvalue for the spin-weighted spherical harmonic lowering operator
|
|
328
|
+
Setting s -> -s gives the negative of the eigenvalue for the raising operator
|
|
329
|
+
"""
|
|
330
|
+
if l + s < 0 or l - s + 1. < 0:
|
|
331
|
+
return 0
|
|
332
|
+
return np.sqrt((l - s + 1.)*(l + s))
|
|
333
|
+
|
|
334
|
+
def Asjlm(s, j, l, m):
|
|
335
|
+
if s >= 0:
|
|
336
|
+
return (-1.)**(m + s)*np.sqrt(4**s*fac(s)**2*(2*l + 1)*(2*j + 1)/fac(2*s))*w3j(s, l, j, 0, m, -m)*w3j(s, l, j, s, -s, 0)
|
|
337
|
+
else:
|
|
338
|
+
return (-1.)**(m)*np.sqrt(4**(-s)*fac(-s)**2*(2*l + 1)*(2*j + 1)/fac(-2*s))*w3j(-s, l, j, 0, m, -m)*w3j(-s, l, j, s, -s, 0)
|
|
339
|
+
|
|
340
|
+
def spin_operator_normalization(s, ns, l):
|
|
341
|
+
s_sgn = np.sign(s)
|
|
342
|
+
nmax1 = np.abs(s) + 1
|
|
343
|
+
Jterm = 1.
|
|
344
|
+
for ni in range(1, ns + 1):
|
|
345
|
+
Jterm *= -s_sgn*muCoupling((nmax1-ni), l)
|
|
346
|
+
return Jterm
|
|
347
|
+
|
pybhpt/teuk.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
from cybhpt_full import TeukolskyMode as TeukolskyModeCython
|
|
2
|
+
|
|
3
|
+
class TeukolskyMode:
|
|
4
|
+
"""A class for computing Teukolsky modes sourced by a point-particle orbiting in a Kerr background.
|
|
5
|
+
|
|
6
|
+
Parameters
|
|
7
|
+
----------
|
|
8
|
+
s : int
|
|
9
|
+
The spin weight of the Teukolsky mode.
|
|
10
|
+
j : int
|
|
11
|
+
The spheroidal harmonic mode number.
|
|
12
|
+
m : int
|
|
13
|
+
The azimuthal harmonic mode number.
|
|
14
|
+
k : int
|
|
15
|
+
The polar harmonic mode number.
|
|
16
|
+
n : int
|
|
17
|
+
The radial harmonic mode number.
|
|
18
|
+
geo : KerrGeodesic class instance
|
|
19
|
+
KerrGeodesic object containing the background motion of the point-particle source.
|
|
20
|
+
auto_solve : bool, optional
|
|
21
|
+
If True, the Teukolsky equation is automatically solved upon initialization. Default is False
|
|
22
|
+
|
|
23
|
+
Attributes
|
|
24
|
+
----------
|
|
25
|
+
spinweight : int
|
|
26
|
+
The spin weight of the Teukolsky mode.
|
|
27
|
+
spheroidalmode : int
|
|
28
|
+
The spheroidal harmonic mode number.
|
|
29
|
+
azimuthalmode : int
|
|
30
|
+
The azimuthal harmonic mode number.
|
|
31
|
+
radialmode : int
|
|
32
|
+
The radial harmonic mode number.
|
|
33
|
+
polarmode : int
|
|
34
|
+
The polar harmonic mode number.
|
|
35
|
+
blackholespin : float
|
|
36
|
+
The spin of the black hole in the Kerr background.
|
|
37
|
+
frequency : float
|
|
38
|
+
The frequency of the Teukolsky mode.
|
|
39
|
+
horizonfrequency : float
|
|
40
|
+
The frequency of the mode at the horizon.
|
|
41
|
+
eigenvalue : float
|
|
42
|
+
The spheroidal eigenvalue of the Teukolsky mode.
|
|
43
|
+
mincouplingmode : int
|
|
44
|
+
The minimum l-mode used for coupling the spherical and spheroidal harmonics
|
|
45
|
+
maxcouplingmode : int
|
|
46
|
+
The maximum l-mode used for coupling the spherical and spheroidal harmonics
|
|
47
|
+
j : int
|
|
48
|
+
Alias for spheroidalmode.
|
|
49
|
+
m : int
|
|
50
|
+
Alias for azimuthalmode.
|
|
51
|
+
k : int
|
|
52
|
+
Alias for polarmode.
|
|
53
|
+
n : int
|
|
54
|
+
Alias for radialmode.
|
|
55
|
+
omega : float
|
|
56
|
+
Alias for frequency.
|
|
57
|
+
a : float
|
|
58
|
+
Alias for blackholespin.
|
|
59
|
+
|
|
60
|
+
Methods
|
|
61
|
+
-------
|
|
62
|
+
solve(geo, method = "AUTO", nsamples = 256, teuk = None, swsh = None)
|
|
63
|
+
Solve the Teukolsky equation for the given mode and geodesic.
|
|
64
|
+
flipspinweight()
|
|
65
|
+
Flip the spin weight of the Teukolsky mode.
|
|
66
|
+
flipspinweightandfrequency()
|
|
67
|
+
Flip the spin weight and frequency of the Teukolsky mode.
|
|
68
|
+
couplingcoefficient(l)
|
|
69
|
+
Compute the coupling coefficient for the given l-mode.
|
|
70
|
+
radialpoint(pos)
|
|
71
|
+
Compute the radial point for the given position.
|
|
72
|
+
radialsolution(bc, pos)
|
|
73
|
+
Compute the radial solution for the given boundary condition and position.
|
|
74
|
+
radialderivative(bc, pos)
|
|
75
|
+
Compute the radial derivative for the given boundary condition and position.
|
|
76
|
+
radialderivative2(bc, pos)
|
|
77
|
+
Compute the second radial derivative for the given boundary condition and position.
|
|
78
|
+
homogeneousradialsolution(bc, pos)
|
|
79
|
+
Compute the homogeneous radial solution for the given boundary condition and position.
|
|
80
|
+
homogeneousradialderivative(bc, pos)
|
|
81
|
+
Compute the homogeneous radial derivative for the given boundary condition and position.
|
|
82
|
+
homogeneousradialderivative2(bc, pos)
|
|
83
|
+
Compute the second homogeneous radial derivative for the given boundary condition and position.
|
|
84
|
+
polarpoint(pos)
|
|
85
|
+
Compute the polar point for the given position.
|
|
86
|
+
polarsolution(pos)
|
|
87
|
+
Compute the polar solution for the given position.
|
|
88
|
+
polarderivative(pos)
|
|
89
|
+
Compute the polar derivative for the given position.
|
|
90
|
+
polarderivative2(pos)
|
|
91
|
+
Compute the second polar derivative for the given position.
|
|
92
|
+
amplitude(bc)
|
|
93
|
+
Compute the Teukolsky amplitude for the given boundary condition.
|
|
94
|
+
precision(bc)
|
|
95
|
+
Compute the precision of the Teukolsky amplitude for the given boundary condition.
|
|
96
|
+
"""
|
|
97
|
+
def __init__(self, s, j, m, k, n, geo, auto_solve = False):
|
|
98
|
+
self.base = TeukolskyModeCython(s, j, m, k, n, geo.base)
|
|
99
|
+
if auto_solve:
|
|
100
|
+
self.solve(geo)
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def spinweight(self):
|
|
104
|
+
return self.base.spinweight
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def spheroidalmode(self):
|
|
108
|
+
return self.base.spheroidalmode
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def azimuthalmode(self):
|
|
112
|
+
return self.base.azimuthalmode
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def radialmode(self):
|
|
116
|
+
return self.base.radialmode
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def polarmode(self):
|
|
120
|
+
return self.base.polarmode
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def blackholespin(self):
|
|
124
|
+
return self.base.blackholespin
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def frequency(self):
|
|
128
|
+
return self.base.frequency
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def horizonfrequency(self):
|
|
132
|
+
return self.base.horizonfrequency
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def eigenvalue(self):
|
|
136
|
+
return self.base.eigenvalue
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def mincouplingmode(self):
|
|
140
|
+
return self.base.mincouplingmode
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def maxcouplingmode(self):
|
|
144
|
+
return self.base.maxcouplingmode
|
|
145
|
+
|
|
146
|
+
# some useful aliases
|
|
147
|
+
@property
|
|
148
|
+
def j(self):
|
|
149
|
+
return self.spheroidalmode
|
|
150
|
+
@property
|
|
151
|
+
def m(self):
|
|
152
|
+
return self.azimuthalmode
|
|
153
|
+
@property
|
|
154
|
+
def k(self):
|
|
155
|
+
return self.polarmode
|
|
156
|
+
@property
|
|
157
|
+
def n(self):
|
|
158
|
+
return self.radialmode
|
|
159
|
+
@property
|
|
160
|
+
def omega(self):
|
|
161
|
+
return self.frequency
|
|
162
|
+
@property
|
|
163
|
+
def a(self):
|
|
164
|
+
return self.blackholespin
|
|
165
|
+
|
|
166
|
+
def solve(self, geo, method = "AUTO", nsamples = 256, teuk = None, swsh = None):
|
|
167
|
+
"""Solve the Teukolsky equation for the given mode and geodesic.
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
geo : KerrGeodesic class instance
|
|
171
|
+
KerrGeodesic object containing the background motion of the point-particle source.
|
|
172
|
+
method : str, optional
|
|
173
|
+
The method to use for solving the Teukolsky equation. Default is "AUTO".
|
|
174
|
+
nsamples : int, optional
|
|
175
|
+
The number of samples to use for the solution. Default is 256.
|
|
176
|
+
teuk : RadialTeukolsky, optional
|
|
177
|
+
RadialTeukolsky object to use for constructing the radial Green function. Default is None.
|
|
178
|
+
swsh : SpheroidalHarmonicMode, optional
|
|
179
|
+
SpheroidalHarmonic object to use for coupling with spheroidal harmonics. Default is None.
|
|
180
|
+
|
|
181
|
+
"""
|
|
182
|
+
if teuk is None or swsh is None:
|
|
183
|
+
self.base.solve(geo.base, method, nsamples)
|
|
184
|
+
else:
|
|
185
|
+
self.base.solve(geo.base, method, nsamples, teuk.base, swsh.base)
|
|
186
|
+
|
|
187
|
+
"""
|
|
188
|
+
Flips the spin-weight of the Teukolsky solutions from :math:`s \rightarrow -s`
|
|
189
|
+
"""
|
|
190
|
+
def flipspinweight(self):
|
|
191
|
+
self.base.flip_spinweight()
|
|
192
|
+
|
|
193
|
+
"""
|
|
194
|
+
Flips the spin-weight and frequency of the Teukolsky solutions from :math:`s \rightarrow -s` and :math:`\omega \rightarrow -\omega`
|
|
195
|
+
"""
|
|
196
|
+
def flipspinweightandfrequency(self):
|
|
197
|
+
self.base.flip_spinweight_frequency()
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
Spherical-spheroidal mixing coefficient between a spherical harmonic :math:`l` mode with a spheroidal :math:`j` mode
|
|
201
|
+
|
|
202
|
+
:param l: spherical harmonic mode
|
|
203
|
+
:type l: int
|
|
204
|
+
"""
|
|
205
|
+
def couplingcoefficient(self, l):
|
|
206
|
+
return self.base.couplingcoefficient(l)
|
|
207
|
+
|
|
208
|
+
def radialpoint(self, pos):
|
|
209
|
+
return self.base.radialpoint(pos)
|
|
210
|
+
|
|
211
|
+
def radialsolution(self, bc, pos):
|
|
212
|
+
return self.base.radialsolution(bc, pos)
|
|
213
|
+
|
|
214
|
+
def radialderivative(self, bc, pos):
|
|
215
|
+
return self.base.radialderivative(bc, pos)
|
|
216
|
+
|
|
217
|
+
def radialderivative2(self, bc, pos):
|
|
218
|
+
return self.base.radialderivative2(bc, pos)
|
|
219
|
+
|
|
220
|
+
def homogeneousradialsolution(self, bc, pos):
|
|
221
|
+
return self.base.homogeneousradialsolution(bc, pos)
|
|
222
|
+
|
|
223
|
+
def homogeneousradialderivative(self, bc, pos):
|
|
224
|
+
return self.base.homogeneousradialderivative(bc, pos)
|
|
225
|
+
|
|
226
|
+
def homogeneousradialderivative2(self, bc, pos):
|
|
227
|
+
return self.base.homogeneousradialderivative2(bc, pos)
|
|
228
|
+
|
|
229
|
+
def polarpoint(self, pos):
|
|
230
|
+
return self.base.polarpoint(pos)
|
|
231
|
+
|
|
232
|
+
def polarsolution(self, pos):
|
|
233
|
+
return self.base.polarsolution(pos)
|
|
234
|
+
|
|
235
|
+
def polarderivative(self, pos):
|
|
236
|
+
return self.base.polarderivative(pos)
|
|
237
|
+
|
|
238
|
+
def polarderivative2(self, pos):
|
|
239
|
+
return self.base.polarderivative2(pos)
|
|
240
|
+
|
|
241
|
+
def amplitude(self, bc):
|
|
242
|
+
return self.base.teukolsky_amplitude(bc)
|
|
243
|
+
|
|
244
|
+
def precision(self, bc):
|
|
245
|
+
return self.base.teukolsky_amplitude_precision(bc)
|