Source code for opendrift.models.openhns

# This file is part of OpenDrift.
#
# OpenDrift is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 2
#
# OpenDrift is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenDrift.  If not, see <https://www.gnu.org/licenses/>.
#
# Copyright 2022, Knut-Frode Dagestad, MET Norway

"""
OpenHNS is a 3D HNS drift module bundled within the OpenDrift framework.
"""

import numpy as np
import logging

logger = logging.getLogger(__name__)

from opendrift.models.oceandrift import OceanDrift, Lagrangian3DArray
from opendrift.config import CONFIG_LEVEL_ESSENTIAL, CONFIG_LEVEL_BASIC, CONFIG_LEVEL_ADVANCED


# Defining the oil element properties
[docs] class HNS(Lagrangian3DArray): """Extending LagrangianArray with variables relevant for HNS particles.""" variables = Lagrangian3DArray.add_variables([ ( 'mass', { 'dtype': np.float32, 'units': 'kg', 'seed': False, 'default': 1 }), ( 'mass_evaporated', { 'dtype': np.float32, 'units': 'kg', 'seed': False, 'default': 0 }), ( 'mass_dissolved', { 'dtype': np.float32, 'units': 'kg', 'seed': False, 'default': 0 }), ( 'viscosity', { 'dtype': np.float32, 'units': 'N s/m2 (Pa s)', 'seed': False, 'default': 0.005 }), ( 'density', { 'dtype': np.float32, 'units': 'kg/m^3', 'seed': False, 'default': 880 }), ( 'wind_drift_factor', { 'dtype': np.float32, # TODO: inherit from 'units': '%', # OceanDrift 'description': 'Elements at the ocean surface are moved by ' 'this fraction of the wind vector, in addition to ' 'currents and Stokes drift', 'default': 0.02 }), ( 'diameter', { 'dtype': np.float32, # Droplet diameter 'units': 'm', 'seed': False, 'default': 0. }) ])
[docs] class OpenHNS(OceanDrift): """Open source HNS drift model based on the OpenDrift framework. Developed at MET Norway based on parameterisations found in open/published litterature. Under construction. """ ElementType = HNS required_variables = { 'x_sea_water_velocity': { 'fallback': None }, 'y_sea_water_velocity': { 'fallback': None }, 'sea_surface_height': {'fallback': 0}, 'x_wind': { 'fallback': None }, 'y_wind': { 'fallback': None }, 'upward_sea_water_velocity': { 'fallback': 0, 'important': False }, 'sea_surface_wave_significant_height': { 'fallback': 0, 'important': False }, 'sea_surface_wave_stokes_drift_x_velocity': { 'fallback': 0, 'important': False }, 'sea_surface_wave_stokes_drift_y_velocity': { 'fallback': 0, 'important': False }, 'sea_surface_wave_period_at_variance_spectral_density_maximum': { 'fallback': 0, 'important': False }, 'sea_surface_wave_mean_period_from_variance_spectral_density_second_frequency_moment': { 'fallback': 0, 'important': False }, 'sea_ice_area_fraction': { 'fallback': 0, 'important': False }, 'sea_ice_x_velocity': { 'fallback': 0, 'important': False }, 'sea_ice_y_velocity': { 'fallback': 0, 'important': False }, 'sea_water_temperature': { 'fallback': 10, 'profiles': True }, 'sea_water_salinity': { 'fallback': 34, 'profiles': True }, 'sea_floor_depth_below_sea_level': { 'fallback': 10000 }, 'ocean_vertical_diffusivity': { 'fallback': 0.02, 'important': False, 'profiles': True }, 'land_binary_mask': { 'fallback': None }, 'ocean_mixed_layer_thickness': { 'fallback': 50, 'important': False }, } hns_types = { 'butyl': {'evaporation_rate': .03, 'dissolution_rate': .05}, 'acetone': {'evaporation_rate': .16, 'dissolution_rate': .01}, 'xylene': {'evaporation_rate': .25, 'dissolution_rate': .1} } # Default colors for plotting status_colors = { 'initial': 'green', 'active': 'blue', 'missing_data': 'gray', 'stranded': 'red', 'evaporated': 'yellow', 'dispersed': 'magenta' } def __init__(self, *args, **kwargs): # Calling general constructor of parent class super(OpenHNS, self).__init__(*args, **kwargs) self._add_config({ 'seed:hns_type': { 'type': 'enum', 'enum': list(self.hns_types), 'default': list(self.hns_types)[0], 'level': CONFIG_LEVEL_ESSENTIAL, 'description': 'HNS type to be used for the simulation' }, }) self._set_config_default('drift:vertical_advection', False) self._set_config_default('drift:vertical_mixing', True) self._set_config_default('drift:current_uncertainty', 0.05) self._set_config_default('drift:wind_uncertainty', 0.5)
[docs] def seed_elements(self, hns_type=None, *args, **kwargs): if hns_type is not None: self.set_config('seed:hns_type', hns_type) self.hns_type = self.hns_types[self.get_config('seed:hns_type')] super(OpenHNS, self).seed_elements(*args, **kwargs)
[docs] def evaporation(self): surface = np.where(self.elements.z == 0)[0] random_number = np.random.uniform(0, 1, len(surface)) evaporated = np.where(random_number > 1-self.hns_type['evaporation_rate'])[0] logger.debug('Evaporating %i of %i elements at ocean surface' % (len(evaporated), len(surface))) self.elements.wind_drift_factor[surface[evaporated]] = 1 # Shall follow wind 100% self.elements.z[surface[evaporated]] = 10 # Moving evaporated elements to 10m height self.elements.mass_evaporated[surface[evaporated]] = self.elements.mass[surface[evaporated]] self.elements.mass[surface[evaporated]] = 0
[docs] def dissolution(self): surface = np.where(self.elements.z == 0)[0] random_number = np.random.uniform(0, 1, len(surface)) dissolved = np.where(random_number > 1-self.hns_type['dissolution_rate'])[0] logger.debug('Dissolving %i of %i elements at ocean surface' % (len(dissolved), len(surface))) self.elements.wind_drift_factor[surface[dissolved]] = 0 # Submerged, no windage self.elements.z[surface[dissolved]] = -10 # Moving dissolved elements to 10m depth self.elements.mass_dissolved[surface[dissolved]] = self.elements.mass[surface[dissolved]] self.elements.mass[surface[dissolved]] = 0
[docs] def update(self): self.evaporation() self.dissolution() self.update_terminal_velocity() #self.vertical_mixing() self.advect_ocean_current() self.stokes_drift() self.advect_wind()