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.
- pyPRMS/Exceptions_custom.py +31 -0
- pyPRMS/__init__.py +52 -0
- pyPRMS/cbh/Cbh.py +431 -0
- pyPRMS/cbh/CbhAscii.py +458 -0
- pyPRMS/cbh/CbhNetcdf.py +199 -0
- pyPRMS/cbh/__init__.py +3 -0
- pyPRMS/constants.py +131 -0
- pyPRMS/control/Control.py +362 -0
- pyPRMS/control/ControlFile.py +161 -0
- pyPRMS/control/ControlVariable.py +208 -0
- pyPRMS/control/__init__.py +3 -0
- pyPRMS/dimensions/Dimension.py +154 -0
- pyPRMS/dimensions/Dimensions.py +256 -0
- pyPRMS/dimensions/__init__.py +2 -0
- pyPRMS/input/DataFile.py +354 -0
- pyPRMS/input/InputVariable.py +61 -0
- pyPRMS/input/__init__.py +0 -0
- pyPRMS/metadata/__init__.py +1 -0
- pyPRMS/metadata/metadata.py +430 -0
- pyPRMS/parameters/ParamDb.py +73 -0
- pyPRMS/parameters/Parameter.py +624 -0
- pyPRMS/parameters/ParameterFile.py +190 -0
- pyPRMS/parameters/ParameterNetCDF.py +74 -0
- pyPRMS/parameters/ParameterSet.py +96 -0
- pyPRMS/parameters/Parameters.py +1506 -0
- pyPRMS/parameters/__init__.py +5 -0
- pyPRMS/plot_helpers.py +305 -0
- pyPRMS/prms_helpers.py +235 -0
- pyPRMS/py.typed +0 -0
- pyPRMS/summary/OutputCSV.py +64 -0
- pyPRMS/summary/OutputVariable.py +163 -0
- pyPRMS/summary/OutputVariables.py +227 -0
- pyPRMS/summary/__init__.py +2 -0
- pyPRMS/utilities/__init__.py +0 -0
- pyPRMS/utilities/convert_cbh.py +107 -0
- pyPRMS/utilities/convert_model_output.py +91 -0
- pyPRMS/utilities/convert_params.py +60 -0
- pyPRMS/version.py +13 -0
- pyPRMS/xml/cbh.xml +163 -0
- pyPRMS/xml/control.xml +1447 -0
- pyPRMS/xml/dimensions.xml +311 -0
- pyPRMS/xml/modules.xml +251 -0
- pyPRMS/xml/parameters.xml +6932 -0
- pyPRMS/xml/time_series_input.xml +198 -0
- pyPRMS/xml/variables.xml +8173 -0
- pyprms-0.9.7.dist-info/LICENSE.md +21 -0
- pyprms-0.9.7.dist-info/METADATA +67 -0
- pyprms-0.9.7.dist-info/RECORD +51 -0
- pyprms-0.9.7.dist-info/WHEEL +5 -0
- pyprms-0.9.7.dist-info/entry_points.txt +3 -0
- 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)
|