pyadps 0.2.0b0__py3-none-any.whl → 0.3.0__py3-none-any.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.
- pyadps/Home_Page.py +11 -5
- pyadps/pages/01_Read_File.py +623 -211
- pyadps/pages/02_View_Raw_Data.py +97 -41
- pyadps/pages/03_Download_Raw_File.py +200 -67
- pyadps/pages/04_Sensor_Health.py +905 -0
- pyadps/pages/05_QC_Test.py +493 -0
- pyadps/pages/06_Profile_Test.py +971 -0
- pyadps/pages/07_Velocity_Test.py +600 -0
- pyadps/pages/08_Write_File.py +623 -0
- pyadps/pages/09_Add-Ons.py +168 -0
- pyadps/utils/__init__.py +5 -3
- pyadps/utils/autoprocess.py +371 -80
- pyadps/utils/logging_utils.py +269 -0
- pyadps/utils/metadata/config.ini +22 -4
- pyadps/utils/metadata/demo.000 +0 -0
- pyadps/utils/metadata/flmeta.json +420 -420
- pyadps/utils/metadata/vlmeta.json +611 -565
- pyadps/utils/multifile.py +292 -0
- pyadps/utils/plotgen.py +505 -3
- pyadps/utils/profile_test.py +720 -125
- pyadps/utils/pyreadrdi.py +164 -92
- pyadps/utils/readrdi.py +436 -186
- pyadps/utils/script.py +197 -147
- pyadps/utils/sensor_health.py +120 -0
- pyadps/utils/signal_quality.py +472 -68
- pyadps/utils/velocity_test.py +79 -31
- pyadps/utils/writenc.py +222 -39
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0.dist-info}/METADATA +63 -33
- pyadps-0.3.0.dist-info/RECORD +35 -0
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0.dist-info}/WHEEL +1 -1
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0.dist-info}/entry_points.txt +1 -0
- pyadps/pages/04_QC_Test.py +0 -334
- pyadps/pages/05_Profile_Test.py +0 -575
- pyadps/pages/06_Velocity_Test.py +0 -341
- pyadps/pages/07_Write_File.py +0 -452
- pyadps/utils/cutbin.py +0 -413
- pyadps/utils/regrid.py +0 -279
- pyadps-0.2.0b0.dist-info/RECORD +0 -31
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0.dist-info}/LICENSE +0 -0
pyadps/utils/profile_test.py
CHANGED
@@ -1,122 +1,63 @@
|
|
1
|
-
import matplotlib.pyplot as plt
|
2
1
|
import numpy as np
|
3
|
-
import
|
4
|
-
from
|
5
|
-
from
|
6
|
-
|
7
|
-
from .plotgen import plotmask, plotvar
|
8
|
-
|
9
|
-
|
10
|
-
class PlotEnds:
|
11
|
-
def __init__(self, pressure, delta=10):
|
12
|
-
self.dep = pressure / 980
|
13
|
-
|
14
|
-
self.n = np.size(self.dep)
|
15
|
-
self.delta = delta
|
16
|
-
self.nmin = 0
|
17
|
-
self.nmax = self.nmin + self.delta
|
18
|
-
self.mmax = 0
|
19
|
-
self.mmin = self.mmax - self.delta
|
20
|
-
|
21
|
-
self.x = np.arange(0, self.n)
|
22
|
-
|
23
|
-
self.start_ens = 0
|
24
|
-
self.end_ens = 0
|
25
|
-
|
26
|
-
self.fig, self.axs = plt.subplots(1, 2, figsize=(12, 8))
|
27
|
-
self.fig.set_facecolor("darkgrey")
|
28
|
-
plt.subplots_adjust(bottom=0.28, right=0.72)
|
29
|
-
|
30
|
-
self.ax_end = self.fig.add_axes(rect=(0.25, 0.08, 0.47, 0.03))
|
31
|
-
self.ax_start = self.fig.add_axes(rect=(0.25, 0.15, 0.47, 0.03))
|
32
|
-
self.ax_button = self.fig.add_axes(rect=(0.81, 0.05, 0.15, 0.075))
|
33
|
-
# self.ax_depmaxbutton = self.fig.add_axes(rect=(0.68, 0.13, 0.04, 0.02))
|
34
|
-
# self.ax_depminbutton = self.fig.add_axes(rect=(0.25, 0.13, 0.04, 0.02))
|
35
|
-
# self.ax_recmaxbutton = self.fig.add_axes(rect=(0.68, 0.06, 0.04, 0.02))
|
36
|
-
# self.ax_recminbutton = self.fig.add_axes(rect=(0.25, 0.06, 0.04, 0.02))
|
37
|
-
|
38
|
-
# Plot
|
39
|
-
self.axs[0].scatter(self.x, self.dep, color="k")
|
40
|
-
self.axs[1].scatter(self.x, self.dep, color="k")
|
41
|
-
|
42
|
-
# Figure Labels
|
43
|
-
for i in range(2):
|
44
|
-
self.axs[i].set_xlabel("Ensemble")
|
45
|
-
self.axs[0].set_xlim([self.nmin - 1, self.nmax])
|
46
|
-
self.axs[1].set_xlim([self.n - self.delta, self.n])
|
47
|
-
self.axs[0].set_ylabel("Depth (m)")
|
48
|
-
self.fig.suptitle("Trim Ends")
|
49
|
-
|
50
|
-
# Display statistics
|
51
|
-
self.axs[0].text(0.82, 0.60, "Statistics", transform=plt.gcf().transFigure)
|
52
|
-
self.max = np.round(np.max(self.dep), decimals=2)
|
53
|
-
self.min = np.round(np.min(self.dep), decimals=2)
|
54
|
-
self.median = np.round(np.median(self.dep), decimals=2)
|
55
|
-
self.mean = np.round(np.mean(self.dep), decimals=2)
|
56
|
-
self.t1 = self.axs[0].text(
|
57
|
-
0.75,
|
58
|
-
0.50,
|
59
|
-
f"Dep. Max = {self.max} \nDep. Min = {self.min} \nDep. Median = {self.median}",
|
60
|
-
transform=plt.gcf().transFigure,
|
61
|
-
)
|
62
|
-
|
63
|
-
self.sl_start = Slider(
|
64
|
-
ax=self.ax_start,
|
65
|
-
label="Dep. Ensemble",
|
66
|
-
valmin=self.nmin,
|
67
|
-
valmax=self.nmax,
|
68
|
-
valinit=0,
|
69
|
-
valfmt="%i",
|
70
|
-
valstep=1,
|
71
|
-
)
|
72
|
-
|
73
|
-
self.sl_end = Slider(
|
74
|
-
ax=self.ax_end,
|
75
|
-
label="Rec. Ensemble",
|
76
|
-
valmin=self.mmin,
|
77
|
-
valmax=self.mmax,
|
78
|
-
valinit=0,
|
79
|
-
valfmt="%i",
|
80
|
-
valstep=1,
|
81
|
-
)
|
82
|
-
|
83
|
-
self.sl_start.on_changed(self.update1)
|
84
|
-
self.sl_end.on_changed(self.update2)
|
85
|
-
self.button = Button(self.ax_button, "Save & Exit")
|
86
|
-
# self.depminbutton = Button(self.ax_depminbutton, "<<")
|
87
|
-
# self.depmaxbutton = Button(self.ax_depmaxbutton, ">>")
|
88
|
-
# self.recminbutton = Button(self.ax_recminbutton, "<<")
|
89
|
-
# self.recmaxbutton = Button(self.ax_recmaxbutton, ">>")
|
90
|
-
|
91
|
-
self.button.on_clicked(self.exitwin)
|
92
|
-
|
93
|
-
def update1(self, value):
|
94
|
-
self.axs[0].scatter(self.x, self.dep, color="k")
|
95
|
-
self.axs[0].scatter(self.x[0:value], self.dep[0:value], color="r")
|
96
|
-
self.start_ens = value
|
97
|
-
|
98
|
-
def update2(self, value):
|
99
|
-
self.axs[1].scatter(self.x, self.dep, color="k")
|
100
|
-
if value < 0:
|
101
|
-
self.axs[1].scatter(
|
102
|
-
self.x[self.n + value : self.n],
|
103
|
-
self.dep[self.n + value : self.n],
|
104
|
-
color="r",
|
105
|
-
)
|
106
|
-
self.end_ens = value
|
2
|
+
import scipy as sp
|
3
|
+
from pyadps.utils.readrdi import ReadFile, check_equal
|
4
|
+
from .plotgen import PlotEnds
|
107
5
|
|
108
|
-
def show(self):
|
109
|
-
plt.show()
|
110
6
|
|
111
|
-
|
112
|
-
|
7
|
+
def trim_ends(ds, mask, transducer_depth=None, delta=20, method="Manual"):
|
8
|
+
"""
|
9
|
+
Trim the ends of the data based on the provided method (e.g., manual selection)
|
10
|
+
to remove invalid or irrelevant data points during deployment and recovery
|
11
|
+
of moorings. This function modifies the mask to reflect the valid data range by marking
|
12
|
+
invalid data points as `1` at the ends.
|
13
|
+
|
14
|
+
Parameters
|
15
|
+
----------
|
16
|
+
ds : pyadps.dataset
|
17
|
+
The pyadps dataframe is loaded to obtain the data from the variable leader.
|
18
|
+
This includes the depth of the transducer and other relevant information
|
19
|
+
for trimming the data.
|
20
|
+
mask : numpy.ndarray
|
21
|
+
A mask array of the same shape as the data, where `1` indicates invalid data
|
22
|
+
and `0` indicates valid data. The function modifies the mask based on the trimming
|
23
|
+
process.
|
24
|
+
method : str, optional
|
25
|
+
The method used for trimming the data. Default is "Manual", which allows the user
|
26
|
+
to manually select the start and end indices for trimming. Other methods can be
|
27
|
+
added if required in the future.
|
28
|
+
|
29
|
+
Returns
|
30
|
+
-------
|
31
|
+
numpy.ndarray
|
32
|
+
The updated mask array with the ends trimmed. Data points at the beginning and end
|
33
|
+
of the array marked as invalid (`1`) based on the trimming results.
|
34
|
+
|
35
|
+
Notes
|
36
|
+
-----
|
37
|
+
- The function uses the transducer depth from the `vlobj` to determine the trimming boundaries.
|
38
|
+
- When using the "Manual" method, the user is prompted with a plot that allows them to select
|
39
|
+
the start and end indices for trimming. These indices are then used to adjust the mask.
|
40
|
+
- The mask array is updated in-place to mark the trimmed areas as invalid.
|
41
|
+
- The trimming process is based on the variable leader data and the selected method.
|
42
|
+
|
43
|
+
Example
|
44
|
+
-------
|
45
|
+
>>> import pyadps
|
46
|
+
>>> ds = pyadps.ReadFile("demo.000")
|
47
|
+
>>> mask = pyadps.default_mask(ds)
|
48
|
+
>>> updated_mask = trim_ends(ds, mask, method="Manual")
|
49
|
+
"""
|
113
50
|
|
51
|
+
if isinstance(ds, ReadFile):
|
52
|
+
vlobj = ds.variableleader
|
53
|
+
transducer_depth = vlobj.vleader["Depth of Transducer"]
|
54
|
+
elif isinstance(ds, np.ndarray) and ds.ndim == 1:
|
55
|
+
transducer_depth = ds
|
56
|
+
else:
|
57
|
+
raise ValueError("Input must be a 1-D numpy array or a PyADPS instance")
|
114
58
|
|
115
|
-
def trim_ends(vlobj, mask, method="Manual"):
|
116
|
-
transducer_depth = vlobj.vleader["Depth of Transducer"]
|
117
|
-
# pressure = vlobj.vleader["Pressure"]
|
118
59
|
if method == "Manual":
|
119
|
-
out = PlotEnds(transducer_depth, delta=
|
60
|
+
out = PlotEnds(transducer_depth, delta=delta)
|
120
61
|
out.show()
|
121
62
|
if out.start_ens > 0:
|
122
63
|
mask[:, 0 : out.start_ens] = 1
|
@@ -127,26 +68,112 @@ def trim_ends(vlobj, mask, method="Manual"):
|
|
127
68
|
return mask
|
128
69
|
|
129
70
|
|
130
|
-
def side_lobe_beam_angle(
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
71
|
+
def side_lobe_beam_angle(
|
72
|
+
ds,
|
73
|
+
mask,
|
74
|
+
orientation="default",
|
75
|
+
water_column_depth=0,
|
76
|
+
extra_cells=2,
|
77
|
+
cells=None,
|
78
|
+
cell_size=None,
|
79
|
+
bin1dist=None,
|
80
|
+
beam_angle=None,
|
81
|
+
):
|
82
|
+
"""
|
83
|
+
Mask the data contaminated due to surface/bottom backscatter based on the
|
84
|
+
side lobe beam angle. This function can correct the orientation of the beam
|
85
|
+
(upward or downward looking) and can account for deletion of additional cells.
|
86
|
+
Water column depth is applicable for downward-looking ADCP.
|
87
|
+
|
88
|
+
Parameters
|
89
|
+
----------
|
90
|
+
ds : pyadps.dataset or np.ndarray
|
91
|
+
The pyadps dataframe is loaded to obtain the data from the fixed and variable leader.
|
92
|
+
This includes the depth of the transducer and other relevant information
|
93
|
+
for trimming the data.
|
94
|
+
|
95
|
+
If numpy.ndarray is loaded, the value should contain the transducer_depth.
|
96
|
+
In such cases provide cells, cell_size, and bin 1 distance.
|
97
|
+
Orientiation should be either 'up' or 'down' and not 'default'.
|
98
|
+
|
99
|
+
mask : numpy.ndarray
|
100
|
+
A mask array where invalid or false data points are marked. The mask is updated
|
101
|
+
based on the calculated side lobe beam angles.
|
102
|
+
orientation : str, optional
|
103
|
+
The orientation of the beam. It can be 'default', 'up', or 'down'.
|
104
|
+
Default orientation, set before deployment, is obtained from the file.
|
105
|
+
If 'up' or 'down' is selected, the function will correct the orientation accordingly.
|
106
|
+
water_column_depth : int, optional
|
107
|
+
The depth of the water column. This value is used to adjust the beam angle
|
108
|
+
calculations. Default is 0.
|
109
|
+
extra_cells : int, optional
|
110
|
+
The number of extra cells to consider when calculating the beam angle. Default is 2.
|
111
|
+
cells: int, optional
|
112
|
+
Number of cells
|
113
|
+
cell_size: int, optional
|
114
|
+
Cell size or depth cell length in cm
|
115
|
+
beam_angle: int, optional
|
116
|
+
Beam angle in degrees
|
117
|
+
|
118
|
+
Returns
|
119
|
+
-------
|
120
|
+
numpy.ndarray
|
121
|
+
The updated mask array with side lobe beam angle adjustments. The mask will
|
122
|
+
reflect valid and invalid data points based on the calculated beam angles.
|
123
|
+
|
124
|
+
Notes
|
125
|
+
-----
|
126
|
+
- The function uses the fixedleader and variableleader to retrieve the necessary information
|
127
|
+
for the beam angle calculation. The `mask` array is updated based on these calculations.
|
128
|
+
- The `orientation` parameter allows for adjustments to account for upward or downward
|
129
|
+
looking ADCPs.
|
130
|
+
- The `water_column_depth` permits detecting the bottom of the ocean for downward looking
|
131
|
+
ADCP.
|
132
|
+
- The `extra_cells` parameter adds additional cells in addition to those masked by beam angle
|
133
|
+
calculation.
|
134
|
+
|
135
|
+
Example
|
136
|
+
-------
|
137
|
+
>>> import pyadps
|
138
|
+
>>> ds = pyadps.ReadFile("demo.000")
|
139
|
+
>>> mask = pyadps.default_mask(ds)
|
140
|
+
>>> updated_mask = side_lobe_beam_angle(ds, mask, orientation='down', water_column_depth=50, extra_cells=3)
|
141
|
+
|
142
|
+
>>> transducer_depth = ds.variableleader.transducer_depth
|
143
|
+
>>> cells = ds.fixedleader.cells
|
144
|
+
>>> cell_size = ds.fixedleader.depth_cell_length
|
145
|
+
>>> new_mask = side_lobe_beam_angle(transducer_depth, orientation='up', cells=cells, cell_size=cell_size, bin1dist=bindist)
|
146
|
+
"""
|
147
|
+
if isinstance(ds, ReadFile) or ds.__class__.__name__ == "ReadFile":
|
148
|
+
flobj = ds.fixedleader
|
149
|
+
vlobj = ds.variableleader
|
150
|
+
beam_angle = int(flobj.system_configuration()["Beam Angle"])
|
151
|
+
cell_size = flobj.field()["Depth Cell Len"]
|
152
|
+
bin1dist = flobj.field()["Bin 1 Dist"]
|
153
|
+
cells = flobj.field()["Cells"]
|
154
|
+
ensembles = flobj.ensembles
|
155
|
+
transducer_depth = vlobj.vleader["Depth of Transducer"]
|
156
|
+
if orientation.lower() == "default":
|
157
|
+
orientation = flobj.system_configuration()["Beam Direction"]
|
158
|
+
elif isinstance(ds, np.ndarray) and np.squeeze(ds).ndim == 1:
|
159
|
+
transducer_depth = ds
|
160
|
+
ensembles = np.size(ds)
|
161
|
+
else:
|
162
|
+
raise ValueError("Input must be a 1-D numpy array or a PyADPS instance")
|
140
163
|
|
141
164
|
if orientation.lower() == "up":
|
142
165
|
sgn = -1
|
143
166
|
water_column_depth = 0
|
144
|
-
|
167
|
+
elif orientation.lower() == "down":
|
145
168
|
sgn = 1
|
169
|
+
else:
|
170
|
+
raise ValueError("Orientation should be either `up` or `down`")
|
146
171
|
|
147
172
|
beam_angle = np.deg2rad(beam_angle)
|
148
173
|
depth = transducer_depth / 10
|
149
|
-
valid_depth = (water_column_depth - sgn*depth) * np.cos(
|
174
|
+
valid_depth = (water_column_depth - sgn * depth) * np.cos(
|
175
|
+
beam_angle
|
176
|
+
) + sgn * bin1dist / 100
|
150
177
|
valid_cells = np.trunc(valid_depth * 100 / cell_size) - extra_cells
|
151
178
|
|
152
179
|
for i in range(ensembles):
|
@@ -185,3 +212,571 @@ def manual_cut_bins(mask, min_cell, max_cell, min_ensemble, max_ensemble):
|
|
185
212
|
mask[min_cell:max_cell, min_ensemble:max_ensemble] = 1
|
186
213
|
|
187
214
|
return mask
|
215
|
+
|
216
|
+
|
217
|
+
def modifiedRegrid2d(
|
218
|
+
ds,
|
219
|
+
data,
|
220
|
+
fill_value,
|
221
|
+
end_cell_option="cell",
|
222
|
+
trimends=None,
|
223
|
+
method="nearest",
|
224
|
+
orientation="default",
|
225
|
+
boundary_limit=0,
|
226
|
+
cells=None,
|
227
|
+
cell_size=None,
|
228
|
+
bin1dist=None,
|
229
|
+
):
|
230
|
+
"""
|
231
|
+
Modified Regrids 2D data onto a new grid based on specified parameters.
|
232
|
+
The function is capable of handling data with non-uniform number of cells
|
233
|
+
and Depth cell length.
|
234
|
+
|
235
|
+
Parameters:
|
236
|
+
-----------
|
237
|
+
ds : pyadps.dataset or numpy.ndarray
|
238
|
+
If pyadps dataframe is loaded, the data from the fixed and variable leader
|
239
|
+
is automatically obtained. This includes the depth of the transducer and other relevant information
|
240
|
+
for trimming the data.
|
241
|
+
|
242
|
+
If numpy.ndarray is loaded, the value should contain the transducer_depth.
|
243
|
+
In such cases provide cells, cell_size, and bin 1 distance.
|
244
|
+
Orientiation should be either 'up' or 'down' and not 'default'.
|
245
|
+
|
246
|
+
|
247
|
+
data : array-like
|
248
|
+
The 2D data array to be regridded.
|
249
|
+
|
250
|
+
fill_value : scalar
|
251
|
+
The value used to fill missing or undefined grid points.
|
252
|
+
|
253
|
+
end_cell_option : str or float, optional, default="cell"
|
254
|
+
The depth of the last bin or boundary for the grid.
|
255
|
+
Options include:
|
256
|
+
- "cell" : Calculates the depth of the default last bin for the grid.
|
257
|
+
Truncates to surface for upward ADCP.
|
258
|
+
- "surface": The data is gridded till the surface
|
259
|
+
- "manual": User-defined depth for the grid.
|
260
|
+
Use boundary_limit option to provide the value.
|
261
|
+
otherwise, a specific numerical depth value can be provided.
|
262
|
+
|
263
|
+
trimends : tuple of floats, optional, default=None
|
264
|
+
If provided, defines the ensemble range (start, end) for
|
265
|
+
calculating the maximum/minimum transducer depth.
|
266
|
+
Helps avoiding the deployment or retrieval data.
|
267
|
+
E.g. (10, 3000)
|
268
|
+
|
269
|
+
method : str, optional, default="nearest"
|
270
|
+
The interpolation method to use for regridding based
|
271
|
+
on scipy.interpolate.interp1d.
|
272
|
+
Options include:
|
273
|
+
- "nearest" : Nearest neighbor interpolation.
|
274
|
+
- "linear" : Linear interpolation.
|
275
|
+
- "cubic" : Cubic interpolation.
|
276
|
+
|
277
|
+
orientation : str, optional, default="up"
|
278
|
+
Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
|
279
|
+
- "up" : Regrid upwards (for upward-looking ADCP).
|
280
|
+
- "down" : Regrid downwards (for downward-looking ADCP).
|
281
|
+
|
282
|
+
boundary_limit : float, optional, default=0
|
283
|
+
The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
|
284
|
+
|
285
|
+
cells: int, optional
|
286
|
+
Number of cells
|
287
|
+
|
288
|
+
cell_size: int, optional
|
289
|
+
Cell size or depth cell length in cm
|
290
|
+
|
291
|
+
bin1dist: int, optional
|
292
|
+
Distance from the first bin in cm
|
293
|
+
|
294
|
+
|
295
|
+
Returns:
|
296
|
+
--------
|
297
|
+
z: regridded depth
|
298
|
+
regridded_data : array-like
|
299
|
+
The regridded 2D data array, based on the specified method,
|
300
|
+
orientation, and other parameters.
|
301
|
+
|
302
|
+
Notes:
|
303
|
+
------
|
304
|
+
- If `end_cell_option == boundary`, then `boundary_limit` is used to regrid the data.
|
305
|
+
- This function allows for flexible regridding of 2D data to fit a new grid, supporting different interpolation methods.
|
306
|
+
- The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
|
307
|
+
"""
|
308
|
+
|
309
|
+
if isinstance(ds, ReadFile) or ds.__class__.__name__ == "ReadFile":
|
310
|
+
flobj = ds.fixedleader
|
311
|
+
vlobj = ds.variableleader
|
312
|
+
# Get values and convert to 'm'
|
313
|
+
bin1dist = flobj.field()["Bin 1 Dist"] / 100
|
314
|
+
transdepth = vlobj.vleader["Depth of Transducer"] / 10
|
315
|
+
cell_size = flobj.field()["Depth Cell Len"] / 100
|
316
|
+
cells = flobj.field()["Cells"]
|
317
|
+
ensembles = flobj.ensembles
|
318
|
+
if orientation.lower() == "default":
|
319
|
+
orientation = flobj.system_configuration()["Beam Direction"]
|
320
|
+
|
321
|
+
elif isinstance(ds, np.ndarray) and np.squeeze(ds).ndim == 1:
|
322
|
+
transdepth = ds / 10
|
323
|
+
ensembles = np.size(ds)
|
324
|
+
|
325
|
+
if cells is None:
|
326
|
+
raise ValueError("Input must include number of cells.")
|
327
|
+
|
328
|
+
if cell_size is None:
|
329
|
+
raise ValueError("Input must include cell size.")
|
330
|
+
else:
|
331
|
+
cell_size = cell_size / 100
|
332
|
+
|
333
|
+
if bin1dist is None:
|
334
|
+
raise ValueError("Input must include bin 1 distance.")
|
335
|
+
else:
|
336
|
+
bin1dist = bin1dist / 100
|
337
|
+
|
338
|
+
if orientation.lower() != "up" and orientation.lower() != "down":
|
339
|
+
raise ValueError("Orientation must be `up` or `down`.")
|
340
|
+
else:
|
341
|
+
raise ValueError("Input must be a 1-D numpy array or a PyADPS instance")
|
342
|
+
|
343
|
+
if orientation.lower() == "up":
|
344
|
+
sgn = -1
|
345
|
+
else:
|
346
|
+
sgn = 1
|
347
|
+
|
348
|
+
# Create a regular grid
|
349
|
+
|
350
|
+
# Find depth of first cell
|
351
|
+
depth = transdepth + sgn * bin1dist
|
352
|
+
# print("depth: ", depth)
|
353
|
+
|
354
|
+
# Find the maximum and minimum depth for first cell for upward
|
355
|
+
# looking ADCP (minimum and maximum for downward looking)
|
356
|
+
if trimends is not None:
|
357
|
+
max_depth = abs(np.min(sgn * depth[trimends[0] : trimends[1]]))
|
358
|
+
min_depth = abs(np.max(sgn * depth[trimends[0] : trimends[1]]))
|
359
|
+
else:
|
360
|
+
max_depth = abs(np.min(sgn * depth))
|
361
|
+
min_depth = abs(np.max(sgn * depth))
|
362
|
+
|
363
|
+
# FIRST CELL
|
364
|
+
# Convert the first cell depth to the first regular grid depth
|
365
|
+
depthfirstcell = max_depth - max_depth % min(cell_size)
|
366
|
+
# print("depthfirstcell: ", depthfirstcell)
|
367
|
+
|
368
|
+
# LAST CELL
|
369
|
+
# Convert the last cell depth to last regular grid depth
|
370
|
+
if end_cell_option.lower() == "surface":
|
371
|
+
# Added one additional negative cell to accomodate 0 m.
|
372
|
+
depthlastcell = sgn * min(cell_size)
|
373
|
+
# print("depthlastcell: ", depthlastcell)
|
374
|
+
elif end_cell_option.lower() == "cell":
|
375
|
+
min_depth_regrid = min_depth - sgn * min_depth % min(cell_size)
|
376
|
+
depthlastcell = min_depth_regrid + sgn * (max(cells) + 1) * min(cell_size)
|
377
|
+
# print("depthlastcell: ", depthlastcell)
|
378
|
+
# Check if this is required. Use 'surface' option
|
379
|
+
if depthlastcell < 0:
|
380
|
+
depthlastcell = sgn * min(cell_size)
|
381
|
+
elif end_cell_option.lower() == "manual":
|
382
|
+
if sgn < 0 and boundary_limit > depthfirstcell:
|
383
|
+
print(
|
384
|
+
"ERROR: For upward looking ADCP, boundary limit should be less than transducer depth"
|
385
|
+
)
|
386
|
+
return
|
387
|
+
if sgn > 0 and boundary_limit < depthfirstcell:
|
388
|
+
print(
|
389
|
+
"ERROR: For downward looking ADCP, boundary limit should be greater than transducer depth"
|
390
|
+
)
|
391
|
+
return
|
392
|
+
# Set the last grid cell depth
|
393
|
+
depthlastcell = boundary_limit
|
394
|
+
else:
|
395
|
+
print("ERROR: `end_cell_option` not recognized.")
|
396
|
+
return
|
397
|
+
|
398
|
+
# Negative used for upward and positive for downward.
|
399
|
+
z = np.arange(sgn * depthfirstcell, sgn * depthlastcell, min(cell_size))
|
400
|
+
regbins = len(z)
|
401
|
+
|
402
|
+
regridded_data = np.zeros((regbins, ensembles))
|
403
|
+
|
404
|
+
# Create original depth array
|
405
|
+
for i, d in enumerate(depth):
|
406
|
+
n = d + sgn * cell_size[i] * cells[i]
|
407
|
+
# np.arange may include unexpected elements due to floating-point
|
408
|
+
# precision issues at the stopping point. Changed to np.linspace.
|
409
|
+
#
|
410
|
+
# depth_bins = np.arange(sgn*d, sgn*n, cell_size)
|
411
|
+
depth_bins = np.linspace(sgn * d, sgn * n, max(cells))
|
412
|
+
# print("depth_bins: ", depth_bins, "len: ", len(depth_bins))
|
413
|
+
# print("data:", data, "len:", len(data))
|
414
|
+
# print("i: ", i)
|
415
|
+
f = sp.interpolate.interp1d(
|
416
|
+
depth_bins,
|
417
|
+
data[:, i],
|
418
|
+
kind=method,
|
419
|
+
fill_value=fill_value,
|
420
|
+
bounds_error=False,
|
421
|
+
)
|
422
|
+
gridz = f(z)
|
423
|
+
|
424
|
+
regridded_data[:, i] = gridz
|
425
|
+
|
426
|
+
return abs(z), regridded_data
|
427
|
+
|
428
|
+
def regrid2d(
|
429
|
+
ds,
|
430
|
+
data,
|
431
|
+
fill_value,
|
432
|
+
end_cell_option="cell",
|
433
|
+
trimends=None,
|
434
|
+
method="nearest",
|
435
|
+
orientation="default",
|
436
|
+
boundary_limit=0,
|
437
|
+
cells=None,
|
438
|
+
cell_size=None,
|
439
|
+
bin1dist=None,
|
440
|
+
):
|
441
|
+
"""
|
442
|
+
Regrids 2D data onto a new grid based on specified parameters.
|
443
|
+
|
444
|
+
Parameters:
|
445
|
+
-----------
|
446
|
+
ds : pyadps.dataset or numpy.ndarray
|
447
|
+
If pyadps dataframe is loaded, the data from the fixed and variable leader
|
448
|
+
is automatically obtained. This includes the depth of the transducer and other relevant information
|
449
|
+
for trimming the data.
|
450
|
+
|
451
|
+
If numpy.ndarray is loaded, the value should contain the transducer_depth.
|
452
|
+
In such cases provide cells, cell_size, and bin 1 distance.
|
453
|
+
Orientiation should be either 'up' or 'down' and not 'default'.
|
454
|
+
|
455
|
+
|
456
|
+
data : array-like
|
457
|
+
The 2D data array to be regridded.
|
458
|
+
|
459
|
+
fill_value : scalar
|
460
|
+
The value used to fill missing or undefined grid points.
|
461
|
+
|
462
|
+
end_cell_option : str or float, optional, default="cell"
|
463
|
+
The depth of the last bin or boundary for the grid.
|
464
|
+
Options include:
|
465
|
+
- "cell" : Calculates the depth of the default last bin for the grid.
|
466
|
+
Truncates to surface for upward ADCP.
|
467
|
+
- "surface": The data is gridded till the surface
|
468
|
+
- "manual": User-defined depth for the grid.
|
469
|
+
Use boundary_limit option to provide the value.
|
470
|
+
otherwise, a specific numerical depth value can be provided.
|
471
|
+
|
472
|
+
trimends : tuple of floats, optional, default=None
|
473
|
+
If provided, defines the ensemble range (start, end) for
|
474
|
+
calculating the maximum/minimum transducer depth.
|
475
|
+
Helps avoiding the deployment or retrieval data.
|
476
|
+
E.g. (10, 3000)
|
477
|
+
|
478
|
+
method : str, optional, default="nearest"
|
479
|
+
The interpolation method to use for regridding based
|
480
|
+
on scipy.interpolate.interp1d.
|
481
|
+
Options include:
|
482
|
+
- "nearest" : Nearest neighbor interpolation.
|
483
|
+
- "linear" : Linear interpolation.
|
484
|
+
- "cubic" : Cubic interpolation.
|
485
|
+
|
486
|
+
orientation : str, optional, default="up"
|
487
|
+
Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
|
488
|
+
- "up" : Regrid upwards (for upward-looking ADCP).
|
489
|
+
- "down" : Regrid downwards (for downward-looking ADCP).
|
490
|
+
|
491
|
+
boundary_limit : float, optional, default=0
|
492
|
+
The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
|
493
|
+
|
494
|
+
cells: int, optional
|
495
|
+
Number of cells
|
496
|
+
|
497
|
+
cell_size: int, optional
|
498
|
+
Cell size or depth cell length in cm
|
499
|
+
|
500
|
+
bin1dist: int, optional
|
501
|
+
Distance from the first bin in cm
|
502
|
+
|
503
|
+
|
504
|
+
Returns:
|
505
|
+
--------
|
506
|
+
z: regridded depth
|
507
|
+
regridded_data : array-like
|
508
|
+
The regridded 2D data array, based on the specified method,
|
509
|
+
orientation, and other parameters.
|
510
|
+
|
511
|
+
Notes:
|
512
|
+
------
|
513
|
+
- If `end_cell_option == boundary`, then `boundary_limit` is used to regrid the data.
|
514
|
+
- This function allows for flexible regridding of 2D data to fit a new grid, supporting different interpolation methods.
|
515
|
+
- The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
|
516
|
+
"""
|
517
|
+
|
518
|
+
if isinstance(ds, ReadFile) or ds.__class__.__name__ == "ReadFile":
|
519
|
+
if not (check_equal(ds.fleader['Cells']) or check_equal(ds.fleader['Depth Cell Len'])):
|
520
|
+
print("\033[93m Warning: The number of cells or depth cell length are not equal. Using the modifiedRegrid2d function, which may take some time.\033[0m")
|
521
|
+
return modifiedRegrid2d(ds, data, fill_value, end_cell_option, trimends, method, orientation,
|
522
|
+
boundary_limit, cells, cell_size, bin1dist)
|
523
|
+
|
524
|
+
flobj = ds.fixedleader
|
525
|
+
vlobj = ds.variableleader
|
526
|
+
# Get values and convert to 'm'
|
527
|
+
bin1dist = flobj.field()["Bin 1 Dist"] / 100
|
528
|
+
transdepth = vlobj.vleader["Depth of Transducer"] / 10
|
529
|
+
cell_size = flobj.field()["Depth Cell Len"] / 100
|
530
|
+
cells = flobj.field()["Cells"]
|
531
|
+
ensembles = flobj.ensembles
|
532
|
+
if orientation.lower() == "default":
|
533
|
+
orientation = flobj.system_configuration()["Beam Direction"]
|
534
|
+
|
535
|
+
elif isinstance(ds, np.ndarray) and np.squeeze(ds).ndim == 1:
|
536
|
+
transdepth = ds / 10
|
537
|
+
ensembles = np.size(ds)
|
538
|
+
|
539
|
+
if cells is None:
|
540
|
+
raise ValueError("Input must include number of cells.")
|
541
|
+
else:
|
542
|
+
if not check_equal(cells):
|
543
|
+
print("\033[93m Warning: The number of cells or depth cell length are not equal. Using the modifiedRegrid2d function, which may take some time.\033[0m")
|
544
|
+
return modifiedRegrid2d(ds, data, fill_value, end_cell_option, trimends, method, orientation,
|
545
|
+
boundary_limit, cells, cell_size, bin1dist)
|
546
|
+
cells = cells[0]
|
547
|
+
|
548
|
+
if cell_size is None:
|
549
|
+
raise ValueError("Input must include cell size.")
|
550
|
+
else:
|
551
|
+
if not check_equal(cell_size):
|
552
|
+
# print("\033[93m Warning: The number of cells or depth cell length are not equal. Using the modifiedRegrid2d function, which may take some time.\033[0m")
|
553
|
+
return modifiedRegrid2d(ds, data, fill_value, end_cell_option, trimends, method, orientation,
|
554
|
+
boundary_limit, cells, cell_size, bin1dist)
|
555
|
+
cell_size = cell_size[0] / 100
|
556
|
+
|
557
|
+
if bin1dist is None:
|
558
|
+
raise ValueError("Input must include bin 1 distance.")
|
559
|
+
else:
|
560
|
+
bin1dist = bin1dist / 100
|
561
|
+
|
562
|
+
if orientation.lower() != "up" and orientation.lower() != "down":
|
563
|
+
raise ValueError("Orientation must be `up` or `down`.")
|
564
|
+
else:
|
565
|
+
raise ValueError("Input must be a 1-D numpy array or a PyADPS instance")
|
566
|
+
|
567
|
+
if orientation.lower() == "up":
|
568
|
+
sgn = -1
|
569
|
+
else:
|
570
|
+
sgn = 1
|
571
|
+
|
572
|
+
# Create a regular grid
|
573
|
+
|
574
|
+
# Find depth of first cell
|
575
|
+
depth = transdepth + sgn * bin1dist
|
576
|
+
|
577
|
+
# Find the maximum and minimum depth for first cell for upward
|
578
|
+
# looking ADCP (minimum and maximum for downward looking)
|
579
|
+
if trimends is not None:
|
580
|
+
max_depth = abs(np.min(sgn * depth[trimends[0] : trimends[1]]))
|
581
|
+
min_depth = abs(np.max(sgn * depth[trimends[0] : trimends[1]]))
|
582
|
+
else:
|
583
|
+
max_depth = abs(np.min(sgn * depth))
|
584
|
+
min_depth = abs(np.max(sgn * depth))
|
585
|
+
|
586
|
+
# FIRST CELL
|
587
|
+
# Convert the first cell depth to the first regular grid depth
|
588
|
+
depthfirstcell = max_depth - max_depth % cell_size
|
589
|
+
|
590
|
+
# LAST CELL
|
591
|
+
# Convert the last cell depth to last regular grid depth
|
592
|
+
if end_cell_option.lower() == "surface":
|
593
|
+
# Added one additional negative cell to accomodate 0 m.
|
594
|
+
depthlastcell = sgn * cell_size
|
595
|
+
elif end_cell_option.lower() == "cell":
|
596
|
+
min_depth_regrid = min_depth - sgn * min_depth % cell_size
|
597
|
+
depthlastcell = min_depth_regrid + sgn * (cells + 1) * cell_size
|
598
|
+
# Check if this is required. Use 'surface' option
|
599
|
+
if depthlastcell < 0:
|
600
|
+
depthlastcell = sgn * cell_size
|
601
|
+
elif end_cell_option.lower() == "manual":
|
602
|
+
if sgn < 0 and boundary_limit > depthfirstcell:
|
603
|
+
print(
|
604
|
+
"ERROR: For upward looking ADCP, boundary limit should be less than transducer depth"
|
605
|
+
)
|
606
|
+
return
|
607
|
+
if sgn > 0 and boundary_limit < depthfirstcell:
|
608
|
+
print(
|
609
|
+
"ERROR: For downward looking ADCP, boundary limit should be greater than transducer depth"
|
610
|
+
)
|
611
|
+
return
|
612
|
+
# Set the last grid cell depth
|
613
|
+
depthlastcell = boundary_limit
|
614
|
+
else:
|
615
|
+
print("ERROR: `end_cell_option` not recognized.")
|
616
|
+
return
|
617
|
+
|
618
|
+
# Negative used for upward and positive for downward.
|
619
|
+
z = np.arange(sgn * depthfirstcell, sgn * depthlastcell, cell_size)
|
620
|
+
regbins = len(z)
|
621
|
+
|
622
|
+
regridded_data = np.zeros((regbins, ensembles))
|
623
|
+
|
624
|
+
# Create original depth array
|
625
|
+
for i, d in enumerate(depth):
|
626
|
+
n = d + sgn * cell_size * cells
|
627
|
+
# np.arange may include unexpected elements due to floating-point
|
628
|
+
# precision issues at the stopping point. Changed to np.linspace.
|
629
|
+
#
|
630
|
+
# depth_bins = np.arange(sgn*d, sgn*n, cell_size)
|
631
|
+
depth_bins = np.linspace(sgn * d, sgn * n, cells)
|
632
|
+
f = sp.interpolate.interp1d(
|
633
|
+
depth_bins,
|
634
|
+
data[:, i],
|
635
|
+
kind=method,
|
636
|
+
fill_value=fill_value,
|
637
|
+
bounds_error=False,
|
638
|
+
)
|
639
|
+
gridz = f(z)
|
640
|
+
|
641
|
+
regridded_data[:, i] = gridz
|
642
|
+
|
643
|
+
return abs(z), regridded_data
|
644
|
+
|
645
|
+
|
646
|
+
def regrid3d(
|
647
|
+
ds,
|
648
|
+
data,
|
649
|
+
fill_value,
|
650
|
+
end_cell_option="cell",
|
651
|
+
trimends=None,
|
652
|
+
method="nearest",
|
653
|
+
orientation="up",
|
654
|
+
boundary_limit=0,
|
655
|
+
cells=None,
|
656
|
+
cell_size=None,
|
657
|
+
bin1dist=None,
|
658
|
+
beams=None,
|
659
|
+
):
|
660
|
+
"""
|
661
|
+
Regrids 3D data onto a new grid based on specified parameters.
|
662
|
+
|
663
|
+
Parameters:
|
664
|
+
-----------
|
665
|
+
ds : pyadps.dataset
|
666
|
+
The pyadps dataframe is loaded to obtain the data from the fixed and variable leader.
|
667
|
+
This includes the depth of the transducer and other relevant information
|
668
|
+
for trimming the data.
|
669
|
+
|
670
|
+
data : array-like
|
671
|
+
The 3D data array to be regridded, with dimensions
|
672
|
+
typically representing time, depth, and another axis (e.g., ensembles).
|
673
|
+
|
674
|
+
fill_value : scalar
|
675
|
+
The value used to fill missing or undefined grid points.
|
676
|
+
|
677
|
+
end_cell_option : str or float, optional, default="cell"
|
678
|
+
The depth of the last bin or boundary for the grid.
|
679
|
+
Options include:
|
680
|
+
- "cell" : Calculates the depth of the default last bin for the grid.
|
681
|
+
Truncates to surface for upward ADCP.
|
682
|
+
- "surface" : The data is gridded till the surface.
|
683
|
+
- "manual" : User-defined depth for the grid.
|
684
|
+
Use boundary_limit option to provide the value.
|
685
|
+
Otherwise, a specific numerical depth value can be provided.
|
686
|
+
|
687
|
+
trimends : tuple of integer, optional, default=None
|
688
|
+
If provided, defines the ensemble range (start, end) for
|
689
|
+
calculating the maximum/minimum transducer depth.
|
690
|
+
Helps avoiding the deployment or retrieval data.
|
691
|
+
E.g., (10, 3000)
|
692
|
+
|
693
|
+
method : str, optional, default="nearest"
|
694
|
+
The interpolation method to use for regridding based
|
695
|
+
on scipy.interpolate.interp1d.
|
696
|
+
Options include:
|
697
|
+
- "nearest" : Nearest neighbor interpolation.
|
698
|
+
- "linear" : Linear interpolation.
|
699
|
+
- "cubic" : Cubic interpolation.
|
700
|
+
|
701
|
+
orientation : str, optional, default="up"
|
702
|
+
Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
|
703
|
+
- "up" : Regrid upwards (for upward-looking ADCP).
|
704
|
+
- "down" : Regrid downwards (for downward-looking ADCP).
|
705
|
+
|
706
|
+
boundary_limit : float, optional, default=0
|
707
|
+
The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
|
708
|
+
|
709
|
+
cells: int, optional
|
710
|
+
Number of cells
|
711
|
+
|
712
|
+
cell_size: int, optional
|
713
|
+
Cell size or depth cell length in cm
|
714
|
+
|
715
|
+
bin1dist: int, optional
|
716
|
+
Distance from the first bin in cm
|
717
|
+
|
718
|
+
beams: int, optional
|
719
|
+
Number of beams
|
720
|
+
|
721
|
+
|
722
|
+
|
723
|
+
Returns:
|
724
|
+
--------
|
725
|
+
z : array-like
|
726
|
+
The regridded depth array.
|
727
|
+
regridded_data : array-like
|
728
|
+
The regridded 3D data array, based on the specified method,
|
729
|
+
orientation, and other parameters.
|
730
|
+
|
731
|
+
Notes:
|
732
|
+
------
|
733
|
+
- If `end_cell_option == boundary`, then `boundary_limit` is used to regrid the data.
|
734
|
+
- This function allows for flexible regridding of 3D data to fit a new grid, supporting different interpolation methods.
|
735
|
+
- The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
|
736
|
+
- This function is an extension of 2D regridding to handle the time dimension or other additional axes in the data.
|
737
|
+
"""
|
738
|
+
|
739
|
+
if isinstance(ds, ReadFile):
|
740
|
+
flobj = ds.fixedleader
|
741
|
+
beams = flobj.field()["Beams"]
|
742
|
+
elif isinstance(ds, np.ndarray) and ds.ndim == 1:
|
743
|
+
if beams is None:
|
744
|
+
raise ValueError("Input must include number of beams.")
|
745
|
+
else:
|
746
|
+
raise ValueError("Input must be a 1-D numpy array or a PyADPS instance")
|
747
|
+
|
748
|
+
z, data_dummy = regrid2d(
|
749
|
+
ds,
|
750
|
+
data[0, :, :],
|
751
|
+
fill_value,
|
752
|
+
end_cell_option=end_cell_option,
|
753
|
+
trimends=trimends,
|
754
|
+
method=method,
|
755
|
+
orientation=orientation,
|
756
|
+
boundary_limit=boundary_limit,
|
757
|
+
cells=cells,
|
758
|
+
cell_size=cell_size,
|
759
|
+
bin1dist=bin1dist,
|
760
|
+
)
|
761
|
+
|
762
|
+
newshape = np.shape(data_dummy)
|
763
|
+
regridded_data = np.zeros((beams, newshape[0], newshape[1]))
|
764
|
+
regridded_data[0, :, :] = data_dummy
|
765
|
+
|
766
|
+
for i in range(beams - 1):
|
767
|
+
z, data_dummy = regrid2d(
|
768
|
+
ds,
|
769
|
+
data[i + 1, :, :],
|
770
|
+
fill_value,
|
771
|
+
end_cell_option=end_cell_option,
|
772
|
+
trimends=trimends,
|
773
|
+
method=method,
|
774
|
+
orientation=orientation,
|
775
|
+
boundary_limit=boundary_limit,
|
776
|
+
cells=cells,
|
777
|
+
cell_size=cell_size,
|
778
|
+
bin1dist=bin1dist,
|
779
|
+
)
|
780
|
+
regridded_data[i + 1, :, :] = data_dummy
|
781
|
+
|
782
|
+
return z, regridded_data
|