lisaanalysistools 1.1.20__cp39-cp39-macosx_15_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.
- lisaanalysistools/git_version.py +7 -0
- lisaanalysistools-1.1.20.dist-info/METADATA +281 -0
- lisaanalysistools-1.1.20.dist-info/RECORD +48 -0
- lisaanalysistools-1.1.20.dist-info/WHEEL +5 -0
- lisaanalysistools-1.1.20.dist-info/licenses/LICENSE +201 -0
- lisatools/.dylibs/libgcc_s.1.1.dylib +0 -0
- lisatools/.dylibs/libstdc++.6.dylib +0 -0
- lisatools/__init__.py +90 -0
- lisatools/_version.py +34 -0
- lisatools/analysiscontainer.py +474 -0
- lisatools/cutils/Detector.cu +307 -0
- lisatools/cutils/Detector.hpp +84 -0
- lisatools/cutils/__init__.py +129 -0
- lisatools/cutils/global.hpp +28 -0
- lisatools/cutils/pycppdetector.pyx +256 -0
- lisatools/datacontainer.py +312 -0
- lisatools/detector.py +867 -0
- lisatools/diagnostic.py +990 -0
- lisatools/git_version.py.in +7 -0
- lisatools/orbit_files/equalarmlength-orbits-best-fit-to-esa.h5 +0 -0
- lisatools/orbit_files/equalarmlength-orbits.h5 +0 -0
- lisatools/orbit_files/esa-trailing-orbits.h5 +0 -0
- lisatools/sampling/__init__.py +0 -0
- lisatools/sampling/likelihood.py +882 -0
- lisatools/sampling/moves/__init__.py +0 -0
- lisatools/sampling/moves/skymodehop.py +110 -0
- lisatools/sampling/prior.py +646 -0
- lisatools/sampling/stopping.py +320 -0
- lisatools/sampling/utility.py +411 -0
- lisatools/sensitivity.py +1554 -0
- lisatools/sources/__init__.py +6 -0
- lisatools/sources/bbh/__init__.py +1 -0
- lisatools/sources/bbh/waveform.py +106 -0
- lisatools/sources/defaultresponse.py +37 -0
- lisatools/sources/emri/__init__.py +1 -0
- lisatools/sources/emri/waveform.py +79 -0
- lisatools/sources/gb/__init__.py +1 -0
- lisatools/sources/gb/waveform.py +69 -0
- lisatools/sources/utils.py +459 -0
- lisatools/sources/waveformbase.py +41 -0
- lisatools/stochastic.py +327 -0
- lisatools/utils/__init__.py +0 -0
- lisatools/utils/constants.py +54 -0
- lisatools/utils/exceptions.py +95 -0
- lisatools/utils/parallelbase.py +11 -0
- lisatools/utils/utility.py +122 -0
- lisatools_backend_cpu/git_version.py +7 -0
- lisatools_backend_cpu/pycppdetector.cpython-39-darwin.so +0 -0
lisatools/stochastic.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import warnings
|
|
3
|
+
from abc import ABC
|
|
4
|
+
from typing import Any, Tuple, Optional, List, Dict
|
|
5
|
+
|
|
6
|
+
import math
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy import interpolate
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import cupy as cp
|
|
12
|
+
|
|
13
|
+
except (ModuleNotFoundError, ImportError):
|
|
14
|
+
import numpy as cp
|
|
15
|
+
|
|
16
|
+
from . import detector as lisa_models
|
|
17
|
+
from .utils.utility import AET
|
|
18
|
+
from .utils.constants import *
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StochasticContribution(ABC):
|
|
22
|
+
"""Base Class for Stochastic Contributions to the PSD."""
|
|
23
|
+
|
|
24
|
+
ndim = None
|
|
25
|
+
added_stochastic_list = []
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def _check_ndim(cls, params: np.ndarray | list) -> None:
|
|
29
|
+
"""Check the dimensionality of the parameters matches the model.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
params: Parameters for stochastic model.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
if cls.ndim is None:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
"When subclassing the StochasticContribution class, must set `ndim` as a static attribute."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if len(params) != cls.ndim:
|
|
41
|
+
raise ValueError("length of parameters is not equivalent to class ndim.")
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def get_Sh(
|
|
45
|
+
cls, f: float | np.ndarray, *params: np.ndarray | list, **kwargs: Any
|
|
46
|
+
) -> float | np.ndarray:
|
|
47
|
+
"""Calculate the power spectral density of the stochastic contribution.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
f: Frequency array.
|
|
51
|
+
*params: Parameters for the stochastic model.
|
|
52
|
+
**kwargs: Keyword arguments for the stochastic model.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
if len(cls.added_stochastic_list) > 0:
|
|
56
|
+
cls._check_ndim(params[0])
|
|
57
|
+
return cls.specific_Sh_function(f, *params, **kwargs)
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def specific_Sh_function(
|
|
61
|
+
f: float | np.ndarray, *args: Any, **kwargs: Any
|
|
62
|
+
) -> float | np.ndarray:
|
|
63
|
+
"""Calculate the power spectral density contained in a stochastic signal contribution.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
f: Frequency array.
|
|
67
|
+
*args: Any arguments for the function.
|
|
68
|
+
**kwargs: Any keyword arguments for the function.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Power spectral density contained in stochastic signal.
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class StochasticContributionContainer:
|
|
78
|
+
"""Container for multiple Stochastic Contributions
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
stochastic_contribution_dict: Dictionary with multiple Stochastic entries.
|
|
82
|
+
Keys are the names and values are of type :class:`StochasticContribution`.
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self, stochastic_contribution_dict: dict[StochasticContribution]
|
|
88
|
+
) -> None:
|
|
89
|
+
self.stochastic_contribution_dict = stochastic_contribution_dict
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def stochastic_contribution_dict(self) -> dict[StochasticContribution]:
|
|
93
|
+
"""Stochastic contribution storage."""
|
|
94
|
+
return self._stochastic_contribution_dict
|
|
95
|
+
|
|
96
|
+
@stochastic_contribution_dict.setter
|
|
97
|
+
def stochastic_contribution_dict(
|
|
98
|
+
self, stochastic_contribution_dict: dict[StochasticContribution]
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Set stochastic_contribution_dict."""
|
|
101
|
+
assert isinstance(stochastic_contribution_dict, dict)
|
|
102
|
+
for key, value in stochastic_contribution_dict.items():
|
|
103
|
+
if not isinstance(value, StochasticContribution):
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"Stochastic model {key} is not of type StochasticContribution."
|
|
106
|
+
)
|
|
107
|
+
self._stochastic_contribution_dict = stochastic_contribution_dict
|
|
108
|
+
|
|
109
|
+
def get_Sh(
|
|
110
|
+
self, f: float | np.ndarray, params_dict: dict[tuple], kwargs_dict: dict[dict]
|
|
111
|
+
) -> np.ndarray:
|
|
112
|
+
"""Calculate Sh for stochastic contribution.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
f: Frequency array.
|
|
116
|
+
params_dict: Dictionary with keys equivalent to ``self.stochastic_contribution_dict.keys()``.
|
|
117
|
+
Values are the parameters for each associated model.
|
|
118
|
+
kwargs_dict: Dictionary with keys equivalent to ``self.stochastic_contribution_dict.keys()``.
|
|
119
|
+
Values are the keyword argument dicts for each associated model.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Stochastic contribution.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
Sh_out = np.zeros_like(f)
|
|
126
|
+
for key in params_dict:
|
|
127
|
+
stochastic_contrib = self.stochastic_contribution_dict[key]
|
|
128
|
+
Sh_out += stochastic_contrib.get_Sh(
|
|
129
|
+
f, params_dict[key], **(kwargs_dict.get(key, {}))
|
|
130
|
+
)
|
|
131
|
+
return Sh_out
|
|
132
|
+
|
|
133
|
+
def __setitem__(self, key: str | int | tuple, val: StochasticContribution) -> None:
|
|
134
|
+
"""Set an item by directly indexing the class object."""
|
|
135
|
+
self.stochastic_contribution_dict[key] = val
|
|
136
|
+
|
|
137
|
+
def __getitem__(self, key: str | int | tuple) -> StochasticContribution:
|
|
138
|
+
"""Get item directly from dictionary."""
|
|
139
|
+
return self.stochastic_contribution_dict[key]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class HyperbolicTangentGalacticForeground(StochasticContribution):
|
|
143
|
+
"""Hyperbolic Tangent-based foreground fitting function."""
|
|
144
|
+
|
|
145
|
+
ndim = 5
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def specific_Sh_function(
|
|
149
|
+
f: float | np.ndarray, amp: float, fk: float, alpha: float, s1: float, s2: float
|
|
150
|
+
) -> float | np.ndarray:
|
|
151
|
+
"""Hyperbolic tangent model 1 for the Galaxy foreground noise
|
|
152
|
+
|
|
153
|
+
This model for the PSD contribution from the Galactic foreground noise is given by
|
|
154
|
+
|
|
155
|
+
.. math::
|
|
156
|
+
|
|
157
|
+
S_\\text{gal} = \\frac{A_\\text{gal}}{2}e^{-s_1f^\\alpha}f^{-7/3}\\left[ 1 + \\tanh{\\left(-s_2 (f - f_k)\\right)} \\right],
|
|
158
|
+
|
|
159
|
+
where :math:`A_\\text{gal}` is the amplitude of the stochastic signal, :math:`f_k` is the knee frequency at which a bend occurs,
|
|
160
|
+
math:`\\alpha` is a power law parameter, :math:`s_1` is a slope parameter below the knee,
|
|
161
|
+
and :math:`s_2` is a slope parameter after the knee.:
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
f: Frequency array.
|
|
165
|
+
amp: Amplitude parameter for the Galaxy.
|
|
166
|
+
fk: Knee frequency in Hz.
|
|
167
|
+
alpha: Power law parameter.
|
|
168
|
+
s1: Slope parameter below knee.
|
|
169
|
+
s2: Slope parameter above knee.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
PSD of the Galaxy foreground noise
|
|
173
|
+
|
|
174
|
+
"""
|
|
175
|
+
Sgal = (
|
|
176
|
+
amp
|
|
177
|
+
* np.exp(-(f**alpha) * s1)
|
|
178
|
+
* (f ** (-7.0 / 3.0))
|
|
179
|
+
* 0.5
|
|
180
|
+
* (1.0 + np.tanh(-(f - fk) * s2))
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return Sgal
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class FittedHyperbolicTangentGalacticForeground(HyperbolicTangentGalacticForeground):
|
|
187
|
+
# TODO: need to verify this is still working
|
|
188
|
+
ndim = 1
|
|
189
|
+
amp = 3.26651613e-44
|
|
190
|
+
alpha = 1.18300266e00
|
|
191
|
+
# Tobs should be in sec.
|
|
192
|
+
day = 86400.0
|
|
193
|
+
month = day * 30.5
|
|
194
|
+
year = 365.25 * 24.0 * 3600.0 # hard coded for initial fits
|
|
195
|
+
|
|
196
|
+
Xobs = [
|
|
197
|
+
1.0 * day,
|
|
198
|
+
3.0 * month,
|
|
199
|
+
6.0 * month,
|
|
200
|
+
1.0 * year,
|
|
201
|
+
2.0 * year,
|
|
202
|
+
4.0 * year,
|
|
203
|
+
10.0 * year,
|
|
204
|
+
]
|
|
205
|
+
knee = [
|
|
206
|
+
1.15120924e-02,
|
|
207
|
+
4.01884128e-03,
|
|
208
|
+
3.47302482e-03,
|
|
209
|
+
2.77606177e-03,
|
|
210
|
+
2.41178384e-03,
|
|
211
|
+
2.09278117e-03,
|
|
212
|
+
1.57362626e-03,
|
|
213
|
+
]
|
|
214
|
+
Slope1 = [
|
|
215
|
+
9.41315118e02,
|
|
216
|
+
1.36887568e03,
|
|
217
|
+
1.68729474e03,
|
|
218
|
+
1.76327234e03,
|
|
219
|
+
2.32678814e03,
|
|
220
|
+
3.01430978e03,
|
|
221
|
+
3.74970124e03,
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
Slope2 = [
|
|
225
|
+
1.03239773e02,
|
|
226
|
+
1.03351646e03,
|
|
227
|
+
1.62204855e03,
|
|
228
|
+
1.68631844e03,
|
|
229
|
+
2.06821665e03,
|
|
230
|
+
2.95774596e03,
|
|
231
|
+
3.15199454e03,
|
|
232
|
+
]
|
|
233
|
+
Tmax = 10 * YRSID_SI
|
|
234
|
+
|
|
235
|
+
@classmethod
|
|
236
|
+
def specific_Sh_function(
|
|
237
|
+
cls, f: float | np.ndarray, Tobs: float
|
|
238
|
+
) -> float | np.ndarray:
|
|
239
|
+
"""Fitted hyperbolic tangent model 1 for the Galaxy foreground noise.
|
|
240
|
+
|
|
241
|
+
This class fits the parameters for :class:`HyperbolicTangentGalacticForeground`
|
|
242
|
+
using analytic estimates from (# TODO). The fit is a function of time, so the user
|
|
243
|
+
inputs ``Tobs``.
|
|
244
|
+
|
|
245
|
+
# Sgal_1d = 2.2e-44*np.exp(-(fr**1.2)*0.9e3)*(fr**(-7./3.))*0.5*(1.0 + np.tanh(-(fr-1.4e-2)*0.7e2))
|
|
246
|
+
# Sgal_3m = 2.2e-44*np.exp(-(fr**1.2)*1.7e3)*(fr**(-7./3.))*0.5*(1.0 + np.tanh(-(fr-4.8e-3)*5.4e2))
|
|
247
|
+
# Sgal_1y = 2.2e-44*np.exp(-(fr**1.2)*2.2e3)*(fr**(-7./3.))*0.5*(1.0 + np.tanh(-(fr-3.1e-3)*1.3e3))
|
|
248
|
+
# Sgal_2y = 2.2e-44*np.exp(-(fr**1.2)*2.2e3)*(fr**(-7./3.))*0.5*(1.0 + np.tanh(-(fr-2.3e-3)*1.8e3))
|
|
249
|
+
# Sgal_4y = 2.2e-44*np.exp(-(fr**1.2)*2.9e3)*(fr**(-7./3.))*0.5*(1.0 + np.tanh(-(fr-2.0e-3)*1.9e3))
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
f: Frequency array.
|
|
253
|
+
Tobs: Observation time in seconds.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
PSD of the Galaxy foreground noise
|
|
257
|
+
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
if Tobs > cls.Tmax:
|
|
261
|
+
raise ValueError(
|
|
262
|
+
"Tobs is greater than the maximum allowable fit which is 10 years."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
# Interpolate
|
|
266
|
+
tck1 = interpolate.splrep(cls.Xobs, cls.Slope1, s=0, k=1)
|
|
267
|
+
tck2 = interpolate.splrep(cls.Xobs, cls.knee, s=0, k=1)
|
|
268
|
+
tck3 = interpolate.splrep(cls.Xobs, cls.Slope2, s=0, k=1)
|
|
269
|
+
s1 = interpolate.splev(Tobs, tck1, der=0).item()
|
|
270
|
+
fk = interpolate.splev(Tobs, tck2, der=0).item()
|
|
271
|
+
s2 = interpolate.splev(Tobs, tck3, der=0).item()
|
|
272
|
+
|
|
273
|
+
return HyperbolicTangentGalacticForeground.specific_Sh_function(
|
|
274
|
+
f, cls.amp, fk, cls.alpha, s1, s2
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
__stock_gb_stochastic_options__ = [
|
|
279
|
+
"HyperbolicTangentGalacticForeground",
|
|
280
|
+
"FittedHyperbolicTangentGalacticForeground",
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def get_stock_gb_stochastic_options() -> List[StochasticContribution]:
|
|
285
|
+
"""Get stock options for stochastic contributions.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
List of stock stochastic options.
|
|
289
|
+
|
|
290
|
+
"""
|
|
291
|
+
return __stock_gb_stochastic_options__
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def get_default_stochastic_from_str(stochastic: str) -> StochasticContribution:
|
|
295
|
+
"""Return a LISA stochastic from a ``str`` input.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
stochastic: Stochastic contribution indicated with a ``str``.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Stochastic contribution associated to that ``str``.
|
|
302
|
+
|
|
303
|
+
"""
|
|
304
|
+
if stochastic not in __stock_gb_stochastic_options__:
|
|
305
|
+
raise ValueError(
|
|
306
|
+
"Requested string stochastic is not available. See lisatools.stochastic documentation."
|
|
307
|
+
)
|
|
308
|
+
return globals()[stochastic]
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def check_stochastic(stochastic: Any) -> StochasticContribution:
|
|
312
|
+
"""Check input stochastic contribution.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
stochastic: Stochastic contribution to check.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Stochastic contribution checked. Adjusted from ``str`` if ``str`` input.
|
|
319
|
+
|
|
320
|
+
"""
|
|
321
|
+
if isinstance(stochastic, str):
|
|
322
|
+
stochastic = get_default_stochastic_from_str(stochastic)
|
|
323
|
+
|
|
324
|
+
if not issubclass(stochastic, StochasticContribution):
|
|
325
|
+
raise ValueError("stochastic argument not given correctly.")
|
|
326
|
+
|
|
327
|
+
return stochastic
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
MSUN_SI = 1.98848e30
|
|
2
|
+
YRSID_SI = 31558149.763545603
|
|
3
|
+
AU_SI = 149597870700.0
|
|
4
|
+
C_SI = 299792458.0
|
|
5
|
+
G_SI = 6.674080e-11
|
|
6
|
+
GMSUN = 1.3271244210789466e20
|
|
7
|
+
MTSUN_SI = 4.925491025873693e-06
|
|
8
|
+
MRSUN_SI = 1476.6250615036158
|
|
9
|
+
PC_SI = 3.0856775814913674e16
|
|
10
|
+
PI = 3.141592653589793238462643383279502884
|
|
11
|
+
PI_2 = 1.570796326794896619231321691639751442
|
|
12
|
+
PI_3 = 1.047197551196597746154214461093167628
|
|
13
|
+
PI_4 = 0.785398163397448309615660845819875721
|
|
14
|
+
SQRTPI = 1.772453850905516027298167483341145183
|
|
15
|
+
SQRTTWOPI = 2.506628274631000502415765284811045253
|
|
16
|
+
INVSQRTPI = 0.564189583547756286948079451560772585
|
|
17
|
+
INVSQRTTWOPI = 0.398942280401432677939946059934381868
|
|
18
|
+
GAMMA = 0.577215664901532860606512090082402431
|
|
19
|
+
SQRT2 = 1.414213562373095048801688724209698079
|
|
20
|
+
SQRT3 = 1.732050807568877293527446341505872367
|
|
21
|
+
SQRT6 = 2.449489742783178098197284074705891392
|
|
22
|
+
INVSQRT2 = 0.707106781186547524400844362104849039
|
|
23
|
+
INVSQRT3 = 0.577350269189625764509148780501957455
|
|
24
|
+
INVSQRT6 = 0.408248290463863016366214012450981898
|
|
25
|
+
F0 = 3.168753578687779e-08
|
|
26
|
+
Omega0 = 1.9909865927683788e-07
|
|
27
|
+
L_SI = 2.5e9
|
|
28
|
+
eorbit = 0.004824185218078991
|
|
29
|
+
ConstOmega = 1.99098659277e-7
|
|
30
|
+
|
|
31
|
+
#### Armlength
|
|
32
|
+
lisaL = 2.5e9 # LISA's arm meters
|
|
33
|
+
lisaLT = lisaL / C_SI # LISA's armn in sec
|
|
34
|
+
|
|
35
|
+
#### Noise levels
|
|
36
|
+
### Optical Metrology System noise
|
|
37
|
+
## Decomposition
|
|
38
|
+
Sloc = (1.7e-12) ** 2 # m^2/Hz
|
|
39
|
+
Ssci = (8.9e-12) ** 2 # m^2/Hz
|
|
40
|
+
Soth = (2.0e-12) ** 2 # m^2/Hz
|
|
41
|
+
|
|
42
|
+
######################
|
|
43
|
+
# Physical constants #
|
|
44
|
+
######################
|
|
45
|
+
|
|
46
|
+
# Mass of Jupiter
|
|
47
|
+
Mjup = 1.898e27
|
|
48
|
+
|
|
49
|
+
#################
|
|
50
|
+
# LISA constant #
|
|
51
|
+
#################
|
|
52
|
+
|
|
53
|
+
# Transfer frequency
|
|
54
|
+
fstar = C_SI / (lisaL * 2 * PI)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Definition of LISA Analysis Tools package common exceptions"""
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
from exceptiongroup import ExceptionGroup
|
|
5
|
+
except (ImportError, ModuleNotFoundError):
|
|
6
|
+
ExceptionGroup = ExceptionGroup
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LISAToolsException(Exception):
|
|
10
|
+
"""Base class for LISA Analysis Tools package exceptions."""
|
|
11
|
+
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CudaException(LISAToolsException):
|
|
16
|
+
"""Base class for CUDA-related exceptions."""
|
|
17
|
+
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CuPyException(LISAToolsException):
|
|
22
|
+
"""Base class for CuPy-related exceptions."""
|
|
23
|
+
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class MissingDependency(LISAToolsException):
|
|
28
|
+
"""Exception raised when a required dependency is missing."""
|
|
29
|
+
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class InvalidInputFile(LISAToolsException):
|
|
34
|
+
"""Exception raised when the content of an input file does not match expectations."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ConfigurationError(LISAToolsException):
|
|
38
|
+
"""Exception raised when configuration setup fails."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ConfigurationMissing(ConfigurationError):
|
|
42
|
+
"""Exception raised when an expected configuration entry is missing."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ConfigurationValidationError(ConfigurationError):
|
|
46
|
+
"""Exception raised when a configuration entry is invalid."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FileManagerException(LISAToolsException):
|
|
50
|
+
"""Exception raised by the FileManager."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class FileNotInRegistry(FileManagerException):
|
|
54
|
+
"""Exception raised when a requested file is not in file registry."""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class FileNotFoundLocally(FileManagerException):
|
|
58
|
+
"""Exception raised when file not found locally but expected to be."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class FileInvalidChecksum(FileManagerException):
|
|
62
|
+
"""Exception raised when file has invalid checksum."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class FileDownloadException(FileManagerException):
|
|
66
|
+
"""Exception raised if file download failed."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class FileDownloadNotFound(FileDownloadException):
|
|
70
|
+
"""Exception raised if file is not found at expected URL."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class FileDownloadConnectionError(FileDownloadException):
|
|
74
|
+
"""Exception raised in case of connection error during download."""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class FileDownloadIntegrityError(FileDownloadException):
|
|
78
|
+
"""Exception raised in case of integrity error after download."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class FileManagerDisabledAccess(FileManagerException):
|
|
82
|
+
"""Exception raised when trying to access a file whose tags are disabled"""
|
|
83
|
+
|
|
84
|
+
disabled_tag: str
|
|
85
|
+
file_name: str
|
|
86
|
+
|
|
87
|
+
def __init__(self, /, *args, disabled_tag: str, file_name: str, **kwargs):
|
|
88
|
+
self.disabled_tag = disabled_tag
|
|
89
|
+
self.file_name = file_name
|
|
90
|
+
super().__init__(*args, **kwargs)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
### Trajectory-related exceptions
|
|
94
|
+
class TrajectoryOffGridException(Exception):
|
|
95
|
+
"""Exception raised when a trajectory goes off-grid (except for the lower boundary in p)."""
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from typing import Optional, Sequence, TypeVar, Union
|
|
2
|
+
import types
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from gpubackendtools import ParallelModuleBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LISAToolsParallelModule(ParallelModuleBase):
|
|
9
|
+
def __init__(self, force_backend=None):
|
|
10
|
+
force_backend_in = ('lisatools', force_backend) if isinstance(force_backend, str) else force_backend
|
|
11
|
+
super().__init__(force_backend_in)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
import typing
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import cupy as cp
|
|
8
|
+
|
|
9
|
+
except (ModuleNotFoundError, ImportError):
|
|
10
|
+
import numpy as cp
|
|
11
|
+
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_array_module(arr: np.ndarray | cp.ndarray) -> object:
|
|
16
|
+
"""Return array library of an array (np/cp).
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
arr: Numpy or Cupy array.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
if isinstance(arr, np.ndarray):
|
|
23
|
+
return np
|
|
24
|
+
elif isinstance(arr, cp.ndarray):
|
|
25
|
+
return cp
|
|
26
|
+
else:
|
|
27
|
+
raise ValueError("arr must be a numpy or cupy array.")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def generate_noise_fd(N: int, df: float, *sensitivity_args: typing.Any, func: typing.Optional[typing.Callable]=None, **sensitivity_kwargs: typing.Any) -> np.ndarray[float]:
|
|
31
|
+
"""Generate noise directly in the Frequency Domain.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
N: Number of points in frequency domain waveform assuming frequencies greater than
|
|
35
|
+
or equal to zero.
|
|
36
|
+
df: Frequency domain bin size (``1 / (N * dt)``).
|
|
37
|
+
sensitivity_args: Arguments for ``func``.
|
|
38
|
+
func: Function for generating the sensitivity curve as a function of frequency. (default: :func:`lisatools.sensitivity.get_sensivity`)
|
|
39
|
+
sensitivity_kwargs: Keyword arguments for ``func``.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
An instance of generated noise in the frequency domain.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
if not isinstance(N, int):
|
|
47
|
+
raise ValueError(f"N must be an integer. See documentation for more information.")
|
|
48
|
+
|
|
49
|
+
if not isinstance(df, float):
|
|
50
|
+
raise ValueError(f"N must be an integer. See documentation for more information.")
|
|
51
|
+
|
|
52
|
+
if func is None:
|
|
53
|
+
# TODO: make this better
|
|
54
|
+
from lisatools.sensitivity import get_sensitivity
|
|
55
|
+
func = get_sensitivity
|
|
56
|
+
|
|
57
|
+
freqs = np.arange(N) * df
|
|
58
|
+
|
|
59
|
+
norm = 0.5 * (1.0 / df) ** 0.5
|
|
60
|
+
psd = func(freqs, *sensitivity_args, **sensitivity_kwargs)
|
|
61
|
+
noise_realization = psd ** (1 / 2) * (
|
|
62
|
+
np.random.normal(0, norm, len(freqs))
|
|
63
|
+
+ 1j * np.random.normal(0, norm, len(freqs))
|
|
64
|
+
)
|
|
65
|
+
return noise_realization
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def AET(
|
|
69
|
+
X: float | np.ndarray, Y: float | np.ndarray, Z: float | np.ndarray
|
|
70
|
+
) -> Tuple[float | np.ndarray, float | np.ndarray, float | np.ndarray]:
|
|
71
|
+
"""Transform to AET from XYZ
|
|
72
|
+
|
|
73
|
+
.. math::
|
|
74
|
+
|
|
75
|
+
A = (Z - X) / \\sqrt(2)
|
|
76
|
+
|
|
77
|
+
.. math::
|
|
78
|
+
|
|
79
|
+
E = (X - 2Y + Z) / \\sqrt(6)
|
|
80
|
+
|
|
81
|
+
.. math::
|
|
82
|
+
|
|
83
|
+
T = (X + Y + Z) / \\sqrt(3)
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
X: X-channel information.
|
|
87
|
+
Y: Y-channel information.
|
|
88
|
+
Z: Z-channel information.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
A, E, T Channels.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
return (
|
|
95
|
+
(Z - X) / np.sqrt(2.0),
|
|
96
|
+
(X - 2.0 * Y + Z) / np.sqrt(6.0),
|
|
97
|
+
(X + Y + Z) / np.sqrt(3.0),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# def searchsorted2d_vec(a, b, xp=None, gpu=None, **kwargs):
|
|
102
|
+
# if xp is None:
|
|
103
|
+
# xp = np
|
|
104
|
+
# else:
|
|
105
|
+
# try:
|
|
106
|
+
# xp.cuda.runtime.setDevice(gpu)
|
|
107
|
+
# except AttributeError:
|
|
108
|
+
# pass
|
|
109
|
+
|
|
110
|
+
# m, n = a.shape
|
|
111
|
+
# max_num = xp.maximum(a.max() - a.min(), b.max() - b.min()) + 1
|
|
112
|
+
# r = max_num * xp.arange(a.shape[0])[:, None]
|
|
113
|
+
# p = xp.searchsorted((a + r).ravel(), (b + r).ravel(), **kwargs).reshape(m, -1)
|
|
114
|
+
|
|
115
|
+
# out = p - n * (xp.arange(m)[:, None])
|
|
116
|
+
# try:
|
|
117
|
+
# xp.cuda.runtime.deviceSynchronize()
|
|
118
|
+
# except AttributeError:
|
|
119
|
+
# pass
|
|
120
|
+
|
|
121
|
+
# return out
|
|
122
|
+
|
|
Binary file
|