diff --git a/tests/make_realistic/.gitignore b/tests/make_realistic/.gitignore index 30abd87d..bb473645 100644 --- a/tests/make_realistic/.gitignore +++ b/tests/make_realistic/.gitignore @@ -1 +1,2 @@ !/ctd.zarr +!/adcp.zarr diff --git a/tests/make_realistic/adcp.zarr/.zattrs b/tests/make_realistic/adcp.zarr/.zattrs new file mode 100644 index 00000000..90d4d0b4 --- /dev/null +++ b/tests/make_realistic/adcp.zarr/.zattrs @@ -0,0 +1,8 @@ +{ + "Conventions": "CF-1.6/CF-1.7", + "feature_type": "trajectory", + "ncei_template_version": "NCEI_NetCDF_Trajectory_Template_v2.0", + "parcels_kernels": "NewParticle_sample_velocity", + "parcels_mesh": "spherical", + "parcels_version": "0.0.1-16-g947e7d0" +} \ No newline at end of file diff --git a/tests/make_realistic/adcp.zarr/.zgroup b/tests/make_realistic/adcp.zarr/.zgroup new file mode 100644 index 00000000..3b7daf22 --- /dev/null +++ b/tests/make_realistic/adcp.zarr/.zgroup @@ -0,0 +1,3 @@ +{ + "zarr_format": 2 +} \ No newline at end of file diff --git a/tests/make_realistic/adcp.zarr/.zmetadata b/tests/make_realistic/adcp.zarr/.zmetadata new file mode 100644 index 00000000..d1bf51f5 --- /dev/null +++ b/tests/make_realistic/adcp.zarr/.zmetadata @@ -0,0 +1,264 @@ +{ + "metadata": { + ".zattrs": { + "Conventions": "CF-1.6/CF-1.7", + "feature_type": "trajectory", + "ncei_template_version": "NCEI_NetCDF_Trajectory_Template_v2.0", + "parcels_kernels": "NewParticle_sample_velocity", + "parcels_mesh": "spherical", + "parcels_version": "0.0.1-16-g947e7d0" + }, + ".zgroup": { + "zarr_format": 2 + }, + "U/.zarray": { + "chunks": [ + 40, + 1 + ], + "compressor": { + "blocksize": 0, + "clevel": 5, + "cname": "lz4", + "id": "blosc", + "shuffle": 1 + }, + "dimension_separator": ".", + "dtype": " None: + # add noise and convert to CSV + file = adcp_make_realistic("adcp.zarr", out_dir=tmpdir, prefix="ADCP") + + # check if CSV is ok and can be loaded + with open(file, mode="r", newline="") as csvfile: + # ignore lines starting with #, we assume that's metadata or comments + reader = csv.reader(line for line in csvfile if not line.startswith("#")) + for _ in reader: + pass # iterate through the rows to check validity diff --git a/virtual_ship/make_realistic/__init__.py b/virtual_ship/make_realistic/__init__.py index d17e5eb9..2c9a17df 100644 --- a/virtual_ship/make_realistic/__init__.py +++ b/virtual_ship/make_realistic/__init__.py @@ -1,5 +1,6 @@ """Conversion functions from zarr instrument results to realistic file formats with data adjusted to be more realistic.""" +from .adcp_make_realistic import adcp_make_realistic from .ctd_make_realistic import ctd_make_realistic -__all__ = ["ctd_make_realistic"] +__all__ = ["adcp_make_realistic", "ctd_make_realistic"] diff --git a/virtual_ship/make_realistic/adcp_make_realistic.py b/virtual_ship/make_realistic/adcp_make_realistic.py new file mode 100644 index 00000000..69c24790 --- /dev/null +++ b/virtual_ship/make_realistic/adcp_make_realistic.py @@ -0,0 +1,66 @@ +"""adcp_make_realistic function.""" + +import numpy as np +import py +import xarray as xr + + +def adcp_make_realistic( + zarr_path: str | py.path.LocalPath, + out_dir: str | py.path.LocalPath, + prefix: str, +) -> py.path.LocalPath: + """ + Take simulated ADCP data, add noise, then save in (an inconvenient educational) CSV format. + + :param zarr_path: Input simulated data. + :param out_dir: Output directory for CSV file. + :param prefix: Prefix for CSV file. + :returns: Path to created file. + """ + original = xr.open_zarr(zarr_path) + + times = original.sel(trajectory=0)["time"].values + depths = original.sel(obs=0)["z"].values + lats = original.sel(trajectory=0)["lat"].values + lons = original.sel(trajectory=0)["lon"].values + all_us = original["U"].values + all_vs = original["V"].values + + all_us, all_vs = _add_noise(times, depths, all_us, all_vs) + + csv = _to_csv(times, depths, lats, lons, all_us, all_vs) + out_file = ( + out_dir.join(f"{prefix}.csv") + if isinstance(out_dir, py.path.LocalPath) + else f"{out_dir}/{prefix}.csv" + ) + with open(out_file, "w") as out_cnv: + out_cnv.write(csv) + + return out_file + + +def _add_noise( + times: np.ndarray, depths: np.ndarray, all_us: np.ndarray, all_vs: np.ndarray +) -> tuple[np.ndarray, np.ndarray]: + return all_us, all_vs + + +def _to_csv( + times: np.ndarray, + depths: np.ndarray, + lats: np.ndarray, + lons: np.ndarray, + all_us: np.ndarray, + all_vs: np.ndarray, +) -> str: + meta = "# depths (m): " + ",".join([str(d) for d in depths]) + header = f"time,lat,lon,{','.join(['u' + str(n) + ',v' + str(n) for n in range(len(depths))])}" + data = [ + f"{str(time)},{lat},{lon},{','.join([str(u) + ',' + str(v) for u, v in zip(us, vs)])}" + for time, lat, lon, us, vs in zip(times, lats, lons, all_us.T, all_vs.T) + ] + + lines = [meta, header] + data + return "\n".join(lines)