pyadps 0.1.0b0__py3-none-any.whl → 0.2.0b0__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.
@@ -2,8 +2,8 @@ import matplotlib.pyplot as plt
2
2
  import numpy as np
3
3
  import streamlit as st
4
4
  from matplotlib.widgets import Button, Slider, TextBox
5
-
6
5
  from pyadps.utils import readrdi as rd
6
+
7
7
  from .plotgen import plotmask, plotvar
8
8
 
9
9
 
@@ -127,7 +127,7 @@ def trim_ends(vlobj, mask, method="Manual"):
127
127
  return mask
128
128
 
129
129
 
130
- def side_lobe_beam_angle(flobj, vlobj, mask, extra_cells=2):
130
+ def side_lobe_beam_angle(flobj, vlobj, mask, orientation='default', water_column_depth=0, extra_cells=2):
131
131
  beam_angle = int(flobj.system_configuration()["Beam Angle"])
132
132
  cell_size = flobj.field()["Depth Cell Len"]
133
133
  bin1dist = flobj.field()["Bin 1 Dist"]
@@ -135,11 +135,19 @@ def side_lobe_beam_angle(flobj, vlobj, mask, extra_cells=2):
135
135
  ensembles = flobj.ensembles
136
136
  transducer_depth = vlobj.vleader["Depth of Transducer"]
137
137
 
138
+ if orientation.lower() == "default":
139
+ orientation = flobj.system_configuration()['Beam Direction']
140
+
141
+ if orientation.lower() == "up":
142
+ sgn = -1
143
+ water_column_depth = 0
144
+ else:
145
+ sgn = 1
146
+
138
147
  beam_angle = np.deg2rad(beam_angle)
139
148
  depth = transducer_depth / 10
140
- valid_depth = depth * np.cos(beam_angle) - bin1dist / 100
149
+ valid_depth = (water_column_depth - sgn*depth) * np.cos(beam_angle) + sgn*bin1dist / 100
141
150
  valid_cells = np.trunc(valid_depth * 100 / cell_size) - extra_cells
142
- st.write(cells, valid_cells[100], extra_cells)
143
151
 
144
152
  for i in range(ensembles):
145
153
  c = int(valid_cells[i])
@@ -153,23 +161,27 @@ def side_lobe_rssi_bump(echo, mask):
153
161
  pass
154
162
 
155
163
 
156
- # read data
157
- # filename = "BGS11000.000"
158
- # fl = rd.FixedLeader(filename, run="fortran")
159
- # vl = rd.VariableLeader(filename, run="fortran")
160
- # echo = rd.echo(filename, run="fortran")
161
- # vel = rd.velocity(filename, run="fortran")
162
- #
163
- # beam_angle = int(fl.system_configuration()["Beam Angle"])
164
- # cell_size = fl.field()["Depth Cell Len"]
165
- # bin1dist = fl.field()["Bin 1 Dist"]
166
- # cells = fl.field()["Cells"]
167
- #
168
- # shape = np.shape(vel[0, :, :])
169
- # mask = np.zeros(shape)
170
- # orig_mask = np.copy(mask)
171
- #
172
- # mask = trim_ends(vl, mask)
173
- # mask = side_lobe_beam_angle(fl, vl, mask)
174
- # plotmask(orig_mask, mask)
175
- # plotvar(echo, "Echo Intensity", mask)
164
+ def manual_cut_bins(mask, min_cell, max_cell, min_ensemble, max_ensemble):
165
+ """
166
+ Apply manual bin cutting by selecting a specific range of cells and ensembles.
167
+
168
+ Parameters:
169
+ mask (numpy array): The mask array to modify.
170
+ min_cell (int): The minimum cell index to mask.
171
+ max_cell (int): The maximum cell index to mask.
172
+ min_ensemble (int): The minimum ensemble index to mask.
173
+ max_ensemble (int): The maximum ensemble index to mask.
174
+
175
+ Returns:
176
+ numpy array: The updated mask with selected areas masked.
177
+ """
178
+ # Ensure the indices are within valid range
179
+ min_cell = max(0, min_cell)
180
+ max_cell = min(mask.shape[0], max_cell)
181
+ min_ensemble = max(0, min_ensemble)
182
+ max_ensemble = min(mask.shape[1], max_ensemble)
183
+
184
+ # Apply mask on the selected range
185
+ mask[min_cell:max_cell, min_ensemble:max_ensemble] = 1
186
+
187
+ return mask
pyadps/utils/readrdi.py CHANGED
@@ -326,7 +326,7 @@ class FileHeader:
326
326
  """
327
327
  file_stats = os.stat(self.filename)
328
328
  sys_file_size = file_stats.st_size
329
- cal_file_size = sum(self.bytes) + 2 * len(self.bytes)
329
+ cal_file_size = sum((self.bytes).astype(int)) + 2 * len(self.bytes)
330
330
 
331
331
  check = dict()
332
332
 
@@ -800,7 +800,8 @@ def vlead_dict(vid):
800
800
 
801
801
  counter = 1
802
802
  for key, value in vname.items():
803
- vlead[key] = getattr(np, value)(vid[:][counter])
803
+ # vlead[key] = getattr(np, value)(vid[:][counter])
804
+ vlead[key] = vid[:][counter]
804
805
  counter += 1
805
806
 
806
807
  return vlead
pyadps/utils/regrid.py CHANGED
@@ -9,49 +9,148 @@ def regrid2d(
9
9
  vlobj,
10
10
  data,
11
11
  fill_value,
12
- minimum_depth="cell",
12
+ end_bin_option="cell",
13
13
  trimends=None,
14
14
  method="nearest",
15
+ orientation="default",
16
+ boundary_limit=0,
15
17
  ):
16
- depth = vlobj.vleader["Depth of Transducer"] / 10
18
+ """
19
+ Regrids 2D data onto a new grid based on specified parameters.
20
+
21
+ Parameters:
22
+ -----------
23
+ flobj : object
24
+ The fixed leader object that contains information
25
+ about the fixed leader data.
26
+
27
+ vlobj : object
28
+ The variable leader object that contains information
29
+ about the variable leader data.
30
+
31
+ data : array-like
32
+ The 2D data array to be regridded.
33
+
34
+ fill_value : scalar
35
+ The value used to fill missing or undefined grid points.
36
+
37
+ end_bin_option : str or float, optional, default="cell"
38
+ The depth of the last bin or boundary for the grid.
39
+ Options include:
40
+ - "cell" : Calculates the depth of the default last bin for the grid.
41
+ Truncates to surface for upward ADCP.
42
+ - "surface": The data is gridded till the surface
43
+ - "manual": User-defined depth for the grid.
44
+ Use boundary_limit option to provide the value.
45
+ otherwise, a specific numerical depth value can be provided.
46
+
47
+ trimends : tuple of floats, optional, default=None
48
+ If provided, defines the ensemble range (start, end) for
49
+ calculating the maximum/minimum transducer depth.
50
+ Helps avoiding the deployment or retrieval data.
51
+ E.g. (10, 3000)
52
+
53
+ method : str, optional, default="nearest"
54
+ The interpolation method to use for regridding based
55
+ on scipy.interpolate.interp1d.
56
+ Options include:
57
+ - "nearest" : Nearest neighbor interpolation.
58
+ - "linear" : Linear interpolation.
59
+ - "cubic" : Cubic interpolation.
60
+
61
+ orientation : str, optional, default="up"
62
+ Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
63
+ - "up" : Regrid upwards (for upward-looking ADCP).
64
+ - "down" : Regrid downwards (for downward-looking ADCP).
65
+
66
+ boundary_limit : float, optional, default=0
67
+ The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
68
+
69
+ Returns:
70
+ --------
71
+ z: regridded depth
72
+ regridded_data : array-like
73
+ The regridded 2D data array, based on the specified method,
74
+ orientation, and other parameters.
75
+
76
+ Notes:
77
+ ------
78
+ - If `end_bin_option == boundary`, then `boundary_limit` is used to regrid the data.
79
+ - This function allows for flexible regridding of 2D data to fit a new grid, supporting different interpolation methods.
80
+ - The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
81
+ """
82
+
83
+ # Get values and convert to 'm'
84
+ bin1dist = flobj.field()["Bin 1 Dist"] / 100
85
+ transdepth = vlobj.vleader["Depth of Transducer"] / 10
17
86
  depth_interval = flobj.field()["Depth Cell Len"] / 100
18
87
  bins = flobj.field()["Cells"]
19
88
  ensembles = flobj.ensembles
20
89
 
90
+ if orientation.lower() == "default":
91
+ orientation = flobj.system_configuration()['Beam Direction']
92
+
93
+ if orientation.lower() == "up":
94
+ sgn = -1
95
+ else:
96
+ sgn = 1
97
+
21
98
  # Create a regular grid
22
- # Find the minimum depth.
23
- if minimum_depth == "surface":
24
- mindepth = depth_interval
25
- elif minimum_depth == "cell":
26
- if trimends is not None:
27
- dm = np.min(depth[trimends[0] : trimends[1]])
28
- else:
29
- dm = np.min(depth)
30
- mintransdepth = dm - bins * depth_interval
31
- mindepth = mintransdepth - mintransdepth % depth_interval
32
- mindepth = mindepth - depth_interval
33
- # If mindepth is above surface choose surface
34
- if mindepth < 0:
35
- mindepth = depth_interval
99
+
100
+ # Find depth of first cell
101
+ depth = transdepth + sgn*bin1dist
102
+
103
+ # Find the maximum and minimum depth for first cell for upward
104
+ # looking ADCP (minimum and maximum for downward looking)
105
+ if trimends is not None:
106
+ max_depth = abs(np.min(sgn*depth[trimends[0] : trimends[1]]))
107
+ min_depth = abs(np.max(sgn*depth[trimends[0] : trimends[1]]))
36
108
  else:
37
- mindepth = depth_interval
109
+ max_depth = abs(np.min(sgn*depth))
110
+ min_depth = abs(np.max(sgn*depth))
38
111
 
39
- maxbins = np.max(depth) // depth_interval + 1
40
- # print(np.max(depth), np.max(depth) % depth_interval)
41
- # if np.max(depth) % depth_interval > depth_interval / 2:
42
- # maxbins = maxbins + 1
112
+ # FIRST CELL
113
+ # Convert the first cell depth to the first regular grid depth
114
+ depthfirstcell = max_depth - max_depth % depth_interval
43
115
 
44
- maxdepth = maxbins * depth_interval
45
- z = np.arange(-1 * maxdepth, -1 * mindepth, depth_interval)
116
+ # LAST CELL
117
+ # Convert the last cell depth to last regular grid depth
118
+ if end_bin_option.lower() == "surface":
119
+ # Added one additional negative cell to accomodate 0 m.
120
+ depthlastcell = sgn * depth_interval
121
+ elif end_bin_option.lower() == "cell":
122
+ min_depth_regrid = min_depth - sgn*min_depth % depth_interval
123
+ depthlastcell = min_depth_regrid + sgn* (bins+1) * depth_interval
124
+ # Check if this is required. Use 'surface' option
125
+ if depthlastcell < 0:
126
+ depthlastcell = sgn*depth_interval
127
+ elif end_bin_option.lower() == "manual":
128
+ if sgn < 0 and boundary_limit > depthfirstcell:
129
+ print("ERROR: For upward looking ADCP, boundary limit should be less than transducer depth")
130
+ return
131
+ if sgn > 0 and boundary_limit < depthfirstcell:
132
+ print("ERROR: For downward looking ADCP, boundary limit should be greater than transducer depth")
133
+ return
134
+ # Set the last grid cell depth
135
+ depthlastcell = boundary_limit
136
+ else:
137
+ print("ERROR: `end_bin_option` not recognized.")
138
+ return
139
+
140
+ # Negative used for upward and positive for downward.
141
+ z = np.arange(sgn * depthfirstcell, sgn * depthlastcell, depth_interval)
46
142
  regbins = len(z)
47
143
 
48
- # print(maxbins, bins, ensemble)
49
- data_regrid = np.zeros((regbins, ensembles))
144
+ regridded_data = np.zeros((regbins, ensembles))
50
145
 
51
146
  # Create original depth array
52
147
  for i, d in enumerate(depth):
53
- n = -1 * d + depth_interval * bins
54
- depth_bins = np.arange(-1 * d, n, depth_interval)
148
+ n = d + sgn*depth_interval * bins
149
+ # np.arange may include unexpected elements due to floating-point
150
+ # precision issues at the stopping point. Changed to np.linspace.
151
+ #
152
+ # depth_bins = np.arange(sgn*d, sgn*n, depth_interval)
153
+ depth_bins = np.linspace(sgn*d, sgn*n, bins)
55
154
  f = sp.interpolate.interp1d(
56
155
  depth_bins,
57
156
  data[:, i],
@@ -61,9 +160,9 @@ def regrid2d(
61
160
  )
62
161
  gridz = f(z)
63
162
 
64
- data_regrid[:, i] = gridz
163
+ regridded_data[:, i] = gridz
65
164
 
66
- return z, data_regrid
165
+ return abs(z), regridded_data
67
166
 
68
167
 
69
168
  def regrid3d(
@@ -71,24 +170,96 @@ def regrid3d(
71
170
  vlobj,
72
171
  data,
73
172
  fill_value,
74
- minimum_depth="cell",
173
+ end_bin_option="cell",
75
174
  trimends=None,
76
175
  method="nearest",
176
+ orientation="up",
177
+ boundary_limit=0,
77
178
  ):
179
+ """
180
+ Regrids 3D data onto a new grid based on specified parameters.
181
+
182
+ Parameters:
183
+ -----------
184
+ flobj : object
185
+ The fixed leader object that contains information
186
+ about the fixed leader data.
187
+
188
+ vlobj : object
189
+ The variable leader object that contains information
190
+ about the variable leader data.
191
+
192
+ data : array-like
193
+ The 3D data array to be regridded, with dimensions
194
+ typically representing time, depth, and another axis (e.g., ensembles).
195
+
196
+ fill_value : scalar
197
+ The value used to fill missing or undefined grid points.
198
+
199
+ end_bin_option : str or float, optional, default="cell"
200
+ The depth of the last bin or boundary for the grid.
201
+ Options include:
202
+ - "cell" : Calculates the depth of the default last bin for the grid.
203
+ Truncates to surface for upward ADCP.
204
+ - "surface" : The data is gridded till the surface.
205
+ - "manual" : User-defined depth for the grid.
206
+ Use boundary_limit option to provide the value.
207
+ Otherwise, a specific numerical depth value can be provided.
208
+
209
+ trimends : tuple of floats, optional, default=None
210
+ If provided, defines the ensemble range (start, end) for
211
+ calculating the maximum/minimum transducer depth.
212
+ Helps avoiding the deployment or retrieval data.
213
+ E.g., (10, 3000)
214
+
215
+ method : str, optional, default="nearest"
216
+ The interpolation method to use for regridding based
217
+ on scipy.interpolate.interp1d.
218
+ Options include:
219
+ - "nearest" : Nearest neighbor interpolation.
220
+ - "linear" : Linear interpolation.
221
+ - "cubic" : Cubic interpolation.
222
+
223
+ orientation : str, optional, default="up"
224
+ Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
225
+ - "up" : Regrid upwards (for upward-looking ADCP).
226
+ - "down" : Regrid downwards (for downward-looking ADCP).
227
+
228
+ boundary_limit : float, optional, default=0
229
+ The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
230
+
231
+ Returns:
232
+ --------
233
+ z : array-like
234
+ The regridded depth array.
235
+ regridded_data : array-like
236
+ The regridded 3D data array, based on the specified method,
237
+ orientation, and other parameters.
238
+
239
+ Notes:
240
+ ------
241
+ - If `end_bin_option == boundary`, then `boundary_limit` is used to regrid the data.
242
+ - This function allows for flexible regridding of 3D data to fit a new grid, supporting different interpolation methods.
243
+ - The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
244
+ - This function is an extension of 2D regridding to handle the time dimension or other additional axes in the data.
245
+ """
246
+
78
247
  beams = flobj.field()["Beams"]
79
248
  z, data_dummy = regrid2d(
80
249
  flobj,
81
250
  vlobj,
82
251
  data[0, :, :],
83
252
  fill_value,
84
- minimum_depth=minimum_depth,
253
+ end_bin_option=end_bin_option,
85
254
  trimends=trimends,
86
255
  method=method,
256
+ orientation=orientation,
257
+ boundary_limit=boundary_limit,
87
258
  )
88
259
 
89
260
  newshape = np.shape(data_dummy)
90
- data_regrid = np.zeros((beams, newshape[0], newshape[1]))
91
- data_regrid[0, :, :] = data_dummy
261
+ regridded_data = np.zeros((beams, newshape[0], newshape[1]))
262
+ regridded_data[0, :, :] = data_dummy
92
263
 
93
264
  for i in range(beams - 1):
94
265
  z, data_dummy = regrid2d(
@@ -96,27 +267,13 @@ def regrid3d(
96
267
  vlobj,
97
268
  data[i + 1, :, :],
98
269
  fill_value,
99
- minimum_depth=minimum_depth,
270
+ end_bin_option=end_bin_option,
100
271
  trimends=trimends,
101
272
  method=method,
273
+ orientation=orientation,
274
+ boundary_limit=boundary_limit,
102
275
  )
103
- data_regrid[i + 1, :, :] = data_dummy
104
-
105
- return z, data_regrid
106
-
107
-
108
- # # read data
109
- # filename = "BGS11000.000"
110
- # fl = rd.FixedLeader(filename, run="fortran")
111
- # vl = rd.VariableLeader(filename, run="fortran")
112
- # # echo = rd.echo(filename, run="fortran")
113
- # vel = rd.velocity(filename, run="fortran")
114
- # pressure = vl.vleader["Pressure"]
115
- #
116
- # shape = np.shape(vel[0, :, :])
117
- # mask = np.zeros(shape)
118
- # orig_mask = np.copy(mask)
119
- #
120
- # z, newvel = regrid2d(fl, vl, vel[0, :, :], fill_value=-32768)
121
- # z, newmask = regrid(mask[:, :], pressure, depth_interval=4, fill_value=1)
122
- # z, newvel3d = regrid3d(vel, pressure, depth_interval=4, fill_value=-32768)
276
+ regridded_data[i + 1, :, :] = data_dummy
277
+
278
+ return z, regridded_data
279
+
@@ -1,39 +1,74 @@
1
1
  from itertools import groupby
2
2
 
3
+ import requests
3
4
  import numpy as np
4
5
  import scipy as sp
5
- import wmm2020
6
6
 
7
+ def wmm2020api(lat1, lon1, year):
8
+ """
9
+ This function uses the WMM2020 API to retrieve the magnetic field values at a given location
10
+ The API need latitude, longitude and year to perform the calculation. The key in the function
11
+ must be updated time to time since the API is subjected to timely updates and the key may change.
12
+
13
+ Args:
14
+ Latitude (float)
15
+ Longitude (float)
16
+ startYear (int)
7
17
 
8
- def magnetic_declination(velocity, lat, lon, depth, year):
18
+ Returns:
19
+ mag -> magnetic declination at the given location in degree.
9
20
  """
10
- The function uses magnetic declination from wmm2020 to correct
11
- the horizontal velocities
21
+ baseurl = "https://www.ngdc.noaa.gov/geomag-web/calculators/calculateDeclination?"
22
+ key = "zNEw7"
23
+ resultFormat="json"
24
+ url = "{}lat1={}&lon1={}&key={}&startYear{}&resultFormat={}".format(baseurl, lat1, lon1, key, year, resultFormat)
25
+ response = requests.get(url)
26
+ data = response.json()
27
+ results = data["result"][0]
28
+ mag = [[results["declination"]]]
29
+
30
+ return mag
31
+
32
+ def magnetic_declination(lat, lon, depth, year):
33
+ """
34
+ The function calculates the magnetic declination at a given location and depth.
35
+ using a local installation of wmm2020 model.
36
+
12
37
 
13
38
  Args:
14
- velocity (numpy array): velocity(beam, depth, time)
15
39
  lat (parameter, float): Latitude in decimals
16
40
  lon (parameter, float): Longitude in decimals
17
41
  depth (parameter, float): depth in m
18
42
  year (parameter, integer): Year
19
43
 
20
44
  Returns:
21
- velocity (numpy array): Rotated velocity using magnetic declination
22
45
  mag: Magnetic declination (degrees)
23
46
  """
47
+ import wmm2020
24
48
  mag = wmm2020.wmm(lat, lon, depth, year)
25
- mag = np.deg2rad(mag.decl.data)
49
+ mag = mag.decl.data
50
+
51
+ return mag
52
+
53
+ def velocity_modifier(velocity, mag):
54
+ """
55
+ The function uses magnetic declination from wmm2020 to correct
56
+ the horizontal velocities
57
+
58
+ Args:
59
+ velocity (numpy array): velocity array
60
+ mag: magnetic declination (degrees)
61
+
62
+ Returns:
63
+ velocity (numpy array): Rotated velocity using magnetic declination
64
+ """
65
+ mag = np.deg2rad(mag[0][0])
26
66
  velocity = np.where(velocity == -32768, np.nan, velocity)
27
- velocity[0, :, :] = velocity[0, :, :] * np.cos(mag) + velocity[1, :, :] * np.sin(
28
- mag
29
- )
30
- velocity[1, :, :] = -1 * velocity[0, :, :] * np.sin(mag) + velocity[
31
- 1, :, :
32
- ] * np.cos(mag)
67
+ velocity[0, :, :] = velocity[0, :, :] * np.cos(mag) + velocity[1, :, :] * np.sin(mag)
68
+ velocity[1, :, :] = -1 * velocity[0, :, :] * np.sin(mag) + velocity[1, :, :] * np.cos(mag)
33
69
  velocity = np.where(velocity == np.nan, -32768, velocity)
34
70
 
35
- return velocity, np.rad2deg(mag)
36
-
71
+ return velocity
37
72
 
38
73
  def velocity_cutoff(velocity, mask, cutoff=250):
39
74
  """