Skip to content

Fixes: vertical_mixing, do3D, expanding some config descriptions #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 11, 2025
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
7 changes: 7 additions & 0 deletions docs/whats_new.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# What's New

## unreleased

* fixed issue when setting `vertical_mixing` False for `OpenOil` was not passed through correctly
* now `do3d=False` and `vertical_mixing=True` can be set at the same time.
* updated some config descriptions


## v0.12.1 (April 8, 2025)

* Correction to `interpolator_filename` handling and improvement in testing.
Expand Down
65 changes: 42 additions & 23 deletions particle_tracking_manager/models/opendrift/config_opendrift.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,16 @@ def check_config_z_value(self) -> Self:
raise ValueError("z needs to be None if seed_seafloor is True.")
return self

@model_validator(mode="after")
def check_config_do3D(self) -> Self:
"""Check if do3D is set correctly."""
if hasattr(self, "vertical_mixing"):
if not self.do3D and self.vertical_mixing:
raise ValueError(
"If do3D is False, vertical_mixing must also be False."
)
return self
# this is not true! For example, OpenOil has by default no vertical advection but yes vertical mixing
# @model_validator(mode="after")
# def check_config_do3D(self) -> Self:
# """Check if do3D is set correctly."""
# if hasattr(self, "vertical_mixing"):
# if not self.do3D and self.vertical_mixing:
# raise ValueError(
# "If do3D is False, vertical_mixing must also be False."
# )
# return self

@model_validator(mode="after")
def setup_interpolator(self) -> Self:
Expand Down Expand Up @@ -484,7 +485,7 @@ class OceanDriftModelConfig(OpenDriftConfig):
description="Activate vertical mixing scheme with inner loop",
title="Vertical Mixing",
json_schema_extra={
"od_mapping": "vertical_mixing:vertical_mixing",
"od_mapping": "drift:vertical_mixing",
"ptm_level": 2,
},
)
Expand Down Expand Up @@ -516,7 +517,7 @@ class OpenOilModelConfig(OceanDriftModelConfig):

oil_film_thickness: float = Field(
default=0.001,
description="Seeding value of oil_film_thickness",
description="Seeding value of oil_film_thickness. Values are calculated by OpenDrift starting from this initial value if `update_oilfilm_thickness==True`.",
title="Oil Film Thickness",
json_schema_extra={
"units": "m",
Expand Down Expand Up @@ -619,7 +620,7 @@ class OpenOilModelConfig(OceanDriftModelConfig):

update_oilfilm_thickness: bool = Field(
default=False,
description="Oil film thickness is calculated at each time step. The alternative is that oil film thickness is kept constant with value provided at seeding.",
description="If True, Oil film thickness is calculated at each time step. If False, oil film thickness is kept constant with value provided at seeding.",
title="Update Oilfilm Thickness",
json_schema_extra={
"od_mapping": "processes:update_oilfilm_thickness",
Expand Down Expand Up @@ -676,15 +677,31 @@ class OpenOilModelConfig(OceanDriftModelConfig):
# return values

@field_validator("oil_type", mode="before")
def map_oil_type_to_id(cls, v):
"""Map oil type to enum name (which is the oil type id)."""
if v in OilTypeEnum.__members__: # Check if it matches a name
return OilTypeEnum[v]
for enum_member in OilTypeEnum: # Check if it matches a value
def map_oil_type_to_name(cls, v):
"""Map input oil type to enum value (which is the oil type name)."""
if (
v in OilTypeEnum.__members__
): # Check if it matches an Enum name (which is the oil type id)
return OilTypeEnum[v] # then return name
for (
enum_member
) in (
OilTypeEnum
): # Check if it matches an Enum value (which is the oil type name/title)
if enum_member.value == v:
return enum_member
raise ValueError(f"Invalid value or name '{v}' for OilTypeEnum")

# @field_validator("oil_type", mode="before")
# def map_oil_type_to_id(cls, v):
# """Map input oil type to enum name (which is the oil type id)."""
# if v in OilTypeEnum.__members__: # Check if it matches an Enum name (which is the oil type id)
# return v # then return id
# for enum_member in OilTypeEnum: # Check if it matches an Enum value (which is the oil type name/title)
# if enum_member.value == v:
# return enum_member # then return id
# raise ValueError(f"Invalid value or name '{v}' for OilTypeEnum")


class LarvalFishModelConfig(OceanDriftModelConfig):
"""Larval fish model configuration for OpenDrift."""
Expand All @@ -693,7 +710,7 @@ class LarvalFishModelConfig(OceanDriftModelConfig):

diameter: float = Field(
default=0.0014,
description="Seeding value of diameter",
description="Seeding value of diameter. The diameter gives the egg diameter so must be used with `hatched=0`.",
title="Diameter",
gt=0,
json_schema_extra={
Expand All @@ -705,7 +722,7 @@ class LarvalFishModelConfig(OceanDriftModelConfig):

neutral_buoyancy_salinity: float = Field(
default=31.25,
description="Seeding value of neutral_buoyancy_salinity",
description="Seeding value of neutral_buoyancy_salinity. This is a property of the egg so must be used with `hatched=0`.",
title="Neutral Buoyancy Salinity",
gt=0,
json_schema_extra={
Expand All @@ -717,8 +734,10 @@ class LarvalFishModelConfig(OceanDriftModelConfig):

stage_fraction: float = Field(
default=0.0,
description="Seeding value of stage_fraction",
description="Seeding value of stage_fraction. stage_fraction tracks percentage of development time completed, from 0 to 1, where a value of 1 means the egg has hatched. If `hatched==1` then `stage_fraction` is ignored.",
title="Stage Fraction",
ge=0,
le=1,
json_schema_extra={
"units": "",
"od_mapping": "seed:stage_fraction",
Expand All @@ -728,7 +747,7 @@ class LarvalFishModelConfig(OceanDriftModelConfig):

hatched: int = Field(
default=0,
description="Seeding value of hatched",
description="Seeding value of hatched. 0 for eggs, 1 for larvae.",
title="Hatched",
ge=0,
le=1,
Expand All @@ -741,7 +760,7 @@ class LarvalFishModelConfig(OceanDriftModelConfig):

length: float = Field(
default=0,
description="Seeding value of length",
description="Seeding value of length. This is not currently used.",
title="Length",
gt=0,
json_schema_extra={
Expand All @@ -753,7 +772,7 @@ class LarvalFishModelConfig(OceanDriftModelConfig):

weight: float = Field(
default=0.08,
description="Seeding value of weight",
description="Seeding value of weight. This is the starting weight for larval fish, whenever they reach that stage.",
title="Weight",
gt=0,
json_schema_extra={
Expand Down
11 changes: 11 additions & 0 deletions particle_tracking_manager/models/opendrift/opendrift.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from ...ocean_model_registry import ocean_model_registry
from ...the_manager import ParticleTrackingManager
from .config_opendrift import OpenDriftConfig, open_drift_mapper
from .enums import OilTypeEnum
from .plot import make_plots
from .utils import (
apply_known_ocean_model_specific_changes,
Expand Down Expand Up @@ -254,6 +255,16 @@ def _modify_opendrift_model_object(self) -> None:
self.o.set_config("drift:vertical_advection", True)
logger.debug("do3D is True so turning on vertical advection.")

# # Assign oil_type for OpenOil simulation by id to be unique since oil names are not unique
# # WARHING! o.set_oiltype_by_id doesn't appear to actually set the oil type (only the name, not the config)
# if self.config.drift_model == "OpenOil":
# oil_type_id = OilTypeEnum(self.config.oil_type).name
# self.o.set_oiltype_by_id(oil_type_id)
# logger.debug(f"Setting oil type {self.config.oil_type} by id {oil_type_id}.")

# TODO: what if I change the order of when config is updated with this method, then
# does it impact that i sed oil type by id?

def _setup_for_simulation(self) -> None:
"""Set up the simulation.
This is run before the simulation starts.
Expand Down
13 changes: 13 additions & 0 deletions tests/test_opendrift.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ def test_OpenOil_oil_type_id():
m = OpenDriftModel(drift_model="OpenOil", oil_type=oil_type, steps=1)


@pytest.mark.slow
def test_OpenOil_vertical_mixing():
m = ptm.OpenDriftModel(
drift_model="OpenOil",
steps=1,
oil_type="AD00010",
do3D=True,
vertical_mixing=False,
)
m.setup_for_simulation()
assert not m.o.get_config("drift:vertical_mixing")


def test_wind_drift():
"""Make sure changed wind drift numbers comes through"""

Expand Down
Loading