Skip to content

Commit 0f44df3

Browse files
committed
add ctd_bgc sampling capability to virtualship run execution
1 parent b1d2e17 commit 0f44df3

5 files changed

Lines changed: 79 additions & 0 deletions

File tree

src/virtualship/expedition/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .ship_config import (
77
ADCPConfig,
88
ArgoFloatConfig,
9+
CTD_BGCConfig,
910
CTDConfig,
1011
DrifterConfig,
1112
ShipConfig,
@@ -17,6 +18,7 @@
1718
"ADCPConfig",
1819
"ArgoFloatConfig",
1920
"CTDConfig",
21+
"CTD_BGCConfig",
2022
"DrifterConfig",
2123
"InputData",
2224
"InstrumentType",

src/virtualship/expedition/do_expedition.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def _load_input_data(
136136
load_adcp=ship_config.adcp_config is not None,
137137
load_argo_float=ship_config.argo_float_config is not None,
138138
load_ctd=ship_config.ctd_config is not None,
139+
load_ctd_bgc=ship_config.ctd_bgc_config is not None,
139140
load_drifter=ship_config.drifter_config is not None,
140141
load_xbt=ship_config.xbt_config is not None,
141142
load_ship_underwater_st=ship_config.ship_underwater_st_config is not None,

src/virtualship/expedition/input_data.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class InputData:
1515
adcp_fieldset: FieldSet | None
1616
argo_float_fieldset: FieldSet | None
1717
ctd_fieldset: FieldSet | None
18+
ctd_bgc_fieldset: FieldSet | None
1819
drifter_fieldset: FieldSet | None
1920
xbt_fieldset: FieldSet | None
2021
ship_underwater_st_fieldset: FieldSet | None
@@ -26,6 +27,7 @@ def load(
2627
load_adcp: bool,
2728
load_argo_float: bool,
2829
load_ctd: bool,
30+
load_ctd_bgc: bool,
2931
load_drifter: bool,
3032
load_xbt: bool,
3133
load_ship_underwater_st: bool,
@@ -39,6 +41,7 @@ def load(
3941
:param load_adcp: Whether to load the ADCP fieldset.
4042
:param load_argo_float: Whether to load the argo float fieldset.
4143
:param load_ctd: Whether to load the CTD fieldset.
44+
:param load_ctd_bgc: Whether to load the CTD BGC fieldset.
4245
:param load_drifter: Whether to load the drifter fieldset.
4346
:param load_ship_underwater_st: Whether to load the ship underwater ST fieldset.
4447
:returns: An instance of this class with loaded fieldsets.
@@ -51,6 +54,10 @@ def load(
5154
argo_float_fieldset = cls._load_argo_float_fieldset(directory)
5255
else:
5356
argo_float_fieldset = None
57+
if load_ctd_bgc:
58+
ctd_bgc_fieldset = cls._load_ctd_bgc_fieldset(directory)
59+
else:
60+
ctd_bgc_fieldset = None
5461
if load_adcp or load_ctd or load_ship_underwater_st or load_xbt:
5562
ship_fieldset = cls._load_ship_fieldset(directory)
5663
if load_adcp:
@@ -74,6 +81,7 @@ def load(
7481
adcp_fieldset=adcp_fieldset,
7582
argo_float_fieldset=argo_float_fieldset,
7683
ctd_fieldset=ctd_fieldset,
84+
ctd_bgc_fieldset=ctd_bgc_fieldset,
7785
drifter_fieldset=drifter_fieldset,
7886
xbt_fieldset=xbt_fieldset,
7987
ship_underwater_st_fieldset=ship_underwater_st_fieldset,
@@ -122,6 +130,48 @@ def _load_ship_fieldset(cls, directory: Path) -> FieldSet:
122130

123131
return fieldset
124132

133+
@classmethod
134+
def _load_ctd_bgc_fieldset(cls, directory: Path) -> FieldSet:
135+
filenames = {
136+
"U": directory.joinpath("ship_uv.nc"),
137+
"V": directory.joinpath("ship_uv.nc"),
138+
"o2": directory.joinpath("ctd_bgc_o2.nc"),
139+
"chl": directory.joinpath("ctd_bgc_chloro.nc"),
140+
}
141+
variables = {"U": "uo", "V": "vo", "o2": "o2", "chl": "chl"}
142+
dimensions = {
143+
"lon": "longitude",
144+
"lat": "latitude",
145+
"time": "time",
146+
"depth": "depth",
147+
}
148+
149+
fieldset = FieldSet.from_netcdf(
150+
filenames, variables, dimensions, allow_time_extrapolation=True
151+
)
152+
fieldset.o2.interp_method = "linear_invdist_land_tracer"
153+
fieldset.chl.interp_method = "linear_invdist_land_tracer"
154+
155+
# make depth negative
156+
for g in fieldset.gridset.grids:
157+
g.negate_depth()
158+
159+
# add bathymetry data
160+
bathymetry_file = directory.joinpath("bathymetry.nc")
161+
bathymetry_variables = ("bathymetry", "deptho")
162+
bathymetry_dimensions = {"lon": "longitude", "lat": "latitude"}
163+
bathymetry_field = Field.from_netcdf(
164+
bathymetry_file, bathymetry_variables, bathymetry_dimensions
165+
)
166+
# make depth negative
167+
bathymetry_field.data = -bathymetry_field.data
168+
fieldset.add_field(bathymetry_field)
169+
170+
# read in data already
171+
fieldset.computeTimeChunk(0, 1)
172+
173+
return fieldset
174+
125175
@classmethod
126176
def _load_drifter_fieldset(cls, directory: Path) -> FieldSet:
127177
filenames = {

src/virtualship/expedition/simulate_measurements.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ..instruments.adcp import simulate_adcp
77
from ..instruments.argo_float import simulate_argo_floats
88
from ..instruments.ctd import simulate_ctd
9+
from ..instruments.ctd_bgc import simulate_ctd_bgc
910
from ..instruments.drifter import simulate_drifters
1011
from ..instruments.ship_underwater_st import simulate_ship_underwater_st
1112
from ..instruments.xbt import simulate_xbt
@@ -75,6 +76,19 @@ def simulate_measurements(
7576
outputdt=timedelta(seconds=10),
7677
)
7778

79+
if len(measurements.ctd_bgcs) > 0:
80+
print("Simulating BGC CTD casts.")
81+
if ship_config.ctd_bgc_config is None:
82+
raise RuntimeError("No configuration for CTD_BGC provided.")
83+
if input_data.ctd_bgc_fieldset is None:
84+
raise RuntimeError("No fieldset for CTD_BGC provided.")
85+
simulate_ctd_bgc(
86+
out_path=expedition_dir.joinpath("results", "ctd_bgc.zarr"),
87+
fieldset=input_data.ctd_bgc_fieldset,
88+
ctd_bgcs=measurements.ctd_bgcs,
89+
outputdt=timedelta(seconds=10),
90+
)
91+
7892
if len(measurements.drifters) > 0:
7993
print("Simulating drifters")
8094
if ship_config.drifter_config is None:

src/virtualship/expedition/simulate_schedule.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from ..instruments.argo_float import ArgoFloat
1111
from ..instruments.ctd import CTD
12+
from ..instruments.ctd_bgc import CTD_BGC
1213
from ..instruments.drifter import Drifter
1314
from ..instruments.xbt import XBT
1415
from ..location import Location
@@ -42,6 +43,7 @@ class MeasurementsToSimulate:
4243
argo_floats: list[ArgoFloat] = field(default_factory=list, init=False)
4344
drifters: list[Drifter] = field(default_factory=list, init=False)
4445
ctds: list[CTD] = field(default_factory=list, init=False)
46+
ctd_bgcs: list[CTD_BGC] = field(default_factory=list, init=False)
4547
xbts: list[XBT] = field(default_factory=list, init=False)
4648

4749

@@ -102,6 +104,7 @@ def simulate(self) -> ScheduleOk | ScheduleProblem:
102104
# check if waypoint was reached in time
103105
if waypoint.time is not None and self._time > waypoint.time:
104106
print(
107+
# TODO: I think this should be wp_i + 1, not wp_i; otherwise it will be off by one
105108
f"Waypoint {wp_i} could not be reached in time. Current time: {self._time}. Waypoint time: {waypoint.time}."
106109
)
107110
return ScheduleProblem(self._time, wp_i)
@@ -251,6 +254,15 @@ def _make_measurements(self, waypoint: Waypoint) -> timedelta:
251254
)
252255
)
253256
time_costs.append(self._ship_config.ctd_config.stationkeeping_time)
257+
elif instrument is InstrumentType.CTD_BGC:
258+
self._measurements_to_simulate.ctd_bgcs.append(
259+
CTD_BGC(
260+
spacetime=Spacetime(self._location, self._time),
261+
min_depth=self._ship_config.ctd_bgc_config.min_depth_meter,
262+
max_depth=self._ship_config.ctd_bgc_config.max_depth_meter,
263+
)
264+
)
265+
time_costs.append(self._ship_config.ctd_bgc_config.stationkeeping_time)
254266
elif instrument is InstrumentType.DRIFTER:
255267
self._measurements_to_simulate.drifters.append(
256268
Drifter(

0 commit comments

Comments
 (0)