source: issm/trunk/src/m/consistency/checkfield.py@ 26744

Last change on this file since 26744 was 26744, checked in by Mathieu Morlighem, 3 years ago

merged trunk-jpl and trunk for revision 26742

File size: 14.0 KB
RevLine 
[21341]1import numpy as np
[12842]2import os
[24313]3from re import findall, split
[17806]4from pairoptions import pairoptions
[24313]5from operator import attrgetter
[17806]6import MatlabFuncs as m
[12842]7
8
[24313]9def checkfield(md, *args):
[25836]10 """CHECKFIELD - check field consistency
[24313]11
[25836]12 Used to check model consistency
13 Requires:
[26744]14 'field' or 'fieldname' option. If 'fieldname' is provided, it will
[25836]15 retrieve it from the model md. (md.(fieldname))
[26744]16 If 'field' is provided, it will assume the argument following 'field'
[25836]17 is a numeric array.
[17806]18
[25836]19 Available options:
20 - NaN: 1 if check that there is no NaN
[26744]21 - size: [lines cols], NaN for non checked dimensions, or 'universal'
[25836]22 for any input type (nodal, element, time series, etc)
23 - > : greater than provided value
24 - >= : greater or equal to provided value
25 - < : smallerthan provided value
26 - <=: smaller or equal to provided value
27 - < vec: smallerthan provided values on each vertex
28 - timeseries: 1 if check time series consistency (size and time)
29 - values: list of acceptable values
30 - numel: list of acceptable number of elements
31 - cell: 1 if check that is cell
32 - empty: 1 if check that non empty
33 - message: overloaded error message
[12842]34
[25836]35 Usage:
36 md = checkfield(md, fieldname, options)
[24313]37 """
[12842]38
[24313]39 #get options
40 options = pairoptions(*args)
[12842]41
[24313]42 #get field from model
43 if options.exist('field'):
44 field = options.getfieldvalue('field')
45 fieldname = options.getfieldvalue('fieldname', 'no fieldname')
46 else:
47 fieldname = options.getfieldvalue('fieldname')
48 fieldprefix = split(r'\[(.*?)\]', fieldname)[0]
49 fieldindexes = findall(r'\[(.*?)\]', fieldname)
50 field = attrgetter(fieldprefix)(md)
51 for index in fieldindexes:
52 try:
53 field = field[index.strip("\'")]
54 except TypeError:
55 field = field[int(index)] #looking for an index and not a key
[17806]56
[24313]57 # that works for py2
58 # exec("field = md.{}".format(fieldname))
59 # exec("field = md.{}".format(fieldname), namespace)
[12842]60
[24313]61 if isinstance(field, (bool, int, float)):
62 field = np.array([field])
[12842]63
[24313]64 #check empty
65 if options.exist('empty'):
66 if not field:
67 md = md.checkmessage(options.getfieldvalue('message', "field '{}' is empty".format(fieldname)))
[23189]68
[24313]69 #Check size
70 if options.exist('size'):
71 fieldsize = options.getfieldvalue('size')
72 if type(fieldsize) == str:
[26744]73 if fieldsize == 'universal':
[24313]74 #Check that vector size will not be confusing for ModelProcessorx
75 if (md.mesh.numberofvertices == md.mesh.numberofelements):
[25836]76 raise Exception('number of vertices is the same as number of elements')
[24313]77 elif (md.mesh.numberofvertices + 1 == md.mesh.numberofelements):
[25836]78 raise Exception('number of vertices + 1 is the same as number of elements')
[24313]79 elif (md.mesh.numberofvertices == md.mesh.numberofelements + 1):
[25836]80 raise Exception('number of vertices is the same as number of elements + 1')
[12842]81
[24313]82 #Uniform field
[25836]83 if (field.shape[0] == 1):
84 if (np.ndim(field) > 1 and np.shape(field)[1] != 1):
[24313]85 md = md.checkmessage(options.getfieldvalue('message', "field '{}' is not supported".format(fieldname)))
[22758]86
[24313]87 #vertex oriented input, only one column allowed
88 elif (np.shape(field)[0] == md.mesh.numberofvertices):
[25836]89 if (np.ndim(field) > 1 and np.shape(field)[1] != 1):
[24313]90 md = md.checkmessage(options.getfieldvalue('message', "field '{}' is not supported".format(fieldname)))
[20500]91
[24313]92 #element oriented input, one or more column (patch) is ok
93 elif (np.shape(field)[0] == md.mesh.numberofelements):
94 #nothing to do here (either constant per element, or defined on nodes)
95 pass
[22758]96
[24313]97 #vertex time series
98 elif (np.shape(field)[0] == md.mesh.numberofvertices + 1):
[25836]99 if (np.ndim(field) > 1 and np.shape(field)[1] <= 1):
[24313]100 md = md.checkmessage(options.getfieldvalue('message', "field '{}' is not supported".format(fieldname)))
[12842]101
[24313]102 #element time series
103 elif (np.shape(field)[0] == md.mesh.numberofelements + 1):
[25836]104 if (np.ndim(field) > 1 and np.shape(field)[1] <= 1):
[24313]105 md = md.checkmessage(options.getfieldvalue('message', "field '{}' is not supported".format(fieldname)))
[12842]106
[24313]107 #else not supported
108 else:
109 md = md.checkmessage(options.getfieldvalue('message', "field '{}' is not supported".format(fieldname)))
[22758]110
[24313]111 else:
[25836]112 raise Exception("fieldsize '{}' not supported yet".format(fieldsize))
[22758]113
[24313]114 else:
115 if len(np.shape(field)) < len(fieldsize):
116 md = md.checkmessage(options.getfieldvalue('message', "field {} has size {} but should be size {}".format(fieldname, np.shape(field), fieldsize)))
117 else:
118 for i in range(np.size(fieldsize)):
119 if (not np.isnan(fieldsize[i])) and (np.shape(field)[i] != fieldsize[i]):
120 md = md.checkmessage(options.getfieldvalue('message', "field {} dimension # {} should be of size {}".format(fieldname, i, fieldsize[i])))
[22758]121
[24313]122 #Check numel
123 if options.exist('numel'):
124 fieldnumel = options.getfieldvalue('numel')
125 if (type(fieldnumel) == int and np.size(field) != fieldnumel) or (type(fieldnumel) == list and np.size(field) not in fieldnumel):
126 if len(fieldnumel) == 1:
127 md = md.checkmessage(options.getfieldvalue('message', "field '{}' size should be {}".format(fieldname, fieldnumel)))
128 elif len(fieldnumel) == 2:
129 md = md.checkmessage(options.getfieldvalue('message', "field '{}' size should be {} or {}".format(fieldname, fieldnumel[0], fieldnumel[1])))
130 else:
131 md = md.checkmessage(options.getfieldvalue('message', "field '{}' size should be {}".format(fieldname, fieldnumel)))
[22758]132
[24313]133 #check NaN
134 if options.getfieldvalue('NaN', 0):
135 if np.any(np.isnan(field)):
136 md = md.checkmessage(options.getfieldvalue('message', "NaN values found in field '{}'".format(fieldname)))
[22758]137
[24313]138 #check Inf
139 if options.getfieldvalue('Inf', 0):
140 if np.any(np.isinf(field)):
141 md = md.checkmessage(options.getfieldvalue('message', "Inf values found in field '{}'".format(fieldname)))
[22758]142
[24313]143 #check cell
144 if options.getfieldvalue('cell', 0):
145 if not isinstance(field, (tuple, list, dict)):
[25836]146 md = md.checkmessage(options.getfieldvalue('message', "field '{}' should be a tuple, list, or dict".format(fieldname)))
[22758]147
[24313]148 #check values
149 if options.exist('values'):
150 fieldvalues = options.getfieldvalue('values')
151 if False in m.ismember(field, fieldvalues):
152 if len(fieldvalues) == 1:
153 md = md.checkmessage(options.getfieldvalue('message', "field '{}' value should be '{}'".format(fieldname, fieldvalues[0])))
154 elif len(fieldvalues) == 2:
[25836]155 md = md.checkmessage(options.getfieldvalue('message', "field '{}' values should be '{}' '{}' or '{}'".format(fieldname, fieldvalues[0], fieldvalues[1])))
[24313]156 else:
157 md = md.checkmessage(options.getfieldvalue('message', "field '{}' should have values in {}".format(fieldname, fieldvalues)))
[12842]158
[24313]159 #check greater
160 if options.exist('>='):
161 lowerbound = options.getfieldvalue('>=')
162 if type(lowerbound) is str:
163 lowerbound = attrgetter(lowerbound)(md)
164 if np.size(lowerbound) > 1: #checking elementwise
165 if any(field < lowerbound):
[25836]166 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values above {}".format(fieldname, lowerbound)))
[24313]167 else:
168 minval = np.nanmin(field)
169 if options.getfieldvalue('timeseries', 0):
170 minval = np.nanmin(field[:-1])
171 elif options.getfieldvalue('singletimeseries', 0):
172 if np.size(field) == 1: #some singletimeseries are just one value
173 minval = field
174 else:
175 minval = np.nanmin(field[0])
[12842]176
[24313]177 if minval < lowerbound:
[25836]178 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values above {}".format(fieldname, lowerbound)))
[16560]179
[24313]180 if options.exist('>'):
181 lowerbound = options.getfieldvalue('>')
182 if type(lowerbound) is str:
183 lowerbound = attrgetter(lowerbound)(md)
184 if np.size(lowerbound) > 1: #checking elementwise
185 if any(field <= lowerbound):
[25836]186 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values above {}".format(fieldname, lowerbound)))
[24313]187 else:
188 minval = np.nanmin(field)
189 if options.getfieldvalue('timeseries', 0):
190 minval = np.nanmin(field[:-1])
191 elif options.getfieldvalue('singletimeseries', 0):
192 if np.size(field) == 1: #some singletimeseries are just one value
193 minval = field
194 else:
195 minval = np.nanmin(field[0])
[12842]196
[24313]197 if minval <= lowerbound:
[25836]198 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values above {}".format(fieldname, lowerbound)))
[20500]199
[24313]200 #check smaller
201 if options.exist('<='):
202 upperbound = options.getfieldvalue('<=')
203 if type(upperbound) is str:
204 upperbound = attrgetter(upperbound)(md)
205 if np.size(upperbound) > 1: #checking elementwise
206 if any(field > upperbound):
[25836]207 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values below {}".format(fieldname, upperbound)))
[24313]208 else:
209 maxval = np.nanmax(field)
210 if options.getfieldvalue('timeseries', 0):
211 maxval = np.nanmax(field[:-1])
212 elif options.getfieldvalue('singletimeseries', 0):
213 if np.size(field) == 1: #some singletimeseries are just one value
214 maxval = field
215 else:
216 maxval = np.nanmax(field[0])
217 elif hasattr(field, 'fov_forward_indices'):
218 maxval = field.fov_forward_indices[0]
219 if maxval > upperbound:
[25836]220 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values below {}".format(fieldname, upperbound)))
[24313]221
222 if options.exist('<'):
223 upperbound = options.getfieldvalue('<')
224 if type(upperbound) is str:
225 upperbound = attrgetter(upperbound)(md)
226 if np.size(upperbound) > 1: #checking elementwise
227 if any(field >= upperbound):
[25836]228 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values below {}".format(fieldname, upperbound)))
[24313]229
230 else:
231 maxval = np.nanmax(field)
232 if options.getfieldvalue('timeseries', 0):
233 maxval = np.nanmax(field[:-1])
234 elif options.getfieldvalue('singletimeseries', 0):
235 if np.size(field) == 1: #some singletimeseries are just one value
236 maxval = field.copy()
237 else:
238 maxval = np.nanmax(field[0])
239
240 if maxval >= upperbound:
[25836]241 md = md.checkmessage(options.getfieldvalue('message', "field {} should have values below {}".format(fieldname, upperbound)))
[24313]242
243 #check file
244 if options.getfieldvalue('file', 0):
245 if not os.path.exists(field):
[25836]246 md = md.checkmessage("file provided in {}: {} does not exist".format(fieldname, field))
[24313]247
248 #Check row of strings
249 if options.exist('stringrow'):
250 if not isinstance(field, list):
[25836]251 md = md.checkmessage(options.getfieldvalue('message', "field {} should be a list".format(fieldname)))
[24313]252
253 #Check forcings (size and times)
254 if options.getfieldvalue('timeseries', 0):
[25836]255 if field.shape[0] == md.mesh.numberofvertices or field.shape[0] == md.mesh.numberofelements:
[24313]256 if np.ndim(field) > 1 and not np.size(field, 1) == 1:
[25836]257 md = md.checkmessage(options.getfieldvalue('message', "field {} should have only one column as there are md.mesh.numberofvertices lines".format(fieldname)))
258 elif field.shape[0] == md.mesh.numberofvertices + 1 or field.shape[0] == md.mesh.numberofelements + 1:
[24313]259 if np.ndim(field) > 1 and not all(field[-1, :] == np.sort(field[-1, :])):
[25836]260 md = md.checkmessage(options.getfieldvalue('message', "field {} columns should be sorted chronologically".format(fieldname)))
[24313]261 if np.ndim(field) > 1 and any(field[-1, 0:-1] == field[-1, 1:]):
[25836]262 md = md.checkmessage(options.getfieldvalue('message', "field {} columns must not contain duplicate timesteps".format(fieldname)))
[24313]263 else:
[25836]264 md = md.checkmessage(options.getfieldvalue('message', "field {} should have md.mesh.numberofvertices or md.mesh.numberofvertices + 1 lines".format(fieldname)))
[24313]265
266 #Check single value forcings (size and times)
267 if options.getfieldvalue('singletimeseries', 0):
[25836]268 if field.shape[0] == 2:
[24313]269 if not all(field[-1, :] == np.sort(field[-1, :])):
[25836]270 md = md.checkmessage(options.getfieldvalue('message', "field {} columns should be sorted chronologically".format(fieldname)))
[24313]271 if any(field[-1, 0:-1] == field[-1, 1:]):
[25836]272 md = md.checkmessage(options.getfieldvalue('message', "field {} columns must not contain duplicate timesteps".format(fieldname)))
273 elif field.shape[0] == 1:
[24313]274 if np.ndim(field) > 1 and not np.size(field, 1) == 1:
[25836]275 md = md.checkmessage(options.getfieldvalue('message', "field {} should be either a scalar or have 2 lines".format(fieldname)))
[24313]276 else:
[25836]277 md = md.checkmessage(options.getfieldvalue('message', "field {} should have 2 lines or be a scalar".format(fieldname)))
[24313]278
279 return md
Note: See TracBrowser for help on using the repository browser.