diff --git a/examples/data/dolfyn/test_data/RDI_7f79.nc b/examples/data/dolfyn/test_data/RDI_7f79.nc index 0d073664..f71b4341 100644 Binary files a/examples/data/dolfyn/test_data/RDI_7f79.nc and b/examples/data/dolfyn/test_data/RDI_7f79.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_7f79_2.nc b/examples/data/dolfyn/test_data/RDI_7f79_2.nc index 4874189d..d794b1f4 100644 Binary files a/examples/data/dolfyn/test_data/RDI_7f79_2.nc and b/examples/data/dolfyn/test_data/RDI_7f79_2.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_test01.nc b/examples/data/dolfyn/test_data/RDI_test01.nc index bd5bbc7f..cec11b0b 100644 Binary files a/examples/data/dolfyn/test_data/RDI_test01.nc and b/examples/data/dolfyn/test_data/RDI_test01.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_test01_clean.nc b/examples/data/dolfyn/test_data/RDI_test01_clean.nc index 6f2e72ea..26b8389c 100644 Binary files a/examples/data/dolfyn/test_data/RDI_test01_clean.nc and b/examples/data/dolfyn/test_data/RDI_test01_clean.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_test01_ofilt.nc b/examples/data/dolfyn/test_data/RDI_test01_ofilt.nc index 37d75ebc..d83facf1 100644 Binary files a/examples/data/dolfyn/test_data/RDI_test01_ofilt.nc and b/examples/data/dolfyn/test_data/RDI_test01_ofilt.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_test01_rotate_beam2inst.nc b/examples/data/dolfyn/test_data/RDI_test01_rotate_beam2inst.nc index 88d70100..27a3e43e 100644 Binary files a/examples/data/dolfyn/test_data/RDI_test01_rotate_beam2inst.nc and b/examples/data/dolfyn/test_data/RDI_test01_rotate_beam2inst.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_test01_rotate_earth2principal.nc b/examples/data/dolfyn/test_data/RDI_test01_rotate_earth2principal.nc index 95ee6209..9b862d8a 100644 Binary files a/examples/data/dolfyn/test_data/RDI_test01_rotate_earth2principal.nc and b/examples/data/dolfyn/test_data/RDI_test01_rotate_earth2principal.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_test01_rotate_inst2earth.nc b/examples/data/dolfyn/test_data/RDI_test01_rotate_inst2earth.nc index db36c437..1b6aec9f 100644 Binary files a/examples/data/dolfyn/test_data/RDI_test01_rotate_inst2earth.nc and b/examples/data/dolfyn/test_data/RDI_test01_rotate_inst2earth.nc differ diff --git a/examples/data/dolfyn/test_data/RDI_withBT.dolfyn.log b/examples/data/dolfyn/test_data/RDI_withBT.dolfyn.log index d79f07fc..178650f3 100644 --- a/examples/data/dolfyn/test_data/RDI_withBT.dolfyn.log +++ b/examples/data/dolfyn/test_data/RDI_withBT.dolfyn.log @@ -3,8 +3,9 @@ root - INFO - cfgid0: [7f, 7f] root - INFO - {'nbyte': 579, 'dat_offsets': array([ 20, 79, 144, 282, 352, 422, 492])} root - INFO - pos 20 root - INFO - id 0 offset 20 -root - INFO - Number of cells set to 17 -root - INFO - Cell size set to 1.0 +root - DEBUG - Number of cells set to 17 +root - DEBUG - Cell size set to 1.0 +root - DEBUG - Bin 1 distance set to 2.09 root - INFO - Read Config root - INFO - Read Fixed root - INFO - id 128 offset 79 @@ -13,9 +14,9 @@ root - INFO - id 512 offset 282 root - INFO - id 768 offset 352 root - INFO - id 1024 offset 422 root - INFO - id 1536 offset 492 -root - INFO - Done: {'prog_ver': 51.41, 'inst_model': 'Workhorse', 'beam_angle': 20, 'freq': 600, 'beam_pattern': 'convex', 'orientation': 'down', 'n_beams': 4, 'n_cells': 17, 'pings_per_ensemble': 1, 'cell_size': 1.0, 'blank_dist': 0.88, 'profiling_mode': 1, 'min_corr_threshold': 64, 'n_code_reps': 5, 'min_prcnt_gd': 0, 'max_error_vel': 2.0, 'sec_between_ping_groups': 0.5, 'coord_sys': 'earth', 'use_pitchroll': 'yes', 'use_3beam': 'yes', 'bin_mapping': 'yes', 'heading_misalign_deg': 0.0, 'magnetic_var_deg': 0.0, 'sensors_src': '01111101', 'sensors_avail': '00111101', 'bin1_dist_m': 2.09, 'transmit_pulse_m': 1.18, 'water_ref_cells': [1, 5], 'false_target_threshold': 50, 'transmit_lag_m': 0.24, 'bandwidth': 0, 'power_level': 255, 'serialnum': 18655} -root - INFO - self._bb False -root - INFO - self.cfgbb: {} +root - INFO - Done: {'prog_ver': 51.41, 'inst_make': 'TRDI', 'inst_type': 'ADCP', 'rotate_vars': ['vel'], 'has_imu': 0, 'inst_model': 'Workhorse', 'beam_angle': 20, 'freq': 600, 'beam_pattern': 'convex', 'orientation': 'down', 'n_beams': 4, 'n_cells': 17, 'pings_per_ensemble': 1, 'cell_size': 1.0, 'blank_dist': 0.88, 'profiling_mode': 1, 'min_corr_threshold': 64, 'n_code_reps': 5, 'min_prcnt_gd': 0, 'max_error_vel': 2.0, 'sec_between_ping_groups': 0.5, 'coord_sys': 'earth', 'use_pitchroll': 'yes', 'use_3beam': 'yes', 'bin_mapping': 'yes', 'heading_misalign_deg': 0.0, 'magnetic_var_deg': 0.0, 'sensors_src': '01111101', 'sensors_avail': '00111101', 'bin1_dist_m': 2.09, 'transmit_pulse_m': 1.18, 'water_ref_cells': [1, 5], 'false_target_threshold': 50, 'transmit_lag_m': 0.24, 'bandwidth': 0, 'power_level': 255, 'serialnum': 18655} +root - INFO - self._BB False +root - INFO - self.cfgBB: {} root - INFO - taking data from pings 0 - 1721 root - INFO - 1721 ensembles will be produced. @@ -98,4 +99,3 @@ root - DEBUG - pos: 865, pos_: 0, nbyte: 138, k: 0, byte_offset: -1 root - DEBUG - Trying to Read 512 root - INFO - Reading code 0x200... root - INFO - Read Corr -root - INFO - success! diff --git a/examples/data/dolfyn/test_data/RDI_withBT.nc b/examples/data/dolfyn/test_data/RDI_withBT.nc index 5864a585..4dceb180 100644 Binary files a/examples/data/dolfyn/test_data/RDI_withBT.nc and b/examples/data/dolfyn/test_data/RDI_withBT.nc differ diff --git a/examples/data/dolfyn/test_data/RiverPro_test01.nc b/examples/data/dolfyn/test_data/RiverPro_test01.nc index 719ad610..2eb6a1dd 100644 Binary files a/examples/data/dolfyn/test_data/RiverPro_test01.nc and b/examples/data/dolfyn/test_data/RiverPro_test01.nc differ diff --git a/examples/data/dolfyn/test_data/dat_rdi_bt.mat b/examples/data/dolfyn/test_data/dat_rdi_bt.mat index 6fd6d2ff..fc14b006 100644 Binary files a/examples/data/dolfyn/test_data/dat_rdi_bt.mat and b/examples/data/dolfyn/test_data/dat_rdi_bt.mat differ diff --git a/examples/data/dolfyn/test_data/dat_vm.mat b/examples/data/dolfyn/test_data/dat_vm.mat index 32c263ec..ceaeeb61 100644 Binary files a/examples/data/dolfyn/test_data/dat_vm.mat and b/examples/data/dolfyn/test_data/dat_vm.mat differ diff --git a/examples/data/dolfyn/test_data/vmdas01_wh.nc b/examples/data/dolfyn/test_data/vmdas01_wh.nc index 0ef4a7bc..2185e144 100644 Binary files a/examples/data/dolfyn/test_data/vmdas01_wh.nc and b/examples/data/dolfyn/test_data/vmdas01_wh.nc differ diff --git a/examples/data/dolfyn/test_data/vmdas02_os.nc b/examples/data/dolfyn/test_data/vmdas02_os.nc index e39001da..9661c8e3 100644 Binary files a/examples/data/dolfyn/test_data/vmdas02_os.nc and b/examples/data/dolfyn/test_data/vmdas02_os.nc differ diff --git a/examples/data/dolfyn/test_data/winriver01.nc b/examples/data/dolfyn/test_data/winriver01.nc index b49bc620..81240296 100644 Binary files a/examples/data/dolfyn/test_data/winriver01.nc and b/examples/data/dolfyn/test_data/winriver01.nc differ diff --git a/examples/data/dolfyn/test_data/winriver02.nc b/examples/data/dolfyn/test_data/winriver02.nc index 29541504..7db174ea 100644 Binary files a/examples/data/dolfyn/test_data/winriver02.nc and b/examples/data/dolfyn/test_data/winriver02.nc differ diff --git a/examples/data/dolfyn/test_data/winriver02_rotate_ship2earth.nc b/examples/data/dolfyn/test_data/winriver02_rotate_ship2earth.nc index 5bebdeaf..d3767e0e 100644 Binary files a/examples/data/dolfyn/test_data/winriver02_rotate_ship2earth.nc and b/examples/data/dolfyn/test_data/winriver02_rotate_ship2earth.nc differ diff --git a/examples/data/dolfyn/test_data/winriver02_transect.nc b/examples/data/dolfyn/test_data/winriver02_transect.nc index f6ea6059..3ae5b5ce 100644 Binary files a/examples/data/dolfyn/test_data/winriver02_transect.nc and b/examples/data/dolfyn/test_data/winriver02_transect.nc differ diff --git a/mhkit/acoustics/__init__.py b/mhkit/acoustics/__init__.py index 35cb2b7f..65af875a 100644 --- a/mhkit/acoustics/__init__.py +++ b/mhkit/acoustics/__init__.py @@ -1,17 +1,17 @@ """ The passive acoustics module provides a set of functions -for analyzing and visualizing passive acoustic monitoring +for analyzing and visualizing passive acoustic monitoring data deployed in water bodies. This package reads in raw -*.wav* files and conducts basic acoustics analysis and +*.wav* files and conducts basic acoustics analysis and visualization. To start using the module, import it directly from MHKiT: ``from mhkit import acoustics``. The analysis functions -are available directly from the main import, while the -I/O and graphics submodules are available from +are available directly from the main import, while the +I/O and graphics submodules are available from ``acoustics.io`` and ``acoustics.graphics``, respectively. -The base functions are intended to be used on top of the I/O submodule, and -include functionality to calibrate data, create spectral densities, sound +The base functions are intended to be used on top of the I/O submodule, and +include functionality to calibrate data, create spectral densities, sound pressure levels, and time or band aggregate spectral data. """ diff --git a/mhkit/acoustics/analysis.py b/mhkit/acoustics/analysis.py index bd0e2007..334bd3c9 100644 --- a/mhkit/acoustics/analysis.py +++ b/mhkit/acoustics/analysis.py @@ -337,7 +337,7 @@ def sound_pressure_spectral_density_level(spsd: xr.DataArray) -> xr.DataArray: def _validate_method( - method: Union[str, Dict[str, Union[float, int]]] + method: Union[str, Dict[str, Union[float, int]]], ) -> Tuple[str, Optional[Union[float, int]]]: """ Validates the 'method' parameter and returns the method name and its argument (if any) diff --git a/mhkit/acoustics/graphics.py b/mhkit/acoustics/graphics.py index 888cec83..f56db845 100644 --- a/mhkit/acoustics/graphics.py +++ b/mhkit/acoustics/graphics.py @@ -1,6 +1,6 @@ """ -This submodule provides essential plotting functions for visualizing passive acoustics -data. The functions allow for customizable plotting of sound pressure spectral density +This submodule provides essential plotting functions for visualizing passive acoustics +data. The functions allow for customizable plotting of sound pressure spectral density levels across time and frequency dimensions. Each plotting function leverages the flexibility of Matplotlib, allowing for passthrough @@ -11,12 +11,12 @@ ------------- 1. **plot_spectrogram**: - - Generates a spectrogram plot from sound pressure spectral density level data, + - Generates a spectrogram plot from sound pressure spectral density level data, with a logarithmic frequency scale by default for improved readability of acoustic data. 2. **plot_spectra**: - - Produces a spectral density plot with a log-transformed x-axis, allowing for clear + - Produces a spectral density plot with a log-transformed x-axis, allowing for clear visualization of spectral density across frequency bands. """ diff --git a/mhkit/acoustics/io.py b/mhkit/acoustics/io.py index 5e04b82d..c1743b0e 100644 --- a/mhkit/acoustics/io.py +++ b/mhkit/acoustics/io.py @@ -1,7 +1,7 @@ """ This submodule provides input/output functions for passive acoustics data, focusing on hydrophone recordings stored in WAV files. The main functionality -includes reading and processing hydrophone data from various manufacturers +includes reading and processing hydrophone data from various manufacturers and exporting audio files for easy playback and analysis. Supported Hydrophone Models @@ -14,28 +14,28 @@ 1. **Data Reading**: - - `read_hydrophone`: Main function to read a WAV file from a hydrophone and - convert it to either a voltage or pressure time series, depending on the + - `read_hydrophone`: Main function to read a WAV file from a hydrophone and + convert it to either a voltage or pressure time series, depending on the availability of sensitivity data. - - `read_soundtrap`: Wrapper for reading Ocean Instruments SoundTrap hydrophone + - `read_soundtrap`: Wrapper for reading Ocean Instruments SoundTrap hydrophone files, automatically using appropriate metadata. - - `read_iclisten`: Wrapper for reading Ocean Sonics icListen hydrophone files, - including metadata processing to apply hydrophone sensitivity for direct + - `read_iclisten`: Wrapper for reading Ocean Sonics icListen hydrophone files, + including metadata processing to apply hydrophone sensitivity for direct sound pressure calculation. 2. **Audio Export**: - - `export_audio`: Converts processed sound pressure data back into a WAV file + - `export_audio`: Converts processed sound pressure data back into a WAV file format, with optional gain adjustment to improve playback quality. 3. **Data Extraction**: - - `_read_wav_metadata`: Extracts metadata from a WAV file, including bit depth + - `_read_wav_metadata`: Extracts metadata from a WAV file, including bit depth and other header information. - - `_calculate_voltage_and_time`: Converts raw WAV data into voltage values and + - `_calculate_voltage_and_time`: Converts raw WAV data into voltage values and generates a time index based on the sampling frequency. """ diff --git a/mhkit/dolfyn/adv/clean.py b/mhkit/dolfyn/adv/clean.py index 69a03587..95fba743 100644 --- a/mhkit/dolfyn/adv/clean.py +++ b/mhkit/dolfyn/adv/clean.py @@ -1,5 +1,4 @@ -"""Module containing functions to clean data -""" +"""Module containing functions to clean data""" import warnings import numpy as np diff --git a/mhkit/dolfyn/io/rdi.py b/mhkit/dolfyn/io/rdi.py index 797f3116..9e93a15c 100644 --- a/mhkit/dolfyn/io/rdi.py +++ b/mhkit/dolfyn/io/rdi.py @@ -177,9 +177,11 @@ def __init__( self.n_cells_diff = 0 self.n_cells_sl = 0 self.cs_diff = 0 + self.cs_sl_diff = 0 self.cs = [] + self.cs_sl = [] self.cfg = {} - self.cfgbb = {} + self.cfgBB = {} self.hdr = {} self.f = lib.bin_reader(self.fname) @@ -189,17 +191,17 @@ def __init__( self._npings = self._filesize // space if self._debug_level > -1: logging.info("Done: {}".format(self.cfg)) - logging.info("self._bb {}".format(self._bb)) - logging.info("self.cfgbb: {}".format(self.cfgbb)) + logging.info("self._BB {}".format(self._BB)) + logging.info("self.cfgBB: {}".format(self.cfgBB)) self.f.seek(self._pos, 0) self.n_avg = navg self.ensemble = lib._ensemble(self.n_avg, self.cfg["n_cells"]) - if self._bb: - self.ensembleBB = lib._ensemble(self.n_avg, self.cfgbb["n_cells"]) + if self._BB: + self.ensembleBB = lib._ensemble(self.n_avg, self.cfgBB["n_cells"]) self.vars_read = lib._variable_setlist(["time"]) - if self._bb: + if self._BB: self.vars_readBB = lib._variable_setlist(["time"]) def code_spacing(self, iternum=50): @@ -212,7 +214,7 @@ def code_spacing(self, iternum=50): # Get basic header data and check dual profile if not self.read_hdr(): raise RuntimeError("No header in this file") - self._bb = self.check_for_double_buffer() + self._BB = self.check_for_double_buffer() # Turn off debugging to check code spacing debug_level = self._debug_level @@ -303,21 +305,21 @@ def load_data(self, nens=None): self.remove_end(iens) break self.ensemble.clean_data() - if self._bb: + if self._BB: self.ensembleBB.clean_data() ens = [self.ensemble] vars = [self.vars_read] datl = [self.outd] cfgl = [self.cfg] - if self._bb: + if self._BB: ens += [self.ensembleBB] vars += [self.vars_readBB] datl += [self.outdBB] - cfgl += [self.cfgbb] + cfgl += [self.cfgBB] - for var, en, dat in zip(vars, ens, datl): + for var, en, dat, cfg in zip(vars, ens, datl, cfgl): for nm in var: - dat = self.save_profiles(dat, nm, en, iens) + dat = self.save_profiles(dat, cfg, nm, en, iens) # reset flag after all variables run self.n_cells_diff = 0 @@ -341,50 +343,35 @@ def load_data(self, nens=None): else: dat["coords"]["time"][iens] = np.median(dates) - # Finalize dataset (runs through both nb and bb) + # Finalize dataset (runs through both NB and BB) for dat, cfg in zip(datl, cfgl): dat, cfg = self.cleanup(dat, cfg) - dat = self.finalize(dat) + dat = self.finalize(dat, cfg) if "vel_bt" in dat["data_vars"]: - dat["attrs"]["rotate_vars"].append("vel_bt") + cfg["rotate_vars"].append("vel_bt") - datbb = self.outdBB if self._bb else None - return self.outd, datbb + datbb = self.outdBB if self._BB else None + return dat, datbb def init_data(self): """Initiate data structure""" outd = { "data_vars": {}, "coords": {}, - "attrs": {}, "units": {}, "long_name": {}, "standard_name": {}, "sys": {}, } - outd["attrs"]["inst_make"] = "TRDI" - outd["attrs"]["inst_type"] = "ADCP" - outd["attrs"]["rotate_vars"] = [ - "vel", - ] - # Currently RDI doesn't use IMUs - outd["attrs"]["has_imu"] = 0 - if self._bb: + if self._BB: outdbb = { "data_vars": {}, "coords": {}, - "attrs": {}, "units": {}, "long_name": {}, "standard_name": {}, "sys": {}, } - outdbb["attrs"]["inst_make"] = "TRDI" - outdbb["attrs"]["inst_type"] = "ADCP" - outdbb["attrs"]["rotate_vars"] = [ - "vel", - ] - outdbb["attrs"]["has_imu"] = 0 # Preallocate variables and data sizes for nm in defs.data_defs: @@ -393,10 +380,10 @@ def init_data(self): ) self.outd = outd - if self._bb: + if self._BB: for nm in defs.data_defs: outdbb = lib._idata( - outdbb, nm, sz=lib._get_size(nm, self._nens, self.cfgbb["n_cells"]) + outdbb, nm, sz=lib._get_size(nm, self._nens, self.cfgBB["n_cells"]) ) self.outdBB = outdbb if self._debug_level > 1: @@ -404,14 +391,14 @@ def init_data(self): if self._debug_level > 1: logging.info("{} ncells, not BB".format(self.cfg["n_cells"])) - if self._bb: - logging.info("{} ncells, BB".format(self.cfgbb["n_cells"])) + if self._BB: + logging.info("{} ncells, BB".format(self.cfgBB["n_cells"])) def read_buffer(self): """Read through the file""" fd = self.f self.ensemble.k = -1 # so that k+=1 gives 0 on the first loop. - if self._bb: + if self._BB: self.ensembleBB.k = -1 # so that k+=1 gives 0 on the first loop. self.print_progress() hdr = self.hdr @@ -771,7 +758,7 @@ def remove_end(self, iens): for nm in self.vars_read: lib._setd(dat, nm, lib._get(dat, nm)[..., :iens]) - def save_profiles(self, dat, nm, en, iens): + def save_profiles(self, dat, cfg, nm, en, iens): """ Reformats profile measurements in the retrieved measurements. @@ -782,7 +769,11 @@ def save_profiles(self, dat, nm, en, iens): Parameters ---------- dat : dict - Raw data dictionary + Contains data for the final dataset. This variable has the same pointer + as the data dictionary `self.outd` or `self.outdBB`. + cfg : dict + Global attributes for the final dataset. This variable has the same pointer + as the configuration dictionary `self.cfg` or `self.cfgBB`. nm : str The name of the profile variable en : dict @@ -828,6 +819,9 @@ def save_profiles(self, dat, nm, en, iens): if self.cs_diff: self.cs.append([iens, self.cfg["cell_size"]]) self.cs_diff = 0 + if self.cs_sl_diff: + self.cs_sl.append([iens, self.cfg["cell_size_sl"]]) + self.cs_sl_diff = 0 # Then copy the ensemble to the dataset. ds[..., iens] = bn @@ -846,10 +840,11 @@ def cleanup(self, dat, cfg): Parameters ---------- dat : dict - The dataset dictionary containing data variables and coordinates to be cleaned up. + Contains data for the final dataset. This variable has the same pointer + as the data dictionary `self.outd` or `self.outdBB`. cfg : dict - Configuration dictionary, which is updated with cell size, range, and additional - attributes after cleanup. + Global attributes for the final dataset. This variable has the same pointer + as the configuration dictionary `self.cfg` or `self.cfgBB`. Returns ------- @@ -859,53 +854,69 @@ def cleanup(self, dat, cfg): """ # Clean up changing cell size, if necessary cs = np.array(self.cs, dtype=np.float32) - cell_sizes = cs[:, 1] + cs_sl = np.array(self.cs_sl, dtype=np.float32) # If cell sizes change, depth-bin average the smaller cell sizes if len(self.cs) > 1: - bins_to_merge = cell_sizes.max() / cell_sizes - idx_start = cs[:, 0].astype(int) - idx_end = np.append(cs[1:, 0], self._nens).astype(int) - dv = dat["data_vars"] - for var in dv: - if (len(dv[var].shape) == 3) and ("_sl" not in var): - # Create a new NaN var to save data in - new_var = (np.zeros(dv[var].shape) * np.nan).astype(dv[var].dtype) - # For each cell size change, reshape and bin-average - for id1, id2, b in zip(idx_start, idx_end, bins_to_merge): - array = np.transpose(dv[var][..., id1:id2]) - bin_arr = np.transpose(np.mean(self.reshape(array, b), axis=-1)) - new_var[: len(bin_arr), :, id1:id2] = bin_arr - # Reset data. This often leaves nan data at farther ranges - dv[var] = new_var + self.merge_bins(cs, dv, sl=False) + if len(self.cs_sl) > 1: + dv = dat["data_vars"] + self.merge_bins(cs_sl, dv, sl=True) # Set cell size and range cfg["n_cells"] = self.ensemble["n_cells"] - cfg["cell_size"] = round(cell_sizes.max(), 3) + cfg["cell_size"] = round(cs[:, 1].max(), 3) + bin1_dist = cfg.pop("bin1_dist_m") dat["coords"]["range"] = ( - cfg["bin1_dist_m"] + np.arange(cfg["n_cells"]) * cfg["cell_size"] + bin1_dist + np.arange(cfg["n_cells"]) * cfg["cell_size"] ).astype(np.float32) - - # Save configuration data as attributes - for nm in cfg: - dat["attrs"][nm] = cfg[nm] + cfg["range_offset"] = round(bin1_dist - cfg["blank_dist"] - cfg["cell_size"], 3) # Clean up surface layer profiles if "surface_layer" in cfg: # RiverPro/StreamPro + # Set SL cell size and range + cfg["cell_size_sl"] = round(cs_sl[:, 1].max(), 3) + cfg["n_cells_sl"] = self.n_cells_sl + bin1_dist_sl = cfg.pop("bin1_dist_m_sl") + # Blank distance not recorded + cfg["blank_dist_sl"] = round(bin1_dist_sl - cfg["cell_size_sl"], 3) + # Range offset not added in "bin1_dist_m_sl" for some reason + bin1_dist_sl += cfg["range_offset"] dat["coords"]["range_sl"] = ( - cfg["bin1_dist_m_sl"] - + np.arange(0, self.n_cells_sl) * cfg["cell_size_sl"] + bin1_dist_sl + np.arange(0, self.n_cells_sl) * cfg["cell_size_sl"] ) # Trim off extra nan data dv = dat["data_vars"] for var in dv: if "sl" in var: dv[var] = dv[var][: self.n_cells_sl] - dat["attrs"]["rotate_vars"].append("vel_sl") + cfg["rotate_vars"].append("vel_sl") return dat, cfg + def merge_bins(self, cs, dv, sl=False): + cell_sizes = cs[:, 1] + bins_to_merge = cell_sizes.max() / cell_sizes + idx_start = cs[:, 0].astype(int) + idx_end = np.append(cs[1:, 0], self._nens).astype(int) + + for var in dv: + if not sl: + flag = "_sl" not in var + elif sl: + flag = "_sl" in var + if (len(dv[var].shape) == 3) and flag: + # Create a new NaN var to save data in + new_var = (np.zeros(dv[var].shape) * np.nan).astype(dv[var].dtype) + # For each cell size change, reshape and bin-average + for id1, id2, b in zip(idx_start, idx_end, bins_to_merge): + array = np.transpose(dv[var][..., id1:id2]) + bin_arr = np.transpose(np.mean(self.reshape(array, b), axis=-1)) + new_var[: len(bin_arr), :, id1:id2] = bin_arr + # Reset data. This often leaves nan data at farther ranges + dv[var] = new_var + def reshape(self, arr, n_bin=None): """ Reshapes the input array `arr` to a shape of (..., n, n_bin). @@ -949,7 +960,7 @@ def reshape(self, arr, n_bin=None): return out - def finalize(self, dat): + def finalize(self, dat, cfg): """ This method cleans up the dataset by removing any attributes that were defined but not loaded, updates configuration attributes, and sets the @@ -959,28 +970,32 @@ def finalize(self, dat): Parameters ---------- dat : dict - The dataset dictionary to be finalized. This dictionary is modified - in place by removing unused attributes, setting configuration values - as attributes, and calculating `fs`. + Contains data for the final dataset. This variable has the same pointer + as the data dictionary `self.outd` or `self.outdBB`. + cfg : dict + Global attributes for the final dataset. This variable has the same pointer + as the configuration dictionary `self.cfg` or `self.cfgBB`. Returns ------- dict The finalized dataset dictionary with cleaned attributes and added metadata. """ + + # Drop empty data variables for nm in set(defs.data_defs.keys()) - self.vars_read: lib._pop(dat, nm) - for nm in self.cfg: - dat["attrs"][nm] = self.cfg[nm] # VMDAS and WinRiver have different set sampling frequency - da = dat["attrs"] - if ("sourceprog" in da) and ( - da["sourceprog"].lower() in ["vmdas", "winriver", "winriver2"] + if ("sourceprog" in cfg) and ( + cfg["sourceprog"].lower() in ["vmdas", "winriver", "winriver2"] ): - da["fs"] = round(1 / np.median(np.diff(dat["coords"]["time"])), 2) + cfg["fs"] = round(1 / np.median(np.diff(dat["coords"]["time"])), 2) else: - da["fs"] = 1 / (da["sec_between_ping_groups"] * da["pings_per_ensemble"]) + cfg["fs"] = 1 / (cfg["sec_between_ping_groups"] * cfg["pings_per_ensemble"]) + + # Save configuration data as attributes + dat["attrs"] = cfg for nm in defs.data_defs: shp = defs.data_defs[nm][0] diff --git a/mhkit/dolfyn/io/rdi_defs.py b/mhkit/dolfyn/io/rdi_defs.py index addbb3ea..86d66ced 100644 --- a/mhkit/dolfyn/io/rdi_defs.py +++ b/mhkit/dolfyn/io/rdi_defs.py @@ -101,7 +101,7 @@ "pitch_std": ([], "data_vars", "float32", "degree", "Pitch Standard Deviation", ""), "roll_std": ([], "data_vars", "float32", "degree", "Roll Standard Deviation", ""), "adc": ([8], "sys", "uint8", "1", "Analog-Digital Converter Output", ""), - "error_status": ([], "attrs", "float32", "1", "Error Status", ""), + "error_status": ([], "sys", "float32", "1", "Error Status", ""), "pressure": ([], "data_vars", "float32", "dbar", "Pressure", "sea_water_pressure"), "pressure_std": ( [], @@ -373,6 +373,11 @@ def read_cfgseg(rdr, bb=False): tmp = fd.read_ui8(5) prog_ver0 = tmp[0] cfg["prog_ver"] = float(tmp[0] + tmp[1] * 0.01) + cfg["inst_make"] = "TRDI" + cfg["inst_type"] = "ADCP" + cfg["rotate_vars"] = ["vel"] + # Currently RDI doesn't use IMUs + cfg["has_imu"] = 0 cfg["inst_model"] = adcp_type.get(tmp[0], "unrecognized instrument") config = tmp[2:4] cfg["beam_angle"] = [15, 20, 30, [0, 25][int(tmp[0] in [11, 47, 66])]][ @@ -400,7 +405,7 @@ def read_cfgseg(rdr, bb=False): if ("n_cells" not in cfg) or (n_cells != cfg["n_cells"]): cfg["n_cells"] = n_cells if rdr._debug_level > 0: - logging.info(f"Number of cells set to {cfg['n_cells']}") + logging.debug(f"Number of cells set to {n_cells}") cfg["pings_per_ensemble"] = fd.read_ui16(1) # Check if cell size has changed cs = float(fd.read_ui16(1) * 0.01) @@ -408,7 +413,7 @@ def read_cfgseg(rdr, bb=False): rdr.cs_diff = cs if "cell_size" not in cfg else (cs - cfg["cell_size"]) cfg["cell_size"] = cs if rdr._debug_level > 0: - logging.info(f"Cell size set to {cfg['cell_size']}") + logging.debug(f"Cell size set to {cs}") cfg["blank_dist"] = round(float(fd.read_ui16(1) * 0.01), 2) cfg["profiling_mode"] = fd.read_ui8(1) cfg["min_corr_threshold"] = fd.read_ui8(1) @@ -427,7 +432,13 @@ def read_cfgseg(rdr, bb=False): cfg["magnetic_var_deg"] = float(fd.read_i16(1) * 0.01) cfg["sensors_src"] = np.binary_repr(fd.read_ui8(1), 8) cfg["sensors_avail"] = np.binary_repr(fd.read_ui8(1), 8) - cfg["bin1_dist_m"] = round(float(fd.read_ui16(1) * 0.01), 4) + # If cell size changes, the bin1 distance will too + # We only want to save the largest, as we depth average smaller cells together + b1d = round(float(fd.read_ui16(1) * 0.01), 4) + if ("bin1_dist_m" not in cfg) or (b1d > cfg["bin1_dist_m"]): + cfg["bin1_dist_m"] = b1d + if rdr._debug_level > 0: + logging.debug(f"Bin 1 distance set to {b1d}") cfg["transmit_pulse_m"] = round(float(fd.read_ui16(1) * 0.01), 2) cfg["water_ref_cells"] = list(fd.read_ui8(2).astype(list)) # list for attrs cfg["false_target_threshold"] = fd.read_ui8(1) @@ -479,18 +490,29 @@ def read_fixed_sl(rdr): """Read surface layer fixed header""" cfg = rdr.cfg cfg["surface_layer"] = 1 - n_cells = rdr.f.read_ui8(1) # Check if n_cells is greater than what was used in prior profiles - if n_cells > rdr.n_cells_sl: - rdr.n_cells_sl = n_cells + n_cells_sl = rdr.f.read_ui8(1) + if n_cells_sl > rdr.n_cells_sl: + rdr.n_cells_sl = n_cells_sl + if ("n_cells_sl" not in cfg) or (n_cells_sl != cfg["n_cells_sl"]): + cfg["n_cells_sl"] = n_cells_sl if rdr._debug_level > 0: - logging.warning( - f"Maximum number of surface layer cells increased to {n_cells}" - ) - cfg["n_cells_sl"] = n_cells - # Assuming surface layer profile cell size never changes - cfg["cell_size_sl"] = float(rdr.f.read_ui16(1) * 0.01) - cfg["bin1_dist_m_sl"] = round(float(rdr.f.read_ui16(1) * 0.01), 4) + logging.debug(f"Number of surface cells set to {n_cells_sl}") + # Cell size also changes + cs_sl = float(rdr.f.read_ui16(1) * 0.01) + if ("cell_size_sl" not in cfg) or (cs_sl != cfg["cell_size_sl"]): + rdr.cs_sl_diff = ( + cs_sl if "cell_size_sl" not in cfg else (cs_sl - cfg["cell_size_sl"]) + ) + cfg["cell_size_sl"] = cs_sl + if rdr._debug_level > 0: + logging.debug(f"Surface layer cell size set to {cs_sl}") + # Only save maximum bin 1 distance + b1d = round(float(rdr.f.read_ui16(1) * 0.01), 4) + if ("bin1_dist_m_sl" not in cfg) or (b1d > cfg["bin1_dist_m_sl"]): + cfg["bin1_dist_m_sl"] = b1d + if rdr._debug_level > 0: + logging.debug(f"Surface layer Bin 1 distance set to {b1d}") if rdr._debug_level > -1: logging.info("Read Surface Layer Config") diff --git a/mhkit/loads/__init__.py b/mhkit/loads/__init__.py index 4c21c739..1016f49c 100644 --- a/mhkit/loads/__init__.py +++ b/mhkit/loads/__init__.py @@ -1,7 +1,7 @@ """ The `loads` package of the MHKiT (Marine and Hydrokinetic Toolkit) library provides tools and functionalities for analyzing and visualizing loads data -from marine and hydrokinetic (MHK) devices. This package is designed to +from marine and hydrokinetic (MHK) devices. This package is designed to assist engineers, researchers, and analysts in understanding the forces and stresses applied to MHK devices under various operational and environmental conditions. diff --git a/mhkit/loads/extreme/__init__.py b/mhkit/loads/extreme/__init__.py index 318a2cdc..f5ac42ae 100644 --- a/mhkit/loads/extreme/__init__.py +++ b/mhkit/loads/extreme/__init__.py @@ -3,7 +3,7 @@ and wave data statistics. It includes methods for calculating peaks over threshold, estimating -short-term extreme distributions,and performing wave amplitude +short-term extreme distributions,and performing wave amplitude normalization for most likely extreme response analysis. """ diff --git a/mhkit/loads/extreme/extremes.py b/mhkit/loads/extreme/extremes.py index 81353127..6a5831b9 100644 --- a/mhkit/loads/extreme/extremes.py +++ b/mhkit/loads/extreme/extremes.py @@ -1,29 +1,29 @@ """ This module provides functionality for estimating the short-term and -long-term extreme distributions of responses in a time series. It -includes methods for analyzing peaks, block maxima, and applying -statistical distributions to model extreme events. The module supports -various methods for short-term extreme estimation, including peaks -fitting with Weibull, tail fitting, peaks over threshold, and block -maxima methods with GEV (Generalized Extreme Value) and Gumbel -distributions. Additionally, it offers functionality to approximate -the long-term extreme distribution by weighting short-term extremes +long-term extreme distributions of responses in a time series. It +includes methods for analyzing peaks, block maxima, and applying +statistical distributions to model extreme events. The module supports +various methods for short-term extreme estimation, including peaks +fitting with Weibull, tail fitting, peaks over threshold, and block +maxima methods with GEV (Generalized Extreme Value) and Gumbel +distributions. Additionally, it offers functionality to approximate +the long-term extreme distribution by weighting short-term extremes across different sea states. Functions: -- ste_peaks: Estimates the short-term extreme distribution from peaks +- ste_peaks: Estimates the short-term extreme distribution from peaks distribution using specified statistical methods. - block_maxima: Finds the block maxima in a time-series data to be used in block maxima methods. -- ste_block_maxima_gev: Approximates the short-term extreme distribution +- ste_block_maxima_gev: Approximates the short-term extreme distribution using the block maxima method with the GEV distribution. -- ste_block_maxima_gumbel: Approximates the short-term extreme +- ste_block_maxima_gumbel: Approximates the short-term extreme distribution using the block maxima method with the Gumbel distribution. -- ste: Alias for `short_term_extreme`, facilitating easier access to the +- ste: Alias for `short_term_extreme`, facilitating easier access to the primary functionality of estimating short-term extremes. -- short_term_extreme: Core function to approximate the short-term extreme +- short_term_extreme: Core function to approximate the short-term extreme distribution from a time series using chosen methods. -- full_seastate_long_term_extreme: Combines short-term extreme +- full_seastate_long_term_extreme: Combines short-term extreme distributions using weights to estimate the long-term extreme distribution. """ diff --git a/mhkit/loads/extreme/mler.py b/mhkit/loads/extreme/mler.py index f77f7d88..63ecb8b4 100644 --- a/mhkit/loads/extreme/mler.py +++ b/mhkit/loads/extreme/mler.py @@ -1,5 +1,5 @@ """ -This module provides functionalities to calculate and analyze Most +This module provides functionalities to calculate and analyze Most Likely Extreme Response (MLER) coefficients for wave energy converter design and risk assessment. It includes functions to: @@ -7,10 +7,10 @@ spectrum and a response Amplitude Response Operator (ARO). - Define and manipulate simulation parameters (`mler_simulation`) used across various MLER analyses. - - Renormalize the incoming amplitude of the MLER wave + - Renormalize the incoming amplitude of the MLER wave (`mler_wave_amp_normalize`) to match the desired peak height for more accurate modeling and analysis. - - Export the wave amplitude time series (`mler_export_time_series`) + - Export the wave amplitude time series (`mler_export_time_series`) based on the calculated MLER coefficients for further analysis or visualization. """ diff --git a/mhkit/loads/extreme/peaks.py b/mhkit/loads/extreme/peaks.py index cd2c1164..9b31bb33 100644 --- a/mhkit/loads/extreme/peaks.py +++ b/mhkit/loads/extreme/peaks.py @@ -1,15 +1,15 @@ """ This module provides utilities for analyzing wave data, specifically for identifying significant wave heights and estimating wave peak -distributions using statistical methods. +distributions using statistical methods. Functions: -- _calculate_window_size: Calculates the window size for peak +- _calculate_window_size: Calculates the window size for peak independence using the auto-correlation function of wave peaks. -- _peaks_over_threshold: Identifies peaks over a specified +- _peaks_over_threshold: Identifies peaks over a specified threshold and returns independent storm peak values adjusted by the threshold. -- global_peaks: Identifies global peaks in a zero-centered +- global_peaks: Identifies global peaks in a zero-centered response time-series based on consecutive zero up-crossings. - number_of_short_term_peaks: Estimates the number of peaks within a specified short-term period. @@ -20,13 +20,13 @@ - automatic_hs_threshold: Determines the best significant wave height threshold for the peaks-over-threshold method. - peaks_distribution_peaks_over_threshold: Estimates the peaks - distribution using the peaks over threshold method by fitting a + distribution using the peaks over threshold method by fitting a generalized Pareto distribution. References: -- Neary, V. S., S. Ahn, B. E. Seng, M. N. Allahdadi, T. Wang, Z. Yang, - and R. He (2020). "Characterization of Extreme Wave Conditions for - Wave Energy Converter Design and Project Risk Assessment.” J. Mar. +- Neary, V. S., S. Ahn, B. E. Seng, M. N. Allahdadi, T. Wang, Z. Yang, + and R. He (2020). "Characterization of Extreme Wave Conditions for + Wave Energy Converter Design and Project Risk Assessment.” J. Mar. Sci. Eng. 2020, 8(4), 289; https://doi.org/10.3390/jmse8040289. """ diff --git a/mhkit/loads/extreme/sample.py b/mhkit/loads/extreme/sample.py index 3da0377d..078b0521 100644 --- a/mhkit/loads/extreme/sample.py +++ b/mhkit/loads/extreme/sample.py @@ -2,10 +2,10 @@ This module provides statistical analysis tools for extreme value analysis in environmental and engineering applications. It focuses on estimating values corresponding to specific return periods based on -the statistical distribution of observed or simulated data. +the statistical distribution of observed or simulated data. Functionality: -- return_year_value: Calculates the value from a given distribution +- return_year_value: Calculates the value from a given distribution corresponding to a specified return year. This function is particularly useful for determining design values for engineering structures or for risk assessment in environmental studies. diff --git a/mhkit/loads/general.py b/mhkit/loads/general.py index 11973144..75646919 100644 --- a/mhkit/loads/general.py +++ b/mhkit/loads/general.py @@ -2,7 +2,7 @@ This module provides tools for analyzing and processing data signals related to turbine blade performance and fatigue analysis. It implements methodologies based on standards such as IEC TS 62600-3:2020 ED1, -incorporating statistical binning, moment calculations, and fatigue +incorporating statistical binning, moment calculations, and fatigue damage estimation using the rainflow counting algorithm. Key functionalities include: @@ -11,8 +11,8 @@ for each bin, following IEC TS 62600-3:2020 ED1 guidelines. It supports output in both pandas DataFrame and xarray Dataset formats. - - `blade_moments`: Calculates the flapwise and edgewise moments of turbine - blades using derived calibration coefficients and raw strain signals. + - `blade_moments`: Calculates the flapwise and edgewise moments of turbine + blades using derived calibration coefficients and raw strain signals. This function is crucial for understanding the loading and performance characteristics of turbine blades. diff --git a/mhkit/loads/graphics.py b/mhkit/loads/graphics.py index 9cd835b8..c458e8d9 100644 --- a/mhkit/loads/graphics.py +++ b/mhkit/loads/graphics.py @@ -1,6 +1,6 @@ """ This module provides functionalities for plotting statistical data -related to a given variable or dataset. +related to a given variable or dataset. - `plot_statistics` is designed to plot raw statistical measures (mean, maximum, minimum, and optional standard deviation) of a @@ -9,8 +9,8 @@ - `plot_bin_statistics` extends these capabilities to binned data, offering a way to visualize binned statistics (mean, maximum, minimum) - along with their respective standard deviations. This function also - supports label and title customization, as well as saving the plot to + along with their respective standard deviations. This function also + supports label and title customization, as well as saving the plot to a specified path. """ diff --git a/mhkit/mooring/graphics.py b/mhkit/mooring/graphics.py index 0ba9bd52..6298e546 100644 --- a/mhkit/mooring/graphics.py +++ b/mhkit/mooring/graphics.py @@ -1,20 +1,20 @@ """ -This module provides a function for creating animated visualizations of a -MoorDyn node position dataset using the matplotlib animation API. +This module provides a function for creating animated visualizations of a +MoorDyn node position dataset using the matplotlib animation API. -It includes the main function `animate`, which creates either 2D or 3D -animations depending on the input parameters. +It includes the main function `animate`, which creates either 2D or 3D +animations depending on the input parameters. -In the animations, the position of nodes in the MoorDyn dataset are plotted -over time, allowing the user to visualize how these positions change. +In the animations, the position of nodes in the MoorDyn dataset are plotted +over time, allowing the user to visualize how these positions change. -This module also includes several helper functions that are used by -`animate` to validate inputs, generate lists of nodes along each axis, -calculate plot limits, and set labels and titles for plots. +This module also includes several helper functions that are used by +`animate` to validate inputs, generate lists of nodes along each axis, +calculate plot limits, and set labels and titles for plots. -The user can specify various parameters for the animation such as the -dimension (2D or 3D), the axes to plot along, the plot limits for each -axis, the interval between frames, whether the animation repeats, and the +The user can specify various parameters for the animation such as the +dimension (2D or 3D), the axes to plot along, the plot limits for each +axis, the interval between frames, whether the animation repeats, and the labels and title for the plot. Requires: diff --git a/mhkit/mooring/io.py b/mhkit/mooring/io.py index 85a3e222..f608e467 100644 --- a/mhkit/mooring/io.py +++ b/mhkit/mooring/io.py @@ -2,12 +2,12 @@ This module provides functions to read and parse MoorDyn output files. The main function read_moordyn takes as input the path to a MoorDyn output file and optionally -the path to a MoorDyn input file. It reads the data from the output file, stores it in an -xarray dataset, and then if provided, parses the input file for additional metadata to store +the path to a MoorDyn input file. It reads the data from the output file, stores it in an +xarray dataset, and then if provided, parses the input file for additional metadata to store as attributes in the dataset. -The helper function _moordyn_input is used to parse the MoorDyn output file. It loops through -each line in the output file, parses various sets of properties and parameters, and stores +The helper function _moordyn_input is used to parse the MoorDyn output file. It loops through +each line in the output file, parses various sets of properties and parameters, and stores them as attributes in the provided dataset. Typical usage example: diff --git a/mhkit/power/characteristics.py b/mhkit/power/characteristics.py index 0ae45a78..24f80713 100644 --- a/mhkit/power/characteristics.py +++ b/mhkit/power/characteristics.py @@ -1,21 +1,21 @@ """ -This module contains functions for calculating electrical power metrics from -measured voltage and current data. It supports both direct current (DC) and -alternating current (AC) calculations, including instantaneous frequency -analysis for AC signals and power calculations for three-phase AC systems. -The calculations can accommodate both line-to-neutral and line-to-line voltage -measurements and offer flexibility in output formats, allowing results to be +This module contains functions for calculating electrical power metrics from +measured voltage and current data. It supports both direct current (DC) and +alternating current (AC) calculations, including instantaneous frequency +analysis for AC signals and power calculations for three-phase AC systems. +The calculations can accommodate both line-to-neutral and line-to-line voltage +measurements and offer flexibility in output formats, allowing results to be saved as either pandas DataFrames or xarray Datasets. Functions: instantaneous_frequency: Calculates the instantaneous frequency of a measured voltage signal over time. - + dc_power: Computes the DC power from voltage and current measurements, providing both individual channel outputs and a gross power calculation. - + ac_power_three_phase: Calculates the magnitude of active AC power for three-phase - systems, considering the power factor and voltage measurement configuration + systems, considering the power factor and voltage measurement configuration (line-to-neutral or line-to-line). """ diff --git a/mhkit/power/quality.py b/mhkit/power/quality.py index 3e020f7a..9d5ce00e 100644 --- a/mhkit/power/quality.py +++ b/mhkit/power/quality.py @@ -1,31 +1,31 @@ """ -This module contains functions for calculating various aspects of power quality, -particularly focusing on the analysis of harmonics, interharmonics and distortion -in electrical power systems. These functions are designed to assist in power -quality assessments by providing tools to analyze voltage and current signals -for their harmonic and interharmonic components based on the guidelines and methodologies +This module contains functions for calculating various aspects of power quality, +particularly focusing on the analysis of harmonics, interharmonics and distortion +in electrical power systems. These functions are designed to assist in power +quality assessments by providing tools to analyze voltage and current signals +for their harmonic and interharmonic components based on the guidelines and methodologies outlined in IEC 61000-4-7:2008 ED2 and in IEC 62600-30:2018 ED1. Functions in this module include: -- harmonics: Calculates the harmonics from time series of voltage or current. - This function returns the amplitude of the time-series data harmonics indexed by - the harmonic frequency, aiding in the identification of harmonic distortions +- harmonics: Calculates the harmonics from time series of voltage or current. + This function returns the amplitude of the time-series data harmonics indexed by + the harmonic frequency, aiding in the identification of harmonic distortions within the power system. -- harmonic_subgroups: Computes the harmonic subgroups as per IEC 61000-4-7 standards. - Harmonic subgroups provide insights into the distribution of power across - different harmonic frequencies, which is crucial for understanding the behavior +- harmonic_subgroups: Computes the harmonic subgroups as per IEC 61000-4-7 standards. + Harmonic subgroups provide insights into the distribution of power across + different harmonic frequencies, which is crucial for understanding the behavior of non-linear loads and their impact on the power quality. -- total_harmonic_current_distortion (THCD): Determines the total harmonic current - distortion, offering a summary metric that quantifies the overall level of - harmonic distortion present in the current waveform. This metric is essential +- total_harmonic_current_distortion (THCD): Determines the total harmonic current + distortion, offering a summary metric that quantifies the overall level of + harmonic distortion present in the current waveform. This metric is essential for assessing compliance with power quality standards and guidelines. -- interharmonics: Identifies and calculates the interharmonics present in the - power system. Interharmonics, which are frequencies that occur between the - fundamental and harmonic frequencies, can arise from various sources and +- interharmonics: Identifies and calculates the interharmonics present in the + power system. Interharmonics, which are frequencies that occur between the + fundamental and harmonic frequencies, can arise from various sources and potentially lead to power quality issues. """ diff --git a/mhkit/tests/utils/test_cache.py b/mhkit/tests/utils/test_cache.py index 3cd5fff4..b7bd85e8 100644 --- a/mhkit/tests/utils/test_cache.py +++ b/mhkit/tests/utils/test_cache.py @@ -1,8 +1,8 @@ """ Unit Testing for MHKiT Cache Utilities -This module provides unit tests for the caching utilities present in the MHKiT library. -These utilities help in caching and retrieving data, ensuring efficient and repeatable +This module provides unit tests for the caching utilities present in the MHKiT library. +These utilities help in caching and retrieving data, ensuring efficient and repeatable data access without redundant computations or network requests. The tests cover: @@ -11,7 +11,7 @@ 3. Usage of appropriate file extensions based on the type of data being cached. 4. Clearing of cache directories as specified. -By running these tests, one can validate that the caching utilities of MHKiT are functioning +By running these tests, one can validate that the caching utilities of MHKiT are functioning as expected, ensuring that users can rely on cached data and metadata when using the MHKiT library. Usage: diff --git a/mhkit/tests/wave/io/hindcast/test_hindcast.py b/mhkit/tests/wave/io/hindcast/test_hindcast.py index 26847dc6..456ea05b 100644 --- a/mhkit/tests/wave/io/hindcast/test_hindcast.py +++ b/mhkit/tests/wave/io/hindcast/test_hindcast.py @@ -1,17 +1,17 @@ """ -This module contains unit tests for the WPTO hindcast data retrieval +This module contains unit tests for the WPTO hindcast data retrieval functions in the mhkit.wave package. The tests are designed to verify the correct functioning of the following functionalities: 1. Retrieval of multiple years of data for a single data type, latitude-longitude pair, and parameter. -2. Retrieval of multiple parameters for a single data type, year, +2. Retrieval of multiple parameters for a single data type, year, and latitude-longitude pair. 3. Retrieval of data for multiple locations for point data and directional spectrum at a single data type, year, and parameter. -The tests use the unittest framework and compare the output of the -hindcast retrieval functions with expected output data. The expected +The tests use the unittest framework and compare the output of the +hindcast retrieval functions with expected output data. The expected data is read from CSV files located in the examples/data/wave directory. Functions tested: @@ -19,7 +19,7 @@ - wave.io.hindcast.hindcast.request_wpto_directional_spectrum Usage: -Run the script directly as a standalone program, or import the +Run the script directly as a standalone program, or import the TestWPTOhindcast class in another test suite. """ diff --git a/mhkit/utils/__init__.py b/mhkit/utils/__init__.py index 328a3320..c89a6430 100644 --- a/mhkit/utils/__init__.py +++ b/mhkit/utils/__init__.py @@ -1,6 +1,6 @@ """ -This module initializes and imports the essential utility functions for data -conversion, statistical analysis, caching, and event detection for the +This module initializes and imports the essential utility functions for data +conversion, statistical analysis, caching, and event detection for the MHKiT library. """ diff --git a/mhkit/utils/cache.py b/mhkit/utils/cache.py index eadfe2ec..c4897c12 100644 --- a/mhkit/utils/cache.py +++ b/mhkit/utils/cache.py @@ -1,28 +1,28 @@ """ This module provides functionality for managing cache files to optimize network requests and computations for handling data. The module focuses -on enabling users to read from and write to cache files, as well as -perform cache clearing operations. Cache files are utilized to store data -temporarily, mitigating the need to re-fetch or recompute the same data multiple +on enabling users to read from and write to cache files, as well as +perform cache clearing operations. Cache files are utilized to store data +temporarily, mitigating the need to re-fetch or recompute the same data multiple times, which can be especially useful in network-dependent tasks. The module consists of two main functions: 1. `handle_caching`: - This function manages the caching of data. It provides options to read from - and write to cache files, depending on whether the data is already provided - or if it needs to be fetched from the cache. If a cache file corresponding - to the given parameters already exists, the function can either load data - from it or clear it based on the parameters passed. It also offers the ability - to store associated metadata along with the data and supports both JSON and - pickle file formats for caching. This function returns the loaded data and + This function manages the caching of data. It provides options to read from + and write to cache files, depending on whether the data is already provided + or if it needs to be fetched from the cache. If a cache file corresponding + to the given parameters already exists, the function can either load data + from it or clear it based on the parameters passed. It also offers the ability + to store associated metadata along with the data and supports both JSON and + pickle file formats for caching. This function returns the loaded data and metadata from the cache file, along with the cache file path. 2. `clear_cache`: - This function enables the clearing of either specific sub-directories or the - entire cache directory, depending on the parameter passed. It removes the - specified directory and then recreates it to ensure future caching tasks can - be executed without any issues. If the specified directory does not exist, + This function enables the clearing of either specific sub-directories or the + entire cache directory, depending on the parameter passed. It removes the + specified directory and then recreates it to ensure future caching tasks can + be executed without any issues. If the specified directory does not exist, the function prints an indicative message. Module Dependencies: diff --git a/mhkit/utils/stat_utils.py b/mhkit/utils/stat_utils.py index 972a84f2..e6cea5c9 100644 --- a/mhkit/utils/stat_utils.py +++ b/mhkit/utils/stat_utils.py @@ -1,9 +1,9 @@ """ -This module contains functions to perform various statistical calculations +This module contains functions to perform various statistical calculations on continuous data. It includes functions for calculating statistics such as mean, max, min, and standard deviation over specific windows, as well as functions -for vector/directional statistics. The module also provides utility functions -to unwrap vectors, compute magnitudes and phases in 2D/3D, and calculate +for vector/directional statistics. The module also provides utility functions +to unwrap vectors, compute magnitudes and phases in 2D/3D, and calculate the root mean squared values of vector components. Functions: @@ -144,7 +144,7 @@ def get_statistics( def vector_statistics( - data: Union[pd.Series, np.ndarray, list] + data: Union[pd.Series, np.ndarray, list], ) -> Tuple[np.ndarray, np.ndarray]: """ Function used to calculate statistics for vector/directional channels based on diff --git a/mhkit/utils/time_utils.py b/mhkit/utils/time_utils.py index 3eb69f7e..a30bd455 100644 --- a/mhkit/utils/time_utils.py +++ b/mhkit/utils/time_utils.py @@ -18,7 +18,7 @@ def matlab_to_datetime( - matlab_datenum: Union[np.ndarray, list, float, int] + matlab_datenum: Union[np.ndarray, list, float, int], ) -> pd.DatetimeIndex: """ Convert MATLAB datenum format to Python datetime @@ -55,7 +55,7 @@ def matlab_to_datetime( def excel_to_datetime( - excel_num: Union[np.ndarray, list, float, int] + excel_num: Union[np.ndarray, list, float, int], ) -> pd.DatetimeIndex: """ Convert Excel datenum format to Python datetime diff --git a/mhkit/utils/type_handling.py b/mhkit/utils/type_handling.py index 09ad5cca..b58fee52 100644 --- a/mhkit/utils/type_handling.py +++ b/mhkit/utils/type_handling.py @@ -1,7 +1,7 @@ """ This module provides utility functions for converting various data types to xarray structures such as xarray.DataArray and xarray.Dataset. It also -includes functions for handling nested dictionaries containing pandas +includes functions for handling nested dictionaries containing pandas DataFrames by converting them to xarray Datasets. Functions: @@ -9,7 +9,7 @@ - to_numeric_array: Converts input data to a numeric NumPy array. - convert_to_dataset: Converts pandas or xarray data structures to xarray.Dataset. - convert_to_dataarray: Converts various data types to xarray.DataArray. -- convert_nested_dict_and_pandas: Recursively converts pandas DataFrames +- convert_nested_dict_and_pandas: Recursively converts pandas DataFrames in nested dictionaries to xarray Datasets. """ @@ -237,7 +237,7 @@ def convert_to_dataarray( def convert_nested_dict_and_pandas( - data: Dict[str, Union[pd.DataFrame, Dict[str, Any]]] + data: Dict[str, Union[pd.DataFrame, Dict[str, Any]]], ) -> Dict[str, Union[xr.Dataset, Dict[str, Any]]]: """ Recursively searches inside nested dictionaries for pandas DataFrames to diff --git a/mhkit/utils/upcrossing.py b/mhkit/utils/upcrossing.py index 1c5eea03..7ab06a0e 100644 --- a/mhkit/utils/upcrossing.py +++ b/mhkit/utils/upcrossing.py @@ -1,7 +1,7 @@ """ Upcrossing Analysis Functions ============================= -This module contains a collection of functions that facilitate upcrossing +This module contains a collection of functions that facilitate upcrossing analyses. Key Functions: @@ -12,8 +12,8 @@ - `heights`: Calculates the height between zero crossings. - `periods`: Calculates the period between zero crossings. - `custom`: Applies a custom, user-defined function between zero crossings. - -Author: + +Author: ------- mbruggs akeeste diff --git a/mhkit/wave/io/hindcast/wind_toolkit.py b/mhkit/wave/io/hindcast/wind_toolkit.py index 2205e2be..7eeead3d 100644 --- a/mhkit/wave/io/hindcast/wind_toolkit.py +++ b/mhkit/wave/io/hindcast/wind_toolkit.py @@ -2,27 +2,27 @@ Wind Toolkit Data Utility Functions =================================== -This module contains a collection of utility functions designed to facilitate -the extraction, caching, and visualization of wind data from the WIND Toolkit -hindcast dataset hosted on AWS. This dataset includes offshore wind hindcast data +This module contains a collection of utility functions designed to facilitate +the extraction, caching, and visualization of wind data from the WIND Toolkit +hindcast dataset hosted on AWS. This dataset includes offshore wind hindcast data with various parameters like wind speed, direction, temperature, and pressure. Key Functions: -------------- -- `region_selection`: Determines which predefined wind region a given latitude +- `region_selection`: Determines which predefined wind region a given latitude and longitude fall within. - -- `get_region_data`: Retrieves latitude and longitude data points for a specified + +- `get_region_data`: Retrieves latitude and longitude data points for a specified wind region. Uses caching to speed up repeated requests. - -- `plot_region`: Plots the geographical extent of a specified wind region and + +- `plot_region`: Plots the geographical extent of a specified wind region and can overlay a given latitude-longitude point. - -- `elevation_to_string`: Converts a parameter (e.g., 'windspeed') and elevation + +- `elevation_to_string`: Converts a parameter (e.g., 'windspeed') and elevation values (e.g., [20, 40, 120]) to the formatted strings used in the WIND Toolkit. - -- `request_wtk_point_data`: Fetches specified wind data parameters for given - latitude-longitude points and years from the WIND Toolkit hindcast dataset. + +- `request_wtk_point_data`: Fetches specified wind data parameters for given + latitude-longitude points and years from the WIND Toolkit hindcast dataset. Supports caching for faster repeated data retrieval. Dependencies: @@ -34,15 +34,15 @@ Notes: ------ -- To access the WIND Toolkit hindcast data, users need to configure `h5pyd` +- To access the WIND Toolkit hindcast data, users need to configure `h5pyd` for data access on HSDS (see the metocean_example or WPTO_hindcast_example notebook for more details). - -- While some functions perform basic checks (e.g., verifying that latitude - and longitude are within a predefined region), it's essential to understand + +- While some functions perform basic checks (e.g., verifying that latitude + and longitude are within a predefined region), it's essential to understand the boundaries of each region and the available parameters and elevations in the dataset. -Author: +Author: ------- akeeste ssolson