diff --git a/autotest/test_parameters.py b/autotest/test_parameters.py new file mode 100644 index 00000000..c8cb87e1 --- /dev/null +++ b/autotest/test_parameters.py @@ -0,0 +1,33 @@ +from pprint import pprint + +from numpy.testing import assert_equal + +from pywatershed.parameters import Parameters, PrmsParameters +from pywatershed.base.parameters import _set_dict_read_write + +from utils import assert_dicts_equal + + +def test_param_dd_param(domain): + # round trip from read-only to read-write to read-only + # use a PRMS Parameter file for now + domain_dir = domain["param_file"].parent + params = Parameters.from_netcdf( + domain_dir / "parameters_PRMSGroundwater.nc" + ) + # These both copy by default + param_dd = params.to_dd() + params2 = Parameters(**param_dd.data) + + assert_dicts_equal(params.data, params2.data) + + param_dd.data_vars["gwflow_coef"] *= 4 + params3 = Parameters(**param_dd.data) + + try: + assert_dicts_equal(params.data, params3.data) + assert False + except AssertionError: + pass + + return diff --git a/autotest/utils.py b/autotest/utils.py index 496d56d3..d6cd0037 100644 --- a/autotest/utils.py +++ b/autotest/utils.py @@ -1,3 +1,4 @@ +from types import MappingProxyType import numpy as np print_ans = False @@ -41,3 +42,22 @@ def assert_or_print( # Always fail if printing answers assert not print_ans return + + +def assert_dicts_equal(dic1, dic2): + assert set(dic1.keys()) == set(dic2.keys()) + + # add cases as needed + for kk, vv in dic1.items(): + if isinstance(vv, (dict, MappingProxyType)): + assert_dicts_equal(dic1[kk], dic2[kk]) + elif isinstance(vv, np.ndarray): + np.testing.assert_equal(dic1[kk], dic2[kk]) + else: + if ( + isinstance(vv, float) + and np.isnan(dic1[kk]) + and np.isnan(dic2[kk]) + ): + continue + assert dic1[kk] == dic2[kk] diff --git a/environment.yml b/environment.yml index 4de85c24..71d39656 100644 --- a/environment.yml +++ b/environment.yml @@ -49,5 +49,5 @@ dependencies: - flake8 - git+https://github.com/modflowpy/flopy.git - jupyter_black - - modflow-devtools + - modflow-devtools < 1.0.0 - pylint diff --git a/pywatershed/base/parameters.py b/pywatershed/base/parameters.py index 551ce99c..ed9ec9cf 100644 --- a/pywatershed/base/parameters.py +++ b/pywatershed/base/parameters.py @@ -1,3 +1,4 @@ +from copy import deepcopy from types import MappingProxyType from typing import Union @@ -51,7 +52,16 @@ def __init__( metadata: dict = None, encoding: dict = None, validate: bool = True, + copy: bool = True, ) -> None: + if copy: + dims = deepcopy(dims) + coords = deepcopy(coords) + data_vars = deepcopy(data_vars) + metadata = deepcopy(metadata) + encoding = deepcopy(encoding) + validate = deepcopy(validate) + super().__init__( dims=dims, coords=coords, @@ -95,17 +105,16 @@ def get_dim_values( return {kk: self.dims[kk] for kk in keys} def to_xr_ds(self) -> xr.Dataset: - # must pass as dictionary not a mapping proxy: dict = mp | {} return dd_to_xr_ds(_set_dict_read_write(self.data)) def to_nc4_ds(self, filename) -> None: - # must pass as dictionary not a mapping proxy: dict = mp | {} dd_to_nc4_ds(_set_dict_read_write(self.data), filename) return - def to_dd(self) -> DatasetDict: - # must pass as dictionary not a mapping proxy: dict = mp | {} - return DatasetDict(_set_dict_read_write(self.data)) + def to_dd(self, copy=True) -> DatasetDict: + return DatasetDict.from_dict( + _set_dict_read_write(self.data), copy=copy + ) @classmethod def merge(cls, *param_list, copy=True, del_global_src=True): @@ -121,10 +130,12 @@ def merge(cls, *param_list, copy=True, del_global_src=True): def _set_dict_read_write(mp: MappingProxyType): dd = mp | {} for kk, vv in dd.items(): - if isinstance(vv, MappingProxyType): - dd[kk] = _set_dict_read_write(dd[kk] | {}) + if isinstance(vv, (dict, MappingProxyType)): + dd[kk] = _set_dict_read_write(vv) elif isinstance(vv, np.ndarray): - vv.flags.writeable = True + # copy is sufficient to make writeable + # https://numpy.org/doc/stable/reference/generated/numpy.copy.html + dd[kk] = dd[kk].copy() return dd diff --git a/pywatershed/static/metadata/parameters.yaml b/pywatershed/static/metadata/parameters.yaml index 93a8286f..82efe03f 100644 --- a/pywatershed/static/metadata/parameters.yaml +++ b/pywatershed/static/metadata/parameters.yaml @@ -2079,7 +2079,7 @@ ppt_zero_thresh: help: The minimum/zero threhold for precipitation maximum: 0.0 minimum: 0.0 - modules: + modules: None type: F units: inches precip_units: @@ -3057,7 +3057,7 @@ stream_tave_init: help: Cold-start stream average stream temperature maximum: unbounded minimum: unbounded - modules: + modules: None type: F units: unknown subbasin_down: