pyPRMS 0.9.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. pyPRMS/Exceptions_custom.py +31 -0
  2. pyPRMS/__init__.py +52 -0
  3. pyPRMS/cbh/Cbh.py +431 -0
  4. pyPRMS/cbh/CbhAscii.py +458 -0
  5. pyPRMS/cbh/CbhNetcdf.py +199 -0
  6. pyPRMS/cbh/__init__.py +3 -0
  7. pyPRMS/constants.py +131 -0
  8. pyPRMS/control/Control.py +362 -0
  9. pyPRMS/control/ControlFile.py +161 -0
  10. pyPRMS/control/ControlVariable.py +208 -0
  11. pyPRMS/control/__init__.py +3 -0
  12. pyPRMS/dimensions/Dimension.py +154 -0
  13. pyPRMS/dimensions/Dimensions.py +256 -0
  14. pyPRMS/dimensions/__init__.py +2 -0
  15. pyPRMS/input/DataFile.py +354 -0
  16. pyPRMS/input/InputVariable.py +61 -0
  17. pyPRMS/input/__init__.py +0 -0
  18. pyPRMS/metadata/__init__.py +1 -0
  19. pyPRMS/metadata/metadata.py +430 -0
  20. pyPRMS/parameters/ParamDb.py +73 -0
  21. pyPRMS/parameters/Parameter.py +624 -0
  22. pyPRMS/parameters/ParameterFile.py +190 -0
  23. pyPRMS/parameters/ParameterNetCDF.py +74 -0
  24. pyPRMS/parameters/ParameterSet.py +96 -0
  25. pyPRMS/parameters/Parameters.py +1506 -0
  26. pyPRMS/parameters/__init__.py +5 -0
  27. pyPRMS/plot_helpers.py +305 -0
  28. pyPRMS/prms_helpers.py +235 -0
  29. pyPRMS/py.typed +0 -0
  30. pyPRMS/summary/OutputCSV.py +64 -0
  31. pyPRMS/summary/OutputVariable.py +163 -0
  32. pyPRMS/summary/OutputVariables.py +227 -0
  33. pyPRMS/summary/__init__.py +2 -0
  34. pyPRMS/utilities/__init__.py +0 -0
  35. pyPRMS/utilities/convert_cbh.py +107 -0
  36. pyPRMS/utilities/convert_model_output.py +91 -0
  37. pyPRMS/utilities/convert_params.py +60 -0
  38. pyPRMS/version.py +13 -0
  39. pyPRMS/xml/cbh.xml +163 -0
  40. pyPRMS/xml/control.xml +1447 -0
  41. pyPRMS/xml/dimensions.xml +311 -0
  42. pyPRMS/xml/modules.xml +251 -0
  43. pyPRMS/xml/parameters.xml +6932 -0
  44. pyPRMS/xml/time_series_input.xml +198 -0
  45. pyPRMS/xml/variables.xml +8173 -0
  46. pyprms-0.9.7.dist-info/LICENSE.md +21 -0
  47. pyprms-0.9.7.dist-info/METADATA +67 -0
  48. pyprms-0.9.7.dist-info/RECORD +51 -0
  49. pyprms-0.9.7.dist-info/WHEEL +5 -0
  50. pyprms-0.9.7.dist-info/entry_points.txt +3 -0
  51. pyprms-0.9.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,190 @@
1
+
2
+ # from typing import Any, Union, Dict, List, OrderedDict as OrderedDictType, Set
3
+ import numpy as np
4
+ from typing import List, Optional, Set
5
+
6
+ from ..Exceptions_custom import ParameterExistsError, ParameterNotValidError
7
+ from .Parameters import Parameters
8
+ from ..constants import DIMENSIONS_HDR, PARAMETERS_HDR, VAR_DELIM, PTYPE_TO_DTYPE
9
+ from ..prms_helpers import get_file_iter
10
+
11
+ from rich.console import Console
12
+ from rich import pretty
13
+
14
+ pretty.install()
15
+ con = Console()
16
+
17
+
18
+ class ParameterFile(Parameters):
19
+
20
+ """Class to handle reading PRMS parameter file format."""
21
+
22
+ def __init__(self, filename: str,
23
+ metadata,
24
+ verbose: Optional[bool] = False):
25
+ # verify: Optional[bool] = True):
26
+ """Create the ParameterFile object.
27
+
28
+ :param filename: name of parameter file
29
+ :param verbose: output debugging information
30
+ :param verify: whether to load the master parameters (default=True)
31
+ """
32
+
33
+ super(ParameterFile, self).__init__(metadata=metadata, verbose=verbose)
34
+
35
+ # self.__filename = None
36
+ # self.__header = None
37
+
38
+ self.__isloaded = False
39
+ self.__updated_parameters: Set[str] = set()
40
+ self.__verbose = verbose
41
+ self.filename = filename
42
+
43
+ @property
44
+ def filename(self) -> str:
45
+ """Get parameter filename.
46
+
47
+ :returns: name of parameter file
48
+ """
49
+
50
+ return self.__filename
51
+
52
+ @filename.setter
53
+ def filename(self, name: str):
54
+ """Set the name of the parameter file.
55
+
56
+ :param name: name of parameter file
57
+ """
58
+
59
+ self.__isloaded = False
60
+ self.__filename = name
61
+ self.__header: List[str] = [] # Initialize the list of file headers
62
+
63
+ self._read()
64
+
65
+ @property
66
+ def headers(self) -> List[str]:
67
+ """Get the headers from the parameter file.
68
+
69
+ :returns: list of headers from parameter file
70
+ """
71
+
72
+ return self.__header
73
+
74
+ @property
75
+ def updated_parameters(self) -> Set[str]:
76
+ """Get list of parameters that had more than one entry in the parameter file.
77
+
78
+ :returns: list of parameters
79
+ """
80
+
81
+ return self.__updated_parameters
82
+
83
+ def _read(self):
84
+ """Read parameter file.
85
+ """
86
+
87
+ if self.__verbose: # pragma: no cover
88
+ con.print('INFO: Reading parameter file')
89
+
90
+ # Read the parameter file into memory and parse it
91
+ it = get_file_iter(self.filename)
92
+
93
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
94
+ # Grab the header stuff first
95
+ for line in it:
96
+ if line.strip('* ') == DIMENSIONS_HDR:
97
+ break
98
+ self.__header.append(line)
99
+
100
+ if self.__verbose: # pragma: no cover
101
+ con.print(f'HEADERS: {self.__header}')
102
+
103
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104
+ # Now process the dimensions
105
+ for line in it:
106
+ if line.strip('* ') == PARAMETERS_HDR:
107
+ break
108
+ if line == VAR_DELIM:
109
+ continue
110
+
111
+ # Add dimension - all dimensions are scalars
112
+ self.dimensions.add(name=line, size=int(next(it)))
113
+
114
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
115
+ # Lastly process the parameters
116
+ for line in it:
117
+ if line == VAR_DELIM:
118
+ continue
119
+ varname = line.split(' ')[0]
120
+ # if self.__verbose: # pragma: no cover
121
+ # print(f'{varname=}')
122
+
123
+ # Add the parameter
124
+ try:
125
+ self.add(name=varname)
126
+ except ParameterExistsError:
127
+ if self.__verbose: # pragma: no cover
128
+ con.print(f'[bold]{varname}[/]: updated with new values')
129
+ self.__updated_parameters.add(varname)
130
+ except ParameterNotValidError:
131
+ if self.__verbose: # pragma: no cover
132
+ con.print(f'[bold]{varname}[/]: [gold3]is not a valid parameter; skipping. [/]')
133
+
134
+ # Skip to the next parameter
135
+ try:
136
+ while next(it) != VAR_DELIM:
137
+ pass
138
+ except StopIteration:
139
+ # Hit end of file
140
+ pass
141
+ continue
142
+
143
+ # Read the dimension names
144
+ ndims = int(next(it)) # number of dimensions for this variable
145
+ dim_names = [next(it) for _ in range(ndims)]
146
+
147
+ # Total dimension size declared for parameter in file; it should equal the size of
148
+ # the declared global dimensions.
149
+ dim_size = int(next(it))
150
+
151
+ # The datatype
152
+ param_dtype = int(next(it))
153
+
154
+ if self.get(varname).is_scalar:
155
+ vals = np.array(next(it), dtype=PTYPE_TO_DTYPE[param_dtype])
156
+ else:
157
+ # Arrays of strings should be objects
158
+ if param_dtype == 4:
159
+ vals = np.zeros(dim_size, dtype=object)
160
+ else:
161
+ vals = np.zeros(dim_size, dtype=PTYPE_TO_DTYPE[param_dtype])
162
+
163
+ for idx in range(0, dim_size):
164
+ # NOTE: string-float to int works but float to int does not
165
+ vals[idx] = next(it)
166
+
167
+ # Make sure there are not any more values in the file
168
+ try:
169
+ cnt = dim_size
170
+ while True:
171
+ cval = next(it)
172
+ if cval[0:4] == VAR_DELIM or cval.strip() == '':
173
+ break
174
+ cnt += 1
175
+
176
+ if cnt > dim_size:
177
+ print(f'WARNING: Too many values specified for {varname}')
178
+ print(f' {dim_size} expected, {cnt} given')
179
+ print(' Removing parameter')
180
+
181
+ self.remove(varname)
182
+ continue
183
+ except StopIteration:
184
+ # Hit the end of the file
185
+ pass
186
+
187
+ self.get(varname).data = vals # type: ignore
188
+
189
+ self.adjust_bounded_parameters()
190
+ self.__isloaded = True
@@ -0,0 +1,74 @@
1
+
2
+ import xarray as xr
3
+ from typing import Optional
4
+
5
+ from .Parameters import Parameters
6
+
7
+
8
+ NCF_TO_NHM_TYPES = {'int32': 1, 'float32': 2, 'float64': 3, '|S1': 4}
9
+
10
+
11
+ class ParameterNetCDF(Parameters):
12
+ """Read parameter database stored in netCDF format"""
13
+
14
+ def __init__(self,
15
+ filename: str,
16
+ metadata,
17
+ verbose: Optional[bool] = False):
18
+ """Initialize ParamDb object.
19
+
20
+ :param filename: Path the ParamDb netcdf file
21
+ :param verbose: Output additional debugging information
22
+ :param verify: Verify parameters against master list
23
+ """
24
+
25
+ super(ParameterNetCDF, self).__init__(metadata=metadata, verbose=verbose)
26
+ self.__filename = filename
27
+ self.__verbose = verbose
28
+
29
+ # Read the parameters from the parameter database
30
+ self._read()
31
+
32
+ def _read(self):
33
+ """Read a parameter netCDF file.
34
+ """
35
+
36
+ xr_df = xr.open_dataset(self.__filename, mask_and_scale=False, decode_timedelta=False)
37
+
38
+ # Populate the dimensions first
39
+ self.dimensions.add(name='one', size=1)
40
+ # self.dimensions.add(name='ndays')
41
+
42
+ for dn, ds in dict(xr_df.sizes).items():
43
+ self.dimensions.add(name=str(dn), size=ds)
44
+
45
+ # Add ndepl using ndeplval
46
+ self.dimensions.add(name='ndepl', size=int(xr_df.sizes['ndeplval'] / 11))
47
+
48
+ # Add nobs if needed
49
+ if not self.dimensions.exists('nobs'):
50
+ if self.dimensions.exists('npoigages'):
51
+ self.dimensions.add(name='nobs', size=self.dimensions.get('npoigages').size)
52
+ else:
53
+ self.dimensions.add(name='nobs', size=0)
54
+
55
+ if not self.dimensions.exists('ngw'):
56
+ self.dimensions.add(name='ngw', size=self.dimensions.get('nhru').size)
57
+
58
+ if not self.dimensions.exists('nssr'):
59
+ self.dimensions.add(name='nssr', size=self.dimensions.get('nhru').size)
60
+
61
+ # Now add the parameters
62
+ for var in xr_df.variables.keys():
63
+ if self.__verbose: # pragma: no cover
64
+ print(str(var))
65
+
66
+ cparam = xr_df[var].T
67
+
68
+ # Add the parameter
69
+ self.add(name=str(var))
70
+
71
+ # Add the data
72
+ self.get(str(var)).data = cparam.values
73
+
74
+ self.adjust_bounded_parameters()
@@ -0,0 +1,96 @@
1
+
2
+ # class ParameterSet(object):
3
+ # def degenerate_parameters(self):
4
+ # """Print parameters that have fewer dimensions than specified in the master parameters."""
5
+ #
6
+ # result = []
7
+ # # TODO: 20230726 PAN - This is not needed with the current parameter code
8
+ # if self.__master_params is not None:
9
+ # for kk, vv in self.parameters.items():
10
+ # try:
11
+ # if set(vv.dimensions.keys()) != set(self.__master_params[kk].dimensions.keys()):
12
+ # if not (set(self.__master_params[kk].dimensions.keys()).issubset(set(HRU_DIMS)) and
13
+ # set(vv.dimensions.keys()).issubset(HRU_DIMS)):
14
+ # result.append(kk)
15
+ # if self.verbose:
16
+ # print(f'Parameter, {kk}, is degenerate')
17
+ # print(' parameter: ', list(vv.dimensions.keys()))
18
+ # print(' master: ', list(self.__master_params[kk].dimensions.keys()))
19
+ # except ValueError:
20
+ # if self.verbose:
21
+ # print(f'ERROR: Parameter, {kk}, is not a valid PRMS parameter')
22
+ # return result
23
+
24
+ # def expand_parameter(self, name: str):
25
+ # """Expand an existing parameter.
26
+ #
27
+ # Expand (e.g. reshape) a parameter, broadcasting existing value(s) into
28
+ # new shape specified by master parameters. The hru_deplcrv parameter has
29
+ # special handling to also update the snarea_curve parameter.
30
+ #
31
+ # :param name: name of parameter
32
+ # """
33
+ # # TODO: 20230726 PAN - This is not needed with the current parameter code
34
+ # if self.__master_params is not None:
35
+ # # 1) make sure parameter exists
36
+ # if self.__master_params.exists(name):
37
+ # # 2) get dimensions from master parameters
38
+ # new_dims = self.__master_params.parameters[name].dimensions.copy()
39
+ #
40
+ # # The new_dims copy is no longer of type Dimensions, instead it
41
+ # # is an OrderedDict
42
+ # # 3) get dimension sizes from global dimensions object
43
+ # for kk, vv in new_dims.items():
44
+ # vv.size = self.__dimensions[kk].size
45
+ #
46
+ # if self.verbose and set(new_dims.keys()) == set(self.__parameters[name].dimensions.keys()):
47
+ # print(f'Parameter, {name}, already has the maximum number of dimensions')
48
+ # print(' current: ', list(self.__parameters[name].dimensions.keys()))
49
+ # print(' requested: ', list(new_dims.keys()))
50
+ #
51
+ # # TODO: Write special case where hru_deplcrv is dimensioned nhru, but
52
+ # # the number of snarea_curve entries is less than nhru * 11.
53
+ # else:
54
+ # # 4) call reshape for the parameter
55
+ # self.__parameters[name].reshape(new_dims)
56
+ #
57
+ # if name == 'hru_deplcrv':
58
+ # # hru_deplcrv needs special handling
59
+ # # 2) get current value of hru_deplcrv, this is the snow_index to use
60
+ # # 3) replace broadcast original value with np.arange(1:nhru)
61
+ # orig_index = self.__parameters[name].data[0] - 1
62
+ # new_indices = np.arange(1, new_dims['nhru'].size + 1)
63
+ # self.__parameters['hru_deplcrv'].data = new_indices
64
+ #
65
+ # # 5) get snarea_curve associated with original hru_deplcrv value
66
+ # curr_snarea_curve = self.__parameters['snarea_curve'].data.reshape((-1, 11))[orig_index, :]
67
+ #
68
+ # # 6) replace current snarea_curve values with broadcast of select snarea_curve*nhru
69
+ # new_snarea_curve = np.broadcast_to(curr_snarea_curve, (new_dims['nhru'].size, 11))
70
+ # # 7) reset snarea_curve dimension size to nhru*11
71
+ # self.__parameters['snarea_curve'].dimensions['ndeplval'].size = new_dims['nhru'].size * 11
72
+ # self.__parameters['snarea_curve'].data = new_snarea_curve.flatten(order='C')
73
+ #
74
+ # if self.verbose:
75
+ # print('hru_deplcrv and snarea_curve have been expanded/updated')
76
+
77
+ # def remove_by_global_id(self, hrus: Optional[List] = None,
78
+ # segs: Optional[List] = None):
79
+ # """Removes data-by-id (nhm_seg, nhm_id) from all parameters.
80
+ #
81
+ # :param hrus: List of HRU IDs to remove
82
+ # :param segs: List of segment IDs to remove
83
+ # """
84
+ # self.__parameters.remove_by_global_id(hrus=hrus, segs=segs)
85
+ #
86
+ # # Adjust the global dimensions
87
+ # if segs is not None:
88
+ # self.__dimensions['nsegment'].size -= len(segs)
89
+ #
90
+ # if hrus is not None:
91
+ # self.__dimensions['nhru'].size -= len(hrus)
92
+ #
93
+ # if self.__dimensions.exists('nssr'):
94
+ # self.__dimensions['nssr'].size -= len(hrus)
95
+ # if self.__dimensions.exists('ngw'):
96
+ # self.__dimensions['ngw'].size -= len(hrus)