pywatershed.Model#

class pywatershed.Model(process_list_or_model_dict, control=None, parameters=None, find_input_files=True, write_control=False)[source]#

Build a model in pywatershed.

This is the class that helps execute sets of Processes in order.

There are two distinct ways of instatniating the Model class described below: 1) PRMS-legacy instantation, 2) pywatershed-centric instatiation.

Parameters:
  • process_list_or_model_dict (Union[list, dict]) – a process list (for PRMS-legacy instantiation) or a model dictionary (for pywatershed style instatiation), see ways of instatiation below.

  • control (Optional[Control]) – Control object or None, see below.

  • parameters (Union[Parameters, dict[Parameters], None]) – A Parameters object of a dictionary of Parameters objects. Default is None. See ways of instatnation below.

  • find_input_files (bool) – Search/find input file on __init__ or delay until run or advance of the model. Delaying (False) allows ModelGraph of the specified model without the need for input files.

  • write_control (Union[bool, str, Path]) – bool, str, or pl.Path a directory into which a copy of the passed control is to be written, default is False. This is for convenience when lost of in-memory manipulations may be made before passing to the model. The output file name has the form %Y-%m-%dT%H:%M:%S.model_control.yaml

PRMS-legacy instantiation#

This method of instantiation supports PRMS files with few modifications. The first three arguments to instantiate a Model this way are:

  • process_list_or_model_dict: A process list of PRMS model components.

  • control: A Control object.

  • parameters: A PrmsParameters object.

The first example below provides details. An extended example is given by examples/02_prms_legacy_models.ipynb.

pywatershed-centric instatiation#

The pywatershed style instatniation handles aribtrary and sundry processes. It is loosely based on how MF6 structures its inputs. All of the work is contained in the first argument, the model dictionary, and the control and parameters are both None.

Instantating a Model this way, only the first of the first three args is used:

  • process_list_or_model_dict: a “model dictionary”, detailed below.

  • control: Do not pass, None is default.

  • parameters: Do not pass, None is default.

This means that the user must understand the model dictionary supplied. A model dictionary has aribtrary keys but prescribed values. Values may be the following kinds of objects: Control, discretization dictionary, process dictionary, process order list, and exchanges dictionaries. (Exchanges are not yet supported). Each of these is described below. Model dictionaries can also be specfied via yaml files. Notes on yaml versus in-memory requirements for the values are described as they are (necessarily) different.

See the second and third examples below for more details and see examples/01_multi-process_models.ipynb for an extended example.

Model dictionary values description:#

  • control - The control object supplies two things for the model 1) the global time discretization (start and stop times, as well as time step), and 2) default global options for all model processes. Only one control object can be included in the model dictionary. Though the key for the control can be arbitrary, the value is either an instance of class Control or, in the case of a yaml model dictionary, a control yaml file to be loaded by Control.from_yaml() (todo: link to this staticmethod).

  • discretizations - Multiple discretizations may be supplied to the model dictionary, each with arbitrary names. These provide spatial discretization information which may be shared by multiple processes specified later in process_list. Each process will refer to its required discretization using the key for that discretization in the model dict. The value of each is an instance of the class Parameters. In a yaml model dictionary, the value of each discretization is a path to a netcdf file for that discretization.

  • processes - Multiple processes with arbitrary keys are to be supplied to model dictionary (if running just one process, there’s no need for the Model class). The values for each process are dictionaries and have the required keys:

    • class - The desired class, in the case of a yaml model dictionary, the string name of a class in the pywatershed namespace

    • parameters - Either a Parameters object or, in the case of a yaml model dictionary, the path of a netcdf file with the parameters

    • dis - The key of the dis to use as specified at the top level of model dictionary.

  • model_order - A list of the processess keys in the order the model is to be executed.

  • exchanges - Future.

Examples:#

These examples will work if you have an editable install of the repository (not installed from pypi).

Construct a PRMS-legacy based model:

>>> import pywatershed as pws
>>> test_data_dir = pws.constants.__pywatershed_root__ / "../test_data"
>>> domain_dir = test_data_dir / "drb_2yr"
>>> # A PRMS-native control file
>>> control_file = domain_dir / "control.test"
>>> # PRMS-native parameter file
>>> parameter_file = domain_dir / "myparam.param"
>>> control = pws.Control.load_prms(
...     control_file, warn_unused_options=False
... )
>>> control.options["input_dir"] = domain_dir / "output"
>>> params = pws.parameters.PrmsParameters.load(parameter_file)
>>> model_procs = [
...     pws.PRMSGroundwater,
...     pws.PRMSChannel,
... ]
>>> model = pws.Model(
...     model_procs,
...     control=control,
...     parameters=params,
... )
PRMSGroundwater jit compiling with numba
PRMSChannel jit compiling with numba
>>> model.run()
100%|█████████████████████████████████████████████████████████| 731/731 [00:00<00:00, 1249.26it/s]
model.run(): finalizing

Construct a model the pywatershed-centric way, in memory:

>>> import pywatershed as pws
>>> test_data_dir = pws.constants.__pywatershed_root__ / "../test_data"
>>> domain_dir = test_data_dir / "drb_2yr"
>>> dis_hru = pws.Parameters.from_netcdf(
...     domain_dir / "parameters_dis_hru.nc", encoding=False
... )
>>> control_file = domain_dir / "control.test"
>>> control = pws.Control.load_prms(
...     control_file, warn_unused_options=False
... )
>>> control.options["input_dir"] = domain_dir
>>> params = {}
>>> for proc in ["SolarGeometry", "Atmosphere", "Canopy", "Snow"]:
...     param_file = domain_dir / f"parameters_PRMS{proc}.nc"
...     params[proc.lower()] = pws.Parameters.from_netcdf(param_file)
...
>>> model_dict = {
...     "control": control,
...     "dis_hru": dis_hru,
...     "model_order": [
...         "prmssolargeometry",
...         "prmsatmosphere",
...         "prmscanopy",
...         "prmssnow",
...     ],
...     "prmssolargeometry": {
...         "class": pws.PRMSSolarGeometry,
...         "parameters": params["solargeometry"],
...         "dis": "dis_hru",
...     },
...     "prmsatmosphere": {
...         "class": pws.PRMSAtmosphere,
...         "parameters": params["atmosphere"],
...         "dis": "dis_hru",
...     },
...     "prmscanopy": {
...         "class": pws.PRMSCanopy,
...         "parameters": params["canopy"],
...         "dis": "dis_hru",
...     },
...     "prmssnow": {
...         "class": pws.PRMSSnow,
...         "parameters": params["snow"],
...         "dis": "dis_hru",
...     },
... }
>>> model = pws.Model(model_dict)
PRMSCanopy jit compiling with numba
PRMSSnow jit compiling with numba
>>> model.run()
100%|███████████████████████████████████████████████████████████| 731/731 [00:07<00:00, 60.76it/s]
model.run(): finalizing

Construct a model the pywatershed-centric way, from a yaml file definition:

>>> import yaml
>>> import pywatershed as pws
>>> test_data_dir = pws.constants.__pywatershed_root__ / "../test_data"
>>> domain_dir = test_data_dir / "drb_2yr"
>>> control = {
...     "start_time": "1979-01-01T00:00:00",
...     "end_time": "1980-12-31T00:00:00",
...     "time_step": 24,
...     "time_step_units": "h",
...     "verbosity": 0,
...     "budget_type": "warn",
...     "input_dir": str(domain_dir),
... }
>>> control_file = domain_dir / "example_control.yaml"
>>> model_dict = {
...     "control": str(control_file),
...     "dis_hru": "parameters_dis_hru.nc",
...     "dis_both": "parameters_dis_both.nc",
...     "solargeometry": {
...         "class": "PRMSSolarGeometry",
...         "parameters": "parameters_PRMSSolarGeometry.nc",
...         "dis": "dis_hru",
...     },
...     "atmosphere": {
...         "class": "PRMSAtmosphere",
...         "parameters": "parameters_PRMSAtmosphere.nc",
...         "dis": "dis_hru",
...     },
...     "canopy": {
...         "class": "PRMSCanopy",
...         "parameters": "parameters_PRMSCanopy.nc",
...         "dis": "dis_hru",
...     },
...     "snow": {
...         "class": "PRMSSnow",
...         "parameters": "parameters_PRMSSnow.nc",
...         "dis": "dis_hru",
...     },
...     "model_order": ["solargeometry", "atmosphere", "canopy", "snow"],
... }
>>> model_dict_file = domain_dir / "example_model_dict.yaml"
>>> dump_dict = {control_file: control, model_dict_file: model_dict}
>>> for key, val in dump_dict.items():
...     with open(key, "w") as file:
...         documents = yaml.dump(val, file)
...
>>> model = pws.Model.from_yaml(model_dict_file)
PRMSCanopy jit compiling with numba
PRMSSnow jit compiling with numba
>>> model.run()
100%|████████████████████████████████████████████████████████████| 731/731 [00:07<00:00, 92.30it/s]
model.run(): finalizing
>>> control_file.unlink()
>>> model_dict_file.unlink()
__init__(process_list_or_model_dict, control=None, parameters=None, find_input_files=True, write_control=False)[source]#

Methods

__init__(process_list_or_model_dict[, ...])

advance()

Advance the model in time.

calculate()

Calculate the model.

finalize()

Finalize the model.

from_yaml(yaml_file)

Instantiate a Model from a yaml file

initialize_netcdf([output_dir, ...])

Initialize NetCDF output files for model (all processes).

model_dict_from_yaml(yaml_file)

Generate a model dictionary from a yaml file.

output()

Output the model at the current time.

run([netcdf_dir, finalize, n_time_steps, ...])

Run the model.

advance()[source]#

Advance the model in time.

calculate()[source]#

Calculate the model.

finalize()[source]#

Finalize the model.

static from_yaml(yaml_file)[source]#

Instantiate a Model from a yaml file

A yaml file that specifies a model_dict as the first argument of Model.

Parameters:

yaml_file (Union[str, Path]) – str or pathlib.Path

Returns:

An instance of Model.

Yaml file structure (strict order not required, but suggested):

  • Control object: Any name can be used but the value must be a control yaml file specified with the suffix “.yaml”. E.g “name: control.yaml” would appear in the passed yaml file. Only one control specification is allowed in the yaml_file. For details on the requirements of the control.yaml file see Control.from_yaml

  • Discretization objects: Any number of discretization objects can be supplied with arbitrary (though unique) names. The values supplied for each discretization must be a valid netcdf file with suffix “.nc”. These can generally be obtained by calling parameters.to_netcdf() on subclasses of Parameters.

  • Process objects: Any number of processes may be specified with arbitrary (though unique) names. Processes are specified as dictionaries in the yaml file, they have additonal key:value pairs. Required key:value pairs are:

    • class: a class that can be from pywatershed import class

    • parameters: a netcdf file that specifies the parameters for the class

    • dis: the name of the discretization for the class as given by a discretization object speficication above.

    Optional key:value pairs: TBD

  • Model order list: a list supplying the order in which the processes are to be executed.

Note: To get a model_dict specfied by the yaml_file, call model_dict_from_yaml instead.

initialize_netcdf(output_dir=None, separate_files=None, budget_args=None, output_vars=None)[source]#

Initialize NetCDF output files for model (all processes).

Parameters:
  • output_dir (Optional[str]) – pl.Path or str of the directory where to write files

  • separate_files (Optional[bool]) – For a given Process, write a single file or separate files for the process’ variables. DEFAULTS to True for performance reasons.

  • budget_args (Optional[dict]) – see Budget.initialize_netcdf(). defaults to None

  • output_vars (Optional[list]) – A list of variables to write. Unrecognized variable names are silently skipped. Defaults to None which writes all variables for all Processes.

static model_dict_from_yaml(yaml_file)[source]#

Generate a model dictionary from a yaml file.

Instead of Model.from_yaml() it can be useful to get the model dictionary before passing it to Model.

Parameters:

yaml_file (Union[str, Path]) – a yaml file

Return type:

dict

Returns:

A model dictionary.

output()[source]#

Output the model at the current time.

run(netcdf_dir=None, finalize=True, n_time_steps=None, output_vars=None)[source]#

Run the model.

Running the model wraps:
  • initializing netcdf output (optional on netcdf_dir argument)

  • full time loop (from control object) with progress reports
    • advance

    • calculate

    • output (optional on output methods requested)

  • finalize (optional)

Parameters:
  • netcdf_dir (Union[str, Path, None]) – optional directory to netcdf output files (initializes netcdf and outputs at each timestep).

  • finalize (bool) – option to not finalize at the end of the time loop. Default is to finalize.

  • n_time_steps (Optional[int]) – the number of timesteps to run

  • output_vars (Optional[list]) – the vars to output to the netcdf_dir