pybhpt 0.9.4__cp314-cp314-macosx_14_0_x86_64.whl → 0.9.8__cp314-cp314-macosx_14_0_x86_64.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-314-darwin.so +0 -0
- pybhpt/geo.py +371 -11
- pybhpt/radial.py +70 -7
- pybhpt/swsh.py +254 -117
- pybhpt/teuk.py +197 -6
- {pybhpt-0.9.4.dist-info → pybhpt-0.9.8.dist-info}/METADATA +14 -4
- pybhpt-0.9.8.dist-info/RECORD +16 -0
- {pybhpt-0.9.4.dist-info → pybhpt-0.9.8.dist-info}/WHEEL +1 -1
- pybhpt-0.9.4.dist-info/RECORD +0 -16
- {pybhpt-0.9.4.dist-info → pybhpt-0.9.8.dist-info}/licenses/LICENSE +0 -0
|
Binary file
|
pybhpt/geo.py
CHANGED
|
@@ -4,21 +4,147 @@ from cybhpt_full import kerr_mino_frequencies_wrapper, kerr_orbital_constants_wr
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
6
|
def kerrgeo_Vt_radial(a, En, Lz, Q, r):
|
|
7
|
+
"""
|
|
8
|
+
The radial part of the potential Vt for the geodesic evolution of $t_p$.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
a : float
|
|
13
|
+
The black hole spin parameter.
|
|
14
|
+
En : float
|
|
15
|
+
The orbital energy of the particle.
|
|
16
|
+
Lz : float
|
|
17
|
+
The z-component of the orbital angular momentum of the particle.
|
|
18
|
+
Q : float
|
|
19
|
+
The Carter constant of the particle.
|
|
20
|
+
r : float
|
|
21
|
+
The radial coordinate.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
float
|
|
26
|
+
The value of the radial potential Vt at the given parameters.
|
|
27
|
+
"""
|
|
7
28
|
return kerr_geo_V01(a, En, Lz, Q, r)
|
|
8
29
|
|
|
9
30
|
def kerrgeo_Vt_polar(a, En, Lz, Q, theta):
|
|
31
|
+
"""
|
|
32
|
+
The polar part of the potential Vt for the geodesic evolution of $t_p$.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
a : float
|
|
37
|
+
The black hole spin parameter.
|
|
38
|
+
En : float
|
|
39
|
+
The orbital energy of the particle.
|
|
40
|
+
Lz : float
|
|
41
|
+
The z-component of the orbital angular momentum of the particle.
|
|
42
|
+
Q : float
|
|
43
|
+
The Carter constant of the particle.
|
|
44
|
+
theta : float
|
|
45
|
+
The polar angle coordinate.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
float
|
|
50
|
+
The value of the polar potential Vt at the given parameters.
|
|
51
|
+
"""
|
|
10
52
|
return kerr_geo_V02(a, En, Lz, Q, theta)
|
|
11
53
|
|
|
12
54
|
def kerrgeo_Vr(a, En, Lz, Q, r):
|
|
55
|
+
"""
|
|
56
|
+
The (squared) radial potential Vr for the geodesic evolution of $r_p$.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
a : float
|
|
61
|
+
The black hole spin parameter.
|
|
62
|
+
En : float
|
|
63
|
+
The orbital energy of the particle.
|
|
64
|
+
Lz : float
|
|
65
|
+
The z-component of the orbital angular momentum of the particle.
|
|
66
|
+
Q : float
|
|
67
|
+
The Carter constant of the particle.
|
|
68
|
+
r : float
|
|
69
|
+
The radial coordinate.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
float
|
|
74
|
+
The value of the radial potential Vr at the given parameters.
|
|
75
|
+
"""
|
|
13
76
|
return kerr_geo_V11(a, En, Lz, Q, r)
|
|
14
77
|
|
|
15
78
|
def kerrgeo_Vtheta(a, En, Lz, Q, theta):
|
|
79
|
+
"""
|
|
80
|
+
The (squared) polar potential Vtheta for the geodesic evolution of $\theta_p$.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
a : float
|
|
85
|
+
The black hole spin parameter.
|
|
86
|
+
En : float
|
|
87
|
+
The orbital energy of the particle.
|
|
88
|
+
Lz : float
|
|
89
|
+
The z-component of the orbital angular momentum of the particle.
|
|
90
|
+
Q : float
|
|
91
|
+
The Carter constant of the particle.
|
|
92
|
+
theta : float
|
|
93
|
+
The polar angle coordinate.
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
float
|
|
98
|
+
The value of the polar potential Vtheta at the given parameters.
|
|
99
|
+
"""
|
|
16
100
|
return kerr_geo_V22(a, En, Lz, Q, theta)
|
|
17
101
|
|
|
18
102
|
def kerrgeo_Vphi_radial(a, En, Lz, Q, r):
|
|
103
|
+
"""
|
|
104
|
+
The (squared) radial potential Vphi for the geodesic evolution of $r_p$.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
a : float
|
|
109
|
+
The black hole spin parameter.
|
|
110
|
+
En : float
|
|
111
|
+
The orbital energy of the particle.
|
|
112
|
+
Lz : float
|
|
113
|
+
The z-component of the orbital angular momentum of the particle.
|
|
114
|
+
Q : float
|
|
115
|
+
The Carter constant of the particle.
|
|
116
|
+
r : float
|
|
117
|
+
The radial coordinate.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
float
|
|
122
|
+
The value of the radial potential Vphi at the given parameters.
|
|
123
|
+
"""
|
|
19
124
|
return kerr_geo_V31(a, En, Lz, Q, r)
|
|
20
125
|
|
|
21
126
|
def kerrgeo_Vphi_polar(a, En, Lz, Q, theta):
|
|
127
|
+
"""
|
|
128
|
+
The (squared) polar potential Vphi for the geodesic evolution of $r_p$.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
a : float
|
|
133
|
+
The black hole spin parameter.
|
|
134
|
+
En : float
|
|
135
|
+
The orbital energy of the particle.
|
|
136
|
+
Lz : float
|
|
137
|
+
The z-component of the orbital angular momentum of the particle.
|
|
138
|
+
Q : float
|
|
139
|
+
The Carter constant of the particle.
|
|
140
|
+
theta : float
|
|
141
|
+
The polar angle coordinate.
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
float
|
|
146
|
+
The value of the polar potential Vphi at the given parameters.
|
|
147
|
+
"""
|
|
22
148
|
return kerr_geo_V32(a, En, Lz, Q, theta)
|
|
23
149
|
|
|
24
150
|
def kerr_mino_frequencies(a, p, e, x):
|
|
@@ -373,25 +499,256 @@ class KerrGeodesic:
|
|
|
373
499
|
"""
|
|
374
500
|
return np.dot(np.array([n, k, m]), (self.frequencies))
|
|
375
501
|
|
|
376
|
-
def
|
|
502
|
+
def psi_radial(self, la):
|
|
503
|
+
"""
|
|
504
|
+
Function that returns the radial true anomaly for a given Mino time value.
|
|
505
|
+
|
|
506
|
+
Parameters
|
|
507
|
+
----------
|
|
508
|
+
la : float or numpy.ndarray
|
|
509
|
+
The Mino time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
510
|
+
|
|
511
|
+
Returns
|
|
512
|
+
-------
|
|
513
|
+
numpy.ndarray
|
|
514
|
+
The radial true anomaly for the given Mino time value(s).
|
|
515
|
+
"""
|
|
516
|
+
if isinstance(la, np.ndarray) or isinstance(la, list):
|
|
517
|
+
return self.base.psi_radial_vec(np.array(la))
|
|
518
|
+
else:
|
|
519
|
+
return self.base.psi_radial(la)
|
|
520
|
+
|
|
521
|
+
def psi_polar(self, la):
|
|
522
|
+
"""
|
|
523
|
+
Function that returns the polar Darwin phase for a given Mino time value.
|
|
524
|
+
|
|
525
|
+
Parameters
|
|
526
|
+
----------
|
|
527
|
+
la : float or numpy.ndarray
|
|
528
|
+
The Mino time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
529
|
+
|
|
530
|
+
Returns
|
|
531
|
+
-------
|
|
532
|
+
numpy.ndarray
|
|
533
|
+
The polar Darwin phase for the given Mino time value(s).
|
|
534
|
+
"""
|
|
535
|
+
if isinstance(la, np.ndarray) or isinstance(la, list):
|
|
536
|
+
return self.base.psi_polar_vec(np.array(la))
|
|
537
|
+
else:
|
|
538
|
+
return self.base.psi_polar(la)
|
|
539
|
+
|
|
540
|
+
def psi_radial_of_t(self, t):
|
|
541
|
+
"""
|
|
542
|
+
Function that returns the radial true anomaly for a given time value.
|
|
543
|
+
|
|
544
|
+
Parameters
|
|
545
|
+
----------
|
|
546
|
+
t : float or numpy.ndarray
|
|
547
|
+
The time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
548
|
+
|
|
549
|
+
Returns
|
|
550
|
+
-------
|
|
551
|
+
numpy.ndarray
|
|
552
|
+
The radial true anomaly for the given time value(s).
|
|
553
|
+
"""
|
|
554
|
+
if isinstance(t, np.ndarray) or isinstance(t, list):
|
|
555
|
+
return self.base.psi_radial_time_vec(np.array(t))
|
|
556
|
+
else:
|
|
557
|
+
return self.base.psi_radial_time(t)
|
|
558
|
+
|
|
559
|
+
def psi_polar_of_t(self, t):
|
|
560
|
+
"""
|
|
561
|
+
Function that returns the polar Darwin phase for a given time value.
|
|
562
|
+
|
|
563
|
+
Parameters
|
|
564
|
+
----------
|
|
565
|
+
t : float or numpy.ndarray
|
|
566
|
+
The time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
567
|
+
|
|
568
|
+
Returns
|
|
569
|
+
-------
|
|
570
|
+
numpy.ndarray
|
|
571
|
+
The polar Darwin phase for the given time value(s).
|
|
572
|
+
"""
|
|
573
|
+
if isinstance(t, np.ndarray) or isinstance(t, list):
|
|
574
|
+
return self.base.psi_polar_time_vec(np.array(t))
|
|
575
|
+
else:
|
|
576
|
+
return self.base.psi_polar_time(t)
|
|
577
|
+
|
|
578
|
+
def time_position(self, la):
|
|
579
|
+
"""
|
|
580
|
+
Function that returns the time position for a given Mino time value.
|
|
581
|
+
|
|
582
|
+
Parameters
|
|
583
|
+
----------
|
|
584
|
+
la : float or numpy.ndarray
|
|
585
|
+
The Mino time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
586
|
+
|
|
587
|
+
Returns
|
|
588
|
+
-------
|
|
589
|
+
numpy.ndarray
|
|
590
|
+
The time position for the given Mino time value(s).
|
|
591
|
+
"""
|
|
592
|
+
if isinstance(la, np.ndarray) or isinstance(la, list):
|
|
593
|
+
return self.base.time_position_vec(np.array(la))
|
|
594
|
+
else:
|
|
595
|
+
return self.base.time_position(la)
|
|
596
|
+
|
|
597
|
+
def radial_position(self, la):
|
|
598
|
+
"""
|
|
599
|
+
Function that returns the radial position for a given Mino time value.
|
|
600
|
+
|
|
601
|
+
Parameters
|
|
602
|
+
----------
|
|
603
|
+
la : float or numpy.ndarray
|
|
604
|
+
The Mino time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
605
|
+
|
|
606
|
+
Returns
|
|
607
|
+
-------
|
|
608
|
+
numpy.ndarray
|
|
609
|
+
The radial position for the given Mino time value(s).
|
|
610
|
+
"""
|
|
611
|
+
if isinstance(la, np.ndarray) or isinstance(la, list):
|
|
612
|
+
return self.base.radial_position_vec(np.array(la))
|
|
613
|
+
else:
|
|
614
|
+
return self.base.radial_position(la)
|
|
615
|
+
|
|
616
|
+
def polar_position(self, la):
|
|
617
|
+
"""
|
|
618
|
+
Function that returns the polar position for a given Mino time value.
|
|
619
|
+
|
|
620
|
+
Parameters
|
|
621
|
+
----------
|
|
622
|
+
la : float or numpy.ndarray
|
|
623
|
+
The Mino time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
624
|
+
|
|
625
|
+
Returns
|
|
626
|
+
-------
|
|
627
|
+
numpy.ndarray
|
|
628
|
+
The polar position for the given Mino time value(s).
|
|
629
|
+
"""
|
|
630
|
+
if isinstance(la, np.ndarray) or isinstance(la, list):
|
|
631
|
+
return self.base.polar_position_vec(np.array(la))
|
|
632
|
+
else:
|
|
633
|
+
return self.base.polar_position(la)
|
|
634
|
+
|
|
635
|
+
def azimuthal_position(self, la):
|
|
636
|
+
"""
|
|
637
|
+
Function that returns the azimuthal position for a given Mino time value.
|
|
638
|
+
|
|
639
|
+
Parameters
|
|
640
|
+
----------
|
|
641
|
+
la : float or numpy.ndarray
|
|
642
|
+
The Mino time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
643
|
+
|
|
644
|
+
Returns
|
|
645
|
+
-------
|
|
646
|
+
numpy.ndarray
|
|
647
|
+
The azimuthal position for the given Mino time value(s).
|
|
648
|
+
"""
|
|
649
|
+
if isinstance(la, np.ndarray) or isinstance(la, list):
|
|
650
|
+
return self.base.azimuthal_position_vec(np.array(la))
|
|
651
|
+
else:
|
|
652
|
+
return self.base.azimuthal_position(la)
|
|
653
|
+
|
|
654
|
+
def radial_position_of_t(self, t):
|
|
655
|
+
"""
|
|
656
|
+
Function that returns the radial position for a given Mino time value.
|
|
657
|
+
|
|
658
|
+
Parameters
|
|
659
|
+
----------
|
|
660
|
+
t : float or numpy.ndarray
|
|
661
|
+
The Mino time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
662
|
+
|
|
663
|
+
Returns
|
|
664
|
+
-------
|
|
665
|
+
numpy.ndarray
|
|
666
|
+
The radial position for the given Mino time value(s).
|
|
667
|
+
"""
|
|
668
|
+
if isinstance(t, np.ndarray) or isinstance(t, list):
|
|
669
|
+
return self.base.radial_position_time_vec(np.array(t))
|
|
670
|
+
else:
|
|
671
|
+
return self.base.radial_position_time(t)
|
|
672
|
+
|
|
673
|
+
def polar_position_of_t(self, t):
|
|
674
|
+
"""
|
|
675
|
+
Function that returns the polar position for a given time value.
|
|
676
|
+
|
|
677
|
+
Parameters
|
|
678
|
+
----------
|
|
679
|
+
t : float or numpy.ndarray
|
|
680
|
+
The time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
681
|
+
|
|
682
|
+
Returns
|
|
683
|
+
-------
|
|
684
|
+
numpy.ndarray
|
|
685
|
+
The polar position for the given time value(s).
|
|
686
|
+
"""
|
|
687
|
+
if isinstance(t, np.ndarray) or isinstance(t, list):
|
|
688
|
+
return self.base.polar_position_time_vec(np.array(t))
|
|
689
|
+
else:
|
|
690
|
+
return self.base.polar_position_time(t)
|
|
691
|
+
|
|
692
|
+
def azimuthal_position_of_t(self, t):
|
|
693
|
+
"""
|
|
694
|
+
Function that returns the azimuthal position for a given time value.
|
|
695
|
+
|
|
696
|
+
Parameters
|
|
697
|
+
----------
|
|
698
|
+
t : float or numpy.ndarray
|
|
699
|
+
The time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
700
|
+
|
|
701
|
+
Returns
|
|
702
|
+
-------
|
|
703
|
+
numpy.ndarray
|
|
704
|
+
The azimuthal position for the given time value(s).
|
|
705
|
+
"""
|
|
706
|
+
if isinstance(t, np.ndarray) or isinstance(t, list):
|
|
707
|
+
return self.base.azimuthal_position_time_vec(np.array(t))
|
|
708
|
+
else:
|
|
709
|
+
return self.base.azimuthal_position_time(t)
|
|
710
|
+
|
|
711
|
+
def mino_of_t(self, t):
|
|
377
712
|
"""
|
|
378
713
|
Function that returns the Mino time for a given Boyer-Lindquist time.
|
|
379
714
|
|
|
380
715
|
Parameters
|
|
381
716
|
----------
|
|
382
|
-
t : float
|
|
383
|
-
The Boyer-Lindquist time.
|
|
717
|
+
t : float or numpy.ndarray
|
|
718
|
+
The Boyer-Lindquist time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
384
719
|
|
|
385
|
-
Returns
|
|
720
|
+
Returns
|
|
386
721
|
-------
|
|
387
|
-
|
|
388
|
-
The Mino time for the given Boyer-Lindquist time.
|
|
722
|
+
numpy.ndarray
|
|
723
|
+
The Mino time for the given Boyer-Lindquist time value(s).
|
|
389
724
|
"""
|
|
390
|
-
|
|
725
|
+
if isinstance(t, np.ndarray) or isinstance(t, list):
|
|
726
|
+
return self.base.mino_time_vec(np.array(t))
|
|
727
|
+
else:
|
|
728
|
+
return self.base.mino_time(t)
|
|
391
729
|
|
|
392
|
-
def
|
|
730
|
+
def position_of_t(self, t):
|
|
393
731
|
"""
|
|
394
|
-
Function that returns the position vector for a given
|
|
732
|
+
Function that returns the position vector (r, theta, phi) for a given Boyer-Lindquist time value.
|
|
733
|
+
|
|
734
|
+
Parameters
|
|
735
|
+
----------
|
|
736
|
+
t : float or numpy.ndarray
|
|
737
|
+
The Boyer-Lindquist time value(s). If a numpy array is provided, the function will return a numpy array of the same shape.
|
|
738
|
+
|
|
739
|
+
Returns
|
|
740
|
+
-------
|
|
741
|
+
numpy.ndarray
|
|
742
|
+
The position vector (r, theta, phi) for the given Boyer-Lindquist time value(s).
|
|
743
|
+
"""
|
|
744
|
+
if isinstance(t, np.ndarray) or isinstance(t, list):
|
|
745
|
+
return self.base.position_time_vec(np.array(t))
|
|
746
|
+
else:
|
|
747
|
+
return self.base.position_time(t)
|
|
748
|
+
|
|
749
|
+
def position(self, la):
|
|
750
|
+
"""
|
|
751
|
+
Function that returns the position vector (t, r, theta, phi) for a given Mino time value.
|
|
395
752
|
Parameters
|
|
396
753
|
----------
|
|
397
754
|
la : float or numpy.ndarray
|
|
@@ -399,9 +756,12 @@ class KerrGeodesic:
|
|
|
399
756
|
Returns
|
|
400
757
|
-------
|
|
401
758
|
numpy.ndarray
|
|
402
|
-
The position vector for the given Mino time value(s).
|
|
759
|
+
The position vector (t, r, theta, phi) for the given Mino time value(s).
|
|
403
760
|
"""
|
|
404
761
|
if isinstance(la, np.ndarray) or isinstance(la, list):
|
|
405
762
|
return self.base.position_vec(np.array(la))
|
|
406
763
|
else:
|
|
407
|
-
return self.base.position(la)
|
|
764
|
+
return self.base.position(la)
|
|
765
|
+
|
|
766
|
+
def __call__(self, la):
|
|
767
|
+
return self.position(la)
|
pybhpt/radial.py
CHANGED
|
@@ -11,14 +11,55 @@ def available_methods():
|
|
|
11
11
|
"""
|
|
12
12
|
return available_methods_cython()
|
|
13
13
|
|
|
14
|
-
def renormalized_angular_momentum(s,
|
|
15
|
-
|
|
14
|
+
def renormalized_angular_momentum(s, j, m, a, omega):
|
|
15
|
+
"""
|
|
16
|
+
Computes the renormalized angular momentum for the given parameters.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
s : int
|
|
21
|
+
The spin weight of the field.
|
|
22
|
+
j : int
|
|
23
|
+
The spheroidal harmonic mode number.
|
|
24
|
+
m : int
|
|
25
|
+
The azimuthal harmonic mode number.
|
|
26
|
+
a : float
|
|
27
|
+
The black hole spin parameter.
|
|
28
|
+
omega : float
|
|
29
|
+
The frequency of the mode.
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
complex
|
|
34
|
+
The renormalized angular momentum.
|
|
35
|
+
"""
|
|
36
|
+
return nu_cython(s, j, m, a, omega)
|
|
37
|
+
|
|
38
|
+
def renormalized_angular_momentum_monodromy(s, j, m, a, omega, la):
|
|
39
|
+
"""
|
|
40
|
+
Computes the renormalized angular momentum using the monodromy method for the given parameters.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
s : int
|
|
45
|
+
The spin weight of the field.
|
|
46
|
+
j : int
|
|
47
|
+
The spheroidal harmonic mode number.
|
|
48
|
+
m : int
|
|
49
|
+
The azimuthal harmonic mode number.
|
|
50
|
+
a : float
|
|
51
|
+
The black hole spin parameter.
|
|
52
|
+
omega : complex
|
|
53
|
+
The frequency of the mode.
|
|
54
|
+
la : complex
|
|
55
|
+
The spheroidal eigenvalue.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
complex
|
|
60
|
+
The renormalized angular momentum.
|
|
61
|
+
"""
|
|
62
|
+
return nu_2_cython(s, j, m, a, omega, la)
|
|
22
63
|
|
|
23
64
|
class RadialTeukolsky:
|
|
24
65
|
"""A class for solving the homogeneous radial Teukolsky equation.
|
|
@@ -378,4 +419,26 @@ class RadialTeukolsky:
|
|
|
378
419
|
elif deriv == 2:
|
|
379
420
|
return self.radialderivatives2(bc)
|
|
380
421
|
else:
|
|
381
|
-
raise ValueError("RadialTeukolsky only solves up to the second derivative")
|
|
422
|
+
raise ValueError("RadialTeukolsky only solves up to the second derivative")
|
|
423
|
+
|
|
424
|
+
def hypergeo_2F1(a, b, c, x):
|
|
425
|
+
"""
|
|
426
|
+
Gauss hypergeometric function 2F1(a, b; c; x). Note that this function is not very stable across the complex domain.
|
|
427
|
+
|
|
428
|
+
Parameters
|
|
429
|
+
----------
|
|
430
|
+
a : complex
|
|
431
|
+
The first parameter of the hypergeometric function.
|
|
432
|
+
b : complex
|
|
433
|
+
The second parameter of the hypergeometric function.
|
|
434
|
+
c : complex
|
|
435
|
+
The third parameter of the hypergeometric function.
|
|
436
|
+
x : complex
|
|
437
|
+
The argument of the hypergeometric function.
|
|
438
|
+
|
|
439
|
+
Returns
|
|
440
|
+
-------
|
|
441
|
+
complex
|
|
442
|
+
The value of the hypergeometric function 2F1(a, b; c; x).
|
|
443
|
+
"""
|
|
444
|
+
return hypergeo_2F1_cython(a, b, c, x)
|
pybhpt/swsh.py
CHANGED
|
@@ -4,114 +4,133 @@ from scipy.special import sph_harm
|
|
|
4
4
|
from scipy.special import binom
|
|
5
5
|
from scipy.special import factorial
|
|
6
6
|
import numpy as np
|
|
7
|
+
from cybhpt_full import YslmCy, clebschCy, w3jCy
|
|
7
8
|
|
|
8
9
|
"""
|
|
9
10
|
Wigner 3j-symbol and Clebsch-Gordon coefficients
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
def fac(n):
|
|
14
|
+
"""
|
|
15
|
+
Computes the factorial of a non-negative integer n.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
n : int
|
|
20
|
+
A non-negative integer.
|
|
21
|
+
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
float
|
|
25
|
+
The factorial of n.
|
|
26
|
+
"""
|
|
13
27
|
if n < 0:
|
|
14
28
|
return 0
|
|
15
29
|
return float(np.math.factorial(n))
|
|
16
30
|
|
|
17
|
-
def Yslm(s, l, m, th):
|
|
31
|
+
def Yslm(s, l, m, th, ph = None):
|
|
32
|
+
"""
|
|
33
|
+
Evaluate the spin-weighted spherical harmonic $Y_{s}^{lm}$ at a given angle theta.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
s : int
|
|
38
|
+
The spin weight of the harmonic.
|
|
39
|
+
l : int
|
|
40
|
+
The angular number of the spherical harmonic.
|
|
41
|
+
m : int
|
|
42
|
+
The azimuthal number of the spherical harmonic.
|
|
43
|
+
th : array_like
|
|
44
|
+
The polar angle(s) at which to evaluate the spherical harmonic.
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
array_like
|
|
49
|
+
The values of the spherical harmonic at the specified angles.
|
|
50
|
+
"""
|
|
51
|
+
assert isinstance(s, int) and isinstance(l, int) and isinstance(m, int), "s, l, and m must be integers"
|
|
52
|
+
if ph is not None:
|
|
53
|
+
return Yslm(s, l, m, th)*np.exp(1.j*m*ph)
|
|
18
54
|
if np.abs(s) > l:
|
|
19
55
|
return 0.*th
|
|
20
56
|
if s == 0:
|
|
21
57
|
return np.real(sph_harm(m, l, 0., th))
|
|
22
58
|
elif s + m < 0:
|
|
23
|
-
return (-1.)**(s+m)*YslmBase(-s, l, -m,
|
|
24
|
-
|
|
25
|
-
|
|
59
|
+
return (-1.)**(s+m)*YslmBase(-s, l, -m, th)
|
|
60
|
+
else:
|
|
61
|
+
return YslmBase(s, l, m, th)
|
|
62
|
+
|
|
63
|
+
def YslmBase(s, l, m, th):
|
|
64
|
+
assert isinstance(s, int) and isinstance(l, int) and isinstance(m, int), "s, l, and m must be integers"
|
|
65
|
+
if not isinstance(th, (int, float)):
|
|
66
|
+
b = np.broadcast(th)
|
|
67
|
+
out = np.empty(b.shape)
|
|
68
|
+
out.flat = [YslmCy(s, l, m, thi) for (thi,) in b]
|
|
26
69
|
else:
|
|
27
|
-
|
|
70
|
+
out = YslmCy(s, l, m, th)
|
|
71
|
+
return out
|
|
28
72
|
|
|
29
|
-
def
|
|
30
|
-
|
|
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
|
|
73
|
+
def Yslm_eigenvalue(s, l, *args):
|
|
74
|
+
return l*(l + 1.) - s*(s + 1.)
|
|
38
75
|
|
|
39
76
|
def clebsch(l1, l2, l3, m1, m2, m3):
|
|
40
|
-
|
|
77
|
+
"""
|
|
78
|
+
Compute the Clebsch-Gordon coefficient <l1,m1,l2,m2|l3,m3>.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
l1 : int
|
|
83
|
+
The angular number of the first state.
|
|
84
|
+
l2 : int
|
|
85
|
+
The angular number of the second state.
|
|
86
|
+
l3 : int
|
|
87
|
+
The angular number of the combined state.
|
|
88
|
+
m1 : int
|
|
89
|
+
The azimuthal number of the first state.
|
|
90
|
+
m2 : int
|
|
91
|
+
The azimuthal number of the second state.
|
|
92
|
+
m3 : int
|
|
93
|
+
The azimuthal number of the combined state.
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
float
|
|
98
|
+
The Clebsch-Gordon coefficient <l1,m1,l2,m2|l3,m3>.
|
|
99
|
+
"""
|
|
100
|
+
assert isinstance(l1, int) and isinstance(l2, int) and isinstance(l3, int), "l1, l2, and l3 must be integers"
|
|
101
|
+
assert isinstance(m1, int) and isinstance(m2, int) and isinstance(m3, int), "m1, m2, and m3 must be integers"
|
|
102
|
+
return clebschCy(l1, l2, l3, m1, m2, m3)
|
|
41
103
|
|
|
42
104
|
def w3j(l1, l2, l3, m1, m2, m3):
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
105
|
+
"""
|
|
106
|
+
Compute the Wigner 3j-symbol
|
|
107
|
+
| l1 l2 l3 |
|
|
108
|
+
| m1 m2 m3 |
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
l1 : int
|
|
113
|
+
The angular number of the first state.
|
|
114
|
+
l2 : int
|
|
115
|
+
The angular number of the second state.
|
|
116
|
+
l3 : int
|
|
117
|
+
The angular number of the combined state.
|
|
118
|
+
m1 : int
|
|
119
|
+
The azimuthal number of the first state.
|
|
120
|
+
m2 : int
|
|
121
|
+
The azimuthal number of the second state.
|
|
122
|
+
m3 : int
|
|
123
|
+
The azimuthal number of the combined state.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
float
|
|
128
|
+
The Wigner 3j-symbol $ \begin{pmatrix} l1 & l2 & l3 \\ m1 & m2 & m3 \end{pmatrix} $
|
|
129
|
+
"""
|
|
130
|
+
assert isinstance(l1, int) and isinstance(l2, int) and isinstance(l3, int), "l1, l2, and l3 must be integers"
|
|
131
|
+
assert isinstance(m1, int) and isinstance(m2, int) and isinstance(m3, int), "m1, m2, and m3 must be integers"
|
|
132
|
+
return w3jCy(l1, l2, l3, m1, m2, m3)
|
|
88
133
|
|
|
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
134
|
"""
|
|
116
135
|
SWSH Eigenvalue Functions
|
|
117
136
|
"""
|
|
@@ -200,7 +219,7 @@ def swsh_eigs(s, l, m, g, nmax=None, return_eigenvectors=True):
|
|
|
200
219
|
kval = l - lmin
|
|
201
220
|
|
|
202
221
|
if nmax is None:
|
|
203
|
-
buffer = round(20 + 2*g)
|
|
222
|
+
buffer = round(20 + 2*np.abs(g))
|
|
204
223
|
Nmax = kval + buffer + 2
|
|
205
224
|
else:
|
|
206
225
|
if nmax < kval:
|
|
@@ -213,26 +232,53 @@ def swsh_eigs(s, l, m, g, nmax=None, return_eigenvectors=True):
|
|
|
213
232
|
|
|
214
233
|
return out
|
|
215
234
|
|
|
216
|
-
def
|
|
217
|
-
|
|
235
|
+
def swsh_coeffs(s, l, m, g, th):
|
|
236
|
+
if g == 0.:
|
|
237
|
+
return Yslm(s, l, m, th)
|
|
238
|
+
|
|
239
|
+
_, eig = swsh_eigs(s, l, m, g, nmax=None, return_eigenvectors=True)
|
|
240
|
+
if g.imag == 0.:
|
|
241
|
+
coeffs = np.real(eig[l - max(abs(s), abs(m))])
|
|
242
|
+
else:
|
|
243
|
+
coeffs = eig[l - max(abs(s), abs(m))]
|
|
244
|
+
return coeffs
|
|
218
245
|
|
|
219
246
|
def swsh_eigenvalue(s, l, m, g, nmax=None):
|
|
247
|
+
"""
|
|
248
|
+
Compute the eigenvalue of the spin-weighted spheroidal harmonic.
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
s : int
|
|
253
|
+
The spin weight of the harmonic.
|
|
254
|
+
l : int
|
|
255
|
+
The angular number of the harmonic.
|
|
256
|
+
m : int
|
|
257
|
+
The azimuthal number of the harmonic.
|
|
258
|
+
g : float or complex
|
|
259
|
+
The spheroidicity parameter.
|
|
260
|
+
nmax : int, optional
|
|
261
|
+
The maximum number of basis functions to use in the computation. If None, a default value is chosen.
|
|
262
|
+
|
|
263
|
+
Returns
|
|
264
|
+
-------
|
|
265
|
+
float or complex
|
|
266
|
+
The eigenvalue of the spin-weighted spheroidal harmonic.
|
|
267
|
+
"""
|
|
220
268
|
if g == 0.:
|
|
221
269
|
return Yslm_eigenvalue(s, l)
|
|
222
270
|
|
|
223
271
|
las = swsh_eigs(s, l, m, g, nmax=nmax, return_eigenvectors=False)
|
|
224
272
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return np.real(eig[l - max(abs(s), abs(m))])
|
|
235
|
-
|
|
273
|
+
idx = l - max(abs(s), abs(m))
|
|
274
|
+
sorted_indices = np.argsort(np.real(las))
|
|
275
|
+
if idx < 0 or idx >= len(sorted_indices):
|
|
276
|
+
raise IndexError(f"Index {idx} out of bounds for eigenvalue array of length {len(sorted_indices)}")
|
|
277
|
+
if g.imag == 0.:
|
|
278
|
+
eigen = np.real(las)[sorted_indices[idx]]
|
|
279
|
+
else:
|
|
280
|
+
eigen = las[sorted_indices[idx]]
|
|
281
|
+
return eigen
|
|
236
282
|
class SWSHBase:
|
|
237
283
|
def __init__(self, *args):
|
|
238
284
|
arg_num = np.array(args).shape[0]
|
|
@@ -247,7 +293,9 @@ class SWSHBase:
|
|
|
247
293
|
|
|
248
294
|
if arg_num > 3:
|
|
249
295
|
self.spheroidicity = args[3]
|
|
250
|
-
|
|
296
|
+
if self.spheroidicity.imag == 0:
|
|
297
|
+
self.spheroidicity = np.real(self.spheroidicity)
|
|
298
|
+
|
|
251
299
|
class SWSHSeriesBase(SWSHBase):
|
|
252
300
|
def __init__(self, s, l, m, g):
|
|
253
301
|
SWSHBase.__init__(self, s, l, m, g)
|
|
@@ -259,7 +307,7 @@ class SWSHSeriesBase(SWSHBase):
|
|
|
259
307
|
kval = self.l - self.lmin
|
|
260
308
|
|
|
261
309
|
if nmax is None:
|
|
262
|
-
buffer = round(20 + 2*self.spheroidicity)
|
|
310
|
+
buffer = round(20 + np.abs(2*self.spheroidicity))
|
|
263
311
|
Nmax = kval + buffer + 2
|
|
264
312
|
else:
|
|
265
313
|
if nmax < kval:
|
|
@@ -283,45 +331,115 @@ class SWSHSeriesBase(SWSHBase):
|
|
|
283
331
|
return scipy.sparse.linalg.eigs(mat, **kwargs)
|
|
284
332
|
|
|
285
333
|
def generate_eigenvalue(self):
|
|
286
|
-
|
|
287
|
-
|
|
334
|
+
if self.spheroidicity.imag == 0.:
|
|
335
|
+
las = np.real(self.eigs(return_eigenvectors=False))
|
|
336
|
+
else:
|
|
337
|
+
las = self.eigs(return_eigenvectors=False)
|
|
338
|
+
pos = np.argsort(np.real(las))[self.l - self.lmin]
|
|
288
339
|
return las[pos]
|
|
289
|
-
|
|
340
|
+
|
|
290
341
|
def generate_eigs(self):
|
|
291
342
|
las, eigs = self.eigs()
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
343
|
+
pos_vec = np.argsort(np.real(las))
|
|
344
|
+
pos = pos_vec[self.l - self.lmin]
|
|
345
|
+
if self.spheroidicity.imag == 0.:
|
|
346
|
+
eigs_temp = np.real(eigs[:, pos])
|
|
347
|
+
eigs_return = np.sign(eigs_temp[self.l - self.lmin])*eigs_temp
|
|
348
|
+
eig = np.real(las[pos])
|
|
349
|
+
else:
|
|
350
|
+
eigs_temp = eigs[:, pos]
|
|
351
|
+
ref = eigs_temp[self.l - self.lmin]
|
|
352
|
+
eigs_temp = eigs_temp/ref
|
|
353
|
+
eigs_norm = np.linalg.norm(eigs_temp)
|
|
354
|
+
eigs_return = eigs_temp/eigs_norm
|
|
355
|
+
eig = las[pos]
|
|
356
|
+
return (eig, eigs_return)
|
|
357
|
+
class SpinWeightedSpheroidalHarmonic(SWSHSeriesBase):
|
|
358
|
+
"""
|
|
359
|
+
A class for generating a spin-weighted spheroidal harmonic.
|
|
360
|
+
|
|
361
|
+
Parameters
|
|
362
|
+
----------
|
|
363
|
+
s : int
|
|
364
|
+
The spin weight of the harmonic.
|
|
365
|
+
l : int
|
|
366
|
+
The angular number of the harmonic.
|
|
367
|
+
m : int
|
|
368
|
+
The azimuthal number of the harmonic.
|
|
369
|
+
g : float or complex
|
|
370
|
+
The spheroidicity parameter.
|
|
371
|
+
|
|
372
|
+
Attributes
|
|
373
|
+
----------
|
|
374
|
+
couplingcoefficients : array_like
|
|
375
|
+
The coupling coefficients between the spin-weighted spheroidal harmonic and the spin-weighted spherical
|
|
296
376
|
|
|
297
|
-
|
|
377
|
+
"""
|
|
298
378
|
def __init__(self, s, l, m, g):
|
|
299
379
|
SWSHSeriesBase.__init__(self, s, l, m, g)
|
|
300
380
|
if self.spheroidicity == 0.:
|
|
301
381
|
self.eval = self.Yslm
|
|
302
382
|
self.eigenvalue = Yslm_eigenvalue(self.s, self.l)
|
|
303
|
-
self.coeffs = np.zeros(self.l - self.lmin)
|
|
383
|
+
self.coeffs = np.zeros(self.l - self.lmin + 1)
|
|
304
384
|
self.coeffs[-1] = 1.
|
|
305
385
|
else:
|
|
306
386
|
self.eval = self.Sslm
|
|
307
387
|
self.eigenvalue, self.coeffs = self.generate_eigs()
|
|
308
|
-
|
|
388
|
+
|
|
389
|
+
@property
|
|
390
|
+
def couplingcoefficients(self):
|
|
391
|
+
return self.coeffs
|
|
392
|
+
|
|
309
393
|
def Yslm(self, l, th):
|
|
394
|
+
"""
|
|
395
|
+
Evaluate the spin-weighted spherical harmonic $Y_{s}^{lm}(theta)$ at a given angle theta.
|
|
396
|
+
|
|
397
|
+
Parameters
|
|
398
|
+
----------
|
|
399
|
+
l : int
|
|
400
|
+
The angular number of the spherical harmonic.
|
|
401
|
+
th : array_like
|
|
402
|
+
The polar angle(s) at which to evaluate the spherical harmonic.
|
|
403
|
+
|
|
404
|
+
Returns
|
|
405
|
+
-------
|
|
406
|
+
array_like
|
|
407
|
+
The values of the spherical harmonic at the specified angles.
|
|
408
|
+
"""
|
|
310
409
|
return Yslm(self.s, l, self.m, th)
|
|
311
410
|
|
|
312
411
|
def Sslm(self, *args):
|
|
412
|
+
"""
|
|
413
|
+
Evaluate the spin-weighted spheroidal harmonic $S_{s}^{lm}(theta)$ at a given angle theta.
|
|
414
|
+
|
|
415
|
+
Parameters
|
|
416
|
+
----------
|
|
417
|
+
th : array_like
|
|
418
|
+
The polar angle(s) at which to evaluate the spheroidal harmonic.
|
|
419
|
+
|
|
420
|
+
Returns
|
|
421
|
+
-------
|
|
422
|
+
array_like
|
|
423
|
+
The values of the spheroidal harmonic at the specified angles.
|
|
424
|
+
"""
|
|
313
425
|
th = args[-1]
|
|
314
426
|
term_num = self.coeffs.shape[0]
|
|
315
|
-
|
|
316
|
-
|
|
427
|
+
if isinstance(th, (int, float)):
|
|
428
|
+
Yslm_array = np.empty(term_num)
|
|
429
|
+
else:
|
|
430
|
+
pts_num = th.shape[0]
|
|
431
|
+
Yslm_array = np.empty((term_num, pts_num))
|
|
317
432
|
for i in range(term_num):
|
|
318
433
|
Yslm_array[i] = self.Yslm(self.lmin + i, th)
|
|
319
434
|
|
|
320
435
|
return np.dot(self.coeffs, Yslm_array)
|
|
321
436
|
|
|
322
|
-
def __call__(self, th):
|
|
323
|
-
|
|
324
|
-
|
|
437
|
+
def __call__(self, th, ph = None):
|
|
438
|
+
out = self.eval(self.l, th)
|
|
439
|
+
if ph is not None:
|
|
440
|
+
out = out*np.exp(1.j*self.m*ph)
|
|
441
|
+
return out
|
|
442
|
+
|
|
325
443
|
def muCoupling(s, l):
|
|
326
444
|
"""
|
|
327
445
|
Eigenvalue for the spin-weighted spherical harmonic lowering operator
|
|
@@ -332,6 +450,25 @@ def muCoupling(s, l):
|
|
|
332
450
|
return np.sqrt((l - s + 1.)*(l + s))
|
|
333
451
|
|
|
334
452
|
def Asjlm(s, j, l, m):
|
|
453
|
+
"""
|
|
454
|
+
Coupling coefficient between scalar and spin-weighted spherical harmonics
|
|
455
|
+
|
|
456
|
+
Parameters
|
|
457
|
+
----------
|
|
458
|
+
s : int
|
|
459
|
+
The spin weight of the harmonic.
|
|
460
|
+
j : int
|
|
461
|
+
The angular number of the scalar harmonic.
|
|
462
|
+
l : int
|
|
463
|
+
The angular number of the spin-weighted harmonic.
|
|
464
|
+
m : int
|
|
465
|
+
The azimuthal number of the harmonics.
|
|
466
|
+
|
|
467
|
+
Returns
|
|
468
|
+
-------
|
|
469
|
+
float
|
|
470
|
+
The coupling coefficient $A_{s}^{jlm}$
|
|
471
|
+
"""
|
|
335
472
|
if s >= 0:
|
|
336
473
|
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
474
|
else:
|
pybhpt/teuk.py
CHANGED
|
@@ -194,52 +194,243 @@ class TeukolskyMode:
|
|
|
194
194
|
Flips the spin-weight and frequency of the Teukolsky solutions from :math:`s \rightarrow -s` and :math:`\omega \rightarrow -\omega`
|
|
195
195
|
"""
|
|
196
196
|
def flipspinweightandfrequency(self):
|
|
197
|
+
"""
|
|
198
|
+
Flips the spin-weight and frequency of the Teukolsky solutions from :math:`s \rightarrow -s` and :math:`\omega \rightarrow -\omega`
|
|
199
|
+
"""
|
|
197
200
|
self.base.flip_spinweight_frequency()
|
|
198
201
|
|
|
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
202
|
def couplingcoefficient(self, l):
|
|
203
|
+
"""
|
|
204
|
+
Spherical-spheroidal mixing coefficient between a spherical harmonic $l$ mode with a spheroidal $j$ mode.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
l : int
|
|
209
|
+
Spherical harmonic mode.
|
|
210
|
+
|
|
211
|
+
Returns
|
|
212
|
+
-------
|
|
213
|
+
float
|
|
214
|
+
The coupling coefficient between the spherical harmonic mode `l` and the spheroidal harmonic mode `j`.
|
|
215
|
+
"""
|
|
206
216
|
return self.base.couplingcoefficient(l)
|
|
207
217
|
|
|
208
218
|
def radialpoint(self, pos):
|
|
219
|
+
"""
|
|
220
|
+
The radial point for the given position `pos`.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
pos : int
|
|
225
|
+
The radial position.
|
|
226
|
+
|
|
227
|
+
Returns
|
|
228
|
+
-------
|
|
229
|
+
float
|
|
230
|
+
The radial point at the given position `pos`.
|
|
231
|
+
"""
|
|
209
232
|
return self.base.radialpoint(pos)
|
|
210
233
|
|
|
211
234
|
def radialsolution(self, bc, pos):
|
|
235
|
+
"""
|
|
236
|
+
The extended homogeneous radial solution for the given boundary condition `bc` and position `pos`.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
bc : str
|
|
241
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
242
|
+
pos : int
|
|
243
|
+
The radial position.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
complex
|
|
248
|
+
The radial solution at the given boundary condition `bc` and position `pos`.
|
|
249
|
+
"""
|
|
212
250
|
return self.base.radialsolution(bc, pos)
|
|
213
251
|
|
|
214
252
|
def radialderivative(self, bc, pos):
|
|
253
|
+
"""
|
|
254
|
+
The derivative of the extended homogeneous radial solution for the given boundary condition `bc` and position `pos`.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
bc : str
|
|
259
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
260
|
+
pos : int
|
|
261
|
+
The radial position.
|
|
262
|
+
|
|
263
|
+
Returns
|
|
264
|
+
-------
|
|
265
|
+
complex
|
|
266
|
+
The radial derivative at the given boundary condition `bc` and position `pos`.
|
|
267
|
+
"""
|
|
215
268
|
return self.base.radialderivative(bc, pos)
|
|
216
269
|
|
|
217
270
|
def radialderivative2(self, bc, pos):
|
|
271
|
+
"""
|
|
272
|
+
The second derivative of the extended homogeneous radial solution for the given boundary condition `bc` and position `pos`.
|
|
273
|
+
|
|
274
|
+
Parameters
|
|
275
|
+
----------
|
|
276
|
+
bc : str
|
|
277
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
278
|
+
pos : int
|
|
279
|
+
The radial position.
|
|
280
|
+
|
|
281
|
+
Returns
|
|
282
|
+
-------
|
|
283
|
+
complex
|
|
284
|
+
The radial second derivative at the given boundary condition `bc` and position `pos`.
|
|
285
|
+
"""
|
|
218
286
|
return self.base.radialderivative2(bc, pos)
|
|
219
287
|
|
|
220
288
|
def homogeneousradialsolution(self, bc, pos):
|
|
289
|
+
"""
|
|
290
|
+
The homogeneous radial solution for the given boundary condition `bc` and position `pos`.
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
bc : str
|
|
295
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
296
|
+
pos : int
|
|
297
|
+
The radial position.
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
complex
|
|
302
|
+
The radial solution at the given boundary condition `bc` and position `pos`.
|
|
303
|
+
"""
|
|
221
304
|
return self.base.homogeneousradialsolution(bc, pos)
|
|
222
305
|
|
|
223
306
|
def homogeneousradialderivative(self, bc, pos):
|
|
307
|
+
"""
|
|
308
|
+
The radial derivative of the homogeneous radial solution for the given boundary condition `bc` and position `pos`.
|
|
309
|
+
|
|
310
|
+
Parameters
|
|
311
|
+
----------
|
|
312
|
+
bc : str
|
|
313
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
314
|
+
pos : int
|
|
315
|
+
The radial position.
|
|
316
|
+
|
|
317
|
+
Returns
|
|
318
|
+
-------
|
|
319
|
+
complex
|
|
320
|
+
The radial derivative at the given boundary condition `bc` and position `pos`.
|
|
321
|
+
"""
|
|
224
322
|
return self.base.homogeneousradialderivative(bc, pos)
|
|
225
323
|
|
|
226
324
|
def homogeneousradialderivative2(self, bc, pos):
|
|
325
|
+
"""
|
|
326
|
+
The second radial derivative of the homogeneous radial solution for the given boundary condition `bc` and position `pos`.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
bc : str
|
|
331
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
332
|
+
pos : int
|
|
333
|
+
The radial position.
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
complex
|
|
338
|
+
The radial second derivative at the given boundary condition `bc` and position `pos`.
|
|
339
|
+
"""
|
|
227
340
|
return self.base.homogeneousradialderivative2(bc, pos)
|
|
228
341
|
|
|
229
342
|
def polarpoint(self, pos):
|
|
343
|
+
"""
|
|
344
|
+
The polar point for the given position `pos`.
|
|
345
|
+
|
|
346
|
+
Parameters
|
|
347
|
+
----------
|
|
348
|
+
pos : int
|
|
349
|
+
The polar position.
|
|
350
|
+
|
|
351
|
+
Returns
|
|
352
|
+
-------
|
|
353
|
+
float
|
|
354
|
+
The polar point at the given position `pos`.
|
|
355
|
+
"""
|
|
230
356
|
return self.base.polarpoint(pos)
|
|
231
357
|
|
|
232
358
|
def polarsolution(self, pos):
|
|
359
|
+
"""
|
|
360
|
+
The polar solution for the given position `pos`.
|
|
361
|
+
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
pos : int
|
|
365
|
+
The polar position.
|
|
366
|
+
|
|
367
|
+
Returns
|
|
368
|
+
-------
|
|
369
|
+
float
|
|
370
|
+
The polar solution at the given position `pos`.
|
|
371
|
+
"""
|
|
233
372
|
return self.base.polarsolution(pos)
|
|
234
373
|
|
|
235
374
|
def polarderivative(self, pos):
|
|
375
|
+
"""
|
|
376
|
+
The derivative of the polar solution for the given position `pos`.
|
|
377
|
+
|
|
378
|
+
Parameters
|
|
379
|
+
----------
|
|
380
|
+
pos : int
|
|
381
|
+
The polar position.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
float
|
|
386
|
+
The polar derivative at the given position `pos`.
|
|
387
|
+
"""
|
|
236
388
|
return self.base.polarderivative(pos)
|
|
237
389
|
|
|
238
390
|
def polarderivative2(self, pos):
|
|
391
|
+
"""
|
|
392
|
+
The second derivative of the polar solution for the given position `pos`.
|
|
393
|
+
|
|
394
|
+
Parameters
|
|
395
|
+
----------
|
|
396
|
+
pos : int
|
|
397
|
+
The polar position.
|
|
398
|
+
|
|
399
|
+
Returns
|
|
400
|
+
-------
|
|
401
|
+
float
|
|
402
|
+
The polar second derivative at the given position `pos`.
|
|
403
|
+
"""
|
|
239
404
|
return self.base.polarderivative2(pos)
|
|
240
405
|
|
|
241
406
|
def amplitude(self, bc):
|
|
407
|
+
"""
|
|
408
|
+
The Teukolsky amplitude for the given boundary condition `bc`.
|
|
409
|
+
|
|
410
|
+
Parameters
|
|
411
|
+
----------
|
|
412
|
+
bc : str
|
|
413
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
414
|
+
|
|
415
|
+
Returns
|
|
416
|
+
-------
|
|
417
|
+
complex
|
|
418
|
+
The Teukolsky amplitude at the given boundary condition `bc`.
|
|
419
|
+
"""
|
|
242
420
|
return self.base.teukolsky_amplitude(bc)
|
|
243
421
|
|
|
244
422
|
def precision(self, bc):
|
|
423
|
+
"""
|
|
424
|
+
The precision of the Teukolsky amplitude for the given boundary condition `bc`.
|
|
425
|
+
|
|
426
|
+
Parameters
|
|
427
|
+
----------
|
|
428
|
+
bc : str
|
|
429
|
+
The boundary condition, either "In" for ingoing or "Up" for upgoing.
|
|
430
|
+
|
|
431
|
+
Returns
|
|
432
|
+
-------
|
|
433
|
+
float
|
|
434
|
+
The precision of the Teukolsky amplitude at the given boundary condition `bc`.
|
|
435
|
+
"""
|
|
245
436
|
return self.base.teukolsky_amplitude_precision(bc)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pybhpt
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.8
|
|
4
4
|
Summary: Black Hole Perturbation Theory and Self-Force Algorithms in Python
|
|
5
5
|
Author-Email: Zach Nasipak <znasipak@gmail.com>
|
|
6
6
|
License: GPL
|
|
@@ -30,11 +30,11 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
|
|
31
31
|
# pybhpt
|
|
32
32
|
|
|
33
|
-
A python package for solving problems in black hole perturbation theory
|
|
33
|
+
A python package for solving problems in black hole perturbation theory.
|
|
34
34
|
|
|
35
35
|
`pybhpt` is a collection of numerical tools for analyzing perturbations of Kerr spacetime, particularly the self-forces and metric-perturbations experienced by small bodies moving in a Kerr background. Subpackages include:
|
|
36
36
|
|
|
37
|
-
- `pybhpt.geodesic`: a module that generates bound timelike geodesics in Kerr spacetime
|
|
37
|
+
- `pybhpt.geodesic`: a module that generates bound periodic timelike geodesics in Kerr spacetime
|
|
38
38
|
- `pybhpt.radial`: a module that calculates homogeneous solutions of the radial Teukolsky equation
|
|
39
39
|
- `pybhpt.swsh`: a module that constructs the spin-weighted spheroidal harmonics
|
|
40
40
|
- `pybhpt.teuk`: a module that evaluates the inhomogeneous solutions (Teukolsky amplitudes) of the radial Teukolsky equation due to a point-particle on a bound timelike Kerr geodesic
|
|
@@ -43,9 +43,11 @@ A python package for solving problems in black hole perturbation theory
|
|
|
43
43
|
- `pybhpt.metric`: a module that produces the coefficients needed to reconstruct the metric from the Hertz potentials
|
|
44
44
|
- `pybhpt.redshift`: a module that computes the generalized Detweiler redshift invariant in a variety of gauges
|
|
45
45
|
|
|
46
|
+
See the [Documentation](https://pybhpt.readthedocs.io/en/latest/) pages for more information about the package, including User Guides and API. References and author information can be found at the bottom of the README.
|
|
47
|
+
|
|
46
48
|
## Quick Installation
|
|
47
49
|
|
|
48
|
-
Tagged releases of `pybhpt` are available as wheel packages for macOS and 64-bit Linux on [PyPI](https://pypi.org/project/
|
|
50
|
+
Tagged releases of `pybhpt` are available as wheel packages for macOS and 64-bit Linux on [PyPI](https://pypi.org/project/pybhpt). Install using `pip`:
|
|
49
51
|
```
|
|
50
52
|
python3 -m pip install pybhpt
|
|
51
53
|
```
|
|
@@ -130,6 +132,14 @@ To include the necessary compiler on Linux:
|
|
|
130
132
|
conda install gcc_linux-64 gxx_linux-64
|
|
131
133
|
```
|
|
132
134
|
|
|
135
|
+
## References
|
|
136
|
+
|
|
137
|
+
Theoretical background for the code and explanations of the numerical methods used within are summarized in the references below:
|
|
138
|
+
|
|
139
|
+
- Z. Nasipak, *Metric reconstruction and the Hamiltonian for eccentric, precessing binaries in the small-mass-ratio limit* (2025) [arXiv:2507.07746](https://arxiv.org/abs/2507.07746)
|
|
140
|
+
- Z. Nasipak, *An adiabatic gravitational waveform model for compact objects undergoing quasi-circular inspirals into rotating massive black holes*, Phys. Rev. D 109, 044020 (2024) [arXiv:2310.19706](https://arxiv.org/abs/2310.19706)
|
|
141
|
+
- Z. Nasipak, *Adiabatic evolution due to the conservative scalar self-force during orbital resonances*, Phys. Rev. D 106, 064042 (2022) [arXiv:2207.02224](https://arxiv.org/abs/2207.02224)
|
|
142
|
+
|
|
133
143
|
## Authors
|
|
134
144
|
|
|
135
145
|
Zachary Nasipak
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
cybhpt_full.cpython-314-darwin.so,sha256=T01VDZVSdS3LaK3jBmOUblHdJHSlZiis9Q88mK5s108,2336832
|
|
2
|
+
pybhpt-0.9.8.dist-info/RECORD,,
|
|
3
|
+
pybhpt-0.9.8.dist-info/WHEEL,sha256=v0AHjqCAlR6lLK4uHrc9lGGk6wkSGzsPHIgpnvL7sa0,142
|
|
4
|
+
pybhpt-0.9.8.dist-info/METADATA,sha256=NIzqGzLYfDWkON7jWb24fRxC7DbZzIrcdMYBF2iOWlA,6367
|
|
5
|
+
pybhpt-0.9.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
6
|
+
pybhpt/geo.py,sha256=VqbarbR1CC7ARD82FI8vmoNnklCZUoraRVJOii3vBlE,23803
|
|
7
|
+
pybhpt/redshift.py,sha256=ozDDbkv34FQ_Op3FotmHJ-J5Vfy2oF4_Ri1HRujnzAY,791
|
|
8
|
+
pybhpt/hertz.py,sha256=9atjscxbR7eszsY6BuJvcy0PqlgMFwYI23kUIn4mUwc,13565
|
|
9
|
+
pybhpt/radial.py,sha256=t9Lx6Cyuprxqk2iowbvHMvsoL-6UO-YnZmO1G1wdyMU,15597
|
|
10
|
+
pybhpt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
pybhpt/swsh.py,sha256=sQR0S-tHXYaAIuuW9kHK99U39htXt5j8oGWPZzIqwt0,14826
|
|
12
|
+
pybhpt/metric.py,sha256=y8ip24rFto7m7rZb0-fuYMrYUmbCzInaWQVYJRI3m3o,11971
|
|
13
|
+
pybhpt/teuk.py,sha256=8rZ-2fyzAfTbt9T4-lkUL8wUmPpORv0VkJCfkpEBssc,13616
|
|
14
|
+
pybhpt/flux.py,sha256=2ahStjbsswsWMNPfDiOlQIrzulJZrhaqoDENC0bCLE4,4299
|
|
15
|
+
pybhpt/.dylibs/libgsl.28.dylib,sha256=cJ5srjDdZDNLeRXrwhARDtAVaHDiePifPX1yaR5KPgc,2439680
|
|
16
|
+
pybhpt/.dylibs/libgslcblas.0.dylib,sha256=xxE7gWPfCeMMkYxciiJ0-IcSJqLwdfHVayP70bOxWL0,272784
|
pybhpt-0.9.4.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
cybhpt_full.cpython-314-darwin.so,sha256=P1p69eiqGqqM1Y5QwYo75PF3Qufss1y2524M_1Xhxko,2286784
|
|
2
|
-
pybhpt/geo.py,sha256=QyiwlXF0cIYSpkV7wC9-ITd2bqIumvXiIt2_uNlhJMc,12237
|
|
3
|
-
pybhpt/redshift.py,sha256=ozDDbkv34FQ_Op3FotmHJ-J5Vfy2oF4_Ri1HRujnzAY,791
|
|
4
|
-
pybhpt/hertz.py,sha256=9atjscxbR7eszsY6BuJvcy0PqlgMFwYI23kUIn4mUwc,13565
|
|
5
|
-
pybhpt/radial.py,sha256=2H3rH4XczAVCbKveHmPO2wt3w8ApPZYWCAwkwnxPnps,13995
|
|
6
|
-
pybhpt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
pybhpt/swsh.py,sha256=WjtpHNZ6c7RVxuml1I6_cEu8D6cb4hD6aiHZVwD7AeA,10462
|
|
8
|
-
pybhpt/metric.py,sha256=y8ip24rFto7m7rZb0-fuYMrYUmbCzInaWQVYJRI3m3o,11971
|
|
9
|
-
pybhpt/teuk.py,sha256=w3tcidkaOGeywZPqjggacYhxaFxgsxXdMlJ2C7TwdMQ,8220
|
|
10
|
-
pybhpt/flux.py,sha256=2ahStjbsswsWMNPfDiOlQIrzulJZrhaqoDENC0bCLE4,4299
|
|
11
|
-
pybhpt/.dylibs/libgsl.28.dylib,sha256=cJ5srjDdZDNLeRXrwhARDtAVaHDiePifPX1yaR5KPgc,2439680
|
|
12
|
-
pybhpt/.dylibs/libgslcblas.0.dylib,sha256=xxE7gWPfCeMMkYxciiJ0-IcSJqLwdfHVayP70bOxWL0,272784
|
|
13
|
-
pybhpt-0.9.4.dist-info/RECORD,,
|
|
14
|
-
pybhpt-0.9.4.dist-info/WHEEL,sha256=IyDInmnGNwPAk8ERKOFXaEfq-LcK0Q6JmkeYwroJxhI,142
|
|
15
|
-
pybhpt-0.9.4.dist-info/METADATA,sha256=EMPVuX-dje_Xt0axnmQHVeuax7rV66-dPzXO0_92ehc,5386
|
|
16
|
-
pybhpt-0.9.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
File without changes
|