doppy 0.3.7__cp310-abi3-macosx_11_0_arm64.whl → 0.4.0__cp310-abi3-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of doppy might be problematic. Click here for more details.
- doppy/defaults.py +17 -1
- doppy/product/stare.py +96 -14
- doppy/raw/__init__.py +2 -2
- doppy/raw/halo_hpl.py +6 -2
- doppy/raw/windcube.py +244 -31
- doppy/raw/wls70.py +7 -2
- doppy/rs.abi3.so +0 -0
- doppy/utils.py +16 -1
- {doppy-0.3.7.dist-info → doppy-0.4.0.dist-info}/METADATA +4 -2
- {doppy-0.3.7.dist-info → doppy-0.4.0.dist-info}/RECORD +13 -13
- {doppy-0.3.7.dist-info → doppy-0.4.0.dist-info}/WHEEL +1 -1
- {doppy-0.3.7.dist-info → doppy-0.4.0.dist-info}/entry_points.txt +0 -0
- {doppy-0.3.7.dist-info → doppy-0.4.0.dist-info}/licenses/LICENSE +0 -0
doppy/defaults.py
CHANGED
|
@@ -1,2 +1,18 @@
|
|
|
1
|
+
DEFAULT_BEAM_ENERGY = 1e-5
|
|
2
|
+
DEFAULT_EFFECTIVE_DIAMETER = 25e-3
|
|
3
|
+
|
|
4
|
+
|
|
1
5
|
class Halo:
|
|
2
|
-
wavelength = 1.565e-6
|
|
6
|
+
wavelength = 1.565e-6 # [m]
|
|
7
|
+
receiver_bandwidth = 50e6 # [Hz]
|
|
8
|
+
beam_energy = DEFAULT_BEAM_ENERGY
|
|
9
|
+
effective_diameter = DEFAULT_EFFECTIVE_DIAMETER
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WindCube:
|
|
13
|
+
# https://doi.org/10.5194/essd-13-3539-2021
|
|
14
|
+
wavelength = 1.54e-6 # [m]
|
|
15
|
+
receiver_bandwidth = 55e6 # [Hz]
|
|
16
|
+
beam_energy = DEFAULT_BEAM_ENERGY
|
|
17
|
+
effective_diameter = 50e-3 # [m]
|
|
18
|
+
focus = 1e3 # [m]
|
doppy/product/stare.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Sequence, Tuple, TypeAlias
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
import numpy.typing as npt
|
|
11
11
|
import scipy
|
|
12
|
-
from scipy.ndimage import uniform_filter
|
|
12
|
+
from scipy.ndimage import median_filter, uniform_filter
|
|
13
13
|
from sklearn.cluster import KMeans
|
|
14
14
|
|
|
15
15
|
import doppy
|
|
@@ -51,6 +51,46 @@ class Stare:
|
|
|
51
51
|
)
|
|
52
52
|
raise TypeError
|
|
53
53
|
|
|
54
|
+
@classmethod
|
|
55
|
+
def mask_nan(cls, x: npt.NDArray[np.float64]) -> npt.NDArray[np.bool_]:
|
|
56
|
+
return np.isnan(x)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_windcube_data(
|
|
60
|
+
cls,
|
|
61
|
+
data: Sequence[str]
|
|
62
|
+
| Sequence[Path]
|
|
63
|
+
| Sequence[bytes]
|
|
64
|
+
| Sequence[BufferedIOBase],
|
|
65
|
+
) -> Stare:
|
|
66
|
+
raws = doppy.raw.WindCubeFixed.from_srcs(data)
|
|
67
|
+
raw = (
|
|
68
|
+
doppy.raw.WindCubeFixed.merge(raws).sorted_by_time().nan_profiles_removed()
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
wavelength = defaults.WindCube.wavelength
|
|
72
|
+
beta = _compute_beta(
|
|
73
|
+
snr=raw.cnr,
|
|
74
|
+
radial_distance=raw.radial_distance,
|
|
75
|
+
wavelength=wavelength,
|
|
76
|
+
beam_energy=defaults.WindCube.beam_energy,
|
|
77
|
+
receiver_bandwidth=defaults.WindCube.receiver_bandwidth,
|
|
78
|
+
focus=defaults.WindCube.focus,
|
|
79
|
+
effective_diameter=defaults.WindCube.effective_diameter,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
mask = _compute_noise_mask_for_windcube(raw)
|
|
83
|
+
return cls(
|
|
84
|
+
time=raw.time,
|
|
85
|
+
radial_distance=raw.radial_distance,
|
|
86
|
+
elevation=raw.elevation,
|
|
87
|
+
beta=beta,
|
|
88
|
+
radial_velocity=raw.radial_velocity,
|
|
89
|
+
mask=mask,
|
|
90
|
+
wavelength=wavelength,
|
|
91
|
+
system_id=raw.system_id,
|
|
92
|
+
)
|
|
93
|
+
|
|
54
94
|
@classmethod
|
|
55
95
|
def from_halo_data(
|
|
56
96
|
cls,
|
|
@@ -95,16 +135,21 @@ class Stare:
|
|
|
95
135
|
raw, intensity_bg_corrected
|
|
96
136
|
)
|
|
97
137
|
wavelength = defaults.Halo.wavelength
|
|
138
|
+
|
|
98
139
|
beta = _compute_beta(
|
|
99
|
-
intensity_noise_bias_corrected,
|
|
100
|
-
raw.radial_distance,
|
|
101
|
-
|
|
102
|
-
|
|
140
|
+
snr=intensity_noise_bias_corrected - 1,
|
|
141
|
+
radial_distance=raw.radial_distance,
|
|
142
|
+
wavelength=wavelength,
|
|
143
|
+
beam_energy=defaults.Halo.beam_energy,
|
|
144
|
+
receiver_bandwidth=defaults.Halo.receiver_bandwidth,
|
|
145
|
+
focus=raw.header.focus_range,
|
|
146
|
+
effective_diameter=defaults.Halo.effective_diameter,
|
|
103
147
|
)
|
|
148
|
+
|
|
104
149
|
mask = _compute_noise_mask(
|
|
105
150
|
intensity_noise_bias_corrected, raw.radial_velocity, raw.radial_distance
|
|
106
151
|
)
|
|
107
|
-
return
|
|
152
|
+
return cls(
|
|
108
153
|
time=raw.time,
|
|
109
154
|
radial_distance=raw.radial_distance,
|
|
110
155
|
elevation=raw.elevation,
|
|
@@ -177,6 +222,36 @@ class Stare:
|
|
|
177
222
|
nc.add_attribute("doppy_version", doppy.__version__)
|
|
178
223
|
|
|
179
224
|
|
|
225
|
+
def _compute_noise_mask_for_windcube(
|
|
226
|
+
raw: doppy.raw.WindCubeFixed,
|
|
227
|
+
) -> npt.NDArray[np.bool_]:
|
|
228
|
+
if np.any(np.isnan(raw.cnr)) or np.any(np.isnan(raw.radial_velocity)):
|
|
229
|
+
raise ValueError("Unexpected nans in crn or radial_velocity")
|
|
230
|
+
|
|
231
|
+
mask = _mask_with_cnr_norm_dist(raw.cnr) | (np.abs(raw.radial_velocity) > 30)
|
|
232
|
+
|
|
233
|
+
cnr = raw.cnr.copy()
|
|
234
|
+
cnr[mask] = np.finfo(float).eps
|
|
235
|
+
cnr_filt = median_filter(cnr, size=(3, 3))
|
|
236
|
+
rel_diff = np.abs(cnr - cnr_filt) / np.abs(cnr)
|
|
237
|
+
diff_mask = rel_diff > 0.25
|
|
238
|
+
|
|
239
|
+
mask = mask | diff_mask
|
|
240
|
+
|
|
241
|
+
return np.array(mask, dtype=np.bool_)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _mask_with_cnr_norm_dist(cnr: npt.NDArray[np.float64]) -> npt.NDArray[np.bool_]:
|
|
245
|
+
th_trunc = -5.5
|
|
246
|
+
std_factor = 2
|
|
247
|
+
log_cnr = np.log(cnr)
|
|
248
|
+
log_cnr_trunc = log_cnr[log_cnr < th_trunc]
|
|
249
|
+
th_trunc_fit = np.percentile(log_cnr_trunc, 90)
|
|
250
|
+
log_cnr_for_fit = log_cnr_trunc[log_cnr_trunc < th_trunc_fit]
|
|
251
|
+
mean, std = scipy.stats.norm.fit(log_cnr_for_fit)
|
|
252
|
+
return np.array(np.log(cnr) < (mean + std_factor * std), dtype=np.bool_)
|
|
253
|
+
|
|
254
|
+
|
|
180
255
|
def _compute_noise_mask(
|
|
181
256
|
intensity: npt.NDArray[np.float64],
|
|
182
257
|
radial_velocity: npt.NDArray[np.float64],
|
|
@@ -197,14 +272,19 @@ def _compute_noise_mask(
|
|
|
197
272
|
|
|
198
273
|
|
|
199
274
|
def _compute_beta(
|
|
200
|
-
|
|
275
|
+
snr: npt.NDArray[np.float64],
|
|
201
276
|
radial_distance: npt.NDArray[np.float64],
|
|
202
|
-
focus: float,
|
|
203
277
|
wavelength: float,
|
|
278
|
+
beam_energy: float,
|
|
279
|
+
receiver_bandwidth: float,
|
|
280
|
+
focus: float,
|
|
281
|
+
effective_diameter: float,
|
|
204
282
|
) -> npt.NDArray[np.float64]:
|
|
205
283
|
"""
|
|
206
284
|
Parameters
|
|
207
285
|
----------
|
|
286
|
+
snr
|
|
287
|
+
for halo: intensity - 1
|
|
208
288
|
radial_distance
|
|
209
289
|
distance from the instrument
|
|
210
290
|
focus
|
|
@@ -236,22 +316,24 @@ def _compute_beta(
|
|
|
236
316
|
doi: https://doi.org/10.5194/amt-13-2849-2020
|
|
237
317
|
"""
|
|
238
318
|
|
|
239
|
-
snr = intensity - 1
|
|
240
319
|
h = scipy.constants.Planck
|
|
241
320
|
c = scipy.constants.speed_of_light
|
|
242
321
|
eta = 1
|
|
243
|
-
E =
|
|
244
|
-
B =
|
|
322
|
+
E = beam_energy
|
|
323
|
+
B = receiver_bandwidth
|
|
245
324
|
nu = c / wavelength
|
|
246
|
-
A_e = _compute_effective_receiver_energy(
|
|
325
|
+
A_e = _compute_effective_receiver_energy(
|
|
326
|
+
radial_distance, wavelength, focus, effective_diameter
|
|
327
|
+
)
|
|
247
328
|
beta = 2 * h * nu * B * radial_distance**2 * snr / (eta * c * E * A_e)
|
|
248
329
|
return np.array(beta, dtype=np.float64)
|
|
249
330
|
|
|
250
331
|
|
|
251
332
|
def _compute_effective_receiver_energy(
|
|
252
333
|
radial_distance: npt.NDArray[np.float64],
|
|
253
|
-
focus: float,
|
|
254
334
|
wavelength: float,
|
|
335
|
+
focus: float,
|
|
336
|
+
effective_diameter: float,
|
|
255
337
|
) -> npt.NDArray[np.float64]:
|
|
256
338
|
"""
|
|
257
339
|
NOTE
|
|
@@ -268,7 +350,7 @@ def _compute_effective_receiver_energy(
|
|
|
268
350
|
wavelength
|
|
269
351
|
laser wavelength
|
|
270
352
|
"""
|
|
271
|
-
D =
|
|
353
|
+
D = effective_diameter
|
|
272
354
|
return np.array(
|
|
273
355
|
np.pi
|
|
274
356
|
* D**2
|
doppy/raw/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from .halo_bg import HaloBg
|
|
2
2
|
from .halo_hpl import HaloHpl
|
|
3
3
|
from .halo_sys_params import HaloSysParams
|
|
4
|
-
from .windcube import WindCube
|
|
4
|
+
from .windcube import WindCube, WindCubeFixed
|
|
5
5
|
from .wls70 import Wls70
|
|
6
6
|
|
|
7
|
-
__all__ = ["HaloHpl", "HaloBg", "HaloSysParams", "WindCube", "Wls70"]
|
|
7
|
+
__all__ = ["HaloHpl", "HaloBg", "HaloSysParams", "WindCube", "WindCubeFixed", "Wls70"]
|
doppy/raw/halo_hpl.py
CHANGED
|
@@ -4,7 +4,7 @@ import functools
|
|
|
4
4
|
import io
|
|
5
5
|
import re
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from datetime import datetime, timedelta
|
|
7
|
+
from datetime import datetime, timedelta, timezone
|
|
8
8
|
from io import BufferedIOBase
|
|
9
9
|
from os.path import commonprefix
|
|
10
10
|
from pathlib import Path
|
|
@@ -309,7 +309,11 @@ def _raw_tuple2halo_hpl(
|
|
|
309
309
|
resolution=float(header_dict["resolution"]),
|
|
310
310
|
scan_type=str(header_dict["scan_type"]),
|
|
311
311
|
focus_range=int(header_dict["focus_range"]),
|
|
312
|
-
start_time=datetime64(
|
|
312
|
+
start_time=datetime64(
|
|
313
|
+
datetime.fromtimestamp(header_dict["start_time"], timezone.utc).replace(
|
|
314
|
+
tzinfo=None
|
|
315
|
+
)
|
|
316
|
+
),
|
|
313
317
|
system_id=str(header_dict["system_id"]),
|
|
314
318
|
instrument_spectral_width=float(header_dict["instrument_spectral_width"])
|
|
315
319
|
if header_dict["instrument_spectral_width"] is not None
|
doppy/raw/windcube.py
CHANGED
|
@@ -7,17 +7,104 @@ from typing import Sequence
|
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import numpy.typing as npt
|
|
10
|
-
from netCDF4 import Dataset, num2date
|
|
10
|
+
from netCDF4 import Dataset, Variable, num2date
|
|
11
11
|
from numpy import datetime64
|
|
12
12
|
|
|
13
13
|
from doppy.utils import merge_all_equal
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
@dataclass
|
|
17
|
+
class WindCubeFixed:
|
|
18
|
+
time: npt.NDArray[datetime64] # dim: (time, )
|
|
19
|
+
radial_distance: npt.NDArray[np.float64] # dim: (radial_distance,)
|
|
20
|
+
azimuth: npt.NDArray[np.float64] # dim: (time, )
|
|
21
|
+
elevation: npt.NDArray[np.float64] # dim: (time, )
|
|
22
|
+
cnr: npt.NDArray[np.float64] # dim: (time, radial_distance)
|
|
23
|
+
relative_beta: npt.NDArray[np.float64] # dim: (time, radial_distance)
|
|
24
|
+
radial_velocity: npt.NDArray[np.float64] # dim: (time, radial_distance)
|
|
25
|
+
doppler_spectrum_width: npt.NDArray[np.float64] # dim: (time, radial_distance)
|
|
26
|
+
radial_velocity_confidence: npt.NDArray[np.float64] # dim: (time, radial_distance)
|
|
27
|
+
ray_accumulation_time: np.float64 # dim: ()
|
|
28
|
+
system_id: str
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_srcs(
|
|
32
|
+
cls,
|
|
33
|
+
data: Sequence[str]
|
|
34
|
+
| Sequence[Path]
|
|
35
|
+
| Sequence[bytes]
|
|
36
|
+
| Sequence[BufferedIOBase],
|
|
37
|
+
) -> list[WindCubeFixed]:
|
|
38
|
+
return [WindCubeFixed.from_fixed_src(src) for src in data]
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_fixed_src(cls, data: str | Path | bytes | BufferedIOBase) -> WindCubeFixed:
|
|
42
|
+
data_bytes = _src_to_bytes(data)
|
|
43
|
+
nc = Dataset("inmemory.nc", "r", memory=data_bytes)
|
|
44
|
+
return _from_fixed_src(nc)
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def merge(cls, raws: list[WindCubeFixed]) -> WindCubeFixed:
|
|
48
|
+
return WindCubeFixed(
|
|
49
|
+
time=np.concatenate([r.time for r in raws]),
|
|
50
|
+
radial_distance=_merge_radial_distance_for_fixed(
|
|
51
|
+
[r.radial_distance for r in raws]
|
|
52
|
+
),
|
|
53
|
+
azimuth=np.concatenate([r.azimuth for r in raws]),
|
|
54
|
+
elevation=np.concatenate([r.elevation for r in raws]),
|
|
55
|
+
radial_velocity=np.concatenate([r.radial_velocity for r in raws]),
|
|
56
|
+
radial_velocity_confidence=np.concatenate(
|
|
57
|
+
[r.radial_velocity_confidence for r in raws]
|
|
58
|
+
),
|
|
59
|
+
cnr=np.concatenate([r.cnr for r in raws]),
|
|
60
|
+
relative_beta=np.concatenate([r.relative_beta for r in raws]),
|
|
61
|
+
doppler_spectrum_width=np.concatenate(
|
|
62
|
+
[r.doppler_spectrum_width for r in raws]
|
|
63
|
+
),
|
|
64
|
+
ray_accumulation_time=merge_all_equal(
|
|
65
|
+
"ray_accumulation_time", [r.ray_accumulation_time for r in raws]
|
|
66
|
+
),
|
|
67
|
+
system_id=merge_all_equal("system_id", [r.system_id for r in raws]),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def __getitem__(
|
|
71
|
+
self,
|
|
72
|
+
index: int
|
|
73
|
+
| slice
|
|
74
|
+
| list[int]
|
|
75
|
+
| npt.NDArray[np.int64]
|
|
76
|
+
| npt.NDArray[np.bool_]
|
|
77
|
+
| tuple[slice, slice],
|
|
78
|
+
) -> WindCubeFixed:
|
|
79
|
+
if isinstance(index, (int, slice, list, np.ndarray)):
|
|
80
|
+
return WindCubeFixed(
|
|
81
|
+
time=self.time[index],
|
|
82
|
+
radial_distance=self.radial_distance,
|
|
83
|
+
azimuth=self.azimuth[index],
|
|
84
|
+
elevation=self.elevation[index],
|
|
85
|
+
radial_velocity=self.radial_velocity[index],
|
|
86
|
+
radial_velocity_confidence=self.radial_velocity_confidence[index],
|
|
87
|
+
cnr=self.cnr[index],
|
|
88
|
+
relative_beta=self.relative_beta[index],
|
|
89
|
+
doppler_spectrum_width=self.doppler_spectrum_width[index],
|
|
90
|
+
ray_accumulation_time=self.ray_accumulation_time,
|
|
91
|
+
system_id=self.system_id,
|
|
92
|
+
)
|
|
93
|
+
raise TypeError
|
|
94
|
+
|
|
95
|
+
def sorted_by_time(self) -> WindCubeFixed:
|
|
96
|
+
sort_indices = np.argsort(self.time)
|
|
97
|
+
return self[sort_indices]
|
|
98
|
+
|
|
99
|
+
def nan_profiles_removed(self) -> WindCubeFixed:
|
|
100
|
+
return self[~np.all(np.isnan(self.cnr), axis=1)]
|
|
101
|
+
|
|
102
|
+
|
|
16
103
|
@dataclass
|
|
17
104
|
class WindCube:
|
|
18
105
|
time: npt.NDArray[datetime64] # dim: (time, )
|
|
19
|
-
radial_distance: npt.NDArray[np.
|
|
20
|
-
height: npt.NDArray[np.
|
|
106
|
+
radial_distance: npt.NDArray[np.float64] # dim: (time, radial_distance)
|
|
107
|
+
height: npt.NDArray[np.float64] # dim: (time,radial_distance)
|
|
21
108
|
azimuth: npt.NDArray[np.float64] # dim: (time, )
|
|
22
109
|
elevation: npt.NDArray[np.float64] # dim: (time, )
|
|
23
110
|
cnr: npt.NDArray[np.float64] # dim: (time, radial_distance)
|
|
@@ -128,6 +215,23 @@ def _merge_scan_index(index_list: list[npt.NDArray[np.int64]]) -> npt.NDArray[np
|
|
|
128
215
|
return np.concatenate(new_index_list)
|
|
129
216
|
|
|
130
217
|
|
|
218
|
+
def _merge_radial_distance_for_fixed(
|
|
219
|
+
radial_distance_list: list[npt.NDArray[np.float64]],
|
|
220
|
+
) -> npt.NDArray[np.float64]:
|
|
221
|
+
if len(radial_distance_list) == 0:
|
|
222
|
+
raise ValueError("cannot merge empty list")
|
|
223
|
+
if not all(
|
|
224
|
+
np.allclose(arr.shape, radial_distance_list[0].shape)
|
|
225
|
+
for arr in radial_distance_list
|
|
226
|
+
):
|
|
227
|
+
raise ValueError("Cannot merge radial distances with different shapes")
|
|
228
|
+
if not all(
|
|
229
|
+
np.allclose(arr, radial_distance_list[0]) for arr in radial_distance_list
|
|
230
|
+
):
|
|
231
|
+
raise ValueError("Cannot merge radial distances")
|
|
232
|
+
return radial_distance_list[0]
|
|
233
|
+
|
|
234
|
+
|
|
131
235
|
def _src_to_bytes(data: str | Path | bytes | BufferedIOBase) -> bytes:
|
|
132
236
|
if isinstance(data, str):
|
|
133
237
|
path = Path(data)
|
|
@@ -140,23 +244,98 @@ def _src_to_bytes(data: str | Path | bytes | BufferedIOBase) -> bytes:
|
|
|
140
244
|
return data
|
|
141
245
|
elif isinstance(data, BufferedIOBase):
|
|
142
246
|
return data.read()
|
|
143
|
-
raise TypeError("Unsupported data type")
|
|
144
247
|
|
|
145
248
|
|
|
146
|
-
def
|
|
147
|
-
scan_index_list = []
|
|
249
|
+
def _from_fixed_src(nc: Dataset) -> WindCubeFixed:
|
|
148
250
|
time_list = []
|
|
149
251
|
cnr_list = []
|
|
252
|
+
relative_beta_list = []
|
|
150
253
|
radial_wind_speed_list = []
|
|
151
254
|
radial_wind_speed_confidence_list = []
|
|
152
255
|
azimuth_list = []
|
|
153
256
|
elevation_list = []
|
|
154
257
|
range_list = []
|
|
155
|
-
|
|
258
|
+
doppler_spectrum_width_list = []
|
|
259
|
+
ray_accumulation_time_list = []
|
|
156
260
|
time_reference = (
|
|
157
261
|
nc["time_reference"][:] if "time_reference" in nc.variables else None
|
|
158
262
|
)
|
|
159
263
|
|
|
264
|
+
expected_dimensions = ("time", "range")
|
|
265
|
+
for _, group in enumerate(
|
|
266
|
+
nc[group] for group in (nc.variables["sweep_group_name"][:])
|
|
267
|
+
):
|
|
268
|
+
time_reference_ = time_reference
|
|
269
|
+
if time_reference is None and "time_reference" in group.variables:
|
|
270
|
+
time_reference_ = group["time_reference"][:]
|
|
271
|
+
|
|
272
|
+
time_list.append(_extract_datetime64_or_raise(group["time"], time_reference_))
|
|
273
|
+
radial_wind_speed_list.append(
|
|
274
|
+
_extract_float64_or_raise(group["radial_wind_speed"], expected_dimensions)
|
|
275
|
+
)
|
|
276
|
+
cnr_list.append(_extract_float64_or_raise(group["cnr"], expected_dimensions))
|
|
277
|
+
relative_beta_list.append(
|
|
278
|
+
_extract_float64_or_raise(group["relative_beta"], expected_dimensions)
|
|
279
|
+
)
|
|
280
|
+
radial_wind_speed_confidence_list.append(
|
|
281
|
+
_extract_float64_or_raise(
|
|
282
|
+
group["radial_wind_speed_ci"], expected_dimensions
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
azimuth_list.append(
|
|
286
|
+
_extract_float64_or_raise(group["azimuth"], expected_dimensions)
|
|
287
|
+
)
|
|
288
|
+
elevation_list.append(
|
|
289
|
+
_extract_float64_or_raise(group["elevation"], expected_dimensions)
|
|
290
|
+
)
|
|
291
|
+
range_list.append(
|
|
292
|
+
_extract_float64_or_raise(group["range"], (expected_dimensions[1],))
|
|
293
|
+
)
|
|
294
|
+
doppler_spectrum_width_list.append(
|
|
295
|
+
_extract_float64_or_raise(
|
|
296
|
+
group["doppler_spectrum_width"], expected_dimensions
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
ray_accumulation_time_list.append(
|
|
300
|
+
_extract_float64_or_raise(
|
|
301
|
+
group["ray_accumulation_time"], expected_dimensions
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
return WindCubeFixed(
|
|
306
|
+
time=np.concatenate(time_list),
|
|
307
|
+
radial_distance=np.concatenate(range_list),
|
|
308
|
+
azimuth=np.concatenate(azimuth_list),
|
|
309
|
+
elevation=np.concatenate(elevation_list),
|
|
310
|
+
radial_velocity=np.concatenate(radial_wind_speed_list),
|
|
311
|
+
radial_velocity_confidence=np.concatenate(radial_wind_speed_confidence_list),
|
|
312
|
+
cnr=np.concatenate(cnr_list),
|
|
313
|
+
relative_beta=np.concatenate(relative_beta_list),
|
|
314
|
+
doppler_spectrum_width=np.concatenate(doppler_spectrum_width_list),
|
|
315
|
+
ray_accumulation_time=merge_all_equal(
|
|
316
|
+
"ray_accumulation_time",
|
|
317
|
+
np.array(ray_accumulation_time_list, dtype=np.float64).tolist(),
|
|
318
|
+
),
|
|
319
|
+
system_id=nc.instrument_name,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _from_vad_or_dbs_src(nc: Dataset) -> WindCube:
|
|
324
|
+
scan_index_list: list[npt.NDArray[np.int64]] = []
|
|
325
|
+
time_list: list[npt.NDArray[np.datetime64]] = []
|
|
326
|
+
cnr_list: list[npt.NDArray[np.float64]] = []
|
|
327
|
+
radial_wind_speed_list: list[npt.NDArray[np.float64]] = []
|
|
328
|
+
radial_wind_speed_confidence_list: list[npt.NDArray[np.float64]] = []
|
|
329
|
+
azimuth_list: list[npt.NDArray[np.float64]] = []
|
|
330
|
+
elevation_list: list[npt.NDArray[np.float64]] = []
|
|
331
|
+
range_list: list[npt.NDArray[np.float64]] = []
|
|
332
|
+
height_list: list[npt.NDArray[np.float64]] = []
|
|
333
|
+
|
|
334
|
+
time_reference = (
|
|
335
|
+
nc["time_reference"][:] if "time_reference" in nc.variables else None
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
expected_dimensions = ("time", "gate_index")
|
|
160
339
|
for i, group in enumerate(
|
|
161
340
|
nc[group] for group in (nc.variables["sweep_group_name"][:])
|
|
162
341
|
):
|
|
@@ -166,16 +345,26 @@ def _from_vad_or_dbs_src(nc: Dataset) -> WindCube:
|
|
|
166
345
|
|
|
167
346
|
time_list.append(_extract_datetime64_or_raise(group["time"], time_reference_))
|
|
168
347
|
radial_wind_speed_list.append(
|
|
169
|
-
_extract_float64_or_raise(group["radial_wind_speed"])
|
|
348
|
+
_extract_float64_or_raise(group["radial_wind_speed"], expected_dimensions)
|
|
170
349
|
)
|
|
171
|
-
cnr_list.append(_extract_float64_or_raise(group["cnr"]))
|
|
350
|
+
cnr_list.append(_extract_float64_or_raise(group["cnr"], expected_dimensions))
|
|
172
351
|
radial_wind_speed_confidence_list.append(
|
|
173
|
-
_extract_float64_or_raise(
|
|
352
|
+
_extract_float64_or_raise(
|
|
353
|
+
group["radial_wind_speed_ci"], expected_dimensions
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
azimuth_list.append(
|
|
357
|
+
_extract_float64_or_raise(group["azimuth"], expected_dimensions)
|
|
358
|
+
)
|
|
359
|
+
elevation_list.append(
|
|
360
|
+
_extract_float64_or_raise(group["elevation"], expected_dimensions)
|
|
361
|
+
)
|
|
362
|
+
range_list.append(
|
|
363
|
+
_extract_float64_or_raise(group["range"], expected_dimensions)
|
|
364
|
+
)
|
|
365
|
+
height_list.append(
|
|
366
|
+
_extract_float64_or_raise(group["measurement_height"], expected_dimensions)
|
|
174
367
|
)
|
|
175
|
-
azimuth_list.append(_extract_float64_or_raise(group["azimuth"]))
|
|
176
|
-
elevation_list.append(_extract_float64_or_raise(group["elevation"]))
|
|
177
|
-
range_list.append(_extract_int64_or_raise(group["range"]))
|
|
178
|
-
height_list.append(_extract_int64_or_raise(group["measurement_height"]))
|
|
179
368
|
scan_index_list.append(np.full(group["time"][:].shape, i, dtype=np.int64))
|
|
180
369
|
|
|
181
370
|
return WindCube(
|
|
@@ -193,7 +382,7 @@ def _from_vad_or_dbs_src(nc: Dataset) -> WindCube:
|
|
|
193
382
|
|
|
194
383
|
|
|
195
384
|
def _extract_datetime64_or_raise(
|
|
196
|
-
nc:
|
|
385
|
+
nc: Variable[npt.NDArray[np.float64]], time_reference: str | None
|
|
197
386
|
) -> npt.NDArray[np.datetime64]:
|
|
198
387
|
match nc.name:
|
|
199
388
|
case "time":
|
|
@@ -211,18 +400,40 @@ def _extract_datetime64_or_raise(
|
|
|
211
400
|
raise ValueError(f"Unexpected variable name {nc.name}")
|
|
212
401
|
|
|
213
402
|
|
|
214
|
-
def
|
|
403
|
+
def _dB_to_ratio(decibels: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
|
|
404
|
+
return 10 ** (0.1 * decibels)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _extract_float64_or_raise(
|
|
408
|
+
nc: Variable[npt.NDArray[np.float64]], expected_dimensions: tuple[str, ...]
|
|
409
|
+
) -> npt.NDArray[np.float64]:
|
|
215
410
|
match nc.name:
|
|
411
|
+
case "range" | "measurement_height":
|
|
412
|
+
if nc.dimensions != expected_dimensions:
|
|
413
|
+
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
414
|
+
if nc.units != "m":
|
|
415
|
+
raise ValueError(f"Unexpected units for {nc.name}")
|
|
416
|
+
if nc[:].mask is not np.bool_(False):
|
|
417
|
+
raise ValueError
|
|
418
|
+
return np.array(nc[:].data, dtype=np.float64)
|
|
216
419
|
case "cnr":
|
|
217
|
-
if nc.dimensions !=
|
|
420
|
+
if nc.dimensions != expected_dimensions:
|
|
218
421
|
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
219
422
|
if nc.units != "dB":
|
|
220
423
|
raise ValueError(f"Unexpected units for {nc.name}")
|
|
221
424
|
if nc[:].mask is not np.bool_(False):
|
|
222
425
|
pass # ignore that array contains masked values
|
|
426
|
+
return _dB_to_ratio(np.array(nc[:].data, dtype=np.float64))
|
|
427
|
+
case "relative_beta":
|
|
428
|
+
if nc.dimensions != expected_dimensions:
|
|
429
|
+
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
430
|
+
if nc.units != "m-1 sr-1":
|
|
431
|
+
raise ValueError(f"Unexpected units for {nc.name}")
|
|
432
|
+
if nc[:].mask is not np.bool_(False):
|
|
433
|
+
pass # ignore that array contains masked values
|
|
223
434
|
return np.array(nc[:].data, dtype=np.float64)
|
|
224
435
|
case "radial_wind_speed":
|
|
225
|
-
if nc.dimensions !=
|
|
436
|
+
if nc.dimensions != expected_dimensions:
|
|
226
437
|
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
227
438
|
if nc.units != "m s-1":
|
|
228
439
|
raise ValueError(f"Unexpected units for {nc.name}")
|
|
@@ -230,7 +441,7 @@ def _extract_float64_or_raise(nc: Dataset) -> npt.NDArray[np.float64]:
|
|
|
230
441
|
pass # ignore that array contains masked values
|
|
231
442
|
return np.array(nc[:].data, dtype=np.float64)
|
|
232
443
|
case "radial_wind_speed_ci":
|
|
233
|
-
if nc.dimensions !=
|
|
444
|
+
if nc.dimensions != expected_dimensions:
|
|
234
445
|
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
235
446
|
if nc.units != "percent":
|
|
236
447
|
raise ValueError(f"Unexpected units for {nc.name}")
|
|
@@ -238,26 +449,28 @@ def _extract_float64_or_raise(nc: Dataset) -> npt.NDArray[np.float64]:
|
|
|
238
449
|
pass # ignore that array contains masked values
|
|
239
450
|
return np.array(nc[:].data, dtype=np.float64)
|
|
240
451
|
case "azimuth" | "elevation":
|
|
241
|
-
if nc.dimensions != (
|
|
452
|
+
if nc.dimensions != (expected_dimensions[0],):
|
|
242
453
|
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
243
454
|
if nc.units != "degrees":
|
|
244
455
|
raise ValueError(f"Unexpected units for {nc.name}")
|
|
245
456
|
if nc[:].mask is not np.bool_(False):
|
|
246
457
|
raise ValueError
|
|
247
458
|
return np.array(nc[:].data, dtype=np.float64)
|
|
248
|
-
case
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
def _extract_int64_or_raise(nc: Dataset) -> npt.NDArray[np.int64]:
|
|
253
|
-
match nc.name:
|
|
254
|
-
case "range" | "measurement_height":
|
|
255
|
-
if nc.dimensions != ("time", "gate_index"):
|
|
459
|
+
case "doppler_spectrum_width":
|
|
460
|
+
if nc.dimensions != expected_dimensions:
|
|
256
461
|
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
257
|
-
if nc.units != "m":
|
|
462
|
+
if nc.units != "m s-1":
|
|
258
463
|
raise ValueError(f"Unexpected units for {nc.name}")
|
|
259
464
|
if nc[:].mask is not np.bool_(False):
|
|
260
|
-
|
|
261
|
-
return np.array(nc[:].data, dtype=np.
|
|
465
|
+
pass # ignore that array contains masked values
|
|
466
|
+
return np.array(nc[:].data, dtype=np.float64)
|
|
467
|
+
case "ray_accumulation_time":
|
|
468
|
+
if nc.dimensions != ():
|
|
469
|
+
raise ValueError(f"Unexpected dimensions for {nc.name}")
|
|
470
|
+
if nc.units != "ms":
|
|
471
|
+
raise ValueError(f"Unexpected units for {nc.name}")
|
|
472
|
+
if nc[:].mask is not np.bool_(False):
|
|
473
|
+
raise ValueError(f"Variable {nc.name} contains masked values")
|
|
474
|
+
return np.array(nc[:].data, dtype=np.float64)
|
|
262
475
|
case _:
|
|
263
476
|
raise ValueError(f"Unexpected variable name {nc.name}")
|
doppy/raw/wls70.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
5
|
from io import BufferedIOBase
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any, Sequence
|
|
@@ -166,7 +166,12 @@ def _raw_rs_to_wls70(
|
|
|
166
166
|
cnr_threshold = float(info["cnr_threshold"])
|
|
167
167
|
data = data.reshape(-1, len(cols))
|
|
168
168
|
time_ts = data[:, 0]
|
|
169
|
-
time = np.array(
|
|
169
|
+
time = np.array(
|
|
170
|
+
[
|
|
171
|
+
datetime64(datetime.fromtimestamp(ts, timezone.utc).replace(tzinfo=None))
|
|
172
|
+
for ts in time_ts
|
|
173
|
+
]
|
|
174
|
+
)
|
|
170
175
|
|
|
171
176
|
position = data[:, 1]
|
|
172
177
|
temperature = data[:, 2]
|
doppy/rs.abi3.so
CHANGED
|
Binary file
|
doppy/utils.py
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
from typing import TypeVar
|
|
1
|
+
from typing import TypeVar, cast
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy.typing import NDArray
|
|
2
5
|
|
|
3
6
|
T = TypeVar("T")
|
|
7
|
+
NT = TypeVar("NT", bound=np.generic)
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
def merge_all_equal(key: str, lst: list[T]) -> T:
|
|
7
11
|
if len(set(lst)) != 1:
|
|
8
12
|
raise ValueError(f"Cannot merge header key {key} values {lst}")
|
|
9
13
|
return lst[0]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def merge_all_close(key: str, lst: list[NDArray[NT]]) -> NT:
|
|
17
|
+
if len(lst) == 0:
|
|
18
|
+
raise ValueError(f"Cannot merge empty list for key {key}")
|
|
19
|
+
if any(arr.size == 0 for arr in lst):
|
|
20
|
+
raise ValueError(f"Cannot merge key {key}, one or more arrays are empty.")
|
|
21
|
+
arr = np.concatenate([arr.flatten() for arr in lst])
|
|
22
|
+
if not np.allclose(arr, arr[0]):
|
|
23
|
+
raise ValueError(f"Cannot merge key {key}, values are not close enough")
|
|
24
|
+
return cast(NT, arr[0])
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: doppy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.10
|
|
7
7
|
Classifier: Programming Language :: Python :: 3.11
|
|
8
8
|
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
11
|
Classifier: Intended Audience :: Science/Research
|
|
11
12
|
Classifier: Operating System :: OS Independent
|
|
@@ -25,6 +26,7 @@ Requires-Dist: py-spy ; extra == 'dev'
|
|
|
25
26
|
Requires-Dist: maturin ==1.4 ; extra == 'dev'
|
|
26
27
|
Requires-Dist: release-version ; extra == 'dev'
|
|
27
28
|
Requires-Dist: pre-commit ; extra == 'dev'
|
|
29
|
+
Requires-Dist: xarray[io] ; extra == 'dev'
|
|
28
30
|
Provides-Extra: dev
|
|
29
31
|
License-File: LICENSE
|
|
30
32
|
License-File: LICENSE
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
doppy-0.
|
|
2
|
-
doppy-0.
|
|
3
|
-
doppy-0.
|
|
4
|
-
doppy-0.
|
|
1
|
+
doppy-0.4.0.dist-info/METADATA,sha256=bnTR2j3JN6ZfhwrYNbhIj0i6HdM-zpergm-CCBQa_5I,4276
|
|
2
|
+
doppy-0.4.0.dist-info/WHEEL,sha256=9pH0c92OgjhIvXK7g4XTh8KK6q5vYgxz1YCJsDe0sC0,103
|
|
3
|
+
doppy-0.4.0.dist-info/entry_points.txt,sha256=9b_Ca7vJoh6AwL3W8qAPh_UmJ_1Pa6hi-TDfCTDjvSk,43
|
|
4
|
+
doppy-0.4.0.dist-info/licenses/LICENSE,sha256=V-0iroMNMI8ctnLgUau1kdFvwhkYhr9vi-5kWKxw2wc,1089
|
|
5
5
|
doppy/options.py,sha256=73BDODO4OYHn2qOshhwz6u6G3J1kNd3uj6P0a3V4HBE,205
|
|
6
6
|
doppy/__init__.py,sha256=Z9aEUlbPRWRUAoB8_-djkgrJuS4-6pjem4-mVSB6Z9I,191
|
|
7
7
|
doppy/product/wind.py,sha256=hkSzAyi4tfPoGwUx8iE3l65oCDyzl_tRgtF2loxaEpY,16557
|
|
8
8
|
doppy/product/__init__.py,sha256=C6s9cX20m9UwRsKo1lZH6TnYFfM5KmsX5MPUyShbgl4,235
|
|
9
9
|
doppy/product/stare_depol.py,sha256=thrWzCpvdH3AiA6gmR37vrH_pDACNY2QTtqPteJ2s8Y,9653
|
|
10
|
-
doppy/product/stare.py,sha256=
|
|
10
|
+
doppy/product/stare.py,sha256=TE6BrrXFuwv9-zLTYCCn3HNpNgjwdldbyU6qk1__FzA,25343
|
|
11
11
|
doppy/bench.py,sha256=iVNYveMVGGRES2oe3Orsn31jQFCKTXOmxRFuFiJ8_OA,248
|
|
12
12
|
doppy/netcdf.py,sha256=34TzB9UyypTwDpT2MfrRD14g2zGP3X-NuEU15mQGaUI,3925
|
|
13
|
-
doppy/utils.py,sha256=
|
|
13
|
+
doppy/utils.py,sha256=cY-tWrArrROX7lg_zJ2M4QJ6GUnU_BjhkUYL5aG9kbA,790
|
|
14
14
|
doppy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
doppy/exceptions.py,sha256=OzdLXmKs3qZrvzwaI0pxjzpM2w9J5Of7dCo_Ekygecc,183
|
|
16
|
-
doppy/defaults.py,sha256=-
|
|
16
|
+
doppy/defaults.py,sha256=-jR_xAVLf_soZNDu3uJ6mhOZa9L1tfKtt0k0tI6Rd9s,472
|
|
17
17
|
doppy/data/cache.py,sha256=cxCZ7HyEQt2EKAGEiMqx3wJ2-5Y3hEAEPQ_XB4gKlkk,1160
|
|
18
18
|
doppy/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
doppy/data/api.py,sha256=QaVKj304OPcu8OF5xgtduQzDis8Srn-I6UgR9qb2u9E,1863
|
|
20
20
|
doppy/data/exceptions.py,sha256=6CS6OHIWq8CqlxiceEvC1j0EfWUYoIfM7dW88apQVn4,89
|
|
21
21
|
doppy/raw/halo_sys_params.py,sha256=DeOIIRtVdO7HnNY3FaI4u035ScI65jfDgUrEccLepEo,3937
|
|
22
|
-
doppy/raw/__init__.py,sha256=
|
|
23
|
-
doppy/raw/wls70.py,sha256=
|
|
22
|
+
doppy/raw/__init__.py,sha256=4pj4xSK9rA0XEfM79NsRBRYId7GaJRDCyPABxbEm63g,260
|
|
23
|
+
doppy/raw/wls70.py,sha256=TePgyhmlWaUQCw1vkWh7uD7KT5EFMaDhNeVYoF2rspc,7510
|
|
24
24
|
doppy/raw/halo_bg.py,sha256=8t9j-SUF1yJht3vrT6KAYJQyxcg3W-0zr8h0jAEhWes,5815
|
|
25
|
-
doppy/raw/halo_hpl.py,sha256=
|
|
26
|
-
doppy/raw/windcube.py,sha256=
|
|
25
|
+
doppy/raw/halo_hpl.py,sha256=yzVPQEATnDVf1K3N-mGLXR4_RuT-E94OVXathT2bHes,18580
|
|
26
|
+
doppy/raw/windcube.py,sha256=vgkoXVgcbI2Or4BsFCeq2STdjUP49h3mBQLoDXdBQHM,19074
|
|
27
27
|
doppy/__main__.py,sha256=zrKQJVj0k0ypBQCGK65Czt9G9FZ_qx3ussw6Q9VJ14g,346
|
|
28
|
-
doppy/rs.abi3.so,sha256=
|
|
29
|
-
doppy-0.
|
|
28
|
+
doppy/rs.abi3.so,sha256=o1UmwiBlSeRyDR2KeIm83bBpGJyTk9DdywuVjJUnTos,2583328
|
|
29
|
+
doppy-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|