lisaanalysistools 1.1.19__cp310-cp310-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.
- lisaanalysistools/git_version.py +7 -0
- lisaanalysistools-1.1.19.dist-info/METADATA +281 -0
- lisaanalysistools-1.1.19.dist-info/RECORD +49 -0
- lisaanalysistools-1.1.19.dist-info/WHEEL +5 -0
- lisaanalysistools-1.1.19.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.pxd +44 -0
- lisatools/cutils/pycppdetector.pyx +222 -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-310-darwin.so +0 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
cimport numpy as np
|
|
3
|
+
from libcpp.string cimport string
|
|
4
|
+
from libcpp cimport bool
|
|
5
|
+
from libc.stdint cimport uintptr_t
|
|
6
|
+
|
|
7
|
+
from gpubackendtools import wrapper
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
cdef class pycppDetector:
|
|
11
|
+
|
|
12
|
+
def __cinit__(self,
|
|
13
|
+
*args,
|
|
14
|
+
**kwargs
|
|
15
|
+
):
|
|
16
|
+
(
|
|
17
|
+
dt,
|
|
18
|
+
N,
|
|
19
|
+
n_arr,
|
|
20
|
+
L_arr,
|
|
21
|
+
x_arr,
|
|
22
|
+
links,
|
|
23
|
+
sc_r,
|
|
24
|
+
sc_e,
|
|
25
|
+
armlength
|
|
26
|
+
), tkwargs = wrapper(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
self.dt = dt
|
|
29
|
+
self.N = N
|
|
30
|
+
self.n_arr = n_arr
|
|
31
|
+
self.L_arr = L_arr
|
|
32
|
+
self.x_arr = x_arr
|
|
33
|
+
self.links = links
|
|
34
|
+
self.sc_r = sc_r
|
|
35
|
+
self.sc_e = sc_e
|
|
36
|
+
self.armlength = armlength
|
|
37
|
+
|
|
38
|
+
cdef size_t n_arr_in = n_arr
|
|
39
|
+
cdef size_t L_arr_in = L_arr
|
|
40
|
+
cdef size_t x_arr_in = x_arr
|
|
41
|
+
cdef size_t links_in = links
|
|
42
|
+
cdef size_t sc_r_in = sc_r
|
|
43
|
+
cdef size_t sc_e_in = sc_e
|
|
44
|
+
|
|
45
|
+
self.g = new OrbitsWrap(
|
|
46
|
+
dt,
|
|
47
|
+
N,
|
|
48
|
+
<double*> n_arr_in,
|
|
49
|
+
<double*> L_arr_in,
|
|
50
|
+
<double*> x_arr_in,
|
|
51
|
+
<int*> links_in,
|
|
52
|
+
<int*> sc_r_in,
|
|
53
|
+
<int*> sc_e_in,
|
|
54
|
+
armlength
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def __dealloc__(self):
|
|
58
|
+
self.g.dealloc()
|
|
59
|
+
if self.g:
|
|
60
|
+
del self.g
|
|
61
|
+
|
|
62
|
+
def __reduce__(self):
|
|
63
|
+
return (rebuild, (self.dt, self.N, self.n_arr, self.L_arr, self.x_arr, self.links, self.sc_r, self.sc_e, self.armlength))
|
|
64
|
+
|
|
65
|
+
def get_window(self, t: float) -> int:
|
|
66
|
+
return self.g.get_window(t)
|
|
67
|
+
|
|
68
|
+
def get_normal_unit_vec_single(self, t: float, link: int) -> np.ndarray:
|
|
69
|
+
cdef VecWrap *out = new VecWrap(0.0, 0.0, 0.0)
|
|
70
|
+
self.g.get_normal_unit_vec_ptr(out, t, link)
|
|
71
|
+
|
|
72
|
+
return np.array([out.x, out.y, out.z])
|
|
73
|
+
|
|
74
|
+
def get_normal_unit_vec_arr(self, t: np.ndarray, link: int) -> np.ndarray:
|
|
75
|
+
output = np.zeros((len(t), 3), dtype=float)
|
|
76
|
+
assert t.ndim == 1
|
|
77
|
+
for i in range(len(t)):
|
|
78
|
+
output[i] = self.get_normal_unit_vec_single(t[i], link)
|
|
79
|
+
|
|
80
|
+
return output
|
|
81
|
+
|
|
82
|
+
def get_normal_unit_vec(self, t: np.ndarray | float, link: int) -> np.ndarray:
|
|
83
|
+
|
|
84
|
+
if isinstance(t, float):
|
|
85
|
+
return self.get_normal_unit_vec_single(t, link)
|
|
86
|
+
elif isinstance(t, np.ndarray):
|
|
87
|
+
return self.get_normal_unit_vec_arr(t, link)
|
|
88
|
+
|
|
89
|
+
def get_link_ind(self, link: int) -> int:
|
|
90
|
+
return self.g.get_link_ind(link)
|
|
91
|
+
|
|
92
|
+
def get_sc_ind(self, sc: int) -> int:
|
|
93
|
+
return self.g.get_sc_ind(sc)
|
|
94
|
+
|
|
95
|
+
def get_light_travel_time_single(self, t: float, link: int) -> float:
|
|
96
|
+
return self.g.get_light_travel_time(t, link)
|
|
97
|
+
|
|
98
|
+
def get_light_travel_time_arr(self, t: np.ndarray, link: int) -> np.ndarray:
|
|
99
|
+
output = np.zeros((len(t),), dtype=float)
|
|
100
|
+
assert t.ndim == 1
|
|
101
|
+
for i in range(len(t)):
|
|
102
|
+
output[i] = self.get_light_travel_time_single(t[i], link)
|
|
103
|
+
|
|
104
|
+
return output
|
|
105
|
+
|
|
106
|
+
def get_light_travel_time(self, t: np.ndarray | float, link: int) -> np.ndarray:
|
|
107
|
+
|
|
108
|
+
if isinstance(t, float):
|
|
109
|
+
print("t", t)
|
|
110
|
+
return self.get_light_travel_time_single(t, link)
|
|
111
|
+
elif isinstance(t, np.ndarray):
|
|
112
|
+
return self.get_light_travel_time_arr(t, link)
|
|
113
|
+
|
|
114
|
+
def get_pos_single(self, t: float, sc: int) -> np.ndarray:
|
|
115
|
+
|
|
116
|
+
cdef VecWrap *out = new VecWrap(0.0, 0.0, 0.0)
|
|
117
|
+
self.g.get_pos_ptr(out, t, sc)
|
|
118
|
+
|
|
119
|
+
return np.array([out.x, out.y, out.z])
|
|
120
|
+
|
|
121
|
+
def get_pos_arr(self, t: np.ndarray, sc: int) -> np.ndarray:
|
|
122
|
+
output = np.zeros((len(t), 3), dtype=float)
|
|
123
|
+
assert t.ndim == 1
|
|
124
|
+
for i in range(len(t)):
|
|
125
|
+
output[i] = self.get_pos_single(t[i], sc)
|
|
126
|
+
|
|
127
|
+
return output
|
|
128
|
+
|
|
129
|
+
def get_pos(self, t: np.ndarray | float, sc: int) -> np.ndarray:
|
|
130
|
+
|
|
131
|
+
if isinstance(t, float):
|
|
132
|
+
return self.get_pos_single(t, sc)
|
|
133
|
+
elif isinstance(t, np.ndarray):
|
|
134
|
+
return self.get_pos_arr(t, sc)
|
|
135
|
+
|
|
136
|
+
def get_pos_arr_wrap(self, *args, **kwargs):
|
|
137
|
+
(pos_x, pos_y, pos_z, t, sc, num), tkwargs = wrapper(*args, **kwargs)
|
|
138
|
+
|
|
139
|
+
cdef size_t pos_x_in = pos_x
|
|
140
|
+
cdef size_t pos_y_in = pos_y
|
|
141
|
+
cdef size_t pos_z_in = pos_z
|
|
142
|
+
cdef size_t t_in = t
|
|
143
|
+
cdef size_t sc_in = sc
|
|
144
|
+
|
|
145
|
+
self.g.get_pos_arr(
|
|
146
|
+
<double *>pos_x_in,
|
|
147
|
+
<double *>pos_y_in,
|
|
148
|
+
<double *>pos_z_in,
|
|
149
|
+
<double *>t_in,
|
|
150
|
+
<int *>sc_in,
|
|
151
|
+
num
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def get_normal_unit_vec_arr_wrap(self, *args, **kwargs):
|
|
155
|
+
(normal_unit_vec_x, normal_unit_vec_y, normal_unit_vec_z, t, sc, num), tkwargs = wrapper(*args, **kwargs)
|
|
156
|
+
|
|
157
|
+
cdef size_t normal_unit_vec_x_in = normal_unit_vec_x
|
|
158
|
+
cdef size_t normal_unit_vec_y_in = normal_unit_vec_y
|
|
159
|
+
cdef size_t normal_unit_vec_z_in = normal_unit_vec_z
|
|
160
|
+
cdef size_t t_in = t
|
|
161
|
+
cdef size_t sc_in = sc
|
|
162
|
+
|
|
163
|
+
self.g.get_normal_unit_vec_arr(
|
|
164
|
+
<double *>normal_unit_vec_x_in,
|
|
165
|
+
<double *>normal_unit_vec_y_in,
|
|
166
|
+
<double *>normal_unit_vec_z_in,
|
|
167
|
+
<double *>t_in,
|
|
168
|
+
<int *>sc_in,
|
|
169
|
+
num
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def get_light_travel_time_arr_wrap(self, *args, **kwargs):
|
|
173
|
+
(ltt, t, link, num), tkwargs = wrapper(*args, **kwargs)
|
|
174
|
+
|
|
175
|
+
cdef size_t ltt_in = ltt
|
|
176
|
+
cdef size_t t_in = t
|
|
177
|
+
cdef size_t link_in = link
|
|
178
|
+
|
|
179
|
+
self.g.get_light_travel_time_arr(
|
|
180
|
+
<double *>ltt_in,
|
|
181
|
+
<double *>t_in,
|
|
182
|
+
<int *>link_in,
|
|
183
|
+
num
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def get_sc_r_from_arr(self, i: int) -> int:
|
|
187
|
+
return self.g.get_sc_r_from_arr(i)
|
|
188
|
+
|
|
189
|
+
def get_sc_e_from_arr(self, i: int) -> int:
|
|
190
|
+
return self.g.get_sc_e_from_arr(i)
|
|
191
|
+
|
|
192
|
+
def get_link_from_arr(self, i: int) -> int:
|
|
193
|
+
return self.g.get_link_from_arr(i)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def ptr(self) -> long:
|
|
198
|
+
return <uintptr_t>self.g
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def rebuild(dt,
|
|
202
|
+
N,
|
|
203
|
+
n_arr,
|
|
204
|
+
L_arr,
|
|
205
|
+
x_arr,
|
|
206
|
+
links,
|
|
207
|
+
sc_r,
|
|
208
|
+
sc_e,
|
|
209
|
+
armlength
|
|
210
|
+
):
|
|
211
|
+
c = pycppDetector(
|
|
212
|
+
dt,
|
|
213
|
+
N,
|
|
214
|
+
n_arr,
|
|
215
|
+
L_arr,
|
|
216
|
+
x_arr,
|
|
217
|
+
links,
|
|
218
|
+
sc_r,
|
|
219
|
+
sc_e,
|
|
220
|
+
armlength
|
|
221
|
+
)
|
|
222
|
+
return c
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import warnings
|
|
3
|
+
from abc import ABC
|
|
4
|
+
from typing import Any, Tuple, Optional, List
|
|
5
|
+
|
|
6
|
+
import math
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy import interpolate
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import cupy as cp
|
|
13
|
+
|
|
14
|
+
except (ModuleNotFoundError, ImportError):
|
|
15
|
+
import numpy as cp
|
|
16
|
+
|
|
17
|
+
from . import detector as lisa_models
|
|
18
|
+
from .utils.utility import AET, get_array_module
|
|
19
|
+
from .utils.constants import *
|
|
20
|
+
from .stochastic import (
|
|
21
|
+
StochasticContribution,
|
|
22
|
+
FittedHyperbolicTangentGalacticForeground,
|
|
23
|
+
)
|
|
24
|
+
from .sensitivity import SensitivityMatrix
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DataResidualArray:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class DataResidualArray:
|
|
32
|
+
"""Container to hold Data, residual, or template information.
|
|
33
|
+
|
|
34
|
+
This class abstracts the connection with the sensitivity matrices to make this analysis
|
|
35
|
+
as generic as possible for the user frontend, while handling
|
|
36
|
+
special computations in the backend.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
data_res_in: Data, residual, or template input information. Can be a list, numpy array
|
|
40
|
+
or another :class:`DataResidualArray`.
|
|
41
|
+
dt: Timestep in seconds.
|
|
42
|
+
f_arr: Frequency array.
|
|
43
|
+
df: Delta f in frequency domain.
|
|
44
|
+
**kwargs: For future compatibility.
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
data_res_in: List[np.ndarray] | np.ndarray | DataResidualArray,
|
|
51
|
+
dt: Optional[float] = None,
|
|
52
|
+
f_arr: Optional[np.ndarray] = None,
|
|
53
|
+
df: Optional[float] = None,
|
|
54
|
+
**kwargs: dict,
|
|
55
|
+
) -> None:
|
|
56
|
+
if isinstance(data_res_in, DataResidualArray):
|
|
57
|
+
for key, item in data_res_in.__dict__.items():
|
|
58
|
+
setattr(self, key, item)
|
|
59
|
+
|
|
60
|
+
else:
|
|
61
|
+
self._check_inputs(dt=dt, f_arr=f_arr, df=df)
|
|
62
|
+
self.data_res_arr = data_res_in
|
|
63
|
+
self._store_time_and_frequency_information(dt=dt, f_arr=f_arr, df=df)
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def init_kwargs(self) -> dict:
|
|
67
|
+
"""Initial dt, df, f_arr"""
|
|
68
|
+
return self._init_kwargs
|
|
69
|
+
|
|
70
|
+
@init_kwargs.setter
|
|
71
|
+
def init_kwargs(self, init_kwargs: dict) -> None:
|
|
72
|
+
"""Set initial kwargs."""
|
|
73
|
+
self._init_kwargs = init_kwargs
|
|
74
|
+
|
|
75
|
+
def _check_inputs(
|
|
76
|
+
self,
|
|
77
|
+
dt: Optional[float] = None,
|
|
78
|
+
f_arr: Optional[np.ndarray] = None,
|
|
79
|
+
df: Optional[float] = None,
|
|
80
|
+
):
|
|
81
|
+
number_of_none = 0
|
|
82
|
+
|
|
83
|
+
number_of_none += 1 if dt is None else 0
|
|
84
|
+
number_of_none += 1 if f_arr is None else 0
|
|
85
|
+
number_of_none += 1 if df is None else 0
|
|
86
|
+
|
|
87
|
+
if number_of_none == 3:
|
|
88
|
+
raise ValueError("Must provide either df, dt, or f_arr.")
|
|
89
|
+
|
|
90
|
+
elif number_of_none == 1:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"Can only provide one of dt, f_arr, or df. Not more than one."
|
|
93
|
+
)
|
|
94
|
+
self.init_kwargs = dict(dt=dt, f_arr=f_arr, df=df)
|
|
95
|
+
|
|
96
|
+
def _store_time_and_frequency_information(
|
|
97
|
+
self,
|
|
98
|
+
dt: Optional[float] = None,
|
|
99
|
+
f_arr: Optional[np.ndarray] = None,
|
|
100
|
+
df: Optional[float] = None,
|
|
101
|
+
):
|
|
102
|
+
if dt is not None:
|
|
103
|
+
self._dt = dt
|
|
104
|
+
self._Tobs = self.data_length * dt
|
|
105
|
+
self._df = 1 / self._Tobs
|
|
106
|
+
self._fmax = 1 / (2 * dt)
|
|
107
|
+
xp = get_array_module(self.data_res_arr)
|
|
108
|
+
self._f_arr = xp.asarray(np.fft.rfftfreq(self.data_length, dt))
|
|
109
|
+
|
|
110
|
+
# transform data
|
|
111
|
+
tmp = xp.fft.rfft(self.data_res_arr, axis=-1) * self._dt
|
|
112
|
+
del self._data_res_arr
|
|
113
|
+
self._data_res_arr = tmp
|
|
114
|
+
self.data_length = self._data_res_arr.shape[-1]
|
|
115
|
+
|
|
116
|
+
elif df is not None:
|
|
117
|
+
self._df = df
|
|
118
|
+
self._Tobs = 1 / self._df
|
|
119
|
+
self._fmax = (self.data_length - 1) * df
|
|
120
|
+
self._dt = 1 / (2 * self._fmax)
|
|
121
|
+
self._f_arr = np.arange(0.0, self._fmax, self._df)
|
|
122
|
+
|
|
123
|
+
elif f_arr is not None:
|
|
124
|
+
self._f_arr = f_arr
|
|
125
|
+
self._fmax = f_arr.max()
|
|
126
|
+
# constant spacing
|
|
127
|
+
if np.all(np.diff(f_arr) == np.diff(f_arr)[0]):
|
|
128
|
+
self._df = np.diff(f_arr)[0].item()
|
|
129
|
+
|
|
130
|
+
if f_arr[0] == 0.0:
|
|
131
|
+
# could be fft because of constant spacing and f_arr[0] == 0.0
|
|
132
|
+
self._Tobs = 1 / self._df
|
|
133
|
+
self._dt = 1 / (2 * self._fmax)
|
|
134
|
+
|
|
135
|
+
else:
|
|
136
|
+
# cannot be fft basis
|
|
137
|
+
self._Tobs = None
|
|
138
|
+
self._dt = None
|
|
139
|
+
|
|
140
|
+
else:
|
|
141
|
+
self._df = None
|
|
142
|
+
self._Tobs = None
|
|
143
|
+
self._dt = None
|
|
144
|
+
|
|
145
|
+
if len(self.f_arr) != self.data_length:
|
|
146
|
+
raise ValueError(
|
|
147
|
+
"Entered or determined f_arr does not have the same length as the data channel inputs."
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def fmax(self):
|
|
152
|
+
"""Maximum frequency."""
|
|
153
|
+
return self._fmax
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def f_arr(self):
|
|
157
|
+
"""Frequency array."""
|
|
158
|
+
return self._f_arr
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def dt(self):
|
|
162
|
+
"""Time step in seconds."""
|
|
163
|
+
if self._dt is None:
|
|
164
|
+
raise ValueError("dt cannot be determined from this f_arr input.")
|
|
165
|
+
|
|
166
|
+
return self._dt
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def Tobs(self):
|
|
170
|
+
"""Observation time in seconds"""
|
|
171
|
+
if self._Tobs is None:
|
|
172
|
+
raise ValueError("Tobs cannot be determined from this f_arr input.")
|
|
173
|
+
|
|
174
|
+
return self._Tobs
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def df(self):
|
|
178
|
+
"""Delta f in the frequency domain."""
|
|
179
|
+
if self._df is None:
|
|
180
|
+
raise ValueError("df cannot be determined from this f_arr input.")
|
|
181
|
+
|
|
182
|
+
return self._df
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def frequency_arr(self) -> np.ndarray:
|
|
186
|
+
"""Frequency array"""
|
|
187
|
+
return self._f_arr
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def data_res_arr(self) -> np.ndarray:
|
|
191
|
+
"""Actual data residual array"""
|
|
192
|
+
return self._data_res_arr
|
|
193
|
+
|
|
194
|
+
@data_res_arr.setter
|
|
195
|
+
def data_res_arr(self, data_res_arr: List[np.ndarray] | np.ndarray) -> None:
|
|
196
|
+
"""Set ``data_res_arr``."""
|
|
197
|
+
self._data_res_arr_input = data_res_arr
|
|
198
|
+
|
|
199
|
+
if (
|
|
200
|
+
isinstance(data_res_arr, np.ndarray) or isinstance(data_res_arr, cp.ndarray)
|
|
201
|
+
) and data_res_arr.ndim == 1:
|
|
202
|
+
data_res_arr = [data_res_arr]
|
|
203
|
+
|
|
204
|
+
elif (
|
|
205
|
+
isinstance(data_res_arr, np.ndarray) or isinstance(data_res_arr, cp.ndarray)
|
|
206
|
+
) and data_res_arr.ndim == 2:
|
|
207
|
+
data_res_arr = list(data_res_arr)
|
|
208
|
+
|
|
209
|
+
new_out = np.full(len(data_res_arr), None, dtype=object)
|
|
210
|
+
self.data_length = None
|
|
211
|
+
for i in range(len(data_res_arr)):
|
|
212
|
+
current_data = data_res_arr[i]
|
|
213
|
+
if isinstance(current_data, np.ndarray) or isinstance(
|
|
214
|
+
current_data, cp.ndarray
|
|
215
|
+
):
|
|
216
|
+
if self.data_length is None:
|
|
217
|
+
self.data_length = len(current_data)
|
|
218
|
+
else:
|
|
219
|
+
assert len(current_data) == self.data_length
|
|
220
|
+
|
|
221
|
+
new_out[i] = current_data
|
|
222
|
+
else:
|
|
223
|
+
raise ValueError
|
|
224
|
+
|
|
225
|
+
self.nchannels = len(new_out)
|
|
226
|
+
xp = get_array_module(new_out[0])
|
|
227
|
+
self._data_res_arr = xp.asarray(list(new_out), dtype=new_out[0].dtype)
|
|
228
|
+
|
|
229
|
+
def __getitem__(self, index: tuple) -> np.ndarray:
|
|
230
|
+
"""Index this class directly in ``self.data_res_arr``."""
|
|
231
|
+
return self.data_res_arr[index]
|
|
232
|
+
|
|
233
|
+
def __setitem__(self, index: tuple, value: float | np.ndarray) -> np.ndarray:
|
|
234
|
+
"""Index this class directly in ``self.data_res_arr``."""
|
|
235
|
+
self.data_res_arr[index] = value
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def ndim(self) -> int:
|
|
239
|
+
"""Number of dimensions in the `data_res_arr`."""
|
|
240
|
+
return self.data_res_arr.ndim
|
|
241
|
+
|
|
242
|
+
def flatten(self) -> np.ndarray:
|
|
243
|
+
"""Flatten the ``data_res_arr``."""
|
|
244
|
+
return self.data_res_arr.flatten()
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def shape(self) -> tuple:
|
|
248
|
+
"""Shape of ``data_res_arr``."""
|
|
249
|
+
return self.data_res_arr.shape
|
|
250
|
+
|
|
251
|
+
def loglog(
|
|
252
|
+
self,
|
|
253
|
+
ax: Optional[List[plt.Axes] | plt.Axes] = None,
|
|
254
|
+
fig: Optional[plt.Figure] = None,
|
|
255
|
+
inds: Optional[List[int] | int] = None,
|
|
256
|
+
char_strain: Optional[bool] = False,
|
|
257
|
+
**kwargs: dict,
|
|
258
|
+
) -> Tuple[plt.Figure, plt.Axes]:
|
|
259
|
+
"""Produce a log-log plot of the data.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
ax: Matplotlib Axes objects to add plots. Either a list of Axes objects or a single Axes object.
|
|
263
|
+
fig: Matplotlib figure object.
|
|
264
|
+
inds: Integer index to select out which data to add to a single access.
|
|
265
|
+
A list can be provided if ax is a list. They must be the same length.
|
|
266
|
+
char_strain: If ``True`` return plot in characteristic strain representation.
|
|
267
|
+
**kwargs: Keyword arguments to be passed to ``loglog`` function in matplotlib.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
Matplotlib figure and axes objects in a 2-tuple.
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
"""
|
|
274
|
+
if ax is None and fig is None:
|
|
275
|
+
nrows = 1
|
|
276
|
+
ncols = self.shape[0]
|
|
277
|
+
|
|
278
|
+
fig, ax = plt.subplots(nrows, ncols, sharex=True, sharey=True)
|
|
279
|
+
ax = ax.ravel()
|
|
280
|
+
inds_list = range(len(ax))
|
|
281
|
+
|
|
282
|
+
elif ax is not None:
|
|
283
|
+
if isinstance(ax, list):
|
|
284
|
+
assert len(ax) == np.prod(self.shape[:-1])
|
|
285
|
+
if inds is None:
|
|
286
|
+
inds_list = list(np.arange(np.prod(self.shape[:-1])))
|
|
287
|
+
else:
|
|
288
|
+
assert isinstance(inds, list) and len(inds) == len(ax)
|
|
289
|
+
inds_list = inds
|
|
290
|
+
|
|
291
|
+
elif isinstance(ax, plt.Axes):
|
|
292
|
+
assert inds is not None and (
|
|
293
|
+
isinstance(inds, tuple) or isinstance(inds, int)
|
|
294
|
+
)
|
|
295
|
+
ax = [ax]
|
|
296
|
+
inds_list = [inds]
|
|
297
|
+
|
|
298
|
+
elif fig is not None:
|
|
299
|
+
raise NotImplementedError
|
|
300
|
+
|
|
301
|
+
for i, ax_tmp in zip(inds_list, ax):
|
|
302
|
+
plot_in = np.abs(self.data_res_arr[i])
|
|
303
|
+
if char_strain:
|
|
304
|
+
plot_in *= self.frequency_arr
|
|
305
|
+
ax_tmp.loglog(self.frequency_arr, plot_in, **kwargs)
|
|
306
|
+
|
|
307
|
+
return (fig, ax)
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def char_strain(self) -> np.ndarray:
|
|
311
|
+
"""Characteristic strain representation of the data."""
|
|
312
|
+
return np.sqrt(self.f_arr) * np.abs(self.data_res_arr)
|