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
2 changes: 2 additions & 0 deletions src/virtualship/cli/_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def log_exception_to_file(
{"name": "vertical_speed_meter_per_second"},
{"name": "cycle_days"},
{"name": "drift_days"},
{"name": "stationkeeping_time", "minutes": True},
],
},
"drifter_config": {
Expand All @@ -149,6 +150,7 @@ def log_exception_to_file(
"attributes": [
{"name": "depth_meter"},
{"name": "lifetime", "minutes": True},
{"name": "stationkeeping_time", "minutes": True},
],
},
}
Expand Down
16 changes: 13 additions & 3 deletions src/virtualship/expedition/simulate_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ def _make_measurements(self, waypoint: Waypoint) -> timedelta:
# time costs of each measurement
time_costs = [timedelta()]

# check if both CTD and CTD_BGC are present
# TODO: this can be avoided if CTD and CTD_BGC are merged into a single instrument
both_ctd_and_bgc = (
InstrumentType.CTD in instruments and InstrumentType.CTD_BGC in instruments
)

for instrument in instruments:
if instrument is InstrumentType.ARGO_FLOAT:
self._measurements_to_simulate.argo_floats.append(
Expand All @@ -268,6 +274,7 @@ def _make_measurements(self, waypoint: Waypoint) -> timedelta:
drift_days=self._expedition.instruments_config.argo_float_config.drift_days,
)
)

elif instrument is InstrumentType.CTD:
self._measurements_to_simulate.ctds.append(
CTD(
Expand All @@ -287,9 +294,12 @@ def _make_measurements(self, waypoint: Waypoint) -> timedelta:
max_depth=self._expedition.instruments_config.ctd_bgc_config.max_depth_meter,
)
)
time_costs.append(
self._expedition.instruments_config.ctd_bgc_config.stationkeeping_time
)
if both_ctd_and_bgc: # only need to add time cost once if both CTD and CTD_BGC are being taken; in reality they would be done on the same instrument
pass
else:
time_costs.append(
self._expedition.instruments_config.ctd_bgc_config.stationkeeping_time
)
elif instrument is InstrumentType.DRIFTER:
self._measurements_to_simulate.drifters.append(
Drifter(
Expand Down
29 changes: 29 additions & 0 deletions src/virtualship/models/expedition.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,22 @@ class ArgoFloatConfig(pydantic.BaseModel):
cycle_days: float = pydantic.Field(gt=0.0)
drift_days: float = pydantic.Field(gt=0.0)

stationkeeping_time: timedelta = pydantic.Field(
serialization_alias="stationkeeping_time_minutes",
validation_alias="stationkeeping_time_minutes",
gt=timedelta(),
)

@pydantic.field_serializer("stationkeeping_time")
def _serialize_stationkeeping_time(self, value: timedelta, _info):
return value.total_seconds() / 60.0

@pydantic.field_validator("stationkeeping_time", mode="before")
def _validate_stationkeeping_time(cls, value: int | float | timedelta) -> timedelta:
return _validate_numeric_mins_to_timedelta(value)

model_config = pydantic.ConfigDict(populate_by_name=True)


class ADCPConfig(pydantic.BaseModel):
"""Configuration for ADCP instrument."""
Expand Down Expand Up @@ -311,6 +327,11 @@ class DrifterConfig(pydantic.BaseModel):
validation_alias="lifetime_minutes",
gt=timedelta(),
)
stationkeeping_time: timedelta = pydantic.Field(
serialization_alias="stationkeeping_time_minutes",
validation_alias="stationkeeping_time_minutes",
gt=timedelta(),
)

model_config = pydantic.ConfigDict(populate_by_name=True)

Expand All @@ -322,6 +343,14 @@ def _serialize_lifetime(self, value: timedelta, _info):
def _validate_lifetime(cls, value: int | float | timedelta) -> timedelta:
return _validate_numeric_mins_to_timedelta(value)

@pydantic.field_serializer("stationkeeping_time")
def _serialize_stationkeeping_time(self, value: timedelta, _info):
return value.total_seconds() / 60.0

@pydantic.field_validator("stationkeeping_time", mode="before")
def _validate_stationkeeping_time(cls, value: int | float | timedelta) -> timedelta:
return _validate_numeric_mins_to_timedelta(value)


class XBTConfig(pydantic.BaseModel):
"""Configuration for xbt instrument."""
Expand Down
6 changes: 4 additions & 2 deletions src/virtualship/static/expedition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,19 @@ instruments_config:
max_depth_meter: -2000.0
min_depth_meter: 0.0
vertical_speed_meter_per_second: -0.1
stationkeeping_time_minutes: 20.0
ctd_config:
max_depth_meter: -2000.0
min_depth_meter: -11.0
stationkeeping_time_minutes: 20.0
stationkeeping_time_minutes: 50.0
ctd_bgc_config:
max_depth_meter: -2000.0
min_depth_meter: -11.0
stationkeeping_time_minutes: 20.0
stationkeeping_time_minutes: 50.0
drifter_config:
depth_meter: -1.0
lifetime_minutes: 60480.0
stationkeeping_time_minutes: 20.0
xbt_config:
max_depth_meter: -285.0
min_depth_meter: -2.0
Expand Down
6 changes: 4 additions & 2 deletions tests/expedition/expedition_dir/expedition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,19 @@ instruments_config:
max_depth_meter: -2000.0
min_depth_meter: 0.0
vertical_speed_meter_per_second: -0.1
stationkeeping_time_minutes: 20.0
ctd_config:
max_depth_meter: -2000.0
min_depth_meter: -11.0
stationkeeping_time_minutes: 20.0
stationkeeping_time_minutes: 50.0
ctd_bgc_config:
max_depth_meter: -2000.0
min_depth_meter: -11.0
stationkeeping_time_minutes: 20.0
stationkeeping_time_minutes: 50.0
drifter_config:
depth_meter: -1.0
lifetime_minutes: 40320.0
stationkeeping_time_minutes: 20.0
ship_underwater_st_config:
period_minutes: 5.0
ship_config:
Expand Down
8 changes: 7 additions & 1 deletion tests/expedition/test_simulate_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,14 @@ def test_time_in_minutes_in_ship_schedule() -> None:
"expedition_dir/expedition.yaml"
).instruments_config
assert instruments_config.adcp_config.period == timedelta(minutes=5)
assert instruments_config.ctd_config.stationkeeping_time == timedelta(minutes=20)
assert instruments_config.ctd_config.stationkeeping_time == timedelta(minutes=50)
assert instruments_config.ctd_bgc_config.stationkeeping_time == timedelta(
minutes=50
)
assert instruments_config.argo_float_config.stationkeeping_time == timedelta(
minutes=20
)
assert instruments_config.drifter_config.stationkeeping_time == timedelta(
minutes=20
)
assert instruments_config.ship_underwater_st_config.period == timedelta(minutes=5)