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
pyPRMS/input/DataFile.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pandas as pd # type: ignore
|
|
3
|
+
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich import pretty
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Optional, Sequence, Union
|
|
8
|
+
pretty.install()
|
|
9
|
+
con = Console()
|
|
10
|
+
|
|
11
|
+
from .InputVariable import InputVariable
|
|
12
|
+
|
|
13
|
+
# TS_FORMAT = '%Y %m %d %H %M %S' # 1915 1 13 0 0 0
|
|
14
|
+
|
|
15
|
+
HEADER_SEP = '//////////'
|
|
16
|
+
STATION_START = '// Station IDs for'
|
|
17
|
+
UNITS_START = '// Unit:'
|
|
18
|
+
DATA_SEP = '####'
|
|
19
|
+
COMMENT = '//'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DataFile(object):
|
|
23
|
+
"""Class for working with observed streamflow in the PRMS ASCII data file format"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, filename: Union[str, os.PathLike],
|
|
26
|
+
missing: Sequence[str] = ('-99.9', '-999.0', '-9999.0'),
|
|
27
|
+
verbose: bool = False,
|
|
28
|
+
include_metadata: bool = True):
|
|
29
|
+
"""Create the DataFile object.
|
|
30
|
+
|
|
31
|
+
:param filename: name of data file
|
|
32
|
+
:param missing: list of missing values
|
|
33
|
+
:param verbose: output debugging information
|
|
34
|
+
:param include_metadata: whether to include metadata
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
self.__missing = missing
|
|
38
|
+
self.filename = filename
|
|
39
|
+
self.__verbose = verbose
|
|
40
|
+
self.__include_metadata = include_metadata
|
|
41
|
+
|
|
42
|
+
self.__timecols = 6 # number columns for time in the file
|
|
43
|
+
self.__header = '' # data file header from first line of the file
|
|
44
|
+
|
|
45
|
+
# Dictionary of input variables and InputVariable objects
|
|
46
|
+
self.__input_vars: Dict[str, InputVariable] = {}
|
|
47
|
+
|
|
48
|
+
# Internal dictionary of input variables and associated metadata
|
|
49
|
+
self.__input_vars_intern: Dict[str, Dict[str, Union[int, str, List[str], pd.DataFrame]]] = {}
|
|
50
|
+
|
|
51
|
+
self.__data_raw: Optional[pd.DataFrame] = None
|
|
52
|
+
|
|
53
|
+
self.load_file(self.filename)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def data(self) -> pd.DataFrame:
|
|
57
|
+
"""Pandas dataframe of the data file for each input variable
|
|
58
|
+
|
|
59
|
+
:returns: Pandas dataframe of the data file
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
return self.__data_raw
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def input_variables(self) -> Dict[str, Dict[str, Union[int, str, List[str], pd.DataFrame]]]:
|
|
66
|
+
"""Get the input variables in the data file.
|
|
67
|
+
|
|
68
|
+
:returns: Dictionary of input variables that are available in the data file
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
return self.__input_vars_intern
|
|
72
|
+
|
|
73
|
+
def data_by_variable(self, variable: str) -> pd.DataFrame:
|
|
74
|
+
"""Get the data for a specific input variable
|
|
75
|
+
|
|
76
|
+
:param variable: name of input variable
|
|
77
|
+
:returns: Pandas dataframe of the data for the input variable
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
import warnings
|
|
81
|
+
|
|
82
|
+
msg = "DataFile.data_by_variable() method to be deprecated"
|
|
83
|
+
warnings.warn(msg, DeprecationWarning)
|
|
84
|
+
data = self.__input_vars[variable].data.copy()
|
|
85
|
+
|
|
86
|
+
# The names are a headache any other way, hard to make them truly
|
|
87
|
+
# backwards compatible using the old code.
|
|
88
|
+
data.columns = variable + "_" + data.columns
|
|
89
|
+
assert type(data) is pd.DataFrame
|
|
90
|
+
return data
|
|
91
|
+
|
|
92
|
+
def get(self, name: str) -> InputVariable:
|
|
93
|
+
"""Get the metadata for a specific input variable.
|
|
94
|
+
|
|
95
|
+
:param name: name of input variable
|
|
96
|
+
:returns: InputVariable object
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
return self.__input_vars[name]
|
|
100
|
+
|
|
101
|
+
def load_file(self, filename: Union[str, os.PathLike]):
|
|
102
|
+
"""Read the PRMS ASCII streamflow data file.
|
|
103
|
+
|
|
104
|
+
:param filename: name of data file
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
header_info = []
|
|
108
|
+
|
|
109
|
+
with open(filename, 'r') as fhdl:
|
|
110
|
+
# First line is a descriptive header
|
|
111
|
+
self.__header = fhdl.readline().rstrip()
|
|
112
|
+
|
|
113
|
+
# Get the input variable names and sizes
|
|
114
|
+
while line := fhdl.readline():
|
|
115
|
+
line = line.rstrip()
|
|
116
|
+
|
|
117
|
+
if len(line) == 0:
|
|
118
|
+
continue
|
|
119
|
+
if line[0:len(COMMENT)] == COMMENT:
|
|
120
|
+
header_info.append(line)
|
|
121
|
+
continue
|
|
122
|
+
if line[0:len(DATA_SEP)] == DATA_SEP:
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
# Get the input variable name and total size for the variable
|
|
126
|
+
nm: str
|
|
127
|
+
sz: Union[str, int]
|
|
128
|
+
|
|
129
|
+
nm, sz = tuple(line.split())
|
|
130
|
+
sz = int(sz)
|
|
131
|
+
|
|
132
|
+
if sz > 0:
|
|
133
|
+
if nm in self.__input_vars_intern:
|
|
134
|
+
raise KeyError(f'{nm} declared multiple times in the data file')
|
|
135
|
+
self.__input_vars_intern[nm] = dict(size=sz)
|
|
136
|
+
|
|
137
|
+
# =============================
|
|
138
|
+
# Process metadata
|
|
139
|
+
self._add_metadata(header_info)
|
|
140
|
+
|
|
141
|
+
# =============================
|
|
142
|
+
# Read the input variables data
|
|
143
|
+
# The first 6 columns are [year month day hour minute seconds]
|
|
144
|
+
time_col_names = ['year', 'month', 'day', 'hour', 'minute', 'second']
|
|
145
|
+
data_col_names = self._data_column_names()
|
|
146
|
+
col_names = time_col_names.copy()
|
|
147
|
+
col_names.extend(data_col_names)
|
|
148
|
+
|
|
149
|
+
# Use pandas to read the data in from the remainder of the file
|
|
150
|
+
self.__data_raw = pd.read_csv(fhdl, sep=r'\s+', header=None, na_values=self.__missing,
|
|
151
|
+
names=col_names, engine='c', skipinitialspace=True)
|
|
152
|
+
self.__data_raw['time'] = pd.to_datetime(self.__data_raw[time_col_names], yearfirst=True)
|
|
153
|
+
self.__data_raw.drop(columns=time_col_names, inplace=True)
|
|
154
|
+
self.__data_raw.set_index('time', inplace=True)
|
|
155
|
+
|
|
156
|
+
# Add data to each input variable
|
|
157
|
+
self._add_variable_data()
|
|
158
|
+
|
|
159
|
+
def _add_metadata(self, header_info: List[str]):
|
|
160
|
+
"""Add metadata from data file.
|
|
161
|
+
|
|
162
|
+
:param header_info: list of header lines from the data file
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
it = iter(header_info)
|
|
166
|
+
if 'Downsizer' in self.__header or 'Bandit' in self.__header:
|
|
167
|
+
for line in it:
|
|
168
|
+
if line[0:len(STATION_START)] == STATION_START:
|
|
169
|
+
# Process the station information
|
|
170
|
+
station_vars = line[len(STATION_START):].replace(' ', '').strip(':').split(',')
|
|
171
|
+
|
|
172
|
+
line = next(it)
|
|
173
|
+
if line == '// ID':
|
|
174
|
+
# Skip the metadata column information line (found Bandit data files)
|
|
175
|
+
line = next(it)
|
|
176
|
+
|
|
177
|
+
while line[0:len(HEADER_SEP)] != HEADER_SEP and len(line) > 2:
|
|
178
|
+
for cvar in station_vars:
|
|
179
|
+
if cvar not in self.__input_vars_intern:
|
|
180
|
+
raise KeyError(f'{cvar} is not one of the input variables declared in the data file')
|
|
181
|
+
self.__input_vars_intern[cvar].setdefault('stations', []).extend(line.
|
|
182
|
+
replace(COMMENT, '').
|
|
183
|
+
replace(' ', '').
|
|
184
|
+
split(','))
|
|
185
|
+
line = next(it)
|
|
186
|
+
elif line[0:len(UNITS_START)] == UNITS_START:
|
|
187
|
+
# Process the units
|
|
188
|
+
while line[0:len(HEADER_SEP)] != HEADER_SEP:
|
|
189
|
+
for elem in (line.replace(UNITS_START, '').replace(COMMENT, '').replace(' ', '').split(',')):
|
|
190
|
+
cvar, cunits = elem.split('=')
|
|
191
|
+
try:
|
|
192
|
+
self.__input_vars_intern[cvar]['units'] = cunits
|
|
193
|
+
except KeyError:
|
|
194
|
+
con.print(f'[red]{cvar}[/] is not a valid input variable name in this data file')
|
|
195
|
+
pass
|
|
196
|
+
line = next(it)
|
|
197
|
+
|
|
198
|
+
def _add_variable_data(self):
|
|
199
|
+
"""Add data to each input variable.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
# Create a data key for each input variable that maps to their respective parts of the dataframe
|
|
203
|
+
st_idx = 0
|
|
204
|
+
for cvar, cmeta in self.__input_vars_intern.items():
|
|
205
|
+
self.__input_vars[cvar] = InputVariable(name=cvar,
|
|
206
|
+
data=self.__data_raw.iloc[:, st_idx:(st_idx + cmeta['size'])],
|
|
207
|
+
units=cmeta.get('units', None))
|
|
208
|
+
# self.__input_vars_intern[cvar]['data'] = self.__data_raw.iloc[:, st_idx:(st_idx + cmeta['size'])]
|
|
209
|
+
st_idx += cmeta['size']
|
|
210
|
+
|
|
211
|
+
def _data_column_names(self) -> List[str]:
|
|
212
|
+
"""Create column names for the dataframe.
|
|
213
|
+
|
|
214
|
+
:returns: list of column names
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
var_col_names = []
|
|
218
|
+
|
|
219
|
+
for cvar, meta in self.__input_vars_intern.items():
|
|
220
|
+
if 'stations' in meta:
|
|
221
|
+
for cstn in meta['stations']:
|
|
222
|
+
var_col_names.append(f'{cvar}_{cstn}')
|
|
223
|
+
else:
|
|
224
|
+
# No usable metadata in the data file
|
|
225
|
+
for idx in range(1, meta['size']+1):
|
|
226
|
+
var_col_names.append(f'{cvar}_{idx}')
|
|
227
|
+
|
|
228
|
+
return var_col_names
|
|
229
|
+
|
|
230
|
+
# def write_selected_stations(self, filename):
|
|
231
|
+
# """Writes station observations to a new file"""
|
|
232
|
+
# # Either writes out all station observations or, if stations are selected,
|
|
233
|
+
# # then a subset of station observations.
|
|
234
|
+
#
|
|
235
|
+
# # Sample header format
|
|
236
|
+
#
|
|
237
|
+
# # $Id:$
|
|
238
|
+
# # ////////////////////////////////////////////////////////////
|
|
239
|
+
# # // Station metadata (listed in the same order as the data):
|
|
240
|
+
# # // ID Type Latitude Longitude Elevation
|
|
241
|
+
# # // <station info>
|
|
242
|
+
# # ////////////////////////////////////////////////////////////
|
|
243
|
+
# # // Unit: runoff = ft3 per sec, elevation = feet
|
|
244
|
+
# # ////////////////////////////////////////////////////////////
|
|
245
|
+
# # runoff <number of stations for each type>
|
|
246
|
+
# # ################################################################################
|
|
247
|
+
#
|
|
248
|
+
# top_line = '$Id:$\n'
|
|
249
|
+
# section_sep = '////////////////////////////////////////////////////////////\n'
|
|
250
|
+
# meta_header_1 = '// Station metadata (listed in the same order as the data):\n'
|
|
251
|
+
# # metaHeader2 = '// ID Type Latitude Longitude Elevation'
|
|
252
|
+
# meta_header_2 = '// %s\n' % ' '.join(self.metaheader)
|
|
253
|
+
# data_section = '################################################################################\n'
|
|
254
|
+
#
|
|
255
|
+
# # ----------------------------------
|
|
256
|
+
# # Get the station information for each selected station
|
|
257
|
+
# type_count = {} # Counts the number of stations for each type of data (e.g. 'runoff')
|
|
258
|
+
# stninfo = ''
|
|
259
|
+
# if self.__selectedStations is None:
|
|
260
|
+
# for xx in self.__stations:
|
|
261
|
+
# if xx[1] not in type_count:
|
|
262
|
+
# # index 1 should be the type field
|
|
263
|
+
# type_count[xx[1]] = 0
|
|
264
|
+
# type_count[xx[1]] += 1
|
|
265
|
+
#
|
|
266
|
+
# stninfo += '// %s\n' % ' '.join(xx)
|
|
267
|
+
# else:
|
|
268
|
+
# for xx in self.__selectedStations:
|
|
269
|
+
# cstn = self.__stations[self.__stationIndex[xx]]
|
|
270
|
+
#
|
|
271
|
+
# if cstn[1] not in type_count:
|
|
272
|
+
# # index 1 should be the type field
|
|
273
|
+
# type_count[cstn[1]] = 0
|
|
274
|
+
#
|
|
275
|
+
# type_count[cstn[1]] += 1
|
|
276
|
+
#
|
|
277
|
+
# stninfo += '// %s\n' % ' '.join(cstn)
|
|
278
|
+
# # stninfo = stninfo.rstrip('\n')
|
|
279
|
+
#
|
|
280
|
+
# # ----------------------------------
|
|
281
|
+
# # Get the units information
|
|
282
|
+
# unit_line = '// Unit:'
|
|
283
|
+
# for uu in self.__units:
|
|
284
|
+
# unit_line += ' %s,' % ' = '.join(uu)
|
|
285
|
+
# unit_line = '%s\n' % unit_line.rstrip(',')
|
|
286
|
+
#
|
|
287
|
+
# # ----------------------------------
|
|
288
|
+
# # Create the list of types of data that are being included
|
|
289
|
+
# tmpl = []
|
|
290
|
+
#
|
|
291
|
+
# # Create list of types in the correct order
|
|
292
|
+
# for (kk, vv) in self.__types.items():
|
|
293
|
+
# if kk in type_count:
|
|
294
|
+
# tmpl.insert(vv[0], [kk, type_count[kk]])
|
|
295
|
+
#
|
|
296
|
+
# type_line = ''
|
|
297
|
+
# for tt in tmpl:
|
|
298
|
+
# type_line += '%s %d\n' % (tt[0], tt[1])
|
|
299
|
+
# # typeLine = typeLine.rstrip('\n')
|
|
300
|
+
#
|
|
301
|
+
# # Write out the header to the new file
|
|
302
|
+
# outfile = open(filename, 'w')
|
|
303
|
+
# outfile.write(top_line)
|
|
304
|
+
# outfile.write(section_sep)
|
|
305
|
+
# outfile.write(meta_header_1)
|
|
306
|
+
# outfile.write(meta_header_2)
|
|
307
|
+
# outfile.write(stninfo)
|
|
308
|
+
# outfile.write(section_sep)
|
|
309
|
+
# outfile.write(unit_line)
|
|
310
|
+
# outfile.write(section_sep)
|
|
311
|
+
# outfile.write(type_line)
|
|
312
|
+
# outfile.write(data_section)
|
|
313
|
+
#
|
|
314
|
+
# # Write out the data to the new file
|
|
315
|
+
# # Using quoting=csv.QUOTE_NONE results in an error when using a customized date_format
|
|
316
|
+
# # A kludgy work around is to write with quoting and then re-open the file
|
|
317
|
+
# # and write it back out, stripping the quote characters.
|
|
318
|
+
# self.data.to_csv(outfile, index=True, header=False, date_format='%Y %m %d %H %M %S', sep=' ')
|
|
319
|
+
# outfile.close()
|
|
320
|
+
#
|
|
321
|
+
# old = open(filename, 'r').read()
|
|
322
|
+
# new = re.sub('["]', '', old)
|
|
323
|
+
# open(filename, 'w').write(new)
|
|
324
|
+
#
|
|
325
|
+
# # def getRecurrenceInterval(self, thetype):
|
|
326
|
+
# # """Returns the recurrence intervals for each station"""
|
|
327
|
+
# #
|
|
328
|
+
# # # Copy the subset of data
|
|
329
|
+
# # xx = self.seldata(thetype)
|
|
330
|
+
# #
|
|
331
|
+
# # ri = np.zeros(xx.shape)
|
|
332
|
+
# # ri[:,:] = -1.
|
|
333
|
+
# #
|
|
334
|
+
# # # for each station we need to compute the RI for non-zero values
|
|
335
|
+
# # for ss in range(0,xx.shape[1]):
|
|
336
|
+
# # tmp = xx[:,ss] # copy values for current station
|
|
337
|
+
# #
|
|
338
|
+
# # # Get array of indices that would result in a sorted array
|
|
339
|
+
# # sorted_ind = np.argsort(tmp)
|
|
340
|
+
# # #print "sorted_ind.shape:", sorted_ind.shape
|
|
341
|
+
# #
|
|
342
|
+
# # numobs = tmp[(tmp > 0.0),].shape[0] # Number of observations > 0.
|
|
343
|
+
# # nyr = float(numobs / 365) # Number of years of non-zero observations
|
|
344
|
+
# #
|
|
345
|
+
# # nz_cnt = 0 # non-zero value counter
|
|
346
|
+
# # for si in sorted_ind:
|
|
347
|
+
# # if tmp[si] > 0.:
|
|
348
|
+
# # nz_cnt += 1
|
|
349
|
+
# # rank = numobs - nz_cnt + 1
|
|
350
|
+
# # ri[si,ss] = (nyr + 1.) / float(rank)
|
|
351
|
+
# # #print "%s: [%d]: %d %d %0.3f %0.3f" % (ss, si, numobs, rank, tmp[si], ri[si,ss])
|
|
352
|
+
# #
|
|
353
|
+
# # return ri
|
|
354
|
+
# ***** END of class streamflow()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class InputVariable(object):
|
|
7
|
+
"""Class for working with input variables."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, name: str,
|
|
10
|
+
data: pd.DataFrame,
|
|
11
|
+
units: Optional[str] = None):
|
|
12
|
+
"""Initialize the InputVariable object.
|
|
13
|
+
|
|
14
|
+
:param name: Name or kind of the input variable
|
|
15
|
+
:param data: Input variable data
|
|
16
|
+
:param units: Units of the input variable
|
|
17
|
+
"""
|
|
18
|
+
self.__name = name
|
|
19
|
+
self.__units = units
|
|
20
|
+
self.data = data
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def data(self) -> pd.DataFrame:
|
|
24
|
+
"""Returns the input variable data.
|
|
25
|
+
|
|
26
|
+
:returns: Input variable dataframe
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
return self.__data
|
|
30
|
+
|
|
31
|
+
@data.setter
|
|
32
|
+
def data(self, data_in: pd.DataFrame):
|
|
33
|
+
"""Set the input variable data.
|
|
34
|
+
|
|
35
|
+
:param data_in: Input variable data
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
col_names = {}
|
|
39
|
+
for xx in data_in.columns:
|
|
40
|
+
col_names[xx] = xx.split('_')[1]
|
|
41
|
+
|
|
42
|
+
self.__data = data_in.copy()
|
|
43
|
+
self.__data.rename(columns=col_names, inplace=True)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def name(self) -> str:
|
|
47
|
+
"""Returns the input variable kind.
|
|
48
|
+
|
|
49
|
+
:returns: Input variable kind
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
return self.__name
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def units(self) -> str:
|
|
56
|
+
"""Returns the input variable units.
|
|
57
|
+
|
|
58
|
+
:returns: Input variable units
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
return self.__units
|
pyPRMS/input/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .metadata import MetaData
|