Skip to content

Commit

Permalink
Merge pull request #1274 from knutfrode/dev
Browse files Browse the repository at this point in the history
Model property reguired_profiles_z_range is now replaced with config …
  • Loading branch information
knutfrode authored Apr 12, 2024
2 parents 26e116f + 3d8f99f commit 1eadfa7
Show file tree
Hide file tree
Showing 18 changed files with 72 additions and 78 deletions.
31 changes: 15 additions & 16 deletions opendrift/models/basemodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ def __init__(self,

super().__init__()

self.profiles_depth = None

self.show_continuous_performance = False

self.origin_marker = None # Dictionary to store named seeding locations
Expand All @@ -234,8 +236,7 @@ def __init__(self,
# List to store GeoJSON dicts of seeding commands
self.seed_geojson = []

self.env = Environment(self.required_variables,
self.required_profiles_z_range, self._config)
self.env = Environment(self.required_variables, self._config)

# Make copies of dictionaries so that they are private to each instance
self.status_categories = ['active'] # Particles are active by default
Expand Down Expand Up @@ -409,6 +410,9 @@ def __init__(self,
'description': 'Add horizontal diffusivity (random walk)',
'level': CONFIG_LEVEL_BASIC
},
'drift:profiles_depth': {'type': 'float', 'default': 50, 'min': 0, 'max': None,
'level': CONFIG_LEVEL_ADVANCED, 'units': 'meters', 'description':
'Environment profiles will be retrieved from surface and down to this depth'},
'drift:wind_uncertainty': {
'type': 'float',
'default': 0,
Expand Down Expand Up @@ -592,8 +596,10 @@ def add_readers_from_file(self, *args, **kwargs):
'''Make readers from a file containing list of URLs or paths to netCDF datasets'''
self.env.add_readers_from_file(*args, **kwargs)

# To be overloaded by sublasses, but this parent method must be called
def prepare_run(self):
pass # to be overloaded when needed
# Copy profile_depth from config
self.profiles_depth = self.get_config('drift:profiles_depth')

def store_present_positions(self, IDs=None, lons=None, lats=None):
"""Store present element positions, in case they shall be moved back"""
Expand Down Expand Up @@ -661,8 +667,7 @@ def interact_with_coastline(self, final=False):
self.time,
self.elements.lon,
self.elements.lat,
self.elements.z,
None)
self.elements.z)
self.environment.land_binary_mask = en.land_binary_mask

if i == 'stranding': # Deactivate elements on land, but not in air
Expand Down Expand Up @@ -749,10 +754,6 @@ def ElementType(self):
def required_variables(self):
"""Any trajectory model implementation must list needed variables."""

@abstractproperty
def required_profiles_z_range(self):
"""Any trajectory model implementation must list range or return None."""

def test_data_folder(self):
import opendrift
return os.path.abspath(
Expand Down Expand Up @@ -914,8 +915,7 @@ def closest_ocean_points(self, lon, lat):
lon=lon,
lat=lat,
z=0 * lon,
time=land_reader.start_time,
profiles=None)[0]['land_binary_mask']
time=land_reader.start_time)[0]['land_binary_mask']
if land.max() == 0:
logger.info('All points are in ocean')
return lon, lat
Expand All @@ -936,8 +936,7 @@ def closest_ocean_points(self, lon, lat):
lon=longrid,
lat=latgrid,
z=0 * longrid,
time=land_reader.start_time,
profiles=None)[0]['land_binary_mask']
time=land_reader.start_time)[0]['land_binary_mask']
if landgrid.min() == 1 or np.isnan(landgrid.min()):
logger.warning('No ocean pixels nearby, cannot move elements.')
return lon, lat
Expand Down Expand Up @@ -1092,8 +1091,7 @@ def seed_elements(self,
self.time = time[0]
env, env_profiles, missing = \
self.env.get_environment(['sea_floor_depth_below_sea_level'],
time=time[0], lon=lon, lat=lat,
z=0*lon, profiles=None)
time=time[0], lon=lon, lat=lat, z=0*lon)
elif seafloor_fallback is not None:
env = {
'sea_floor_depth_below_sea_level':
Expand Down Expand Up @@ -2046,7 +2044,8 @@ def run(self,
self.elements.lon,
self.elements.lat,
self.elements.z,
self.required_profiles)
self.required_profiles,
self.profiles_depth)

self.store_previous_variables()

Expand Down
31 changes: 13 additions & 18 deletions opendrift/models/basemodel/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,21 @@ class Environment(Timeable, Configurable):
readers: OrderedDict
priority_list: OrderedDict
required_variables: Dict
required_profiles_z_range: List[float] # [min_depth, max_depth]

discarded_readers: Dict

proj_latlon = pyproj.Proj('+proj=latlong')

__finalized__ = False

def __init__(self, required_variables, required_profiles_z_range, _config):
def __init__(self, required_variables, _config):
super().__init__()

self.readers = OrderedDict()
self.priority_list = OrderedDict()
self.discarded_readers = {}

self.required_variables = required_variables
self.required_profiles_z_range = required_profiles_z_range
self._config = _config # reference to simulation config

# Add constant and fallback environment variables to config
Expand Down Expand Up @@ -521,7 +519,7 @@ def missing_variables(self):
if var not in self.priority_list
]

def get_environment(self, variables, time, lon, lat, z, profiles):
def get_environment(self, variables, time, lon, lat, z, profiles=None, profiles_depth=None):
'''Retrieve environmental variables at requested positions.
Args:
Expand All @@ -536,7 +534,9 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
z: depth to get value for
profiles: ?
profiles: list of variables for which profiles are needed
profiles_depth: depth of profiles in meters, as a positive number
Updates:
Buffer (raw data blocks) for each reader stored for performance:
Expand All @@ -562,6 +562,8 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
for readername, reader in self.readers.copy().items():
self.discard_reader_if_not_relevant(reader, time)

if profiles_depth is None:
profiles_depth = np.abs(z).max()
if 'drift:truncate_ocean_model_below_m' in self._config:
truncate_depth = self.get_config(
'drift:truncate_ocean_model_below_m')
Expand All @@ -570,12 +572,7 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
truncate_depth)
z = z.copy()
z[z < -truncate_depth] = -truncate_depth
if self.required_profiles_z_range is not None:
self.required_profiles_z_range = np.array(
self.required_profiles_z_range)
self.required_profiles_z_range[
self.required_profiles_z_range <
-truncate_depth] = -truncate_depth
profiles_depth = np.minimum(profiles_depth, truncate_depth)

# Initialise more lazy readers if necessary
missing_variables = ['missingvar']
Expand Down Expand Up @@ -631,7 +628,7 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
'Missing variables: calling get_environment recursively'
)
return self.get_environment(
variables, time, lon, lat, z, profiles)
variables, time, lon, lat, z, profiles, profiles_depth)
continue
# Fetch given variables at given positions from current reader
try:
Expand All @@ -648,7 +645,7 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
env_tmp, env_profiles_tmp = \
reader.get_variables_interpolated(
variable_group, profiles_from_reader,
self.required_profiles_z_range, time,
profiles_depth, time,
lon[missing_indices], lat[missing_indices],
z[missing_indices], self.proj_latlon)

Expand All @@ -662,7 +659,7 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
'Missing variables: calling get_environment recursively'
)
return self.get_environment(
variables, time, lon, lat, z, profiles)
variables, time, lon, lat, z, profiles, profiles_depth)
continue

except Exception as e: # Unknown error
Expand Down Expand Up @@ -693,7 +690,7 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
'Missing variables: calling get_environment recursively'
)
return self.get_environment(
variables, time, lon, lat, z, profiles)
variables, time, lon, lat, z, profiles, profiles_depth)
continue

# Copy retrieved variables to env array, and mask nan-values
Expand Down Expand Up @@ -806,9 +803,7 @@ def get_environment(self, variables, time, lon, lat, z, profiles):
logger.debug('Creating empty dictionary for profiles not '
'profided by any reader: ' +
str(self.required_profiles))
env_profiles = {}
env_profiles['z'] = \
np.array(self.required_profiles_z_range)[::-1]
env_profiles = {'z': [0, -profiles_depth]}
if var not in env_profiles:
logger.debug(
' Using fallback value %s for %s for all profiles'
Expand Down
4 changes: 1 addition & 3 deletions opendrift/models/chemicaldrift.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@ class ChemicalDrift(OceanDrift):
'pH_sediment':{'fallback': 6.9, 'profiles': False}, # supplied by the user, with pH_sediment as standard name #
}

# The depth range (in m) which profiles shall cover
required_profiles_z_range = [-20, 0]


def specie_num2name(self,num):
return self.name_species[num]
Expand Down Expand Up @@ -374,6 +371,7 @@ def prepare_run(self):
if (hasattr(value,'sigma') or hasattr(value,'z') ):
self.DOC_vertical_levels_given = True

super(ChemicalDrift, self).prepare_run()

def init_species(self):
# Initialize specie types
Expand Down
1 change: 0 additions & 1 deletion opendrift/models/larvalfish.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ class LarvalFish(OceanDrift):
'sea_surface_wave_stokes_drift_y_velocity': {'fallback': 0},
}

required_profiles_z_range = [0, -50] # The depth range (in m) which profiles should cover

def __init__(self, *args, **kwargs):

Expand Down
4 changes: 2 additions & 2 deletions opendrift/models/model_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,10 @@ class ModelTemplate(OceanDrift):
# If the attribute 'profiles' is True for a variable (as for
# ocean_vertical_diffusivity in the example above), a vertical profile
# of this variable is obtained over the vertical depth interval as
# defined below. These profiles are used for the vertical turbulence scheme:
# defined by config setting drift:profiles_depth. These profiles are used for the vertical turbulence scheme:
# https://opendrift.github.io/_modules/opendrift/models/oceandrift.html#OceanDrift.vertical_mixing
# Profiles are typically required for ocean_vertical_diffusivity, and sometimes for
# salinity and temperature in order to calculate the stratification.
required_profiles_z_range = [-50, 0]

def __init__(self, *args, **kwargs):

Expand Down Expand Up @@ -236,6 +235,7 @@ def update(self):

def prepare_run(self):
"""Code to be run before a simulation loop (``update()``)"""
# This method must also call the corresponding method of parent class

def bottom_interaction(self, seafloor_depth):
"""Sub method of vertical_mixing, determines settling"""
15 changes: 8 additions & 7 deletions opendrift/models/oceandrift.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ class OceanDrift(OpenDriftSimulation):
'land_binary_mask': {'fallback': None},
}

# The depth range (in m) which profiles shall cover
required_profiles_z_range = [-20, 0]

def __init__(self, *args, **kwargs):

Expand All @@ -116,6 +114,9 @@ def __init__(self, *args, **kwargs):
else:
logger.debug('No machine learning correction available.')

if hasattr(self, 'required_profiles_z_range'):
raise ValueError('self.required_profiles_z_range is obsolete, and replaced by config setting drift:profile_depth. Please update your model.')

# Calling general constructor of parent class
super(OceanDrift, self).__init__(*args, **kwargs)

Expand All @@ -138,9 +139,6 @@ def __init__(self, *args, **kwargs):
'vertical_mixing:TSprofiles': {'type': 'bool', 'default': False, 'level':
CONFIG_LEVEL_ADVANCED,
'description': 'Update T and S profiles within inner loop of vertical mixing. This takes more time, but may be slightly more accurate.'},
'drift:profiles_depth': {'type': 'float', 'default': 50, 'min': 0, 'max': None,
'level': CONFIG_LEVEL_ADVANCED, 'units': 'meters', 'description':
'Environment profiles will be retrieved from surface and down to this depth'},
'drift:wind_drift_depth': {'type': 'float', 'default': 0.1,
'min': 0, 'max': 10, 'units': 'meters',
'description': 'The direct wind drift (windage) is linearly decreasing from the surface value (wind_drift_factor) until 0 at this depth.',
Expand Down Expand Up @@ -394,6 +392,9 @@ def update_terminal_velocity(self, Tprofiles=None, Sprofiles=None,
def prepare_vertical_mixing(self):
pass # To be implemented by subclasses as needed

def prepare_run(self):
super(OceanDrift, self).prepare_run()

def vertical_advection(self):
"""Move particles vertically according to vertical ocean current
Expand Down Expand Up @@ -520,15 +521,15 @@ def vertical_mixing(self, store_depths=False):
diffusivity_fallback):
Kprofiles = self.environment_profiles[
'ocean_vertical_diffusivity']
mixing_z = self.environment_profiles['z']
mixing_z = self.environment_profiles['z'].copy()
logger.debug('Using diffusivity from ocean model')
else:
logger.debug('Using diffusivity from Large1994 since model diffusivities not available')
mixing_z = mixing_z_analytical
Kprofiles = self.get_diffusivity_profile('windspeed_Large1994', np.abs(mixing_z))
elif diffusivity_model == 'constant':
logger.debug('Using constant diffusivity specified by fallback_values[''ocean_vertical_diffusivity''] = %s m2.s-1' % (diffusivity_fallback))
mixing_z = self.environment_profiles['z']
mixing_z = self.environment_profiles['z'].copy()
Kprofiles = diffusivity_fallback*np.ones(
self.environment_profiles['ocean_vertical_diffusivity'].shape) # keep constant value for ocean_vertical_diffusivity
else:
Expand Down
2 changes: 0 additions & 2 deletions opendrift/models/openberg_old.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ class OpenBergOld(OpenDriftSimulation):
'land_binary_mask': {'fallback': None},
}

required_profiles_z_range = [-120, 0] # [min_depth, max_depth]

# Default colors for plotting
status_colors = {'initial': 'green', 'active': 'blue',
'missing_data': 'gray', 'stranded': 'red'}
Expand Down
2 changes: 0 additions & 2 deletions opendrift/models/openhns.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,6 @@ class OpenHNS(OceanDrift):
},
}

# The depth range (in m) which profiles shall cover
required_profiles_z_range = [-20, 0]

max_speed = 1.3 # m/s

Expand Down
5 changes: 2 additions & 3 deletions opendrift/models/openoil/openoil.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,6 @@ class OpenOil(OceanDrift):
},
}

# The depth range (in m) which profiles shall cover
required_profiles_z_range = [-20, 0]

max_speed = 1.3 # m/s

Expand Down Expand Up @@ -673,7 +671,6 @@ def oil_weathering(self):
self.timer_end('main loop:updating elements:oil weathering')

def prepare_run(self):

if self.oil_weathering_model == 'noaa':
self.noaa_mass_balance = {}
# Populate with seeded mass spread on oiltype.mass_fraction
Expand Down Expand Up @@ -706,6 +703,8 @@ def prepare_run(self):
logger.warning('Could not load max water content file')
print(e)

super(OpenOil, self).prepare_run()

def oil_weathering_noaa(self):
'''Oil weathering scheme adopted from NOAA PyGNOME model:
https://github.com/NOAA-ORR-ERD/PyGnome
Expand Down
2 changes: 0 additions & 2 deletions opendrift/models/pelagicegg.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ class PelagicEggDrift(OceanDrift):
'upward_sea_water_velocity': {'fallback': 0},
}

# The depth range (in m) which profiles shall cover
required_profiles_z_range = [-120, 0]

# Default colors for plotting
status_colors = {'initial': 'green', 'active': 'blue',
Expand Down
5 changes: 1 addition & 4 deletions opendrift/models/radionuclides.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ class RadionuclideDrift(OceanDrift):
'conc3': {'fallback': 1.e-3},
}

# The depth range (in m) which profiles shall cover
required_profiles_z_range = [-20, 0]


def specie_num2name(self,num):
return self.name_species[num]
Expand Down Expand Up @@ -226,7 +223,7 @@ def prepare_run(self):
logger.info('nspecies: %s' % self.nspecies)
logger.info('Transfer rates:\n %s' % self.transfer_rates)


super(RadionuclideDrift, self).prepare_run()


def init_species(self):
Expand Down
Loading

0 comments on commit 1eadfa7

Please sign in to comment.