@@ -157,31 +157,39 @@ def water_depth_from_amplitude(ds, thresh=10, nfilt=None) -> None:
157157 "Please manually remove 'depth' if it needs to be recalculated."
158158 )
159159
160+ # Use "avg" velocty if standard isn't available.
161+ # Should not matter which is used.
162+ tag = []
163+ if hasattr (ds , "vel" ):
164+ tag += ["" ]
165+ if hasattr (ds , "vel_avg" ):
166+ tag += ["_avg" ]
167+
160168 # This finds the maximum of the echo profile:
161- inds = np .argmax (ds ["amp" ].values , axis = 1 )
169+ inds = np .argmax (ds ["amp" + tag [ 0 ] ].values , axis = 1 )
162170 # This finds the first point that increases (away from the profiler) in
163171 # the echo profile
164- edf = np .diff (ds ["amp" ].values .astype (np .int16 ), axis = 1 )
172+ edf = np .diff (ds ["amp" + tag [ 0 ] ].values .astype (np .int16 ), axis = 1 )
165173 inds2 = (
166- np .max (
174+ np .nanmax (
167175 (edf < 0 )
168- * np .arange (ds ["vel" ].shape [1 ] - 1 , dtype = np .uint8 )[None , :, None ],
176+ * np .arange (ds ["vel" + tag [ 0 ] ].shape [1 ] - 1 , dtype = np .uint8 )[None , :, None ],
169177 axis = 1 ,
170178 )
171179 + 1
172180 )
173181
174182 # Calculate the depth of these quantities
175- d1 = ds ["range" ].values [inds ]
176- d2 = ds ["range" ].values [inds2 ]
183+ d1 = ds ["range" + tag [ 0 ] ].values [inds ]
184+ d2 = ds ["range" + tag [ 0 ] ].values [inds2 ]
177185 # Combine them:
178186 D = np .vstack ((d1 , d2 ))
179187 # Take the median value as the estimate of the surface:
180- d = np .median (D , axis = 0 )
188+ d = np .nanmedian (D , axis = 0 )
181189
182190 # Throw out values that do not increase near the surface by *thresh*
183- for ip in range (ds ["vel" ].shape [1 ]):
184- itmp = np .min (inds [:, ip ])
191+ for ip in range (ds ["vel" + tag [ 0 ] ].shape [1 ]):
192+ itmp = np .nanmin (inds [:, ip ])
185193 if (edf [itmp :, :, ip ] < thresh ).all ():
186194 d [ip ] = np .nan
187195
@@ -199,7 +207,7 @@ def water_depth_from_amplitude(ds, thresh=10, nfilt=None) -> None:
199207
200208 ds ["depth" ] = xr .DataArray (
201209 d .astype ("float32" ),
202- dims = ["time" ],
210+ dims = ["time" + tag [ 0 ] ],
203211 attrs = {"units" : "m" , "long_name" : long_name , "standard_name" : "depth" },
204212 )
205213
@@ -230,7 +238,7 @@ def water_depth_from_pressure(ds, salinity=35) -> None:
230238 ds : xarray.Dataset
231239 The full adcp dataset
232240 salinity: numeric
233- Water salinity in psu . Default = 35
241+ Water salinity in PSU . Default = 35
234242
235243 Returns
236244 -------
@@ -259,16 +267,26 @@ def water_depth_from_pressure(ds, salinity=35) -> None:
259267 "The variable 'depth' already exists. "
260268 "Please manually remove 'depth' if it needs to be recalculated."
261269 )
262- if "pressure" not in ds .data_vars :
270+ pressure = [v for v in ds .data_vars if "pressure" in v ]
271+ if not pressure :
263272 raise NameError ("The variable 'pressure' does not exist." )
264- elif not ds ["pressure" ].sum ():
265- raise ValueError ("Pressure data not recorded." )
266- if "temp" not in ds .data_vars :
273+ else :
274+ for p in pressure :
275+ if not ds [p ].sum ():
276+ pressure .remove (p )
277+ if not pressure :
278+ raise ValueError ("Pressure data not recorded." )
279+ temp = [
280+ v
281+ for v in ds .data_vars
282+ if (("temp" in v ) and ("clock" not in v ) and ("press" not in v ))
283+ ]
284+ if not temp :
267285 raise NameError ("The variable 'temp' does not exist." )
268286
269287 # Density calcation
270- P = ds [" pressure" ] .values
271- T = ds [" temp" ].values # temperature, degC
288+ P = ds [pressure [ 0 ]] .values # pressure, dbar
289+ T = ds [temp [ 0 ] ].values # temperature, degC
272290 S = salinity # practical salinity
273291 rho0 = 1027 # kg/m^3
274292 T0 = 10 # degC
@@ -291,7 +309,7 @@ def water_depth_from_pressure(ds, salinity=35) -> None:
291309
292310 ds ["water_density" ] = xr .DataArray (
293311 rho .astype ("float32" ),
294- dims = ["time" ],
312+ dims = [ds [ pressure [ 0 ]]. dims [ 0 ] ],
295313 attrs = {
296314 "units" : "kg m-3" ,
297315 "long_name" : "Water Density" ,
@@ -301,7 +319,7 @@ def water_depth_from_pressure(ds, salinity=35) -> None:
301319 )
302320 ds ["depth" ] = xr .DataArray (
303321 d .astype ("float32" ),
304- dims = ["time" ],
322+ dims = [ds [ pressure [ 0 ]]. dims [ 0 ] ],
305323 attrs = {"units" : "m" , "long_name" : long_name , "standard_name" : "depth" },
306324 )
307325
@@ -319,7 +337,7 @@ def nan_beyond_surface(*args, **kwargs):
319337
320338
321339def remove_surface_interference (
322- ds , val = np .nan , beam_angle = None , inplace = False
340+ ds , val = np .nan , beam_angle = None , cell_size = None , inplace = False
323341) -> Optional [xr .Dataset ]:
324342 """
325343 Mask the values of 3D data (vel, amp, corr, echo) that are beyond the surface.
@@ -332,6 +350,8 @@ def remove_surface_interference(
332350 Specifies the value to set the bad values to. Default is `numpy.nan`
333351 beam_angle : int
334352 ADCP beam inclination angle in degrees. Default = dataset.attrs['beam_angle']
353+ cell_size : float
354+ ADCP beam cellsize in meters. Default = dataset.attrs['cell_size']
335355 inplace : bool
336356 When True the existing data object is modified. When False
337357 a copy is returned. Default = False
@@ -352,51 +372,78 @@ def remove_surface_interference(
352372 raise KeyError (
353373 "Depth variable 'depth' does not exist in input dataset."
354374 "Please calculate 'depth' using the function 'water_depth_from_pressure'"
355- "or 'water_depth_from_amplitude."
375+ "or 'water_depth_from_amplitude, or it can be found from the 'dist_bt'"
376+ "(bottom track) or 'dist_alt' (altimeter) variables, if available."
356377 )
357378
358379 if beam_angle is None :
359380 if hasattr (ds , "beam_angle" ):
360381 beam_angle = np .deg2rad (ds .attrs ["beam_angle" ])
361382 else :
362- raise Exception (
383+ raise KeyError (
363384 "'beam_angle` not found in dataset attributes. "
364385 "Please supply the ADCP's beam angle."
365386 )
366387 else :
367388 beam_angle = np .deg2rad (beam_angle )
368389
390+ if cell_size is None :
391+ # Fetch cell size
392+ cell_sizes = [
393+ a
394+ for a in ds .attrs
395+ if (
396+ ("cell_size" in a )
397+ and ("_bt" not in a )
398+ and ("_alt" not in a )
399+ and ("wave" not in a )
400+ )
401+ ]
402+ if cell_sizes :
403+ cs = cell_sizes [0 ]
404+ else :
405+ raise KeyError (
406+ "'cell_size` not found in dataset attributes. "
407+ "Please supply the ADCP's cell size."
408+ )
409+ else :
410+ cs = [cell_size ]
411+
369412 if not inplace :
370413 ds = ds .copy (deep = True )
371414
372415 # Get all variables with 'range' coordinate
373416 profile_vars = [h for h in ds .keys () if any (s for s in ds [h ].dims if "range" in s )]
374417
375- # Surface interference distance
376418 # Apply range_offset if available
377419 range_offset = __check_for_range_offset (ds )
378420 if range_offset :
379421 range_limit = (
380- (ds ["depth" ] - range_offset ) * np .cos (beam_angle ) - ds .attrs ["cell_size" ]
422+ (ds ["depth" ] - range_offset ) * np .cos (beam_angle ) - ds .attrs [cs ]
381423 ) + range_offset
382424 else :
383- range_limit = ds ["depth" ] * np .cos (beam_angle ) - ds .attrs ["cell_size" ]
384-
385- bds = ds ["range" ] > range_limit
425+ range_limit = ds ["depth" ] * np .cos (beam_angle ) - ds .attrs [cs ]
386426
387427 # Echosounder data needs only be trimmed at water surface
388428 if "echo" in profile_vars :
389429 mask_echo = ds ["range_echo" ] > ds ["depth" ]
390430 ds ["echo" ].values [..., mask_echo ] = val
391431 profile_vars .remove ("echo" )
392432
393- # Correct rest of "range" data for surface interference
433+ # Correct profile measurements for surface interference
394434 for var in profile_vars :
435+ # Use correct coordinate tag
436+ if "_" in var and ("gd" not in var ):
437+ tag = "_" + "_" .join (var .split ("_" )[1 :])
438+ else :
439+ tag = ""
440+ mask = ds ["range" + tag ] > range_limit
441+ # Remove values
395442 a = ds [var ].values
396443 try : # float dtype
397- a [..., bds ] = val
444+ a [..., mask ] = val
398445 except : # int dtype
399- a [..., bds ] = 0
446+ a [..., mask ] = 0
400447 ds [var ].values = a
401448
402449 if not inplace :
@@ -434,10 +481,13 @@ def correlation_filter(ds, thresh=50, inplace=False) -> Optional[xr.Dataset]:
434481 ds = ds .copy (deep = True )
435482
436483 # 4 or 5 beam
484+ tag = []
485+ if hasattr (ds , "vel" ):
486+ tag += ["" ]
437487 if hasattr (ds , "vel_b5" ):
438- tag = ["" , "_b5" ]
439- else :
440- tag = ["" ]
488+ tag + = ["_b5" ]
489+ if hasattr ( ds , "vel_avg" ) :
490+ tag + = ["_avg " ]
441491
442492 # copy original ref frame
443493 coord_sys_orig = ds .coord_sys
@@ -456,7 +506,7 @@ def correlation_filter(ds, thresh=50, inplace=False) -> Optional[xr.Dataset]:
456506 ds [var + tg ].attrs ["Comments" ] = (
457507 "Filtered of data with a correlation value below "
458508 + str (thresh )
459- + ds . corr .units
509+ + ds [ " corr" + tg ] .units
460510 )
461511
462512 rotate2 (ds , coord_sys_orig , inplace = True )
0 commit comments