:py:mod:`gnome.spill_container` =============================== .. py:module:: gnome.spill_container .. autoapi-nested-parse:: spill_container.py Implements a container for spills -- keeps all the data from each spill in one set of arrays. The spills themselves provide some of the arrays themselves (adding more each time LEs are released). Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: gnome.spill_container.FateDataView gnome.spill_container.SpillContainerData gnome.spill_container.SpillContainer gnome.spill_container.SpillContainerPairData gnome.spill_container.SpillContainerPair .. py:class:: FateDataView Bases: :py:obj:`gnome.AddLogger` need a docstring -- what is this for? .. py:attribute:: _dicts_ :value: ('surface_weather', 'subsurf_weather', 'skim', 'burn', 'disperse', 'non_weather', 'all') .. py:method:: reset() .. py:method:: _get_fate_mask(sc, fate) get fate_status mask over SC - only include LEs with 'mass' > 0.0 .. py:method:: _set_data(sc, array_types, fate_mask, fate_status) Set the data arrays in the FateDataView fate_mask is the data already masked for the desired 'fate' option fate_status is the status the mask is for ('surface_weather', etc.) .. py:method:: get_data(sc, array_types, fate_status='surface_weather') Get data that matches the given fate_status. Since this is weathering data, only include elements with 'mass' > 0 Options are: 'all', 'surface_weather', 'subsurf_weather', 'skim', 'non_weather', 'burn' .. py:method:: update_sc(sc, fate_status='surface_weather') update SC arrays with FateDataView arrays for specified fate - update all arrays just to make sure everything is in sync After update, remove LEs with mass = 0. Since weatherers call this at the end of a weathering step, this ensures zero mass LEs are removed from the arrays. .. note:: the 'id' of each LE corresponds with the index into SC array when it was added. if LEs are removed, then this will not be the case. Do not rely on this indexing. Instead, get the mask again - the assumption is that the fate_mask should be the same between getting the data and resync'ing the original arrays in the SC .. py:method:: _reset_fatedata(sc, ix) reset all arrays that contain LE with 'id' = ix .. py:class:: SpillContainerData(data_arrays=None, uncertain=False) Bases: :py:obj:`object` A really simple SpillContainer -- holds the data arrays, but doesn't manage spills, etc. Think of it as a read-only SpillContainer. Designed primarily to hold data retrieved from cache Initialize a SimpleSpillContainer. :param uncertain=False: flag indicating whether this holds uncertainty elements or not :param data_arrays=None: A dict of all the data arrays you want to hold NOTE: no error checking! they should be correctly aligned, etc. The common use-case for this is for loading from cache for re-rendering, etc. Note: initialize current_time_stamp attribute to None. It is responsibility of caller to set current_time_stamp (for eg: Model) .. py:property:: num_released The number of elements currently in the SpillContainer If SpillContainer is initialized, all data_arrays exist as ndarrays even if no elements are released. So this will always return a valid int >= 0. .. py:property:: data_arrays Returns a dict of the all the data arrays .. py:method:: __contains__(item) .. py:method:: __getitem__(data_name) The basic way to access data for the LEs :param data_name: the name of the array to be returned example: a_spill_container['positions'] give you the (x,y,z positions array of the elements) :raises KeyError: raised if the data is not there .. py:method:: __setitem__(data_name, array) sets the data item careful! -- this should probably only be used for testing! as all arrays need to be compatible It will be checked to at least be size-consistent with the rest of the data, and type-consistent if the data array is being replaced It will not allow user to add a new data_array - only existing data_arrays can be modified. All data_arrays are defined in prepare_for_model_run .. py:method:: __eq__(other) Compare equality of two SpillContanerData objects .. py:method:: __ne__(other) Return self!=value. .. py:method:: __len__() The "length" of a spill container is the number of elements in it. The first dimension of any ndarray in our data_arrays will always be the number of elements that are contained in a SpillContainer. .. py:method:: keys() a keys() function so it looks a bit more like a dict .. py:class:: SpillContainer(uncertain=False) Bases: :py:obj:`gnome.AddLogger`, :py:obj:`SpillContainerData` Container class for all spills -- it takes care of capturing the released LEs from all the spills, putting them all in a single set of arrays. Many of the "fields" associated with a collection of elements are optional, or used only by some movers, so only the ones required will be requested by each mover. The data for the elements is stored in the _data_arrays dict. They can be accessed by indexing. For example: positions = spill_container['positions'] : returns a (num_LEs, 3) array of world_point_types Initialize a SimpleSpillContainer. :param uncertain=False: flag indicating whether this holds uncertainty elements or not :param data_arrays=None: A dict of all the data arrays you want to hold NOTE: no error checking! they should be correctly aligned, etc. The common use-case for this is for loading from cache for re-rendering, etc. Note: initialize current_time_stamp attribute to None. It is responsibility of caller to set current_time_stamp (for eg: Model) .. py:property:: total_mass return total mass spilled in 'kg' .. py:property:: substances Returns list of substances for weathering - not including None since that is non-weathering. Currently, only one weathering substance is supported .. py:property:: array_types user can modify ArrayType initial_value in middle of run. Changing the shape should throw an error. Change the dtype at your own risk. This returns a new dict so user cannot add/delete an ArrayType in middle of run. Use prepare_for_model_run() to do add an ArrayType. .. py:attribute:: __repr__ .. py:method:: _reset_arrays() reset _array_types dict so it contains default keys/values .. py:method:: _reset__substances_spills() reset internal attributes to None and empty list []: 1. _substances_spills: data structure to contain spills per substance 2. _oil_comp_array_len: max number of psuedocomponents - relevant if more than one substance is used. 3. _fate_data_list: list of FateDataView() objects. One object per substance if substance is not None .. py:method:: _reset__fate_data_view() .. py:method:: reset_fate_dataview() reset data arrays for each fate_dataviewer. Each substance that is not None has a fate_dataviewer object. .. py:method:: _set_substancespills() _substances could change when spills are added/deleted using _spills_changed callback to reset self._substance_spills to None This checks to make sure that the substance s set correctly and that there is not more than one substance All spills that are 'on' are included. A spill that is off isn't really being modeled so ignore it. .. note:: Should not be called in middle of run. prepare_for_model_run() will invoke this if self._substance_spills is None. This is another view of the data - it doesn't contain any state that needs to be persisted. .. py:method:: _set_fate_data() If the substance is not None, initialize the FateDataView object. .. py:method:: _spills_changed(*args) call back called on spills add/delete/replace Callback simply resets the internal _substance_spills attribute to None since the old _substance_spills value could now be invalid. .. py:method:: _get_s_id(substance) Look in the _substances_spills data structure of substance and return the corresponding s_id .. py:method:: _get_fatedataview() return the FateDataView object .. py:method:: _array_name(at) given an array type, return the name of the array. This can be string, in which case, it is the name of the array so return it. If its not a string, then return the at.name attribute. .. py:method:: _append_data_arrays(num_released) initialize data arrays once spill has spawned particles Data arrays are set to their initial_values :param int num_released: number of particles released .. py:method:: substancefatedata(substance, array_types, fate='surface_weather') Only one substance now! todo: fix this so it works for type of fate requested return the data for specified substance data must contain array names specified in 'array_types' .. py:method:: iterspillsbysubstance() iterate through the substances spills datastructure and return the spills associated with each substance. This is used by release_elements DataStructure contains all spills. If some spills contain None for substance, these will be returned .. py:method:: itersubstancedata(array_types, fate_status='surface_weather') There is only one substance allowed per SpillContainer, so this is returns the data cooresponding to the fate_status. This is only here to preserve compatiblity returns (substance, substance_data) This is used by weatherers - if a substance is None, StopIteration is raised :param array_types: iterable containing array that should be in the data. This could be a set of strings corresponding with array names or ArrayType objects which have a name attribute :param select='select': a string stating the type of data to be returned. Default if 'surface', so all elements with status_codes==oil_status.in_water and z == 0 in positions array :returns: (substance, substance_data) for each iteration substance: substance object substance_data: dict of numpy arrays associated with substance with elements in_water and on surface if select == 'surface' or subsurface if select == 'subsurface' .. py:method:: update_from_fatedataview(fate_status='surface_weather') let's only update the arrays that were changed only update if a copy of 'data' exists. .. py:method:: get_substances(complete=True) only one substance... .. py:method:: rewind() In the rewind operation, we: - rewind all the spills - restore _array_types to contain only defaults - movers/weatherers could have been deleted and we don't want to carry associated data_arrays - prepare_for_model_run() will be called before the next run and new arrays can be given - purge the data arrays - we gather data arrays for each contained spill - the stored arrays are cleared, then replaced with appropriate empty arrays .. py:method:: get_spill_mask(spill) .. py:method:: uncertain_copy() Returns a copy of the spill_container suitable for uncertainty It has all the same spills, with the same ids, and the uncertain flag set to True .. py:method:: prepare_for_model_run(array_types=None, time_step=300) called when setting up the model prior to 1st time step This is considered 0th timestep by model Make current_time optional since SpillContainer doesn't require it especially for 0th step; however, the model needs to set it because it will write_output() after each step. The data_arrays along with the current_time_stamp must be set in order to write_output() :param model_start_time: model_start_time to initialize current_time_stamp. This is the time_stamp associated with 0-th step so initial conditions for data arrays :param array_types: a set of additional names and/or array_types to append to standard array_types attribute. Set can contain only strings or a tuple with (string, ArrayType). See Note below. .. note:: set can contains strings or tuples. If set contains only strings, say: {'mass', 'windages'}, then SpillContainer looks for corresponding ArrayType object defined in gnome.array_types for 'mass' and 'windages'. If set contains a tuple, say: {('mass', gnome.array_types.mass)}, then SpillContainer uses the ArrayType defined in the tuple. .. note:: The SpillContainer iterates through each of the item in array_types and checks to see if there is an associated initializer in any Spill. If corresponding initializer is found, it gets the array_types from initializer and appends them to its own list. This was added for the case where 'droplet_diameter' array is defined/used by initializer (InitRiseVelFromDropletSizeFromDist) and we would like to see it in output, but no Mover/Weatherer needs it. .. py:method:: initialize_data_arrays() initialize_data_arrays() is called without input data during rewind and prepare_for_model_run to define all data arrays. At this time the arrays are empty. .. py:method:: _get_fate_mask(fate) get fate_status mask over SC - only include LEs with 'mass' > 0.0 .. py:method:: release_elements(start_time, end_time, environment=None) :param start_time: -- beginning of the release :param end_time: -- end of the release. This calls release_elements on all of the contained spills, and adds the elements to the data arrays :returns: total number of particles released .. py:method:: split_element(ix, num, l_frac=None) split an element into specified number. For data, like mass, that gets divided, l_frac can be optionally provided. l_frac is a list containing fraction of component's value given to each new element. len(l_frac) must be equal to num and sum(l_frac) == 1.0 :param ix: id of element to be split - before splitting each element has a unique 'id' defined in 'id' data array :type ix: int :param num: split ix into 'num' number of elements :type num: int :param l_frac: list containing fractions that sum to 1.0 with len(l_frac) == num :type l_frac: list or tuple or numpy array .. py:method:: model_step_is_done() Called at the end of a time step Need to remove particles marked as to_be_removed... .. py:method:: __str__() Return str(self). .. py:class:: SpillContainerPairData(sc, u_sc=None) Bases: :py:obj:`object` A really simple SpillContainerPair - holds SpillContainerPairData objects, but doen't manage spills, etc. Think of it as a read-only SpillContainerPair. Designed primarily to hold data retrieved from cache Initialize object with the spill_containers passed in .. py:property:: uncertain .. py:property:: LE_data .. py:method:: __repr__() Return repr(self). .. py:method:: items() returns a tuple of the enclosed spill containers if uncertainty is off, just one is in the tuple if uncertainly is on -- then it is a two-tuple: (certain_container, uncertain_container) To act on both: for sc in spill_container_pair.items(): do_something_with(sc) NOTE: cache code counts on the uncertain SpillContainer being last .. py:method:: LE(prop_name, uncertain=False) .. py:method:: __eq__(other) Compare equality of two SpillContainerPairData objects .. py:method:: __ne__(other) Return self!=value. .. py:class:: SpillContainerPair(uncertain=False) Bases: :py:obj:`SpillContainerPairData` Container holds two SpillContainers, one contains the certain spills while the other contains uncertainty spills if model uncertainty is on. initialize object: init spill_container, _uncertain and u_spill_container if uncertain Note: all operations like add, remove, replace and __iter__ are exposed to user for the spill_container.spills OrderedCollection .. py:property:: uncertain .. py:property:: num_released elements released by (forecast, uncertain) spills .. py:method:: rewind() rewind spills in spill_container .. py:method:: __repr__() unambiguous repr .. py:method:: _add_spill_pair(pair_tuple) add both certain and uncertain spills given as a pair .. py:method:: _add_item(item) could be a spill pair or a forecast spill - add appropriately .. py:method:: add(spills) Add spill to spill_container and make copy in u_spill_container if uncertainty is on Note: Method can take either a list, tuple, or list of tuples with following assumptions: 1. spills = Spill() # A spill object, if uncertainty is on, make a copy for uncertain_spill_container. 2. spills = [s0, s1, ..,] # List of forecast spills. if uncertain, make a copy of each and add to uncertain_spill_container 3. spills = (s0, uncertain_s0) # tuple of length two. Assume first one is forecast spill and second one is the uncertain copy. Used when restoring from save file 4. spills = [(s0, uncertain_s0), ..] # list of tuples of length two. Added for completeness. .. py:method:: append(spill) .. py:method:: remove(ident) remove object from spill_container.spills and the corresponding uncertainty spill as well .. py:method:: __getitem__(ident) only return the certain spill .. py:method:: __setitem__(ident, new_spill) .. py:method:: __delitem__(ident) .. py:method:: __iadd__(rop) .. py:method:: __iter__() iterates over the spills defined in spill_container .. py:method:: __len__() It refers to the total number of spills that have been added The uncertain and certain spill containers will contain the same number of spills return the length of spill_container.spills .. py:method:: __contains__(ident) looks to see if ident which is the id of a spill belongs in the _spill_container.spills OrderedCollection .. py:method:: to_dict() takes the instance of SpillContainerPair class and outputs a dict with: 'spills': call to_dict() on spills ordered collection stored in certain spill container if uncertain, then also return: 'uncertain_spills': call to_dict() on spills ordered collection stored in uncertain spill container The input param json_ is not used. It is there to keep the same interface for all to_dict() functions .. py:method:: update_from_dict(dict_) takes a dict {'spills': [list of spill objects]}, checks them against the forecast spills contained in _spill_container.spills and updates if they are different It also creates a copy of the different spill and replaces the corresponding spill in _u_spill_container This is primarily intended for the webapp so the dict_ will only contain a list of forecast spills .. py:method:: spill_by_index(index, uncertain=False) return either the forecast spill or the uncertain spill at specified index .. py:method:: index(spill) Look for spill in forecast SpillContainer or uncertain SpillContainer and return the index of ordered collection where spill is found .. py:method:: clear() clear all spills from container pairs