From d38a8264e3a0b6c3cd6c8cff3a77901e89d8994b Mon Sep 17 00:00:00 2001 From: Kristen Thyng Date: Thu, 4 Apr 2024 17:14:38 -0500 Subject: [PATCH] tons of updates to and for docs including errors I caught in the process of writing the tutorial as well as tests to demonstrate. --- docs/configuration.md | 6 +- docs/environment.yml | 1 + docs/index.rst | 1 + docs/quick_start.md | 21 +- docs/tutorial.md | 278 ++++++++++++++++++ environment.yml | 1 + .../models/opendrift/opendrift.py | 69 ++++- .../the_manager_config.json | 2 +- tests/test_opendrift.py | 169 +++++++---- 9 files changed, 479 insertions(+), 69 deletions(-) create mode 100644 docs/tutorial.md diff --git a/docs/configuration.md b/docs/configuration.md index f5087a8..b67e175 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -160,9 +160,11 @@ m.show_config(key="ocean_model") The built-in ocean models are: * NWGOA (1999–2008) over the Northwest Gulf of Alaska (Danielson, S. L., K. S. Hedstrom, E. Curchitser, 2016. Cook Inlet Model Calculations, Final Report to Bureau of Ocean Energy Management, M14AC00014, OCS Study BOEM 2015-050, University of Alaska Fairbanks, Fairbanks, AK, 149 pp.) * CIOFS (1999–2022) across Cook Inlet, Alaska, a hindcast version of NOAA's CIOFS model. (Thyng, K. M., C. Liu, M. Feen, E. L. Dobbins, 2023. Cook Inlet Circulation Modeling, Final Report to Oil Spill Recovery Institute, Axiom Data Science, Anchorage, AK.) -* CIOFS_NOW (mid-2021 through 48 hours from present time) which is the nowcast/forecast version of the CIOFS model. (Shi, L., L. Lanerolle, Y. Chen, D. Cao, R. Patchen, A. Zhang, +* CIOFSOP (mid-2021 through 48 hours from present time) which is the nowcast/forecast version of the CIOFS model. (Shi, L., L. Lanerolle, Y. Chen, D. Cao, R. Patchen, A. Zhang, and E. P. Myers, 2020. NOS Cook Inlet Operational Forecast System: Model development and hindcast skill assessment, NOAA Technical Report NOS CS 40, Silver Spring, Maryland, September 2020.) +If you are running locally on an Axiom server you can use `ocean_model_local=True` to access the model output locally instead of remotely. + An alternative ocean model can be used instead by initializing the `Manager` then setting up the reader manually, as shown in a {ref}`Quick Start` example: ``` @@ -239,6 +241,8 @@ To limit the variables saved in the export file, input a list of just the variab m = ptm.OpenDriftModel(export_variables=[]) ``` +The default list of `export_variables` is set in `config_model` but is modified depending on the `drift_model` set. + #### How to modify details for Stokes Drift diff --git a/docs/environment.yml b/docs/environment.yml index d93874c..bc68348 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -8,6 +8,7 @@ dependencies: - adios_db - aiohttp - appdirs + - cmocean - numpy - xarray # These are needed for the docs themselves diff --git a/docs/index.rst b/docs/index.rst index 8e54638..ef7bf80 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,6 +23,7 @@ To install from PyPI: :caption: Examples and demos quick_start + tutorial configuration diff --git a/docs/quick_start.md b/docs/quick_start.md index 529b41d..b927efe 100644 --- a/docs/quick_start.md +++ b/docs/quick_start.md @@ -59,19 +59,28 @@ This demo will run using easily-available ROMS model output from `xroms`. import particle_tracking_manager as ptm import xroms +import xarray as xr -m = ptm.OpenDriftModel(lon = -90, lat = 28.7, number=1, steps=2) +m = ptm.OpenDriftModel(lon = -90, lat = 28.7, number=10, steps=20, + use_static_masks=True) + url = xroms.datasets.CLOVER.fetch("ROMS_example_full_grid.nc") -reader_kwargs = dict(loc=url, kwargs_xarray={}) -m.add_reader(**reader_kwargs) +ds = xr.open_dataset(url, decode_times=False) +m.add_reader(ds=ds) # m.run_all() or the following m.seed() m.run() ``` +Plot using `OpenDrift`'s built in plotting. Many options are available, including animations (see [OpenDrift docs for more information](https://opendrift.github.io/)). + +```{code-cell} ipython3 +m.o.plot(fast=True) +``` + ## Idealized simulation To run an idealized scenario, no reader should be added but configuration parameters can be manually changed, for example: @@ -80,7 +89,7 @@ To run an idealized scenario, no reader should be added but configuration parame import particle_tracking_manager as ptm from datetime import datetime m = ptm.OpenDriftModel(lon=4.0, lat=60.0, start_time=datetime(2015, 9, 22, 6), - use_auto_landmask=True,) + use_auto_landmask=True, steps=20) # idealized simulation, provide a fake current m.o.set_config('environment:fallback:y_sea_water_velocity', 1) @@ -92,6 +101,10 @@ m.seed() m.run() ``` +```{code-cell} ipython3 +m.o.plot(fast=True) +``` + ## Ways to Get Information Check drifter initialization properties: diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 0000000..1e5fabe --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,278 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.16.1 +kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +--- + +# Tutorial + +Particle Tracking Manager (PTM) is a wrapper around particle tracking codes to easily run particle simulations in select (or user-input) ocean models. Currently, `OpenDrift` is included. In this tutorial we demonstrate using the four wrapped drift models from `OpenDrift` along with some high level configuration changes. + +```{code-cell} ipython3 +import xarray as xr +import particle_tracking_manager as ptm +import xroms +import cmocean.cm as cmo +``` + +## Ocean Models + +Three ocean models are built into PTM and can be accessed by name `ocean_model=` "NWGOA", "CIOFS", and "CIOFSOP", and either accessed remotely or locally if run on an internal server (at Axiom) (with `ocean_model_local=True`). Alternatively, a user can input their own xarray Dataset, which we will do for this tutorial. When you input your own Dataset, you have to add the reader by hand as opposed to being able to input the `ocean_model` name in the initial call. + +```{code-cell} ipython3 +url = xroms.datasets.CLOVER.fetch("ROMS_example_full_grid.nc") +ds = xr.open_dataset(url, decode_times=False) +``` + +## Drift Models + +After a drift simulation is run, results can be found in file with name `m.outfile_name`. + +### OceanDrift (Physics) + +This model can in 2D or 3D with or without horizontal or vertical mixing, wind drift, Stokes drift, etc. By default this would be run at the surface in 2D but we can input selections to run in 3D starting at depth. + +#### Initialize manager `m` + +```{code-cell} ipython3 +m = ptm.OpenDriftModel(lon=-90, lat=28.7, number=10, steps=40, + z=-5, do3D=True, horizontal_diffusivity=100,) +``` + +The drift_model-specific parameters chosen by the user and PTM for this simulation are: + +```{code-cell} ipython3 +m.drift_model_config() +``` + +You can also find the full PTM and `OpenDrift` configuration information with: + +```{code-cell} ipython3 +--- +editable: true +slideshow: + slide_type: '' +--- +m.show_config() +``` + +#### Add reader and run + +```{code-cell} ipython3 +m.add_reader(ds=ds) +m.run_all() +``` + +#### Plot + +```{code-cell} ipython3 +m.o.plot(linecolor="z", fast=True, cmap=cmo.deep) +``` + +### Leeway (Search and Rescue) + +These are simulations of objects that stay at the surface and are transported by both the wind and ocean currents at rates that depend on how much the object sticks up out of and down into the water. The constants to use for those rates have been experimentally determined by the coastguard and are used in this model. + +#### Initialize manager `m` + +```{code-cell} ipython3 +m = ptm.OpenDriftModel(drift_model="Leeway", lon = -89.8, lat = 29.08, + number=10, steps=40, + object_type="Fishing vessel, general (mean values)") + +# This drift model requires wind data to be set which isn't present in model output +m.o.set_config('environment:constant:x_wind', -1) +m.o.set_config('environment:constant:y_wind', 1) +``` + +The objects that can be modeled are: + +```{code-cell} ipython3 +m.show_config(key="seed:object_type")["enum"] +``` + +The drift_model-specific parameters chosen by the user and PTM for this simulation are: + +```{code-cell} ipython3 +m.drift_model_config() +``` + +#### Add reader and run + +```{code-cell} ipython3 +m.add_reader(ds=ds) +m.run_all() +``` + +#### Plot + +```{code-cell} ipython3 +m.o.plot(fast=True) +``` + +### LarvalFish + +This model simulates eggs and larvae that move in 3D with the currents and some basic behavior and vertical movement. It also simulates some basic growth of the larvae. + +There are specific seeding options for this model: +* 'diameter' +* 'neutral_buoyancy_salinity' +* 'stage_fraction' +* 'hatched' +* 'length' +* 'weight' + +#### Eggs + +An optional general flag is to initialize the drifters at the seabed, which might make sense for eggs and is demonstrated here. + +##### Initialize manager `m` + +```{code-cell} ipython3 +m = ptm.OpenDriftModel(drift_model="LarvalFish", lon=-89.85, lat=28.8, number=10, steps=45, + do3D=True, seed_seafloor=True) +``` + +The drift_model-specific parameters chosen by the user and PTM for this simulation are: + +```{code-cell} ipython3 +m.drift_model_config() +``` + +##### Add reader and run + +```{code-cell} ipython3 +m.add_reader(ds=ds) +m.run_all() +``` + +##### Plot + +```{code-cell} ipython3 +m.o.plot(linecolor="z", fast=True, cmap=cmo.deep_r) +``` + +```{code-cell} ipython3 +m.o.plot_property('length') +m.o.plot_property('weight') +m.o.plot_property('diameter') +m.o.plot_property('stage_fraction') +``` + +Output from the simulation can be viewed in the history or elements, or from the output file. + +```{code-cell} ipython3 +m.outfile_name +``` + +```{code-cell} ipython3 +m.o.history["z"].data +``` + +```{code-cell} ipython3 +m.o.elements +``` + +#### Hatched! + +##### Initialize manager `m` + +```{code-cell} ipython3 +m = ptm.OpenDriftModel(drift_model="LarvalFish", lon=-89.85, lat=28.8, number=10, steps=45, + do3D=True, seed_seafloor=True, hatched=1) +``` + +The drift_model-specific parameters chosen by the user and PTM for this simulation are: + +```{code-cell} ipython3 +m.drift_model_config() +``` + +##### Add reader and run + +```{code-cell} ipython3 +m.add_reader(ds=ds) +m.run_all() +``` + +##### Plot + +```{code-cell} ipython3 +m.o.plot(linecolor="z", fast=True, cmap=cmo.deep_r) +``` + +```{code-cell} ipython3 +m.o.plot_property('length') +m.o.plot_property('weight') +m.o.plot_property('diameter') +m.o.plot_property('stage_fraction') +``` + +### OpenOil + +This model simulates the transport of oil. Processes optionally modeled (which are included in PTM by default) include: +* "emulsification" +* "dispersion" +* "evaporation" +* "update_oilfilm_thickness" +* "biodegradation" + +There are also specific seeding options for this model: +* "m3_per_hour" +* "oil_film_thickness" +* "droplet_size_distribution" +* "droplet_diameter_mu" +* "droplet_diameter_sigma" +* "droplet_diameter_min_subsea" +* "droplet_diameter_max_subsea" + +#### Initialize manager `m` + +```{code-cell} ipython3 +m = ptm.OpenDriftModel(drift_model="OpenOil", lon=-89.85, lat=28.08, number=10, steps=45, + z=-10, do3D=True, oil_type='GENERIC BUNKER C') +m.o.set_config('environment:constant:x_wind', -1) +m.o.set_config('environment:constant:y_wind', 1) +``` + +List available oil types from NOAA's ADIOS database: + +```{code-cell} ipython3 +m.show_config(key="seed:oil_type") +``` + +The drift_model-specific parameters chosen by the user and PTM for this simulation are: + +```{code-cell} ipython3 +m.drift_model_config() +``` + +#### Add reader and run + +```{code-cell} ipython3 +m.add_reader(ds=ds) +m.run_all() +``` + +#### Plot + +```{code-cell} ipython3 +m.o.plot(linecolor="z", fast=True, cmap=cmo.deep_r) +``` + +Plot the oil budget. + +```{code-cell} ipython3 +m.o.plot_oil_budget(show_wind_and_current=False) +``` + +```{code-cell} ipython3 + +``` diff --git a/environment.yml b/environment.yml index 7452fc1..5bd95a2 100644 --- a/environment.yml +++ b/environment.yml @@ -11,6 +11,7 @@ dependencies: - fastparquet - jupyter - jupyterlab + - kerchunk - xarray - numpy - opendrift diff --git a/particle_tracking_manager/models/opendrift/opendrift.py b/particle_tracking_manager/models/opendrift/opendrift.py index 0dc10bf..e77cd3c 100644 --- a/particle_tracking_manager/models/opendrift/opendrift.py +++ b/particle_tracking_manager/models/opendrift/opendrift.py @@ -405,10 +405,28 @@ def __setattr_model__(self, name: str, value) -> None: self.o.set_config("drift:vertical_advection", True) elif name == "do3D" and value and self.drift_model == "Leeway": self.logger.info( - "do3D is True but drift_model is Leeway so " "changing do3D to False." + "do3D is True but drift_model is Leeway so changing do3D to False." ) self.do3D = False + # if drift_model is LarvalFish, vertical_mixing has to be True + if name == "vertical_mixing" and not value and self.drift_model == "LarvalFish": + raise ValueError( + "drift_model is LarvalFish which always has vertical mixing on in OpenDrift so vertical_mixing must be True." + ) + + # if drift_model is LarvalFish, surface_only can't be True + if name == "surface_only" and value and self.drift_model == "LarvalFish": + raise ValueError( + "drift_model is LarvalFish which is always 3D in OpenDrift so surface_only must be False." + ) + + # if drift_model is LarvalFish, do3D has to be True + if name == "do3D" and not value and self.drift_model == "LarvalFish": + raise ValueError( + "drift_model is LarvalFish which is always 3D in OpenDrift so do3D must be True." + ) + # Make sure vertical_mixing_timestep equals default value if vertical_mixing False if name in ["vertical_mixing", "vertical_mixing_timestep"]: vmtdef = self.config_model["vertical_mixing_timestep"]["default"] @@ -506,6 +524,17 @@ def __setattr_model__(self, name: str, value) -> None: vars = ["object_type"] self.__dict__["export_variables"] += vars self.config_model["export_variables"]["value"] += vars + elif name == "export_variables" and self.drift_model == "LarvalFish": + vars = [ + "diameter", + "neutral_buoyancy_salinity", + "stage_fraction", + "hatched", + "length", + "weight", + ] + self.__dict__["export_variables"] += vars + self.config_model["export_variables"]["value"] += vars self._update_config() @@ -725,13 +754,12 @@ def run_add_reader( # ds["angle"] = grid["angle"] try: - units_date = pd.Timestamp( - ds.ocean_time.attrs["units"].split("since ")[1] - ) - except KeyError: # for remote - units_date = pd.Timestamp( - ds.ocean_time.encoding["units"].split("since ")[0] - ) + units = ds.ocean_time.attrs["units"] + except KeyError: + units = ds.ocean_time.encoding["units"] + datestr = units.split("since ")[1] + units_date = pd.Timestamp(datestr) + # use reader start time if not otherwise input if self.start_time is None: self.logger.info("setting reader start_time as simulation start_time") @@ -740,11 +768,14 @@ def run_add_reader( self.start_time = units_date + pd.to_timedelta( ds.ocean_time[0].values, unit="s" ) - # narrow model output to simulation time if possible before sending to Reader if self.start_time is not None and self.end_time is not None: + dt_model = float( + ds.ocean_time[1] - ds.ocean_time[0] + ) # time step of the model output in seconds start_time_num = (self.start_time - units_date).total_seconds() - end_time_num = (self.end_time - units_date).total_seconds() + # want to include the next ocean model output after the last drifter simulation time + end_time_num = (self.end_time - units_date).total_seconds() + dt_model ds = ds.sel(ocean_time=slice(start_time_num, end_time_num)) self.logger.info("Narrowed model output to simulation time") else: @@ -783,11 +814,29 @@ def run_add_reader( def run_seed(self): """Seed drifters for model.""" + already_there = [ + "seed:number", + "seed:z", + "seed:seafloor", + "seed:droplet_diameter_mu", + "seed:droplet_diameter_min_subsea", + "seed:droplet_size_distribution", + "seed:droplet_diameter_sigma", + "seed:droplet_diameter_max_subsea", + "seed:object_type", + ] + seed_kws = { "time": self.start_time.to_pydatetime(), "z": self.z, } + # update seed_kws with drift_model-specific seed parameters + seedlist = self.drift_model_config(prefix="seed") + seedlist = [(one, two) for one, two in seedlist if one not in already_there] + seedlist = [(one.replace("seed:", ""), two) for one, two in seedlist] + seed_kws.update(seedlist) + if self.seed_flag == "elements": # add additional seed parameters seed_kws.update( diff --git a/particle_tracking_manager/the_manager_config.json b/particle_tracking_manager/the_manager_config.json index d58c4db..38013a9 100644 --- a/particle_tracking_manager/the_manager_config.json +++ b/particle_tracking_manager/the_manager_config.json @@ -159,7 +159,7 @@ }, "use_static_masks": { "type": "bool", - "default": false, + "default": true, "ptm_level": 2, "description": "Set to True to use static masks for known models instead of wetdry masks. If False, the masks are change in time." }, diff --git a/tests/test_opendrift.py b/tests/test_opendrift.py index 43854f5..1fd212e 100644 --- a/tests/test_opendrift.py +++ b/tests/test_opendrift.py @@ -119,16 +119,9 @@ def test_init(self): ) -class TestOpenDriftModel_OceanDrift(unittest.TestCase): +class TestOpenDriftModel_OceanDrift_static_mask(unittest.TestCase): def setUp(self): - self.model = OpenDriftModel(drift_model="OceanDrift") - - def test_error_no_end_of_simulation(self): - self.model.do3D = False - self.use_static_masks = False - # need to input steps, duration, or end_time but don't here - with pytest.raises(ValueError): - self.model.add_reader(ds=ds) + self.model = OpenDriftModel(drift_model="OceanDrift", use_static_masks=True) def test_ocean_model_not_known_ds_None(self): self.model.ocean_model = "wrong_name" @@ -137,23 +130,6 @@ def test_ocean_model_not_known_ds_None(self): with pytest.raises(ValueError): self.model.add_reader(ds=ds) - def test_drop_vars_do3D_false(self): - self.model.do3D = False - self.use_static_masks = False - self.model.steps = 4 - self.model.add_reader(ds=ds) - assert self.model.reader.variables == [ - "x_sea_water_velocity", - "y_sea_water_velocity", - "land_binary_mask", - "x_wind", - "y_wind", - "wind_speed", - "sea_water_speed", - ] - assert "wetdry_mask_rho" in self.model.reader.Dataset.data_vars - assert "mask_rho" not in self.model.reader.Dataset.data_vars - def test_drop_vars_do3D_true(self): self.model.do3D = True self.model.steps = 4 @@ -171,7 +147,6 @@ def test_drop_vars_do3D_true(self): def test_drop_vars_use_static_masks(self): self.model.do3D = False - self.model.use_static_masks = True self.model.duration = pd.Timedelta("24h") self.model.add_reader(ds=ds) assert self.model.reader.variables == [ @@ -201,28 +176,38 @@ def test_drop_vars_no_wind(self): ] -class TestOpenDriftModel_LarvalFish(unittest.TestCase): +class TestOpenDriftModel_OceanDrift_wetdry_mask(unittest.TestCase): def setUp(self): - self.model = OpenDriftModel(drift_model="LarvalFish") + self.model = OpenDriftModel(drift_model="OceanDrift", use_static_masks=False) + + def test_error_no_end_of_simulation(self): + self.model.do3D = False + # need to input steps, duration, or end_time but don't here + with pytest.raises(ValueError): + self.model.add_reader(ds=ds) def test_drop_vars_do3D_false(self): self.model.do3D = False - self.model.end_time = pd.Timestamp("1970-01-01T02:00") + self.model.steps = 4 self.model.add_reader(ds=ds) assert self.model.reader.variables == [ "x_sea_water_velocity", "y_sea_water_velocity", - "sea_water_salinity", - "sea_water_temperature", "land_binary_mask", "x_wind", "y_wind", "wind_speed", "sea_water_speed", ] + assert "wetdry_mask_rho" in self.model.reader.Dataset.data_vars + assert "mask_rho" not in self.model.reader.Dataset.data_vars - def test_drop_vars_do3D_true(self): - self.model.do3D = True + +class TestOpenDriftModel_LarvalFish(unittest.TestCase): + def setUp(self): + self.model = OpenDriftModel(drift_model="LarvalFish", do3D=True) + + def test_drop_vars_wind(self): self.model.duration = pd.Timedelta("1h") self.model.add_reader(ds=ds) assert self.model.reader.variables == [ @@ -238,26 +223,8 @@ def test_drop_vars_do3D_true(self): "sea_water_speed", ] - def test_drop_vars_no_wind(self): - self.model.stokes_drift = False - self.model.wind_drift_factor = 0 - self.model.wind_uncertainty = 0 - self.model.vertical_mixing = False - self.model.steps = 3 - self.model.add_reader(ds=ds) - # import pdb; pdb.set_trace() - assert self.model.reader.variables == [ - "x_sea_water_velocity", - "y_sea_water_velocity", - "sea_water_salinity", - "sea_water_temperature", - "land_binary_mask", - "sea_water_speed", - ] - def test_drift_model(): - """can't change the drift_model after class initialization""" with pytest.raises(ValueError): m = OpenDriftModel(drift_model="not_a_real_model") @@ -268,6 +235,7 @@ def setUp(self): # self.m.config_model = {"test_key": {"value": "old_value"}} def test_set_drift_model(self): + """can't change the drift_model after class initialization""" with self.assertRaises(KeyError): self.m.drift_model = "new_model" @@ -326,12 +294,16 @@ def test_vertical_mixing_false_mixed_layer_depth_not_default(self): class TestOpenDriftModel_Leeway(unittest.TestCase): def setUp(self): - self.m = OpenDriftModel(drift_model="Leeway") + self.m = OpenDriftModel( + drift_model="Leeway", object_type=">PIW, scuba suit (face up)" + ) def test_leeway_model_wind_drift_factor_not_default(self): self.m.wind_drift_factor = 10 d = self.m.show_config(key="wind_drift_factor") assert d["value"] == d["default"] + assert self.m.object_type == ">PIW, scuba suit (face up)" + assert self.m.o._config["object_type"]["value"] == ">PIW, scuba suit (face up)" def test_leeway_model_wind_drift_depth_not_default(self): self.m.wind_drift_depth = 10 @@ -364,5 +336,96 @@ def test_horizontal_diffusivity_logic(): assert m.horizontal_diffusivity == 150.0 # known grid values +def test_LarvalFish_disallowed_settings(): + """LarvalFish is incompatible with some settings. + + LarvalFish has to always be 3D. + """ + + with pytest.raises(ValueError): + m = OpenDriftModel(drift_model="LarvalFish", vertical_mixing=False) + + with pytest.raises(ValueError): + m = OpenDriftModel(drift_model="LarvalFish", surface_only=True) + + m = OpenDriftModel(drift_model="LarvalFish", do3D=True) + with pytest.raises(ValueError): + m.surface_only = True + + with pytest.raises(ValueError): + m = OpenDriftModel(drift_model="LarvalFish", do3D=False) + + +def test_LarvalFish_seeding(): + """Make sure special seed parameter comes through""" + + m = OpenDriftModel( + drift_model="LarvalFish", + lon=-151, + lat=60, + do3D=True, + hatched=1, + start_time="2021-01-01T00:00:00", + use_auto_landmask=True, + ) + m.seed() + assert m.o.elements_scheduled.hatched == 1 + + +def test_OpenOil_seeding(): + """Make sure special seed parameters comes through""" + + m = OpenDriftModel( + drift_model="OpenOil", + lon=-151, + lat=60, + do3D=True, + start_time="2021-01-01T00:00:00", + use_auto_landmask=True, + m3_per_hour=5, + droplet_diameter_max_subsea=0.1, + droplet_diameter_min_subsea=0.01, + droplet_diameter_mu=0.01, + droplet_size_distribution="normal", + droplet_diameter_sigma=10, + oil_film_thickness=5, + oil_type="GENERIC DIESEL", + ) + m.o.set_config("environment:constant:x_wind", -1) + m.o.set_config("environment:constant:y_wind", -1) + m.o.set_config("environment:constant:x_sea_water_velocity", -1) + m.o.set_config("environment:constant:y_sea_water_velocity", -1) + m.seed() + + # to check impact of m3_per_hour: mass_oil for m3_per_hour of 1 * 5 + assert np.allclose(m.o.elements_scheduled.mass_oil, 8.412 * 5) + assert m.o._config["m3_per_hour"]["value"] == 5 + assert m.o._config["droplet_diameter_max_subsea"]["value"] == 0.1 + assert m.o._config["droplet_diameter_min_subsea"]["value"] == 0.01 + assert m.o._config["droplet_diameter_mu"]["value"] == 0.01 + assert m.o._config["droplet_size_distribution"]["value"] == "normal" + assert m.o._config["droplet_diameter_sigma"]["value"] == 10 + assert m.o.elements_scheduled.oil_film_thickness == 5 + assert m.o._config["oil_type"]["value"] == "GENERIC DIESEL" + + +def test_wind_drift(): + """Make sure changed wind drift numbers comes through""" + + m = OpenDriftModel( + drift_model="OceanDrift", + lon=-151, + lat=60, + do3D=True, + wind_drift_factor=1, + wind_drift_depth=10, + start_time="2021-01-01T00:00:00", + use_auto_landmask=True, + ) + m.seed() + assert m.o.elements_scheduled.wind_drift_factor == 1 + assert m.o._config["wind_drift_depth"]["value"] == 10 + + if __name__ == "__main__": unittest.main()