Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
//"args": ["-v", "2", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4"]
//"args": ["-v", "2", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--plot_time", "/longitude_time"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--plot_universals"]
"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--plot_time", "/latitude_time"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--plot_time", "/latitude_time"]
// brizo 20250916T230652 has several ESP Samples from stoqs_lrauv_sep2025
//"args": ["-v", "2", "--log_file", "brizo/missionlogs/2025/20250916_20250922/20250916T230652/202509162306_202509180305.nc4", "--plot_time", "/longitude_time"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109_cleaned_by_quinn.nc4", "--plot_time", "/longitude_time"]
Expand All @@ -69,6 +69,9 @@
//"args": ["-v", "1", "--log_file", "tethys/missionlogs/2012/20120908_20120920/20120917T025522/201209170255_201209171110.nc4", "--plot_time", "/longitude_time"]
// Single spike in longitude_time
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--plot_time", "/NAL9602/longitude_fix_time"]
// Fails in nudge_positions - bad time removed by outlier detection, then that index re-appears in longitude_fix_time
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250911T073640/202509110736_202509120809.nc4", "--plot_universals"]
"args": ["-v", "2", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250911T073640/202509110736_202509120809.nc4", "--plot_time", "/NAL9602/longitude_fix_time"]
},
{
"name": "2.0 - calibrate.py",
Expand Down Expand Up @@ -125,11 +128,13 @@
"program": "${workspaceFolder}/src/data/combine.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--plot"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--plot"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250916_20250922/20250916T230652/202509162306_202509180305.nc4", "--plot"]
//"args": ["-v", "1", "--log_file", "tethys/missionlogs/2012/20120908_20120920/20120909T010636/201209090106_201209091521.nc4", "--plot"]
// Conflicting sizes for nudged_time and data - fixed by filtering GPS fixes to be monotonically increasing
//"args": ["-v", "1", "--log_file", "tethys/missionlogs/2012/20120908_20120920/20120917T025522/201209170255_201209171110.nc4", "--plot"]
// Fails in nudge_positions, maybe bad GPS data?
"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250911T073640/202509110736_202509120809.nc4", "--plot"]

},
{
Expand Down Expand Up @@ -348,7 +353,7 @@
// Lots bad time values in brizo 20250914T080941 due to memory corruption on the vehicle
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4"]
//"args": ["-v", "2", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--clobber"]
"args": ["-v", "2", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--clobber", "--no_cleanup"]
//"args": ["-v", "2", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250914T080941/202509140809_202509150109.nc4", "--clobber", "--no_cleanup"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250916_20250922/20250916T230652/202509162306_202509180305.nc4", "--no_cleanup"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250916_20250922/20250916T230652/202509162306_202509180305.nc4", "--no_cleanup", "--clobber"]
// Has different universals time coodinates for longitude/latitude and depth
Expand All @@ -366,6 +371,12 @@
//"args": ["-v", "1", "--auv_name", "pontus", "--start", "20250601T000000", "--end", "20250721T000000", "--noinput", "--num_cores", "1", "--no_cleanup"]
//"args": ["-v", "1", "--auv_name", "pontus", "--start", "20250601T000000", "--end", "20250702T000000", "--noinput", "--num_cores", "1", "--no_cleanup", "--clobber"]
//"args": ["-v", "1", "--log_file", "pontus/missionlogs/2025/20250623_20250707/20250707T043011/slate.nc4", "--no_cleanup"]
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250903_20250903/20250903T202626/202509032026_202509032030.nc4", "--no_cleanup"]
// Does not have ctdseabird data, hence no coordinates for resampling
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250903_20250903/20250903T202626/202509032026_202509032030.nc4", "--no_cleanup"]
// Fails in nudge_positions, maybe bad GPS data?
//"args": ["-v", "1", "--log_file", "brizo/missionlogs/2025/20250909_20250915/20250911T073640/202509110736_202509120809.nc4", "--no_cleanup"]
"args": ["-v", "1", "--auv_name", "brizo", "--start", "20250901T000000", "--end", "20251001T000000", "--noinput", "--num_cores", "1", "--no_cleanup"]
},

]
Expand Down
117 changes: 59 additions & 58 deletions src/data/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,66 +568,67 @@ def process_combined(self) -> Path: # noqa: C901, PLR0912, PLR0915
np.mean(np.diff(self.combined_nc[timevar])) / np.timedelta64(1, "s")
)

time_coord = variable_time_coord_mapping.get(variable)
depth_coord = (
time_coord[:-5] + "_depth"
if time_coord and time_coord.endswith("_time")
else f"{group_name}_depth"
)
lat_coord = (
time_coord[:-5] + "_latitude"
if time_coord and time_coord.endswith("_time")
else f"{group_name}_latitude"
)
lon_coord = (
time_coord[:-5] + "_longitude"
if time_coord and time_coord.endswith("_time")
else f"{group_name}_longitude"
)

# Add interpolated depth, latitude, and longitude variables
if depth_coord in self.combined_nc:
self.aligned_nc[depth_coord].attrs = self.combined_nc[depth_coord].attrs
self.aligned_nc[depth_coord] = xr.DataArray(
depth_interp(var_time).astype(np.float64).tolist(),
dims={timevar},
coords=[self.combined_nc[variable].get_index(timevar)],
name=depth_coord,
)
self.aligned_nc[depth_coord].attrs["long_name"] = "Depth"
self.aligned_nc[depth_coord].attrs["comment"] = "depth from Group_Universals.nc"
# Determine coordinate variable names based on group
depth_coord = f"{group_name}_depth"
lat_coord = f"{group_name}_latitude"
lon_coord = f"{group_name}_longitude"

TINY_SAMPLE_RATE = 10e-2
self.aligned_nc[depth_coord].attrs["instrument_sample_rate_hz"] = (
sample_rate_str = (
f"{sample_rate:.2f}" if sample_rate > TINY_SAMPLE_RATE else f"{sample_rate:.6f}"
)

self.aligned_nc[lat_coord] = xr.DataArray(
lat_interp(var_time).astype(np.float64).tolist(),
dims={timevar},
coords=[self.combined_nc[variable].get_index(timevar)],
name=lat_coord,
)
self.aligned_nc[lat_coord].attrs = self.combined_nc["nudged_latitude"].attrs
self.aligned_nc[lat_coord].attrs["comment"] += (
f". Variable nudged_latitude linearly"
f" interpolated onto {variable.split('_')[0]} time values."
)
self.aligned_nc[lat_coord].attrs["long_name"] = "Latitude"
self.aligned_nc[lat_coord].attrs["instrument_sample_rate_hz"] = sample_rate

self.aligned_nc[lon_coord] = xr.DataArray(
lon_interp(var_time).astype(np.float64).tolist(),
dims={timevar},
coords=[self.combined_nc[variable].get_index(timevar)],
name=lon_coord,
)
self.aligned_nc[lon_coord].attrs = self.combined_nc["nudged_longitude"].attrs
self.aligned_nc[lon_coord].attrs["comment"] += (
f". Variable nudged_longitude linearly"
f" interpolated onto {variable.split('_')[0]} time values."
)
self.aligned_nc[lon_coord].attrs["long_name"] = "Longitude"
self.aligned_nc[lon_coord].attrs["instrument_sample_rate_hz"] = sample_rate
# Create depth coordinate - only if not already created for this group
if depth_coord not in self.aligned_nc:
self.aligned_nc[depth_coord] = xr.DataArray(
depth_interp(var_time).astype(np.float64).tolist(),
dims={timevar},
coords=[self.combined_nc[variable].get_index(timevar)],
name=depth_coord,
)
# Copy attributes from combined_nc if they exist - ensusre proper standard_name
if depth_coord in self.combined_nc:
self.aligned_nc[depth_coord].attrs = self.combined_nc[depth_coord].attrs.copy()
self.aligned_nc[depth_coord].attrs["long_name"] = "Depth"
self.aligned_nc[depth_coord].attrs["standard_name"] = "depth"
self.aligned_nc[depth_coord].attrs["comment"] = "depth from Group_Universals.nc"
self.aligned_nc[depth_coord].attrs["instrument_sample_rate_hz"] = sample_rate_str

# Create latitude coordinate - only if not already created for this group
if lat_coord not in self.aligned_nc:
self.aligned_nc[lat_coord] = xr.DataArray(
lat_interp(var_time).astype(np.float64).tolist(),
dims={timevar},
coords=[self.combined_nc[variable].get_index(timevar)],
name=lat_coord,
)
self.aligned_nc[lat_coord].attrs = self.combined_nc["nudged_latitude"].attrs.copy()
self.aligned_nc[lat_coord].attrs["comment"] += (
f". Variable nudged_latitude linearly"
f" interpolated onto {group_name} time values."
)
# Ensure proper standard_name
self.aligned_nc[lat_coord].attrs["long_name"] = "Latitude"
self.aligned_nc[lat_coord].attrs["standard_name"] = "latitude"
self.aligned_nc[lat_coord].attrs["instrument_sample_rate_hz"] = sample_rate_str

# Create longitude coordinate - only if not already created for this group
if lon_coord not in self.aligned_nc:
self.aligned_nc[lon_coord] = xr.DataArray(
lon_interp(var_time).astype(np.float64).tolist(),
dims={timevar},
coords=[self.combined_nc[variable].get_index(timevar)],
name=lon_coord,
)
self.aligned_nc[lon_coord].attrs = self.combined_nc["nudged_longitude"].attrs.copy()
self.aligned_nc[lon_coord].attrs["comment"] += (
f". Variable nudged_longitude linearly"
f" interpolated onto {group_name} time values."
)
# Ensure proper standard_name
self.aligned_nc[lon_coord].attrs["long_name"] = "Longitude"
self.aligned_nc[lon_coord].attrs["standard_name"] = "longitude"
self.aligned_nc[lon_coord].attrs["instrument_sample_rate_hz"] = sample_rate_str

# Update spatial temporal bounds for global metadata
if pd.to_datetime(self.aligned_nc[timevar][0].values).tz_localize(UTC) < pd.to_datetime(
Expand Down Expand Up @@ -661,13 +662,13 @@ def process_combined(self) -> Path: # noqa: C901, PLR0912, PLR0915
)
self.aligned_nc[variable].attrs = self.combined_nc[variable].attrs
if (
time_coord in self.aligned_nc
timevar in self.aligned_nc
and depth_coord in self.aligned_nc
and lat_coord in self.aligned_nc
and lon_coord in self.aligned_nc
):
self.aligned_nc[variable].attrs["coordinates"] = (
f"{time_coord} {depth_coord} {lat_coord} {lon_coord}"
f"{timevar} {depth_coord} {lat_coord} {lon_coord}"
)
else:
self.logger.info("Skipping setting coordinates attribute for %s", variable)
Expand Down
35 changes: 34 additions & 1 deletion src/data/nc42netcdfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def extract_groups_to_files_netcdf4(self, log_file: str) -> Path:

# Extract all other groups
all_groups = list(src_dataset.groups.keys())
for group_name in SCIENG_PARMS:
for group_name in sorted(SCIENG_PARMS):
if group_name == "/" or group_name not in all_groups:
if group_name != "/" and group_name not in all_groups:
self.logger.warning("Group %s not found in %s", group_name, input_file)
Expand Down Expand Up @@ -555,11 +555,44 @@ def _process_single_time_coordinate( # noqa: PLR0913

# Get the valid time subset
valid_time_data = original_time_data[spike_removed_indices]
self.logger.debug(
"Before monotonic: len(valid_time_data)=%d, len(spike_removed_indices)=%d, "
"type(spike_removed_indices)=%s",
len(valid_time_data),
len(spike_removed_indices),
type(spike_removed_indices).__name__,
)
self.logger.debug(
"valid_time_data shape/size: %s",
getattr(valid_time_data, "shape", len(valid_time_data)),
)

# Apply monotonic filtering
mono_indices_in_filtered = self._get_monotonic_indices(valid_time_data)
self.logger.debug(
"After monotonic: len(mono_indices)=%d, max(mono_indices)=%s, type(mono_indices)=%s",
len(mono_indices_in_filtered),
max(mono_indices_in_filtered) if len(mono_indices_in_filtered) > 0 else "N/A",
type(mono_indices_in_filtered).__name__,
)

# Convert monotonic indices back to original array indices
if len(mono_indices_in_filtered) > 0 and max(mono_indices_in_filtered) >= len(
spike_removed_indices
):
self.logger.error(
"BUG: monotonic indices out of range! max(mono_indices)=%d >= "
"len(spike_removed)=%d",
max(mono_indices_in_filtered),
len(spike_removed_indices),
)
self.logger.error("mono_indices_in_filtered: %s", mono_indices_in_filtered[:20])
self.logger.error("spike_removed_indices: %s", spike_removed_indices[:20])
# Clamp to valid range to prevent crash
mono_indices_in_filtered = [
i for i in mono_indices_in_filtered if i < len(spike_removed_indices)
]

final_indices = [spike_removed_indices[i] for i in mono_indices_in_filtered]

# Store data for plotting if requested (do this before early return)
Expand Down
46 changes: 36 additions & 10 deletions src/data/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ def wrapper(self, log_file: str):
return func(self, log_file)
except (TestMission, FailedMission, EOFError) as e:
self.logger.info(str(e))
except Exception:
# Catch all other exceptions and log full traceback
self.logger.exception("Exception occurred while processing %s", log_file)
raise
finally:
if hasattr(self, "log_handler"):
# Cleanup and archiving logic
Expand Down Expand Up @@ -545,9 +549,14 @@ def align(self, mission: str = "", log_file: str = "") -> None:
netcdf_dir = align_netcdf.process_cal()
align_netcdf.write_combined_netcdf(netcdf_dir)
except (FileNotFoundError, EOFError) as e:
align_netcdf.logger.error("%s %s", mission, e) # noqa: TRY400
error_message = f"{mission} {e}"
align_netcdf.logger.error("%s %s", mission or log_file, e) # noqa: TRY400
error_message = f"{mission or log_file} {e}"
raise InvalidCalFile(error_message) from e
except Exception:
align_netcdf.logger.exception(
"Exception occurred during alignment of %s", mission or log_file
)
raise
finally:
align_netcdf.logger.removeHandler(self.log_handler)

Expand Down Expand Up @@ -601,9 +610,14 @@ def resample(self, mission: str = "", log_file: str = "") -> None:
raise ValueError(error_message)
subprocess.run([wget_path, dap_file_str, "-O", nc_file_str], check=True) # noqa: S603
try:
resamp.resample_mission(nc_file)
resamp.resample_align_file(nc_file)
except (FileNotFoundError, InvalidAlignFile) as e:
self.logger.error("%s %s", nc_file, e) # noqa: TRY400
except Exception:
resamp.logger.exception(
"Exception occurred during resampling of %s", mission or log_file
)
raise
finally:
resamp.logger.removeHandler(self.log_handler)

Expand Down Expand Up @@ -981,11 +995,17 @@ def extract(self, log_file: str) -> None:
extract.logger.setLevel(self._log_levels[self.config["verbose"]])
extract.logger.addHandler(self.log_handler)

url = os.path.join(BASE_LRAUV_WEB, log_file) # noqa: PTH118
output_dir = Path(BASE_LRAUV_PATH, Path(log_file).parent)
extract.logger.info("Downloading %s", url)
input_file = extract.download_with_pooch(url, output_dir)
return extract.extract_groups_to_files_netcdf4(input_file)
try:
url = os.path.join(BASE_LRAUV_WEB, log_file) # noqa: PTH118
output_dir = Path(BASE_LRAUV_PATH, Path(log_file).parent)
extract.logger.info("Downloading %s", url)
input_file = extract.download_with_pooch(url, output_dir)
return extract.extract_groups_to_files_netcdf4(input_file)
except Exception:
extract.logger.exception("Exception occurred during extraction of %s", log_file)
raise
finally:
extract.logger.removeHandler(self.log_handler)

def combine(self, log_file: str) -> None:
self.logger.info("Combining netCDF files for log file: %s", log_file)
Expand All @@ -1002,8 +1022,14 @@ def combine(self, log_file: str) -> None:
combine.logger.setLevel(self._log_levels[self.config["verbose"]])
combine.logger.addHandler(self.log_handler)

combine.combine_groups()
combine.write_netcdf()
try:
combine.combine_groups()
combine.write_netcdf()
except Exception:
combine.logger.exception("Exception occurred during combine of %s", log_file)
raise
finally:
combine.logger.removeHandler(self.log_handler)

@log_file_processor
def process_log_file(self, log_file: str) -> None:
Expand Down
Loading
Loading