:py:mod:`gnome.weatherers` ========================== .. py:module:: gnome.weatherers Submodules ---------- .. toctree:: :titlesonly: :maxdepth: 1 bio_degradation/index.rst cleanup/index.rst core/index.rst dissolution/index.rst emulsification/index.rst evaporation/index.rst manual_beaching/index.rst natural_dispersion/index.rst roc/index.rst spreading/index.rst Package Contents ---------------- Classes ~~~~~~~ .. autoapisummary:: gnome.weatherers.Weatherer gnome.weatherers.HalfLifeWeatherer gnome.weatherers.Skimmer gnome.weatherers.Burn gnome.weatherers.ChemicalDispersion gnome.weatherers.Beaching gnome.weatherers.Evaporation gnome.weatherers.NaturalDispersion gnome.weatherers.Dissolution gnome.weatherers.Emulsification gnome.weatherers.Biodegradation gnome.weatherers.Langmuir gnome.weatherers.FayGravityViscous gnome.weatherers.ConstantArea gnome.weatherers.ROC_Burn gnome.weatherers.ROC_Disperse gnome.weatherers.ROC_Skim Functions ~~~~~~~~~ .. autoapisummary:: gnome.weatherers.weatherer_sort Attributes ~~~~~~~~~~ .. autoapisummary:: gnome.weatherers.sort_order gnome.weatherers.weatherer_schemas gnome.weatherers.weatherers_idx gnome.weatherers.weatherers_by_name gnome.weatherers.standard_weatherering_sets .. py:class:: Weatherer(**kwargs) Bases: :py:obj:`gnome.movers.movers.Process` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation Base weatherer class; defines the API for all weatherers Passes optional arguments to base (Process) class via super. See base class for optional arguments: `gnome.movers.mover.Process` adds 'mass_components', 'mass' to array_types since all weatherers need these. .. py:attribute:: _schema .. py:method:: __repr__() Return repr(self). .. py:method:: initialize_data(sc, num_released) Let weatherers have a way to customize the initialization of data arrays. Currently, only some weatherers use this to customize initialization of data arrays. If movers also move towards this implementation, then move to 'Process' base class. .. py:method:: prepare_for_model_run(sc) Override for weatherers so they can initialize correct 'mass_balance' key and set initial value to 0.0 .. py:method:: weather_elements(sc, time_step, model_time) Run the equivalent of get_move for weathering processes. It modifies the SpillContainer's data arrays; most weatherers update 'mass_components' and 'mass' Some objects do not implement this since they update arrays like 'area' in model_step_is_done() .. py:method:: _halflife(M_0, factors, time) Assumes our factors are half-life values .. py:method:: _exp_decay(M_0, lambda_, time) Exponential decay: x(t) = exp(lambda_*time) The `lambda_` should be 'negative' in order for function to decay .. py:method:: get_wind_speed(points, model_time, min_val=0, coord_sys='r', fill_value=1.0) Wrapper for the weatherers so they can get wind speeds .. py:method:: check_time(wind, model_time) Should have an option to extrapolate but for now we do by default TODO, FIXME: This function does not appear to be used by anything. Removing it does not break any of the unit tests. If it is not used, it should probably go away. .. py:class:: HalfLifeWeatherer(half_lives=(15.0 * 60, ), **kwargs) Bases: :py:obj:`Weatherer` Give half-life for all components and decay accordingly The half_lives are a property of HalfLifeWeatherer. If the len(half_lives) != gnome.array_types.mass_components.shape[0] then, only keep the number of elements of half_lives that equal the length of half_lives and consequently the mass_components array. The default is 5, it is possible to change default but not easily done. HalfLifeWeatherer is currently more for testing, so will change this if it becomes more widely used and there is a need for user to change default number of mass components. half_lives could be constants or could be something more complex like a function of time (not implemented yet). Not storing 'half_lives' in data_arrays since they are neither time-varying nor varying per LE. .. py:property:: half_lives .. py:attribute:: _schema .. py:method:: weather_elements(sc, time_step, model_time) weather elements over time_step .. py:class:: Skimmer(amount=0, units=None, water=None, **kwargs) Bases: :py:obj:`CleanUpBase` Just need to add a few internal methods for Skimmer + Burn common code Currently defined as a base class. initialize Skimmer object - calls base class __init__ using super() active_range is required cleanup operations must have a valid datetime - cannot use -inf and inf active_range is used to get the mass removal rate .. py:property:: units return units for amount skimmed .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'skimmer' .. py:attribute:: _req_refs :value: ['water'] .. py:method:: _validunits(value) checks if units are either valid_vol_units or valid_mass_units .. py:method:: prepare_for_model_run(sc) no need to call base class since no new array_types were added .. py:method:: prepare_for_model_step(sc, time_step, model_time) Do sub timestep resolution here so numbers add up correctly Mark LEs to be skimmed - do them in order right now. Assume all LEs that are released together will be skimmed together since they would be closer to each other in position. Assumes: there is more mass in water than amount of mass to be skimmed. The LEs marked for Skimming are marked only once - code checks to see if any LEs are marked for skimming and if none are found, it marks them. .. py:method:: _mass_to_remove(substance) use density at 15C, ie corresponding with API to do mass/volume conversion .. py:method:: weather_elements(sc, time_step, model_time) Assumes there is only ever 1 substance being modeled! remove mass equally from LEs marked to be skimmed .. py:class:: Burn(area=None, thickness=None, active_range=(InfDateTime('-inf'), InfDateTime('inf')), area_units='m^2', thickness_units='m', efficiency=1.0, wind=None, water=None, **kwargs) Bases: :py:obj:`CleanUpBase` Just need to add a few internal methods for Skimmer + Burn common code Currently defined as a base class. Set the area of boomed oil to be burned. Cleanup operations must have a valid datetime for active start, cannot use -inf. Cannot set active stop - burn automatically stops when oil/water thickness reaches 2mm. :param float area: area of boomed oil/water mixture to burn :param float thickness: thickness of boomed oil/water mixture :param datetime active_range: time when the burn starts is the only thing we track. However we give a range to be consistent with all other weatherers. :param str area_units: default is 'm^2' :param str thickness_units: default is 'm' :param float efficiency: burn efficiency, must be greater than 0 and less than or equal to 1.0 :param wind: gnome.environment.Wind object. Only used to set efficiency if efficiency is None. Efficiency is defined as: 1 - 0.07 * wind.get_value(model_time) where wind.get_value(model_time) is value of wind at model_time Kwargs passed onto base class: :param str name: name of object :param bool on: whether object is on or not for the run .. py:property:: area_units .. py:property:: active_range .. py:property:: thickness .. py:property:: thickness_units .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'burn' .. py:attribute:: _req_refs :value: ['water', 'wind'] .. py:attribute:: valid_area_units .. py:attribute:: valid_length_units .. py:method:: _log_thickness_warning() when thickness or thickness_units are updated, check to see that the value in SI units is > _min_thickness. If it is not, then log a warning .. py:method:: prepare_for_model_run(sc) resets internal _oilwater_thickness variable to initial thickness specified by user and active stop to 'inf' again. initializes sc.mass_balance['burned'] = 0.0 .. py:method:: prepare_for_model_step(sc, time_step, model_time) 1. set 'active' flag based on active start, and model_time 2. Mark LEs to be burned - do them in order right now. Assume all LEs that are released together will be burned together since they would be closer to each other in position. Assumes: there is more mass in water than amount of mass to be burned. The LEs marked for Burning are marked only once - during the very first step that the object becomes active .. py:method:: _init_rate_duration(avg_frac_oil=1) burn duration based on avg_frac_oil content for LEs marked for burn __init__ invokes this to initialize all parameters assuming frac_water = 0.0 .. py:method:: _set_burn_params(sc, substance) Once LEs are marked for burn, the frac_water does not change set burn rate for oil/water thickness, as well as volume burn rate for oil: If data contains LEs marked for burning, then: avg_frac_oil = mass_weighed_avg(1 - data['frac_water']) _oilwater_thick_burnrate = 0.000058 * avg_frac_oil _oil_vol_burnrate = _oilwater_thick_burnrate * avg_frac_oil * area The burn duration is also known if efficiency is constant. However, if efficiency is based on variable wind, then duration cannot be computed. .. py:method:: _set_efficiency(points, model_time) return burn efficiency either from efficiency attribute or computed from wind .. py:method:: weather_elements(sc, time_step, model_time) 1. figure out the mass to remove for current timestep based on rate and efficiency. Find fraction of total mass and remove equally from all 'mass_components' of LEs marked for burning. 2. update 'mass' array and the amount burned in mass_balance dict 3. append to _burn_duration for each timestep .. py:class:: ChemicalDispersion(fraction_sprayed, active_range=(InfDateTime('-inf'), InfDateTime('inf')), waves=None, efficiency=1.0, **kwargs) Bases: :py:obj:`CleanUpBase` Just need to add a few internal methods for Skimmer + Burn common code Currently defined as a base class. another mass removal mechanism. The volume specified gets dispersed with efficiency based on wave conditions. :param volume: volume of oil (not oil/water?) applied with surfactant :type volume: float :param units: volume units :type units: str :param active_range: Range of datetimes for when the mover should be active :type active_range: 2-tuple of datetimes :param waves: waves object - query to get height. It must contain get_value() method. Default is None to support object creation by WebClient before a waves object is defined :type waves: an object with same interface as gnome.environment.Waves Optional Argument: Either efficiency or waves must be set before running the model. If efficiency is not set, then use wave height to estimate an efficiency :param efficiency: efficiency of operation. :type efficiency: float between 0 and 1 remaining kwargs include 'on' and 'name' and these are passed to base class via super .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'chem_dispersion' .. py:attribute:: _req_refs :value: ['waves'] .. py:method:: prepare_for_model_run(sc) reset _rate to None. It gets set when LEs are marked to be dispersed. .. py:method:: prepare_for_model_step(sc, time_step, model_time) 1. invoke base class method (using super) to set active flag 2. mark LEs for removal 3. set internal _rate attribute for mass removal [kg/sec] .. py:method:: _set_efficiency(points, model_time) .. py:method:: weather_elements(sc, time_step, model_time) for now just take away 0.1% at every step .. py:class:: Beaching(active_range, units='m^3', timeseries=None, water=None, **kwargs) Bases: :py:obj:`gnome.weatherers.cleanup.RemoveMass`, :py:obj:`gnome.weatherers.Weatherer` It isn't really a response/cleanup option; however, it works in the same manner in that Beaching removes mass at a user specified rate. Mixin the RemoveMass functionality. Initialization for the manual beaching events. :param timeseries: array containing the volume of oil beached at specified time. The time corresponds with end time of the beaching contains: [(t0, v0), (t1, v1), ..] Assumes the delta time (t1 - t0) is larger than model's time_step. .. note:: Assumes the model's time_step is smaller than the timeseries timestep, meaning the fixme: water is never used -- it should be removed. .. py:property:: timeseries .. py:property:: units .. py:attribute:: _schema .. py:method:: convert_to_internal_volume() .. py:method:: prepare_for_model_run(sc) Preparation of data arrays related to beaching .. py:method:: _remove_mass(time_step, model_time, substance) returns the mass to be removed over time interval: (model_time, model_time + time_step) .. note:: invoked by weather_elements only if object is active for the step. fixme: the conversion to mass should happen all at once when the timeseries is set. and it should use substance.standard_density .. py:method:: weather_elements(sc, time_step, model_time) remove equal fraction of mass from each component. .. py:class:: Evaporation(water=None, wind=None, **kwargs) Bases: :py:obj:`gnome.weatherers.Weatherer` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation :param conditions: gnome.environment.Conditions object which contains things like water temperature :param wind: wind object for obtaining speed at specified time :type wind: Wind API, specifically must have get_value(time) method .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'evaporation' .. py:attribute:: _req_refs :value: ['water', 'wind'] .. py:method:: prepare_for_model_run(sc) add evaporated key to mass_balance for now also add 'density' key here Assumes all spills have the same type of oil .. py:method:: _mass_transport_coeff(points, model_time) Is wind a function of only model_time? How about time_step? at present yes since wind only contains timeseries data K = c * U ** 0.78 if U <= 10 m/s K = 0.06 * c * U ** 2 if U > 10 m/s If K is expressed in m/sec, then Buchanan and Hurford set c = 0.0025 U is wind_speed 10m above the surface .. note:: wind speed is at least 1 m/s, unless there is an ice aware wind. .. note:: ice aware wind enforces minimum speed before applying coverage factor. .. py:method:: _set_evap_decay_constant(points, model_time, data, substance, time_step) .. py:method:: weather_elements(sc, time_step, model_time) weather elements over time_step - sets 'evaporation' in sc.mass_balance - currently also sets 'density' in sc.mass_balance but may update this as we add more weatherers and perhaps density gets set elsewhere Following diff eq models rate of change each pseudocomponent of oil:: dm(t)/dt = -(1 - fw) * A/B * m(t) Over a time-step, A, B, C are assumed constant. m(t) is the component mass at beginning of timestep; m(t + Dt) is mass at end of timestep:: m(t + Dt) = m(t) * exp(-L * Dt) L := (1 - fw) * A/B Define properties for each pseudocomponent of oil and constants:: vp: vapor pressure mw: molecular weight The following quantities are defined for a given blob of oil. The thickness of the blob is same for all LEs regardless of how many LEs are used to model the blob:: area: area computed from fay spreading m_i: mass of component 'i' sum_m_mw: sum(m_i/mw_i) over all components effect of wind - mass transport coefficient:: K: See _mass_transport_coeff() Finally, Evaporation of component 'i' for blob of oil:: A = area * K * vp B = gas_constant * water_temp * sum_m_mw L becomes:: L = (1 - fw) * area * K * vp/(gas_constant * water_temp * sum_m_mw) .. py:class:: NaturalDispersion(waves=None, water=None, algorithm='D&S1988', **kwargs) Bases: :py:obj:`gnome.weatherers.Weatherer` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation :param conditions: gnome.environment.Conditions object which contains things like water temperature :param waves: waves object for obtaining wave_height, etc at given time .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'dispersion' .. py:attribute:: _req_refs :value: ['waves', 'water'] .. py:attribute:: _algorithms_opts .. py:method:: prepare_for_model_run(sc) add dispersion and sedimentation keys to mass_balance Assumes all spills have the same type of oil .. py:method:: prepare_for_model_step(sc, time_step, model_time) Set/update arrays used by dispersion module for this timestep: .. py:method:: weather_elements(sc, time_step, model_time) weather elements over time_step - sets 'natural_dispersion' and 'sedimentation' in sc.mass_balance .. py:method:: disperse_oil_Li(time_step, frac_water, mass, viscosity, density, area, disp_out, sed_out, droplet_avg_size, frac_breaking_waves, disp_wave_energy, wave_height, visc_w, rho_w, sediment, V_entrain, ka) Oil natural dispersion algorithm developed by Li et al., (2017) .. py:method:: disperse_oil_DS(time_step, frac_water, mass, viscosity, density, area, disp_out, sed_out, droplet_avg_size, frac_breaking_waves, disp_wave_energy, wave_height, visc_w, rho_w, sediment, V_entrain, ka) Oil natural dispersion model developed by Delvgine and Sweeney (1988) Right now we just want to recreate what the lib_gnome dispersion function is doing...but in python. This will allow us to more easily refactor, and we can always then put it back into lib_gnome if necessary. (TODO: Not quite finished with the function yet.) .. py:class:: Dissolution(waves=None, wind=None, **kwargs) Bases: :py:obj:`gnome.weatherers.Weatherer` Dissolution is still under development and not recommended for use. :param waves: waves object for obtaining wave_height, etc. at a given time .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'dissolution' .. py:attribute:: _req_refs :value: ['waves', 'wind'] .. py:method:: prepare_for_model_run(sc) Add dissolution key to mass_balance if it doesn't exist. - Assumes all spills have the same type of oil - let's only define this the first time .. py:method:: prepare_for_model_step(sc, time_step, model_time) Set/update arrays used by dispersion module for this timestep .. py:method:: initialize_data(sc, num_released) initialize the newly released portions of our data arrays: If on is False, then arrays should not be included - dont' initialize .. py:method:: _initialize_k_ow(sc, num_released) Initialize the molar averaged oil/water partition coefficient. Actually, there is nothing to do to initialize our partition coefficient, as it is recalculated in dissolve_oil() .. py:method:: dissolve_oil(data, substance, **kwargs) Here is where we calculate the dissolved oil. We will outline the steps as we go along, but off the top of my head: - recalculate the partition coefficient (K_ow) - droplet distribution per LE should be calculated by the natural dispersion process and saved in the data arrays before the dissolution weathering process. - for each LE: .. note:: right now the natural dispersion process only calculates a single average droplet size. But we still treat it as an iterable. - for each droplet size category: - calculate the water phase transfer velocity (k_w) - calculate the mass xfer rate coefficient (beta) - calculate the water column time fraction (f_wc) - calculate the mass dissolved during refloat period - calculate the mass dissolved from the slick during the calm period. - the mass dissolved in the water column and the slick is summed per mass fraction (should only be aromatic fractions) - the sum of dissolved masses are compared to the existing mass fractions and adjusted to make sure we don't dissolve more mass than exists in the mass fractions. .. py:method:: oil_avg_density(masses, densities) .. py:method:: oil_total_volume(masses, densities) .. py:method:: state_variable(masses, densities, arom_mask) .. py:method:: beta_coeff(k_w, K_ow, v_inert) .. py:method:: water_column_time_fraction(points, model_time, water_phase_xfer_velocity) .. py:method:: calm_between_wave_breaks(points, model_time, time_step, time_spent_in_wc=0.0) .. py:method:: oil_concentration(masses, densities) .. py:method:: droplet_subsurface_mass_xfer_rate(droplet_avg_size, k_w, oil_concentrations, partition_coeffs, arom_mask, total_volumes) Here we are implementing something similar to equations - 1.26: this should estimate the mass xfer rate in kg/s .. note:: For this equation to work, we need to estimate the total surface area of all droplets, not just a single one - 1.27: this should estimate the mass xfer rate per unit area in kg/(m^2 * s) - 1.28: combines equations 1.26 and 1.27 - 1.29: estimates the surface area of a single droplet. We return the mass xfer rate in units (kg/s) .. note:: The Cohen equation (eq. 1.1, 1.27), I believe, is actually expressed in kg/(m^2 * hr). So we need to convert our time units. .. note:: for now, we are receiving a single average droplet size, which we assume will account for 100% of the oil volume. In the future we will need to work with something like:: [(drop_size, vol_fraction, k_w_drop), ... ] This is because each droplet bin will represent a fraction of the total oil volume (or mass?), and will have its own distinct rise velocity. oil_concentrations and partition coefficients will be the same regardless of droplet size. .. py:method:: slick_subsurface_mass_xfer_rate(points, model_time, oil_concentration, partition_coeff, slick_area, arom_mask) Here we are implementing something similar to equation 1.21 of our dissolution document. The Cohen equation (eq. 1.1), I believe, is actually expressed in kg/(m^2 * hr). So we need to convert our time units. We return the mass xfer rate in units (kg/s) .. py:method:: weather_elements(sc, time_step, model_time) weather elements over time_step .. py:class:: Emulsification(waves=None, **kwargs) Bases: :py:obj:`gnome.weatherers.Weatherer` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation :param conditions: gnome.environment.Conditions object which contains things like water temperature :param waves: waves object for obtaining emulsification wind speed at specified time :type waves: get_emulsification_wind(model_time) .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'emulsification' .. py:attribute:: _req_refs :value: ['waves'] .. py:method:: prepare_for_model_run(sc) add water_content key to mass_balance Assumes all spills have the same type of oil .. py:method:: prepare_for_model_step(sc, time_step, model_time) Set/update arrays used by emulsification module for this timestep: .. py:method:: weather_elements_lehr(sc, time_step, model_time) weather elements over time_step - sets 'water_content' in sc.mass_balance .. py:method:: weather_elements_adios2(sc, time_step, model_time) weather elements over time_step - sets 'water_content' in sc.mass_balance .. py:method:: weather_elements(sc, time_step, model_time) weather elements over time_step - sets 'water_content' in sc.mass_balance .. py:method:: _H_log(k, x) logistic function for turning on emulsification .. py:method:: _H_4(k, x) symmetric function for turning on emulsification .. py:method:: _Bw(x_visc, x_sig_min, x_fasph, x_r, x_s) .. py:method:: _water_uptake_coeff(points, model_time, substance) Use higher of wind or pseudo wind corresponding to wave height if (H0 > 0) HU = 2.0286 * sqrt(g * H0) if (HU < 4.429) HU = pow(HU / .71, .813) if (U < HU) U = HU k_emul = 6.0 * K0Y * U * U / d_max .. py:class:: Biodegradation(waves=None, **kwargs) Bases: :py:obj:`gnome.weatherers.Weatherer` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation Base weatherer class; defines the API for all weatherers Passes optional arguments to base (Process) class via super. See base class for optional arguments: `gnome.movers.mover.Process` adds 'mass_components', 'mass' to array_types since all weatherers need these. .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'biodegradation' .. py:attribute:: _req_refs :value: ['waves'] .. py:method:: prepare_for_model_run(sc) Add biodegradation key to mass_balance if it doesn't exist. - Assumes all spills have the same type of oil - let's only define this the first time .. py:method:: initialize_data(sc, num_released) Initialize needed weathering data arrays but only if 'on' is True .. py:method:: bio_degradate_oil(K, data, yield_factor) Calculate oil bio degradation K - biodegradation rate coefficients are calculated for temperate or arctic environment conditions yield_factor - specific surface value (sq meter per kg) yield_factor = 1 / ( d * ro) where d - droplet diameter ro - droplet density data['mass_components'] - mass of pseudocomponents .. py:method:: get_K_comp_rates(type_and_bp) Get bio degradation rate coefficient based on component type and its boiling point for temparate or arctic environment conditions. It must take into consideration saturates below C30 and aromatics only. type_and_bp - a tuple ('type', 'boiling_point') - 'type': component type, string - 'boiling_point': float value self.arctic - flag for arctic conditions - TRUE if arctic conditions (below 6 deg C) - FALSE if temperate Rate units: kg/m^2 per day(!) .. py:method:: weather_elements(sc, time_step, model_time) weather elements over time_step .. py:class:: Langmuir(water=None, wind=None, **kwargs) Bases: :py:obj:`gnome.weatherers.core.Weatherer` Easiest to define this as a weathering process that updates 'area' array initialize wind to (0, 0) if it is None .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'langmuir' .. py:attribute:: _req_refs :value: ['water', 'wind'] .. py:method:: _get_frac_coverage(points, model_time, rel_buoy, thickness) return fractional coverage for a blob of oil with inputs; relative_buoyancy, and thickness Assumes the thickness is the minimum oil thickness associated with max area achievable by Fay Spreading Frac coverage bounds are constants. If computed frac_coverge is outside the bounds of (0.1, or 1.0), then limit it to: 0.1 <= frac_cov <= 1.0 .. py:method:: _wind_speed_bound(rel_buoy, thickness) return min/max wind speed for given rel_buoy, thickness such that Langmuir effect is within bounds: 0.1 <= frac_coverage <= 1.0 .. py:method:: weather_elements(sc, time_step, model_time) set the 'area' array based on the Langmuir process This only applies to particles marked for weathering on the surface: ie fate_status is surface_weather .. py:class:: FayGravityViscous(water=None, thickness_limit=None, **kwargs) Bases: :py:obj:`gnome.weatherers.core.Weatherer` Model the FayGravityViscous spreading of the oil. For instantaneous release, this assumes all LEs released together spread as a blob following Fay (1971). The blob can be partitioned into 'N' LEs and the assumption is that the thickness and initial volume of the blob applies to all LEs in it. For continuous release, the spreading algorithm is similar to Dodge et al., (1983), where blob volume is considered as the cumulative volume of oil varying with time during the release period initialize object - invoke super, add required data_arrays. .. py:attribute:: _schema .. py:attribute:: _ref_as :value: 'spreading' .. py:attribute:: _req_refs :value: ['water'] .. py:method:: _gravity_spreading_t0(water_viscosity, relative_buoyancy, blob_init_vol, spreading_const) :staticmethod: time for the initial transient phase of spreading to complete. This depends on blob volume, but is on the order of minutes. Cache up to 10 inputs - don't expect 10 or more spills in one scenario. .. py:method:: _time_to_reach_max_area(water_viscosity, rel_buoy, blob_init_vol) just a convenience function to compute the time to reach max area All inputs are scalars .. py:method:: init_area(water_viscosity, relative_buoyancy, blob_init_vol) This takes scalars inputs since water_viscosity, init_volume and relative_buoyancy for a bunch of LEs released together will be the same :param water_viscosity: viscosity of water :type water_viscosity: float :param blob_init_volume: total initial volume of all LEs released together :type blob_init_volume: float :param relative_buoyancy: relative buoyancy of oil wrt water: (rho_water - rho_oil)/rho_water where rho is the density :type relative_buoyancy: float Equation for gravity spreading: :: A0 = PI*(k2**4/k1**2)*((V0**5*g*dbuoy)/(nu_h2o**2))**(1./6.) .. py:method:: update_area(water_viscosity, relative_buoyancy, blob_init_vol, area, max_area_le, time_step, vol_frac_le_st, age) update area array in place, also return area array each blob is defined by its age. This updates the area of each blob, as such, use the mean relative_buoyancy for each blob. Still check and ensure relative buoyancy is > 0 for all LEs :param water_viscosity: viscosity of water :type water_viscosity: float :param relative_buoyancy: relative buoyancy of oil wrt water at release time. This does not change over time. :type relative_buoyancy: float :param blob_init_volume: numpy array of floats containing initial release volume of blob. This is the same for all LEs released together. Note that for continuous release, the blob_init_vol will be updated as cumulative volume within the release duration. :type blob_init_volume: numpy array :param area: numpy array of floats containing area of each LE. Assume The LEs with same age belong to the same blob. Sum these up to get the area of the blob to compare it to max_area (or min thickness). Keep updating blob area till max_area is achieved. Equally divide updated_blob_area into the number of LEs used to model the blob. :type area: numpy array :param max_area_le: bool array. If a LE area reaches max_area_le beyond which it will not spread, toggle the LEs associated with that LE to True. Max spreading is based on min thickness based on initial viscosity of oil. This is used by Langmuir since the process acts on particles after spreading completes. :type max_area_le: numpy array of bools :param time_step: time step of simulation, which is in seconds. :type max_area_le: float :param vol_frac_le_st: numpy array the same size as area. This is the volume fraction of each LE. It is used to convert the computation into element-based. :type vol_frac_le_st: numpy array of int32 :param age: numpy array the same size as area and blob_init_volume. This is the age of each LE. The LEs with the same age belong to the same blob. Age is in seconds. :type age: numpy array of int32 :returns: (updated 'area' array, updated 'at_max_area' array). It also changes the input 'area' array and the 'at_max_area' bool array inplace. However, the input arrays could be copies so best to also return the updates. .. py:method:: get_thickness_limit(vo) :staticmethod: return the spreading thickness limit based on viscosity todo: documented in langmiur docs 1. vo >= 1e-4; limit = 1e-4 m 2. 1e-4 > vo >= 1e-6; limit = 1e-5 + 0.9091*(vo - 1e-6) m 3. 1e-6 > vo; limit = 1e-5 m .. py:method:: _set_thickness_limit(vo) sets internal thickness_limit variable .. py:method:: prepare_for_model_run(sc) Assumes only one type of substance is spilled That's now TRUE! .. py:method:: _set_init_relative_buoyancy(substance) set the initial relative buoyancy of oil wrt water use temperature of water to get oil density if relative_buoyancy < 0 raises a GnomeRuntimeError - particles will sink. .. py:method:: initialize_data(sc, num_released) initialize 'relative_buoyancy'. Note that initialization of spreading area for LEs is done in release object. If on is False, then arrays should not be included - dont' initialize .. py:method:: weather_elements(sc, time_step, model_time) Update 'area', 'fay_area' for previously released particles The updated 'area', 'fay_area' is associated with age of particles at: model_time + time_step .. py:class:: ConstantArea(area, **kwargs) Bases: :py:obj:`gnome.weatherers.core.Weatherer` Used for testing and diagnostics - must be manually hooked up Base weatherer class; defines the API for all weatherers Passes optional arguments to base (Process) class via super. See base class for optional arguments: `gnome.movers.mover.Process` adds 'mass_components', 'mass' to array_types since all weatherers need these. .. py:attribute:: _ref_as :value: 'spreading' .. py:method:: initialize_data(sc, num_released) If on is False, then arrays should not be included - dont' initialize .. py:method:: weather_elements(sc, time_step, model_time) return the area array as it was entered since that contains area per LE if there is more than one LE. Kept the interface the same as FayGravityViscous since WeatheringData will call it the same way. .. py:class:: ROC_Burn(offset=None, boom_length=None, boom_draft=None, speed=None, throughput=None, burn_efficiency_type=None, units=_si_units, **kwargs) Bases: :py:obj:`Response` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation Base weatherer class; defines the API for all weatherers Passes optional arguments to base (Process) class via super. See base class for optional arguments: `gnome.movers.mover.Process` adds 'mass_components', 'mass' to array_types since all weatherers need these. .. py:attribute:: _si_units .. py:attribute:: _units_type .. py:attribute:: _ref_as :value: 'roc_burn' .. py:attribute:: _schema .. py:method:: prepare_for_model_run(sc) Override for weatherers so they can initialize correct 'mass_balance' key and set initial value to 0.0 .. py:method:: prepare_for_model_step(sc, time_step, model_time) 1. set 'active' flag based on timeseries and model_time 2. Mark LEs to be burned, do them in order right now. assume all LEs that are released together will be burned together since they would be closer to each other in position. .. py:method:: _collect(sc, time_step, model_time) .. py:method:: _transit(sc, time_step, model_time) .. py:method:: _burn(sc, time_step, model_time) .. py:method:: _clean(sc, time_step, model_time) .. py:method:: weather_elements(sc, time_step, model_time) Remove mass from each le equally for now, no flagging for not just make sure it's from floating oil. .. py:class:: ROC_Disperse(transit=None, pass_length=4, dosage=None, dosage_type='auto', cascade_on=False, cascade_distance=None, loading_type='simultaneous', pass_type='bidirectional', disp_oil_ratio=None, disp_eff=None, platform=None, units=None, wind=None, onsite_reload_refuel=False, **kwargs) Bases: :py:obj:`Response` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation Base weatherer class; defines the API for all weatherers Passes optional arguments to base (Process) class via super. See base class for optional arguments: `gnome.movers.mover.Process` adds 'mass_components', 'mass' to array_types since all weatherers need these. .. py:attribute:: _attr .. py:attribute:: _si_units .. py:attribute:: _units_type .. py:attribute:: _ref_as :value: 'roc_disperse' .. py:attribute:: _req_refs :value: ['wind'] .. py:attribute:: _schema .. py:attribute:: wind_eff_list :value: [15, 30, 45, 60, 70, 78, 80, 82, 83, 84, 84, 84, 84, 84, 83, 83, 82, 80, 79, 78, 77, 75, 73, 71,... .. py:attribute:: visc_eff_table .. py:method:: get_mission_data(dosage=None, area=None, pass_len=None, efficiency=None, units=None) Given a dosage and an area to spray, will return a tuple of information as follows: Minimize number of passes by using high swath_width. If pump rate cannot get to the dosage necessary, reduce the swath width until it can. Default units are ('gal/acre', 'm^3, 'nm', percent) Return tuple is as below (num_passes, disp/pass, oil/pass) (number, gal, ft, gal/min) .. py:method:: prepare_for_model_run(sc) Override for weatherers so they can initialize correct 'mass_balance' key and set initial value to 0.0 .. py:method:: dosage_from_thickness(sc) .. py:method:: get_disp_eff_avg(sc, model_time) .. py:method:: get_disp_eff(sc, model_time) .. py:method:: prepare_for_model_step(sc, time_step, model_time) .. py:method:: simulate_boat(sc, time_step, model_time) .. py:method:: simulate_plane(sc, time_step, model_time) .. py:method:: reset_for_return_to_base(model_time, message) .. py:method:: update_time(time_remaining, model_time, time_step) .. py:method:: dispersable_oil_idxs(sc) .. py:method:: dispersable_oil_amount(sc, units='gal') .. py:method:: weather_elements(sc, time_step, model_time) Run the equivalent of get_move for weathering processes. It modifies the SpillContainer's data arrays; most weatherers update 'mass_components' and 'mass' Some objects do not implement this since they update arrays like 'area' in model_step_is_done() .. py:class:: ROC_Skim(speed=None, storage=None, swath_width=None, group=None, throughput=None, nameplate_pump=None, skim_efficiency_type=None, recovery=None, recovery_ef=None, decant=None, decant_pump=None, discharge_pump=None, rig_time=None, transit_time=None, units=_si_units, **kwargs) Bases: :py:obj:`Response` Base Weathering agent. This is almost exactly like the base Mover in the way that it acts upon the model. It contains the same API as the mover as well. Not Serializable since it does is partial implementation Base weatherer class; defines the API for all weatherers Passes optional arguments to base (Process) class via super. See base class for optional arguments: `gnome.movers.mover.Process` adds 'mass_components', 'mass' to array_types since all weatherers need these. .. py:attribute:: _si_units .. py:attribute:: _units_type .. py:attribute:: _schema .. py:method:: prepare_for_model_run(sc) Override for weatherers so they can initialize correct 'mass_balance' key and set initial value to 0.0 .. py:method:: prepare_for_model_step(sc, time_step, model_time) sets active flag based on time_span and on flag. Object is active if following hold and 'on' is True: 1. active start <= (model_time + time_step/2) so object is on for more than half the timestep 2. (model_time + time_step/2) <= active_stop so again the object is on for at least half the time step flag to true. :param sc: an instance of gnome.spill_container.SpillContainer class :param time_step: time step in seconds :param model_time_datetime: current model time as datetime object .. py:method:: _collect(sc, time_step, model_time) .. py:method:: _transit(sc, time_step, model_time) .. py:method:: _offload(sc, time_step, model_time) .. py:method:: weather_elements(sc, time_step, model_time) Remove mass from each le equally for now, no flagging for now just make sure the mass is from floating oil. .. py:method:: _getRecoveryEfficiency() .. py:data:: sort_order .. py:data:: weatherer_schemas .. py:data:: weatherers_idx .. py:data:: weatherers_by_name .. py:data:: standard_weatherering_sets .. py:function:: weatherer_sort(weatherer) Returns an int describing the sorting order of the weatherer or None if an order is not defined for the weatherer :param weatherer: weatherer instance