:py:mod:`gnome.model` ===================== .. py:module:: gnome.model .. autoapi-nested-parse:: module with the core Model class, and various supporting classes This is the main class that contains objects used to model trajectory and weathering processes. It runs the loop through time, etc. The code comes with a full-featured version -- you may want a simpler one if you aren't doing a full-on oil spill model. The model contains: * map * collection of environment objects * collection of movers * collection of weatherers * spills * its own attributes In pseudo code, the model loop is defined below. In the first step, it sets up the model run and in subsequent steps the model moves and weathers elements. .. code-block:: python for each_timestep(): if initial_timestep: setup_model_run() setup_time_step() move_the_elements() beach_refloat_the_elements() weather_the_elements() write_output() step_is_done() step_num += 1 Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: gnome.model.Model .. py:class:: Model(name='Model', time_step=timedelta(minutes=15), start_time=round_time(datetime.now(), 3600), duration=timedelta(days=1), weathering_substeps=1, map=None, uncertain=False, cache_enabled=False, mode=None, make_default_refs=True, location=[], environment=[], outputters=[], movers=[], weatherers=[], spills=[], uncertain_spills=[], weathering_activated=False, **kwargs) Bases: :py:obj:`gnome.gnomeobject.GnomeId` PyGnome Model Class Initializes a model. All arguments have a default. :param time_step=timedelta(minutes=15): model time step in seconds or as a timedelta object. NOTE: if you pass in a number, it WILL be interpreted as seconds :param start_time=datetime.now(): start time of model, datetime object. Rounded to the nearest hour. :param duration=timedelta(days=1): How long to run the model, a timedelta object. :param weathering_substeps=1: How many weathering substeps to run inside a single model time step. :param map=gnome.map.GnomeMap(): The land-water map. :param uncertain=False: Flag for setting uncertainty. :param cache_enabled=False: Flag for setting whether the model should cache results to disk. :param mode='Gnome': The runtime 'mode' that the model should use. This is a value that the Web Client uses to decide which UI views it should present. .. py:property:: uncertain Uncertainty attribute of the model. If flag is toggled, rewind model .. py:property:: uncertain_spills .. py:property:: cache_enabled If True, then generated data is cached .. py:property:: has_weathering_uncertainty .. py:property:: has_weathering .. py:property:: start_time Start time of the simulation .. py:property:: time_step time step over which the dynamics is computed .. py:property:: current_time_step Current timestep of the simulation .. py:property:: duration total duration of the model run .. py:property:: map land water map used for simulation .. py:property:: num_time_steps Read only attribute computed number of timesteps based on py:attribute:`duration` and py:attribute:`time_step` .. py:attribute:: _schema .. py:attribute:: _oc_list :value: ['movers', 'weatherers', 'environment', 'outputters'] .. py:attribute:: modes .. py:attribute:: next .. py:method:: load_savefile(filename) :classmethod: Load a model instance from a save file :param filename: the filename of the save file -- usually a zip file, but can also be a directry with the full contents of a zip file :return: a model instance all set up from the savefile. .. py:method:: _register_callbacks() Register callbacks with the OrderedCollections .. py:method:: add_weathering(which='standard') Add the weatherers :param which='standard': which weatheres to add. Default is 'standard', which will add all the standard weathering algorithms if you don't want them all, you can specify a list: ['evaporation', 'dispersion']. Options are: - 'evaporation' - 'dispersion' - 'emulsification' - 'dissolution': Dissolution, - 'half_life_weatherer' see: ``gnome.weatherers.__init__.py`` for the full list .. py:method:: reset(**kwargs) Resets model to defaults -- Caution -- clears all movers, spills, etc. Takes same keyword arguments as :meth:`__init__()` .. py:method:: rewind() Rewinds the model to the beginning (start_time) .. py:method:: update_from_dict(dict_, refs=None) functions in common_object. .. py:method:: _reset_num_time_steps() reset number of time steps if duration, or time_step change .. py:method:: contains_object(obj_id) .. py:method:: find_by_class(obj, collection, ret_all=False) Look for an object that isinstance() of obj in specified colleciton. By default, it will return the first object of this type. To get all obects of this type, set ret_all to True .. py:method:: find_by_attr(attr, value, collection, allitems=False) find first object in collection where the 'attr' attribute matches 'value'. This is primarily used to find 'wind', 'water', 'waves' objects in environment collection. Use the '_ref_as' attribute to search. # fixme: why don't we look for wind, water or waves directly? Ignore AttributeError since all objects in collection may not contain the attribute over which we are searching. :param attr: attribute whose value must match :type attr: str :param value: desired value of the attribute :type value: str :param OrderedCollection collection: the ordered collection in which to search .. py:method:: _order_weatherers() use weatherer_sort to sort the weatherers .. py:method:: _attach_default_refs(ref_dict) Model invokes the default reference attachment system. Please note the structure of this function as an example of how to extend the system to contained child objects. .. py:method:: setup_model_run() Runs the setup procedure preceding a model run. When complete, the model should be ready to run to completion without additional prep Currently this function consists of the following operations: 1. Set up special objects. Some weatherers currently require other weatherers to exist. This step satisfies those requirements 2. Remake collections in case ordering constraints apply (weatherers) 3. Compile array_types and run setup procedure on spills array_types defines what data arrays are required by the various components of the model 4. Attach default references 5. Call prepare_for_model_run on all relevant objects 6. Conduct miscellaneous prep items. See section in code for details. .. py:method:: post_model_run() A place where the model goes through all collections and calls post_model_run if the object has it. .. py:method:: setup_time_step() sets up everything for the current time_step: .. py:method:: move_elements() Moves elements: - loops through all the movers. and moves the elements - sets new_position array for each spill - calls the beaching code to beach the elements that need beaching. - sets the new position .. py:method:: _update_fate_status(sc) WeatheringData used to perform this operation in weather_elements; however, WeatheringData is one of the objects in weatherers collection so just let model do this for now. Eventually, we want to get rid of 'fate_status' array and only manipulate 'status_codes'. Until then, update fate_status in move_elements .. py:method:: weather_elements() Weathers elements: - loops through all the weatherers, passing in the spill_container and the time range - a weatherer modifies the data arrays in the spill container, so a particular time range should not be run multiple times. It is expected that we are processing a sequence of contiguous time ranges. - Note: If there are multiple sequential weathering processes, some inaccuracy could occur. A proposed solution is to 'super-sample' the model time step so that it will be replaced with many smaller time steps. We'll have to see if this pans out in practice. .. py:method:: _split_into_substeps() :return: sequence of (datetime, timestep) (Note: we divide evenly on second boundaries. Thus, there will likely be a remainder that needs to be included. We include this remainder, which results in 1 more sub-step than we requested.) .. py:method:: step_is_done() Loop through movers and weatherers and call model_step_is_done Remove elements that marked for removal Output data .. py:method:: write_output(valid, messages=None) .. py:method:: step() Steps the model forward in time. NOTE: in theory, it could also go backward with a negative time step, for hindcasting, but that has not been tested. .. py:method:: output_step(isvalid) .. py:method:: release_elements(start_time, end_time) release elements into the model :param start_time: -- beginning of the release :param end_time: -- end of the release. .. py:method:: compile_env() Produces a dictionary of objects that describe the model environmental conditions Currently, only works with the 'water' object because the other environmental phenomena are not compatible yet .. py:method:: __iter__() Rewinds the model and returns itself so it can be iterated over. .. py:method:: __next__() (This method satisfies Python's iterator and generator protocols) :return: the step number .. py:method:: full_run(rewind=True) Do a full run of the model. :param rewind=True: whether to rewind the model first -- if set to false, model will be run from the current step to the end :returns: list of outputter info dicts .. py:method:: _add_to_environ_collec(obj_added) if an environment object exists in obj_added, but not in the Model's environment collection, then add it automatically. todo: maybe we don't want to do this - revisit this requirement JAH 9/22/2021: We sort of need this now because a lot of script behavior expects it. A lamentable state of affairs indeed. CHB: maybe this could be more standardized though -- pity to have hard coded what all the possible environment types are. perhaps all objects could have a "need_env_objects" attribute? .. py:method:: _callback_add_mover(obj_added) Callback after mover has been added .. py:method:: _callback_add_outputter(obj_added) Callback after outputter has been added .. py:method:: _callback_add_weatherer_env(obj_added) Callback after weatherer/environment object has been added. 'waves' environment object contains 'wind' and 'water' so add those to environment collection and the 'water' attribute. todo: simplify this .. py:method:: _callback_add_spill(obj_added) .. py:method:: __eq__(other) .. function:: __eq__(other) Since this class is designed as a mixin with one objective being to save _state of the object, then recreate a new object with the same _state. Defines a base implementation of __eq__ so an object before persistence can be compared with a new object created after it is persisted. It can be overridden by the class with which it is mixed. It looks at attributes defined in self._state and checks that the values match It uses allclose() check for floats and numpy arrays, to avoid floating point tolerances: set to: RTOL=1e-05, ATOL=1e-08 :param other: another GnomeObject used for comparison in obj1 == other NOTE: super is not used. .. py:method:: __ne__(other) Return self!=value. .. py:method:: spills_update_from_dict(value) invoke SpillContainerPair().update_from_dict .. py:method:: save(saveloc='.', refs=None, overwrite=True) save the model state in saveloc. If self.zipsave is True, then a zip archive is created and model files are saved to the archive. :param saveloc=".": a directory or filename. If a directory, then either the model is saved into that dir, or a zip archive is created in that dir (with a .gnome extension). The file(s) are clobbered when save() is called. :type saveloc: A dir or file name (relative or full path) as a string. :param refs=None: dict of references mapping 'id' to a string used for the reference. The value could be a unique integer or it could be a filename. It is up to the creator of the reference list to decide how to reference a nested object. :param overwrite=True: :returns: references This overrides the base class save(). Model contains collections and model must invoke save for each object in the collection. It must also save the data in the SpillContainer's if it is a mid-run save. .. py:method:: _save_spill_data(saveloc, nc_filename) save the data arrays for current timestep to NetCDF If saveloc is zipfile, then move NetCDF to zipfile .. py:method:: load(saveloc='.', filename=None, refs=None) :classmethod: Load an instance of this class from an archive or folder :param saveloc: Can be an open zipfile.ZipFile archive, a folder, or a filename. If it is an open zipfile or folder, it must contain a ``.json`` file that describes an instance of this object type. If ``filename`` is not specified, it will load the first instance of this object discovered. If a filename, it must be a zip archive or a json file describing an object of this type. :param filename: If saveloc is an open zipfile or folder, this indicates the name of the file to be loaded. If saveloc is a filename, is parameter is ignored. :param refs: A dictionary of id -> object instances that will be used to complete references, if available. .. py:method:: _load_spill_data(saveloc, filename, nc_file) load NetCDF file and add spill data back in - designed for savefiles .. py:method:: merge(model) merge 'model' into self .. py:method:: check_inputs() check the user inputs before running the model raise an exception if user can't run the model todo: check if all spills start after model ends fixme: This should probably be broken out into its own module, class, something -- with each test independent. .. py:method:: validate() invoke validate for all gnome objects contained in model todo: should also check wind, water, waves are defined if weatherers are defined .. py:method:: _validate_env_coll(refs, raise_exc=False) validate refs + log warnings or raise error if required refs not found. If refs is None, model must query its weatherers/movers/environment collections to figure out what objects it needs to have in environment. .. py:method:: set_make_default_refs(value) make default refs for all items in ('weatherers', 'movers', 'environment') collections .. py:method:: list_spill_properties() Convenience method to list properties of a spill that can be retrieved using get_spill_property :return: list of spill simulation attributes .. py:method:: get_spill_property(prop_name, ucert=False) Convenience method to allow user to look up properties of a spill. :param prop_name: name of property: use `model.list_properties()` to see all the options. :type prop_name: str :param ucert: whether to get it from the uncertainty spill :type ucert: bool :returns: np.array .. py:method:: get_spill_data(target_properties, conditions, ucert=0) Convenience method to allow user to write an expression to filter raw spill data Example case:: get_spill_data('position && mass', 'position > 50 && spill_num == 1 || status_codes == 1' ) WARNING: EXPENSIVE! USE AT YOUR OWN RISK ON LARGE num_elements! Example spill element properties are below. This list may not contain all properties tracked by the model. 'positions', 'next_positions', 'last_water_positions', 'status_codes', 'spill_num', 'id', 'mass', 'age' .. py:method:: add_env(env, quash=False)