1 | |
---|
2 | import string, re, os, sys, traceback |
---|
3 | |
---|
4 | def strmm3( mm ): |
---|
5 | return string.join( map( lambda x: '%s="%s" [correct: "%s"]' % x, mm ), '; ' ) |
---|
6 | |
---|
7 | from fcc_utils import mipTableScan |
---|
8 | |
---|
9 | class reportSection: |
---|
10 | |
---|
11 | def __init__(self,id,cls,parent=None, description=None): |
---|
12 | self.id = id |
---|
13 | self.cls = cls |
---|
14 | self.parent = parent |
---|
15 | self.description = description |
---|
16 | self.records = [] |
---|
17 | self.subsections = [] |
---|
18 | self.closed = False |
---|
19 | self.npass = 0 |
---|
20 | self.fail = 0 |
---|
21 | self.auditDone = True |
---|
22 | |
---|
23 | def addSubSection( self, id, cls, description=None): |
---|
24 | assert not self.closed, 'Attempt to add sub-section to closed report section' |
---|
25 | self.subsections.append( reportSection(id, cls, parent=self, description=description ) ) |
---|
26 | self.auditDone = False |
---|
27 | return self.subsections[-1] |
---|
28 | |
---|
29 | def addRecord( self, id, cls, res, msg ): |
---|
30 | assert not self.closed, 'Attempt to add record to closed report section' |
---|
31 | self.records.append( (id, cls, res, msg) ) |
---|
32 | self.auditDone = False |
---|
33 | |
---|
34 | def close(self): |
---|
35 | self.closed = True |
---|
36 | |
---|
37 | def reopen(self): |
---|
38 | self.closed = False |
---|
39 | |
---|
40 | def audit(self): |
---|
41 | if self.auditDone: |
---|
42 | return |
---|
43 | self.closed = True |
---|
44 | self.fail = 0 |
---|
45 | self.npass = 0 |
---|
46 | for ss in self.subsections: |
---|
47 | ss.audit() |
---|
48 | self.fail += ss.fail |
---|
49 | self.npass += ss.npass |
---|
50 | |
---|
51 | for r in self.records: |
---|
52 | if r[2]: |
---|
53 | self.npass += 1 |
---|
54 | else: |
---|
55 | self.fail += 1 |
---|
56 | |
---|
57 | class abortChecks(Exception): |
---|
58 | pass |
---|
59 | class loggedException(Exception): |
---|
60 | pass |
---|
61 | class baseException(Exception): |
---|
62 | |
---|
63 | def __init__(self,msg): |
---|
64 | self.msg = 'utils_c4:: %s' % msg |
---|
65 | |
---|
66 | def __str__(self): |
---|
67 | return unicode(self).encode('utf-8') |
---|
68 | |
---|
69 | def __unicode__(self): |
---|
70 | return self.msg % tuple([force_unicode(p, errors='replace') |
---|
71 | for p in self.params]) |
---|
72 | class checkSeq: |
---|
73 | def __init__(self): |
---|
74 | pass |
---|
75 | |
---|
76 | def check(self,x): |
---|
77 | d = map( lambda i: x[i+1] - x[i], range(len(x)-1) ) |
---|
78 | self.delt = sum(d)/len(d) |
---|
79 | self.dmx = max(d) |
---|
80 | self.dmn = min(d) |
---|
81 | return self.dmx - self.dmn < abs(self.delt)*1.e-4 |
---|
82 | |
---|
83 | cs = checkSeq() |
---|
84 | |
---|
85 | class checkBase: |
---|
86 | |
---|
87 | def __init__(self,cls="CORDEX",reportPass=True,parent=None,monitor=None): |
---|
88 | self.cls = cls |
---|
89 | self.project = cls |
---|
90 | self.abortMessageCount = parent.abortMessageCount |
---|
91 | self.monitor = monitor |
---|
92 | ## check done earlier |
---|
93 | ## assert cls in ['CORDEX','SPECS'],'This version of the checker only supports CORDEX, SPECS' |
---|
94 | self.re_isInt = re.compile( '[0-9]+' ) |
---|
95 | self.errorCount = 0 |
---|
96 | self.passCount = 0 |
---|
97 | self.missingValue = 1.e20 |
---|
98 | from file_utils import ncLib |
---|
99 | if ncLib == 'netCDF4': |
---|
100 | import numpy |
---|
101 | self.missingValue = numpy.float32(self.missingValue) |
---|
102 | self.parent = parent |
---|
103 | self.reportPass=reportPass |
---|
104 | self.pcfg = parent.pcfg |
---|
105 | ################################ |
---|
106 | self.requiredGlobalAttributes = self.pcfg.requiredGlobalAttributes |
---|
107 | self.controlledGlobalAttributes = self.pcfg.controlledGlobalAttributes |
---|
108 | self.globalAttributesInFn = self.pcfg.globalAttributesInFn |
---|
109 | self.requiredVarAttributes = self.pcfg.requiredVarAttributes |
---|
110 | self.drsMappings = self.pcfg.drsMappings |
---|
111 | ####################################### |
---|
112 | self.checks = () |
---|
113 | self.messageCount = 0 |
---|
114 | self.init() |
---|
115 | if not hasattr( self.parent, 'amapListDraft' ): |
---|
116 | self.parent.amapListDraft = [] |
---|
117 | |
---|
118 | def isInt(self,x): |
---|
119 | return self.re_isInt.match( x ) != None |
---|
120 | |
---|
121 | def logMessage(self, msg, error=False ): |
---|
122 | self.messageCount += 1 |
---|
123 | assert self.abortMessageCount < 0 or self.abortMessageCount > self.messageCount, 'Raising error [TESTX01], perhaps for testing' |
---|
124 | if self.parent != None and self.parent.log != None: |
---|
125 | if error: |
---|
126 | self.parent.log.error( msg ) |
---|
127 | else: |
---|
128 | self.parent.log.info( msg ) |
---|
129 | else: |
---|
130 | print msg |
---|
131 | |
---|
132 | doThis = True |
---|
133 | if self.appendLogfile[0] != None and doThis: |
---|
134 | if self.monitor != None: |
---|
135 | nofh0 = self.monitor.get_open_fds() |
---|
136 | xlog = self.c4i.getFileLog( self.appendLogfile[1], flf=self.appendLogfile[0] ) |
---|
137 | if error: |
---|
138 | xlog.error( msg ) |
---|
139 | else: |
---|
140 | xlog.info( msg ) |
---|
141 | self.c4i.closeFileLog() |
---|
142 | if self.monitor != None: |
---|
143 | nofh9 = self.monitor.get_open_fds() |
---|
144 | if nofh9 > nofh0: |
---|
145 | print 'Leaking file handles [1]: %s --- %s' % (nofh0, nofh9) |
---|
146 | |
---|
147 | def log_exception( self, msg): |
---|
148 | if self.parent != None and self.parent.log != None: |
---|
149 | self.parent.log.error("Exception has occured" ,exc_info=1) |
---|
150 | else: |
---|
151 | traceback.print_exc(file=sys.stdout) |
---|
152 | |
---|
153 | def log_error( self, msg ): |
---|
154 | self.lastError = msg |
---|
155 | self.errorCount += 1 |
---|
156 | self.logMessage( '%s.%s: FAILED:: %s' % (self.id,self.checkId,msg), error=True ) |
---|
157 | |
---|
158 | def log_pass( self ): |
---|
159 | self.passCount = True |
---|
160 | if self.reportPass: |
---|
161 | self.logMessage( '%s.%s: OK' % (self.id,self.checkId) ) |
---|
162 | |
---|
163 | def log_abort( self ): |
---|
164 | self.completed = False |
---|
165 | self.logMessage( '%s.%s: ABORTED:: Errors too severe to complete further checks in this module' % (self.id,'xxx') ) |
---|
166 | raise abortChecks |
---|
167 | |
---|
168 | def status(self): |
---|
169 | return '%s.%s' % (self.id,self.checkId) |
---|
170 | |
---|
171 | def test(self,res,msg,abort=False,part=False,appendLogfile=(None,None)): |
---|
172 | self.appendLogfile = appendLogfile |
---|
173 | if res: |
---|
174 | if not part: |
---|
175 | self.log_pass() |
---|
176 | else: |
---|
177 | self.log_error(msg) |
---|
178 | if abort: |
---|
179 | self.log_abort() |
---|
180 | return res |
---|
181 | |
---|
182 | def runChecks(self): |
---|
183 | |
---|
184 | try: |
---|
185 | for c in self.checks: |
---|
186 | c() # run check |
---|
187 | self.completed = True |
---|
188 | except abortChecks: |
---|
189 | ## error logging done before raising this exception |
---|
190 | return |
---|
191 | except: |
---|
192 | self.log_exception( 'Exception caught by runChecks' ) |
---|
193 | raise loggedException |
---|
194 | ##traceback.print_exc(file=open("errlog.txt","a")) |
---|
195 | ##logger.error("Exception has occured" ,exc_info=1) |
---|
196 | |
---|
197 | class checkFileName(checkBase): |
---|
198 | |
---|
199 | def init(self): |
---|
200 | self.id = 'C4.001' |
---|
201 | self.checkId = 'unset' |
---|
202 | self.step = 'Initialised' |
---|
203 | self.checks = (self.do_check_fn,) |
---|
204 | #### |
---|
205 | |
---|
206 | def check(self,fn): |
---|
207 | self.errorCount = 0 |
---|
208 | assert type(fn) in [type('x'),type(u'x')], '1st argument to "check" method of checkGrids shound be a string variable name (not %s)' % type(fn) |
---|
209 | self.fn = fn |
---|
210 | |
---|
211 | self.runChecks() |
---|
212 | ### |
---|
213 | def do_check_fn(self): |
---|
214 | fn = self.fn |
---|
215 | self.errorCount = 0 |
---|
216 | self.completed = False |
---|
217 | |
---|
218 | ## check basic parsing of file name |
---|
219 | self.checkId = '001' |
---|
220 | self.test( fn[-3:] == '.nc', 'File name ending ".nc" expected', abort=True, part=True ) |
---|
221 | bits = string.split( fn[:-3], '_' ) |
---|
222 | self.fnParts = bits[:] |
---|
223 | |
---|
224 | if self.pcfg.domainIndex != None: |
---|
225 | self.domain = self.fnParts[self.pcfg.domainIndex] |
---|
226 | else: |
---|
227 | self.domain = None |
---|
228 | ##if self.cls == 'CORDEX': |
---|
229 | ##self.fnPartsOkLen = [8,9] |
---|
230 | ##self.fnPartsOkFixedLen = [8,] |
---|
231 | ##self.fnPartsOkUnfixedLen = [9,] |
---|
232 | ##checkTrangeLen = True |
---|
233 | ##elif self.cls == 'SPECS': |
---|
234 | ##self.fnPartsOkLen = [6,7] |
---|
235 | ##self.fnPartsOkFixedLen = [6,] |
---|
236 | ##self.fnPartsOkUnfixedLen = [7,] |
---|
237 | ##checkTrangeLen = False |
---|
238 | |
---|
239 | self.test( len(bits) in self.pcfg.fnPartsOkLen, 'File name not parsed in %s elements [%s]' % (str(self.pcfg.fnPartsOkLen),str(bits)), abort=True ) |
---|
240 | |
---|
241 | if self.pcfg.groupIndex != None: |
---|
242 | self.group = self.fnParts[self.pcfg.groupIndex] |
---|
243 | else: |
---|
244 | self.group = None |
---|
245 | |
---|
246 | if self.pcfg.freqIndex != None: |
---|
247 | self.freq = self.fnParts[self.pcfg.freqIndex] |
---|
248 | elif self.group == 'fx': |
---|
249 | self.freq = 'fx' |
---|
250 | else: |
---|
251 | self.freq = None |
---|
252 | |
---|
253 | ##if self.cls == 'CORDEX': |
---|
254 | ##self.freq = self.fnParts[7] |
---|
255 | ##elif self.cls == 'SPECS': |
---|
256 | ##self.freq = self.fnParts[1] |
---|
257 | |
---|
258 | self.var = self.fnParts[0] |
---|
259 | |
---|
260 | self.isFixed = self.freq == 'fx' |
---|
261 | |
---|
262 | self.checkId = '002' |
---|
263 | if not self.isFixed: |
---|
264 | |
---|
265 | ## test time segment |
---|
266 | bits = string.split( self.fnParts[-1], '-' ) |
---|
267 | self.test( len(bits) == 2, 'File time segment [%s] will not parse into 2 elements' % (self.fnParts[-1] ), abort=True, part=True ) |
---|
268 | |
---|
269 | self.test( len(bits[0]) == len(bits[1]), 'Start and end time specified in file name [%s] of unequal length' % (self.fnParts[-1] ), abort=True, part=True ) |
---|
270 | |
---|
271 | for b in bits: |
---|
272 | self.test( self.isInt(b), 'Time segment in filename [%s] contains non integer characters' % (self.fnParts[-1] ), abort=True, part=True ) |
---|
273 | self.log_pass() |
---|
274 | self.fnTimeParts = bits[:] |
---|
275 | |
---|
276 | self.checkId = '003' |
---|
277 | if self.isFixed: |
---|
278 | self.test( len(self.fnParts) in self.pcfg.fnPartsOkFixedLen, 'Number of file name elements not acceptable for fixed data' ) |
---|
279 | |
---|
280 | self.checkId, ok = ('004',True) |
---|
281 | if len(self.fnParts) == 9 and self.pcfg.checkTrangeLen: |
---|
282 | ltr = { 'mon':6, 'sem':6, 'day':8, '3hr':[10,12], '6hr':10 } |
---|
283 | ok &=self.test( self.freq in ltr.keys(), 'Frequency [%s] not recognised' % self.freq, part=True ) |
---|
284 | if ok: |
---|
285 | if type( ltr[self.freq] ) == type(0): |
---|
286 | msg = 'Length of time range parts [%s,%s] not equal to required length [%s] for frequency %s' % (self.fnTimeParts[0],self.fnTimeParts[1],ltr[self.freq],self.freq) |
---|
287 | ok &= self.test( len(self.fnTimeParts[0]) == ltr[self.freq], msg, part=True ) |
---|
288 | elif type( ltr[self.freq] ) in [type([]),type( () )]: |
---|
289 | msg = 'Length of time range parts [%s,%s] not in acceptable list [%s] for frequency %s' % (self.fnTimeParts[0],self.fnTimeParts[1],str(ltr[self.freq]),self.freq) |
---|
290 | ok &= self.test( len(self.fnTimeParts[0]) in ltr[self.freq], msg, part=True ) |
---|
291 | |
---|
292 | if ok: |
---|
293 | self.log_pass() |
---|
294 | self.completed = True |
---|
295 | |
---|
296 | class checkGlobalAttributes(checkBase): |
---|
297 | |
---|
298 | def init(self): |
---|
299 | self.id = 'C4.002' |
---|
300 | self.checkId = 'unset' |
---|
301 | self.step = 'Initialised' |
---|
302 | self.checks = (self.do_check_ga,) |
---|
303 | |
---|
304 | def check(self,globalAts, varAts,varName,varGroup, vocabs, fnParts): |
---|
305 | self.errorCount = 0 |
---|
306 | assert type(varName) in [type('x'),type(u'x')], '1st argument to "check" method of checkGrids shound be a string variable name (not %s)' % type(varName) |
---|
307 | self.var = varName |
---|
308 | self.globalAts = globalAts |
---|
309 | self.varAts = varAts |
---|
310 | self.varGroup = varGroup |
---|
311 | self.vocabs = vocabs |
---|
312 | self.fnParts = fnParts |
---|
313 | self.runChecks() |
---|
314 | |
---|
315 | def getDrs( self ): |
---|
316 | assert self.completed, 'method getDrs should not be called if checks have not been completed successfully' |
---|
317 | ee = {} |
---|
318 | for k in self.drsMappings: |
---|
319 | if self.drsMappings[k] == '@var': |
---|
320 | ee[k] = self.var |
---|
321 | elif self.drsMappings[k] == '@ensemble': |
---|
322 | ee[k] = "r%si%sp%s" % (self.globalAts["realization"],self.globalAts["initialization_method"],self.globalAts["physics_version"]) |
---|
323 | elif self.drsMappings[k] == '@forecast_reference_time': |
---|
324 | x = self.globalAts.get("forecast_reference_time",'yyyy-mm-dd Thh:mm:ssZ' ) |
---|
325 | ee[k] = "%s%s%s" % (x[:4],x[5:7],x[8:10]) |
---|
326 | else: |
---|
327 | ee[k] = self.globalAts[ self.drsMappings[k] ] |
---|
328 | |
---|
329 | for k in ['creation_date','tracking_id']: |
---|
330 | if k in self.globalAts.keys(): |
---|
331 | ee[k] = self.globalAts[k] |
---|
332 | |
---|
333 | return ee |
---|
334 | |
---|
335 | def do_check_ga(self): |
---|
336 | varName = self.var |
---|
337 | globalAts = self.globalAts |
---|
338 | varAts = self.varAts |
---|
339 | varGroup = self.varGroup |
---|
340 | vocabs = self.vocabs |
---|
341 | fnParts = self.fnParts |
---|
342 | |
---|
343 | self.completed = False |
---|
344 | self.checkId = '001' |
---|
345 | m = [] |
---|
346 | for k in self.requiredGlobalAttributes: |
---|
347 | if not globalAts.has_key(k): |
---|
348 | m.append(k) |
---|
349 | |
---|
350 | if not self.test( len(m) == 0, 'Required global attributes missing: %s' % str(m) ): |
---|
351 | gaerr = True |
---|
352 | for k in m: |
---|
353 | self.parent.amapListDraft.append( '#@;%s=%s|%s=%s' % (k,'__absent__',k,'<insert attribute value and uncomment>') ) |
---|
354 | |
---|
355 | self.checkId = '002' |
---|
356 | |
---|
357 | self.test( varAts.has_key( varName ), 'Expected variable [%s] not present' % varName, abort=True, part=True ) |
---|
358 | msg = 'Variable %s not in table %s' % (varName,varGroup) |
---|
359 | self.test( vocabs['variable'].isInTable( varName, varGroup ), msg, abort=True, part=True ) |
---|
360 | |
---|
361 | self.checkId = '003' |
---|
362 | |
---|
363 | mipType = vocabs['variable'].getAttr( varName, varGroup, 'type' ) |
---|
364 | thisType = {'real':'float32', 'integer':'int32' }.get( mipType, mipType ) |
---|
365 | self.test( mipType == None or varAts[varName]['_type'] == thisType, 'Variable [%s/%s] not of type %s [%s]' % (varName,varGroup,thisType,varAts[varName]['_type']) ) |
---|
366 | |
---|
367 | self.checkId = '004' |
---|
368 | m = [] |
---|
369 | reqAts = self.requiredVarAttributes[:] |
---|
370 | if varGroup != 'fx' and self.pcfg.project in ['CORDEX']: |
---|
371 | reqAts.append( 'cell_methods' ) |
---|
372 | for k in reqAts + vocabs['variable'].lists(varName, 'addRequiredAttributes'): |
---|
373 | if not varAts[varName].has_key(k): |
---|
374 | m.append(k) |
---|
375 | if not self.test( len(m) == 0, 'Required variable attributes missing: %s' % str(m) ): |
---|
376 | vaerr = True |
---|
377 | for k in m: |
---|
378 | self.parent.amapListDraft.append( '#@var=%s;%s=%s|%s=%s' % (varName,k,'__absent__',k,'<insert attribute value and uncomment>') ) |
---|
379 | print self.parent.amapListDraft[-1] |
---|
380 | ##vaerr = not self.test( len(m) == 0, 'Required variable attributes missing: %s' % str(m) ) |
---|
381 | |
---|
382 | ##if vaerr or gaerr: |
---|
383 | ##self.log_abort() |
---|
384 | |
---|
385 | ## need to insert a check that variable is present |
---|
386 | self.checkId = '005' |
---|
387 | ok = True |
---|
388 | hm = varAts[varName].get( 'missing_value', None ) != None |
---|
389 | hf = varAts[varName].has_key( '_FillValue' ) |
---|
390 | if hm or hf: |
---|
391 | ok &= self.test( hm, 'missing_value must be present if _FillValue is [%s]' % varName ) |
---|
392 | ok &= self.test( hf, '_FillValue must be present if missing_value is [%s]' % varName ) |
---|
393 | if mipType == 'real': |
---|
394 | if varAts[varName].has_key( 'missing_value' ): |
---|
395 | msg = 'Variable [%s] has incorrect attribute missing_value=%s [correct: %s]' % (varName,varAts[varName]['missing_value'],self.missingValue) |
---|
396 | ok &= self.test( varAts[varName]['missing_value'] == self.missingValue, msg, part=True ) |
---|
397 | if varAts[varName].has_key( '_FillValue' ): |
---|
398 | msg = 'Variable [%s] has incorrect attribute _FillValue=%s [correct: %s]' % (varName,varAts[varName]['_FillValue'],self.missingValue) |
---|
399 | ok &= self.test( varAts[varName]['_FillValue'] == self.missingValue, msg, part=True ) |
---|
400 | |
---|
401 | mm = [] |
---|
402 | |
---|
403 | contAts = ['long_name', 'standard_name', 'units'] |
---|
404 | if varGroup != 'fx': |
---|
405 | contAts.append( 'cell_methods' ) |
---|
406 | hcm = varAts[varName].has_key( "cell_methods" ) |
---|
407 | for k in contAts + vocabs['variable'].lists(varName,'addControlledAttributes'): |
---|
408 | targ = varAts[varName].get( k, 'Attribute not present' ) |
---|
409 | val = vocabs['variable'].getAttr( varName, varGroup, k ) |
---|
410 | |
---|
411 | if k == "cell_methods": |
---|
412 | if val != None: |
---|
413 | parenthesies1 = [] |
---|
414 | targ0 = targ[:] |
---|
415 | while string.find( targ, '(' ) != -1: |
---|
416 | i0 = targ.index( '(' ) |
---|
417 | i1 = targ.index( ')' ) |
---|
418 | parenthesies1.append( targ[i0:i1+1] ) |
---|
419 | targ = targ[:i0-1] + targ[i1+1:] |
---|
420 | parenthesies2 = [] |
---|
421 | val0 = val[:] |
---|
422 | while string.find( val, '(' ) != -1: |
---|
423 | i0 = val.index( '(' ) |
---|
424 | i1 = val.index( ')' ) |
---|
425 | parenthesies2.append( val[i0:i1+1] ) |
---|
426 | val = val[:i0-1] + val[i1+1:] |
---|
427 | for p in parenthesies2: |
---|
428 | if p not in parenthesies1: |
---|
429 | mm.append( (k,parenthesies1,p) ) |
---|
430 | if string.find( targ, val): |
---|
431 | mm.append( (k,targ,val) ) |
---|
432 | elif targ != 'Attribute not present' and targ != val: |
---|
433 | mm.append( (k,targ,val) ) |
---|
434 | |
---|
435 | ok &= self.test( len(mm) == 0, 'Variable [%s] has incorrect attributes: %s' % (varName, strmm3(mm)), part=True ) |
---|
436 | if len( mm ) != 0: |
---|
437 | if self.parent.amapListDraft == None: |
---|
438 | self.parent.amapListDraft = [] |
---|
439 | for m in mm: |
---|
440 | self.parent.amapListDraft.append( '@var=%s;%s=%s|%s=%s' % (varName,m[0],m[1],m[0],m[2]) ) |
---|
441 | |
---|
442 | if ok: |
---|
443 | self.log_pass() |
---|
444 | |
---|
445 | if varGroup != 'fx' and hcm: |
---|
446 | self.isInstantaneous = string.find( varAts[varName]['cell_methods'], 'time: point' ) != -1 |
---|
447 | else: |
---|
448 | self.isInstantaneous = True |
---|
449 | |
---|
450 | self.checkId = '006' |
---|
451 | m = [] |
---|
452 | for a in self.controlledGlobalAttributes: |
---|
453 | if globalAts.has_key(a): |
---|
454 | try: |
---|
455 | if not vocabs[a].check( str(globalAts[a]) ): |
---|
456 | m.append( (a,globalAts[a],vocabs[a].note) ) |
---|
457 | except: |
---|
458 | print 'failed trying to check global attribute %s' % a |
---|
459 | raise |
---|
460 | |
---|
461 | if not self.test( len(m) == 0, 'Global attributes do not match constraints: %s' % str(m) ): |
---|
462 | for t in m: |
---|
463 | self.parent.amapListDraft.append( '#@;%s=%s|%s=%s' % (t[0],str(t[1]),t[0],'<insert attribute value and uncomment>' + str(t[2]) ) ) |
---|
464 | |
---|
465 | self.checkId = '007' |
---|
466 | m = [] |
---|
467 | for i in range(len(self.globalAttributesInFn)): |
---|
468 | if self.globalAttributesInFn[i] != None: |
---|
469 | targVal = fnParts[i] |
---|
470 | if self.globalAttributesInFn[i][0] == "@": |
---|
471 | if self.globalAttributesInFn[i][1:] == "mip_id": |
---|
472 | thisVal = string.split( globalAts["table_id"] )[1] |
---|
473 | elif self.globalAttributesInFn[i][1:] == "experiment_family": |
---|
474 | thisVal = globalAts["experiment_id"][:-4] |
---|
475 | elif self.globalAttributesInFn[i][1:] == "ensemble": |
---|
476 | thisVal = "r%si%sp%s" % (globalAts["realization"],globalAts["initialization_method"],globalAts["physics_version"]) |
---|
477 | elif self.globalAttributesInFn[i][1:] == "forecast_reference_time": |
---|
478 | x = self.globalAts.get("forecast_reference_time",'yyyy-mm-dd Thh:mm:ssZ' ) |
---|
479 | thisVal = "S%s%s%s" % (x[:4],x[5:7],x[8:10]) |
---|
480 | elif self.globalAttributesInFn[i][1:] == "series": |
---|
481 | thisVal = 'series%s' % globalAts["series"] |
---|
482 | elif self.globalAttributesInFn[i][1:] == "experiment_family": |
---|
483 | thisVal = globalAts["experiment_id"][:-4] |
---|
484 | else: |
---|
485 | assert False, "Not coded to deal with this configuration: globalAttributesInFn[%s]=%s" % (i,self.globalAttributesInFn[i]) |
---|
486 | ##print "Generated text val: %s:: %s" % (self.globalAttributesInFn[i], thisVal) |
---|
487 | |
---|
488 | else: |
---|
489 | thisVal = globalAts[self.globalAttributesInFn[i]] |
---|
490 | |
---|
491 | if thisVal != targVal: |
---|
492 | m.append( (i,self.globalAttributesInFn[i]) ) |
---|
493 | |
---|
494 | self.test( len(m) == 0,'File name segments do not match corresponding global attributes: %s' % str(m) ) |
---|
495 | |
---|
496 | self.completed = True |
---|
497 | |
---|
498 | class checkStandardDims(checkBase): |
---|
499 | |
---|
500 | def init(self): |
---|
501 | self.id = 'C4.003' |
---|
502 | self.checkId = 'unset' |
---|
503 | self.step = 'Initialised' |
---|
504 | self.checks = (self.do_check,) |
---|
505 | self.plevRequired = self.pcfg.plevRequired |
---|
506 | self.plevValues = self.pcfg.plevValues |
---|
507 | self.heightRequired = self.pcfg.heightRequired |
---|
508 | self.heightValues = self.pcfg.heightValues |
---|
509 | self.heightRange = self.pcfg.heightRange |
---|
510 | |
---|
511 | def check(self,varName,varGroup, da, va, isInsta): |
---|
512 | self.errorCount = 0 |
---|
513 | assert type(varName) in [type('x'),type(u'x')], '1st argument to "check" method of checkGrids shound be a string variable name (not %s)' % type(varName) |
---|
514 | self.var = varName |
---|
515 | self.varGroup = varGroup |
---|
516 | self.da = da |
---|
517 | self.va = va |
---|
518 | self.isInsta = isInsta |
---|
519 | self.runChecks() |
---|
520 | |
---|
521 | def do_check(self): |
---|
522 | varName = self.var |
---|
523 | varGroup = self.varGroup |
---|
524 | da = self.da |
---|
525 | va = self.va |
---|
526 | isInsta = self.isInsta |
---|
527 | |
---|
528 | self.errorCount = 0 |
---|
529 | self.completed = False |
---|
530 | self.checkId = '001' |
---|
531 | if varGroup != 'fx': |
---|
532 | ok = True |
---|
533 | self.test( 'time' in da.keys(), 'Time dimension not found' , abort=True, part=True ) |
---|
534 | if not isInsta: |
---|
535 | ok &= self.test( da['time'].get( 'bounds', 'xxx' ) == 'time_bnds', 'Required bounds attribute not present or not correct value', part=True ) |
---|
536 | |
---|
537 | ## is time zone designator needed? |
---|
538 | tunits = da['time'].get( 'units', 'xxx' ) |
---|
539 | if self.project == 'CORDEX': |
---|
540 | ok &= self.test( tunits in ["days since 1949-12-01 00:00:00Z", "days since 1949-12-01 00:00:00", "days since 1949-12-01"], |
---|
541 | 'Time units [%s] attribute not set correctly to "days since 1949-12-01 00:00:00Z"' % tunits, part=True ) |
---|
542 | else: |
---|
543 | ok &= self.test( tunits[:10] == "days since", 'time units [%s] attribute not set correctly to "days since ....."' % tunits, part=True ) |
---|
544 | |
---|
545 | ok &= self.test( da['time'].has_key( 'calendar' ), 'Time: required attribute calendar missing', part=True ) |
---|
546 | |
---|
547 | ok &= self.test( da['time']['_type'] == "float64", 'Time: data type not float64', part=True ) |
---|
548 | |
---|
549 | if ok: |
---|
550 | self.log_pass() |
---|
551 | self.calendar = da['time'].get( 'calendar', 'None' ) |
---|
552 | else: |
---|
553 | self.calendar = 'None' |
---|
554 | self.checkId = '002' |
---|
555 | if varName in self.plevRequired: |
---|
556 | ok = True |
---|
557 | self.test( 'plev' in va.keys(), 'plev coordinate not found %s' % str(va.keys()), abort=True, part=True ) |
---|
558 | |
---|
559 | ok &= self.test( int( va['plev']['_data'][0] ) == self.plevValues[varName], \ |
---|
560 | 'plev value [%s] does not match required [%s]' % (va['plev']['_data'],self.plevValues[varName] ), part=True ) |
---|
561 | |
---|
562 | plevAtDict = {'standard_name':"air_pressure", \ |
---|
563 | 'long_name':"pressure", \ |
---|
564 | 'units':"Pa", \ |
---|
565 | 'positive':"down", \ |
---|
566 | 'axis':"Z" } |
---|
567 | |
---|
568 | if varName in ['clh','clm','cll']: |
---|
569 | plevAtDict['bounds']= "plev_bnds" |
---|
570 | |
---|
571 | for k in plevAtDict.keys(): |
---|
572 | ok &= self.test( va['plev'].get( k, None ) == plevAtDict[k], |
---|
573 | 'plev attribute %s absent or wrong value (should be %s)' % (k,plevAtDict[k]), part=True ) |
---|
574 | |
---|
575 | if varName in ['clh','clm','cll']: |
---|
576 | self.test( "plev_bnds" in va.keys(), 'plev_bnds variable not found %s' % str(va.keys()), abort=True, part=True ) |
---|
577 | mm = [] |
---|
578 | for k in plevAtDict.keys(): |
---|
579 | if k != 'bounds' and k in va['plev_bnds'].keys(): |
---|
580 | if va['plev_bnds'][k] != va['plev'][k]: |
---|
581 | mm.append(k) |
---|
582 | ok &= self.test( len(mm) == 0, 'Attributes of plev_bnds do not match those of plev: %s' % str(mm), part=True ) |
---|
583 | |
---|
584 | bndsVals = {'clh':[44000, 0], 'clm':[68000, 44000], 'cll':[100000, 68000] } |
---|
585 | res = self.test( len( va['plev_bnds']['_data'] ) == 2, 'plev_bnds array is of wrong length', part=True ) |
---|
586 | ok &= res |
---|
587 | if res: |
---|
588 | kk = 0 |
---|
589 | for i in [0,1]: |
---|
590 | if int(va['plev_bnds']['_data'][i]) != bndsVals[varName][i]: |
---|
591 | kk+=1 |
---|
592 | ok &= self.test( kk == 0, 'plev_bnds values not correct: should be %s' % str(bndsVals[varName]), part=True ) |
---|
593 | |
---|
594 | if ok: |
---|
595 | self.log_pass() |
---|
596 | |
---|
597 | self.checkId = '003' |
---|
598 | if varName in self.heightRequired: |
---|
599 | heightAtDict = {'long_name':"height", 'standard_name':"height", 'units':"m", 'positive':"up", 'axis':"Z" } |
---|
600 | ok = True |
---|
601 | ok &= self.test( 'height' in va.keys(), 'height coordinate not found %s' % str(va.keys()), abort=True, part=True ) |
---|
602 | ##ok &= self.test( abs( va['height']['_data'] - self.heightValues[varName]) < 0.001, \ |
---|
603 | ##'height value [%s] does not match required [%s]' % (va['height']['_data'],self.heightValues[varName] ), part=True ) |
---|
604 | |
---|
605 | ok1 = self.test( len( va['height']['_data'] ) == 1, 'More height values (%s) than expected (1)' % (len( va['height']['_data'])), part=True ) |
---|
606 | if ok1: |
---|
607 | r = self.heightRange[varName] |
---|
608 | ok1 &= self.test( r[0] <= va['height']['_data'][0] <= r[1], \ |
---|
609 | 'height value [%s] not in specified range [%s]' % (va['height']['_data'], (self.heightRange[varName] ) ), part=True ) |
---|
610 | |
---|
611 | ok &= ok1 |
---|
612 | |
---|
613 | for k in heightAtDict.keys(): |
---|
614 | val = va['height'].get( k, "none" ) |
---|
615 | if not self.test( val == heightAtDict[k], \ |
---|
616 | 'height attribute %s absent or wrong value (should be %s)' % (k,heightAtDict[k]), part=True ): |
---|
617 | self.parent.amapListDraft.append( '@var=%s;%s=%s|%s=%s' % ('height',k,val,k,heightAtDict[k]) ) |
---|
618 | ok = False |
---|
619 | |
---|
620 | if ok: |
---|
621 | self.log_pass() |
---|
622 | |
---|
623 | self.completed = True |
---|
624 | |
---|
625 | class checkGrids(checkBase): |
---|
626 | |
---|
627 | def init(self): |
---|
628 | self.id = 'C4.004' |
---|
629 | self.checkId = 'unset' |
---|
630 | self.step = 'Initialised' |
---|
631 | self.checks = (self.do_check_rp,self.do_check_intd) |
---|
632 | |
---|
633 | def check(self,varName, domain, da, va): |
---|
634 | self.errorCount = 0 |
---|
635 | assert type(varName) in [type('x'),type(u'x')], '1st argument to "check" method of checkGrids shound be a string variable name (not %s)' % type(varName) |
---|
636 | self.var = varName |
---|
637 | self.domain = domain |
---|
638 | self.da = da |
---|
639 | self.va = va |
---|
640 | |
---|
641 | self.runChecks() |
---|
642 | ##for c in self.checks: |
---|
643 | ##c() |
---|
644 | ##self.do_check_rp() |
---|
645 | ##self.do_check_intd() |
---|
646 | |
---|
647 | def do_check_rp(self): |
---|
648 | varName = self.var |
---|
649 | domain = self.domain |
---|
650 | da = self.da |
---|
651 | va = self.va |
---|
652 | if va[varName].get( 'grid_mapping', None ) == "rotated_pole": |
---|
653 | self.checkId = '001' |
---|
654 | atDict = { 'grid_mapping_name':'rotated_latitude_longitude' } |
---|
655 | atDict['grid_north_pole_latitude'] = self.pcfg.rotatedPoleGrids[domain]['grid_np_lat'] |
---|
656 | if self.pcfg.rotatedPoleGrids[domain]['grid_np_lon'] != 'N/A': |
---|
657 | atDict['grid_north_pole_longitude'] = self.pcfg.rotatedPoleGrids[domain]['grid_np_lon'] |
---|
658 | |
---|
659 | self.checkId = '002' |
---|
660 | self.test( 'rlat' in da.keys() and 'rlon' in da.keys(), 'rlat and rlon not found (required for grid_mapping = rotated_pole )', abort=True, part=True ) |
---|
661 | |
---|
662 | atDict = {'rlat':{'long_name':"rotated latitude", 'standard_name':"grid_latitude", 'units':"degrees", 'axis':"Y", '_type':'float64'}, |
---|
663 | 'rlon':{'long_name':"rotated longitude", 'standard_name':"grid_longitude", 'units':"degrees", 'axis':"X", '_type':'float64'} } |
---|
664 | mm = [] |
---|
665 | for k in ['rlat','rlon']: |
---|
666 | for k2 in atDict[k].keys(): |
---|
667 | if atDict[k][k2] != da[k].get(k2, None ): |
---|
668 | mm.append( (k,k2) ) |
---|
669 | self.test( len(mm) == 0, 'Required attributes of grid coordinate arrays not correct: %s' % str(mm) ) |
---|
670 | |
---|
671 | self.checkId = '003' |
---|
672 | ok = True |
---|
673 | for k in ['rlat','rlon']: |
---|
674 | res = len(da[k]['_data']) == self.pcfg.rotatedPoleGrids[domain][ {'rlat':'nlat','rlon':'nlon' }[k] ] |
---|
675 | if not res: |
---|
676 | self.test( res, 'Size of %s dimension does not match specification (%s,%s)' % (k,a,b), part=True ) |
---|
677 | ok = False |
---|
678 | |
---|
679 | a = ( da['rlat']['_data'][0], da['rlat']['_data'][-1], da['rlon']['_data'][0], da['rlon']['_data'][-1] ) |
---|
680 | b = map( lambda x: self.pcfg.rotatedPoleGrids[domain][x], ['s','n','w','e'] ) |
---|
681 | mm = [] |
---|
682 | for i in range(4): |
---|
683 | if a[i] != b[i]: |
---|
684 | mm.append( (a[i],b[i]) ) |
---|
685 | |
---|
686 | ok &= self.test( len(mm) == 0, 'Domain boundaries for rotated pole grid do not match %s' % str(mm), part=True ) |
---|
687 | |
---|
688 | for k in ['rlat','rlon']: |
---|
689 | ok &= self.test( cs.check( da[k]['_data'] ), '%s values not evenly spaced -- min/max delta = %s, %s' % (k,cs.dmn,cs.dmx), part=True ) |
---|
690 | |
---|
691 | if ok: |
---|
692 | self.log_pass() |
---|
693 | |
---|
694 | def do_check_intd(self): |
---|
695 | varName = self.var |
---|
696 | domain = self.domain |
---|
697 | da = self.da |
---|
698 | va = self.va |
---|
699 | if domain[-1] == 'i': |
---|
700 | self.checkId = '002' |
---|
701 | self.test( 'lat' in da.keys() and 'lon' in da.keys(), 'lat and lon not found (required for interpolated data)', abort=True, part=True ) |
---|
702 | |
---|
703 | atDict = {'lat':{'long_name':"latitude", 'standard_name':"latitude", 'units':"degrees_north", '_type':'float64'}, |
---|
704 | 'lon':{'long_name':"longitude", 'standard_name':"longitude", 'units':"degrees_east", '_type':'float64'} } |
---|
705 | mm = [] |
---|
706 | for k in ['lat','lon']: |
---|
707 | for k2 in atDict[k].keys(): |
---|
708 | if atDict[k][k2] != da[k].get(k2, None ): |
---|
709 | mm.append( (k,k2) ) |
---|
710 | |
---|
711 | self.test( len(mm) == 0, 'Required attributes of grid coordinate arrays not correct: %s' % str(mm), part=True ) |
---|
712 | |
---|
713 | ok = True |
---|
714 | for k in ['lat','lon']: |
---|
715 | res = len(da[k]['_data']) >= self.pcfg.interpolatedGrids[domain][ {'lat':'nlat','lon':'nlon' }[k] ] |
---|
716 | if not res: |
---|
717 | a,b = len(da[k]['_data']), self.pcfg.interpolatedGrids[domain][ {'lat':'nlat','lon':'nlon' }[k] ] |
---|
718 | self.test( res, 'Size of %s dimension does not match specification (%s,%s)' % (k,a,b), part=True ) |
---|
719 | ok = False |
---|
720 | |
---|
721 | a = ( da['lat']['_data'][0], da['lat']['_data'][-1], da['lon']['_data'][0], da['lon']['_data'][-1] ) |
---|
722 | b = map( lambda x: self.pcfg.interpolatedGrids[domain][x], ['s','n','w','e'] ) |
---|
723 | rs = self.pcfg.interpolatedGrids[domain]['res'] |
---|
724 | c = [-rs,rs,-rs,rs] |
---|
725 | mm = [] |
---|
726 | for i in range(4): |
---|
727 | if a[i] != b[i]: |
---|
728 | x = (a[i]-b[i])/c[i] |
---|
729 | if x < 0 or abs( x - int(x) ) > 0.001: |
---|
730 | skipThis = False |
---|
731 | if self.project == 'CORDEX': |
---|
732 | if domain[:3] == 'ANT': |
---|
733 | if i == 2 and abs( a[i] - 0.25 ) < 0.001: |
---|
734 | skipThis = True |
---|
735 | elif i == 3 and abs( a[i] - 359.75 ) < 0.001: |
---|
736 | skipThis = True |
---|
737 | if not skipThis: |
---|
738 | mm.append( (a[i],b[i]) ) |
---|
739 | |
---|
740 | ok &= self.test( len(mm) == 0, 'Interpolated grid boundary error: File %s; Req. %s' % (str(a),str(b)), part=True ) |
---|
741 | |
---|
742 | for k in ['lat','lon']: |
---|
743 | ok &= self.test( cs.check( da[k]['_data'] ), '%s values not evenly spaced -- min/max delta = %s, %s' % (k,cs.dmn,cs.dmx), part=True ) |
---|
744 | if ok: |
---|
745 | self.log_pass() |
---|
746 | |
---|
747 | class mipVocab: |
---|
748 | |
---|
749 | def __init__(self,pcfg,dummy=False): |
---|
750 | project = pcfg.project |
---|
751 | if dummy: |
---|
752 | self.pcfg = pcfg |
---|
753 | return self.dummyMipTable() |
---|
754 | ##assert project in ['CORDEX','SPECS'],'Project %s not recognised' % project |
---|
755 | ##if project == 'CORDEX': |
---|
756 | ##dir = 'cordex_vocabs/mip/' |
---|
757 | ##tl = ['fx','sem','mon','day','6h','3h'] |
---|
758 | ##vgmap = {'6h':'6hr','3h':'3hr'} |
---|
759 | ##fnpat = 'CORDEX_%s' |
---|
760 | ##elif project == 'SPECS': |
---|
761 | ##dir = 'specs_vocabs/mip/' |
---|
762 | ##tl = ['fx','Omon','Amon','Lmon','OImon','day','6hrLev'] |
---|
763 | ##vgmap = {} |
---|
764 | ##fnpat = 'SPECS_%s' |
---|
765 | dir, tl, vgmap, fnpat = pcfg.mipVocabPars |
---|
766 | ms = mipTableScan() |
---|
767 | self.varInfo = {} |
---|
768 | self.varcons = {} |
---|
769 | for f in tl: |
---|
770 | vg = vgmap.get( f, f ) |
---|
771 | self.varcons[vg] = {} |
---|
772 | fn = fnpat % f |
---|
773 | ll = open( '%s%s' % (dir,fn) ).readlines() |
---|
774 | ee = ms.scan_table(ll,None,asDict=True) |
---|
775 | for v in ee.keys(): |
---|
776 | ## set global default: type float |
---|
777 | eeee = { 'type':pcfg.defaults.get( 'variableDataType', 'float' ) } |
---|
778 | ar = [] |
---|
779 | ac = [] |
---|
780 | for a in ee[v][1].keys(): |
---|
781 | eeee[a] = ee[v][1][a] |
---|
782 | ##if 'positive' in eeee.keys(): |
---|
783 | ##ar.append( 'positive' ) |
---|
784 | ##ac.append( 'positive' ) |
---|
785 | self.varInfo[v] = {'ar':ar, 'ac':ac } |
---|
786 | self.varcons[vg][v] = eeee |
---|
787 | |
---|
788 | def dummyMipTable(self): |
---|
789 | self.varInfo = {} |
---|
790 | self.varcons = {} |
---|
791 | ee = { 'standard_name':'sn%s', 'long_name':'n%s', 'units':'1' } |
---|
792 | dir, tl, vgmap, fnpat = self.pcfg.mipVocabPars |
---|
793 | for f in tl: |
---|
794 | vg = vgmap.get( f, f ) |
---|
795 | self.varcons[vg] = {} |
---|
796 | for i in range(12): |
---|
797 | v = 'v%s' % i |
---|
798 | eeee = {} |
---|
799 | eeee['standard_name'] = ee['standard_name'] % i |
---|
800 | eeee['long_name'] = ee['long_name'] % i |
---|
801 | eeee['cell_methods'] = 'time: point' |
---|
802 | eeee['units'] = ee['units'] |
---|
803 | eeee['type'] = 'float' |
---|
804 | ar = [] |
---|
805 | ac = [] |
---|
806 | self.varInfo[v] = {'ar':ar, 'ac':ac } |
---|
807 | self.varcons[vg][v] = eeee |
---|
808 | |
---|
809 | def lists( self, k, k2 ): |
---|
810 | if k2 == 'addRequiredAttributes': |
---|
811 | return self.varInfo[k]['ar'] |
---|
812 | elif k2 == 'addControlledAttributes': |
---|
813 | return self.varInfo[k]['ac'] |
---|
814 | else: |
---|
815 | raise 'mipVocab.lists called with bad list specifier %s' % k2 |
---|
816 | |
---|
817 | def isInTable( self, v, vg ): |
---|
818 | assert vg in self.varcons.keys(), '%s not found in self.varcons.keys() [%s]' % (vg,str(self.varcons.keys()) ) |
---|
819 | return (v in self.varcons[vg].keys()) |
---|
820 | |
---|
821 | def getAttr( self, v, vg, a ): |
---|
822 | assert vg in self.varcons.keys(), '%s not found in self.varcons.keys()' |
---|
823 | assert v in self.varcons[vg].keys(), '%s not found in self.varcons[%s].keys()' % (v,vg) |
---|
824 | |
---|
825 | return self.varcons[vg][v][a] |
---|
826 | |
---|
827 | class patternControl: |
---|
828 | |
---|
829 | def __init__(self,tag,pattern,list=None): |
---|
830 | try: |
---|
831 | self.re_pat = re.compile( pattern ) |
---|
832 | except: |
---|
833 | print "Failed to compile pattern >>%s<< (%s)" % (pattern, tag) |
---|
834 | self.pattern = pattern |
---|
835 | self.list = list |
---|
836 | |
---|
837 | def check(self,val): |
---|
838 | self.note = '-' |
---|
839 | m = self.re_pat.match( val ) |
---|
840 | if self.list == None: |
---|
841 | self.note = "simple test" |
---|
842 | return m != None |
---|
843 | else: |
---|
844 | if m == None: |
---|
845 | self.note = "no match %s::%s" % (val,self.pattern) |
---|
846 | return False |
---|
847 | if not m.groupdict().has_key("val"): |
---|
848 | self.note = "no 'val' in match" |
---|
849 | return False |
---|
850 | self.note = "val=%s" % m.groupdict()["val"] |
---|
851 | return m.groupdict()["val"] in self.list |
---|
852 | |
---|
853 | class listControl: |
---|
854 | def __init__(self,tag,list,split=False,splitVal=None): |
---|
855 | self.list = list |
---|
856 | self.tag = tag |
---|
857 | self.split = split |
---|
858 | self.splitVal = splitVal |
---|
859 | |
---|
860 | def check(self,val): |
---|
861 | self.note = '-' |
---|
862 | if len(self.list) < 4: |
---|
863 | self.note = str( self.list ) |
---|
864 | else: |
---|
865 | self.note = str( self.list[:4] ) |
---|
866 | if self.split: |
---|
867 | if self.splitVal == None: |
---|
868 | vs = string.split( val ) |
---|
869 | else: |
---|
870 | vs = string.split( val, self.spliVal ) |
---|
871 | return all( map( lambda x: x in self.list, vs ) ) |
---|
872 | else: |
---|
873 | return val in self.list |
---|
874 | |
---|
875 | |
---|
876 | class checkByVar(checkBase): |
---|
877 | |
---|
878 | def init(self): |
---|
879 | self.id = 'C5.001' |
---|
880 | self.checkId = 'unset' |
---|
881 | self.step = 'Initialised' |
---|
882 | self.checks = (self.checkTrange,) |
---|
883 | |
---|
884 | def setLogDict( self,fLogDict ): |
---|
885 | self.fLogDict = fLogDict |
---|
886 | |
---|
887 | def impt(self,flist): |
---|
888 | ee = {} |
---|
889 | for f in flist: |
---|
890 | fn = string.split(f, '/' )[-1] |
---|
891 | fnParts = string.split( fn[:-3], '_' ) |
---|
892 | ##if self.cls == 'CORDEX': |
---|
893 | ##isFixed = fnParts[7] == 'fx' |
---|
894 | ##group = fnParts[7] |
---|
895 | ##elif self.cls == 'SPECS': |
---|
896 | ##isFixed = fnParts[1] == 'fx' |
---|
897 | ##group = fnParts[1] |
---|
898 | |
---|
899 | if self.pcfg.freqIndex != None: |
---|
900 | freq = fnParts[self.pcfg.freqIndex] |
---|
901 | else: |
---|
902 | freq = None |
---|
903 | |
---|
904 | isFixed = freq == 'fx' |
---|
905 | group = fnParts[ self.pcfg.groupIndex ] |
---|
906 | |
---|
907 | if isFixed: |
---|
908 | trange = None |
---|
909 | else: |
---|
910 | trange = string.split( fnParts[-1], '-' ) |
---|
911 | var = fnParts[0] |
---|
912 | thisKey = string.join( fnParts[:-1], '.' ) |
---|
913 | if group not in ee.keys(): |
---|
914 | ee[group] = {} |
---|
915 | if thisKey not in ee[group].keys(): |
---|
916 | ee[group][thisKey] = [] |
---|
917 | ee[group][thisKey].append( (f,fn,group,trange) ) |
---|
918 | |
---|
919 | nn = len(flist) |
---|
920 | n2 = 0 |
---|
921 | for k in ee.keys(): |
---|
922 | for k2 in ee[k].keys(): |
---|
923 | n2 += len( ee[k][k2] ) |
---|
924 | |
---|
925 | assert nn==n2, 'some file lost!!!!!!' |
---|
926 | self.info = '%s files, %s frequencies' % (nn,len(ee.keys()) ) |
---|
927 | self.ee = ee |
---|
928 | |
---|
929 | def check(self, recorder=None,calendar='None',norun=False): |
---|
930 | self.errorCount = 0 |
---|
931 | self.recorder=recorder |
---|
932 | self.calendar=calendar |
---|
933 | if calendar == '360-day': |
---|
934 | self.enddec = 30 |
---|
935 | else: |
---|
936 | self.enddec = 31 |
---|
937 | mm = { 'enddec':self.enddec } |
---|
938 | self.pats = {'mon':('(?P<d>[0-9]{3})101','(?P<e>[0-9]{3})012'), \ |
---|
939 | 'sem':('(?P<d>[0-9]{3})(012|101)','(?P<e>[0-9]{3})(011|010)'), \ |
---|
940 | 'day':('(?P<d>[0-9]{3}[16])0101','(?P<e>[0-9]{3}[50])12%(enddec)s' % mm), \ |
---|
941 | 'subd':('(?P<d>[0-9]{4})0101(?P<h1>[0-9]{2})(?P<mm>[30]0){0,1}$', '(?P<e>[0-9]{4})12%(enddec)s(?P<h2>[0-9]{2})([30]0){0,1}$' % mm ), \ |
---|
942 | 'subd2':('(?P<d>[0-9]{4})0101(?P<h1>[0-9]{2})', '(?P<e>[0-9]{4})010100' ) } |
---|
943 | |
---|
944 | if not norun: |
---|
945 | self.runChecks() |
---|
946 | |
---|
947 | def checkTrange(self): |
---|
948 | keys = self.ee.keys() |
---|
949 | keys.sort() |
---|
950 | for k in keys: |
---|
951 | if k != 'fx': |
---|
952 | keys2 = self.ee[k].keys() |
---|
953 | keys2.sort() |
---|
954 | for k2 in keys2: |
---|
955 | self.checkThisTrange( self.ee[k][k2], k ) |
---|
956 | |
---|
957 | def checkThisTrange( self, tt, group): |
---|
958 | |
---|
959 | if group in ['3hr','6hr']: |
---|
960 | kg = 'subd' |
---|
961 | else: |
---|
962 | kg = group |
---|
963 | ps = self.pats[kg] |
---|
964 | rere = (re.compile( ps[0] ), re.compile( ps[1] ) ) |
---|
965 | |
---|
966 | n = len(tt) |
---|
967 | self.checkId = '001' |
---|
968 | for j in range(n): |
---|
969 | if self.monitor != None: |
---|
970 | nofh0 = self.monitor.get_open_fds() |
---|
971 | t = tt[j] |
---|
972 | fn = t[1] |
---|
973 | isFirst = j == 0 |
---|
974 | isLast = j == n-1 |
---|
975 | lok = True |
---|
976 | for i in [0,1]: |
---|
977 | if not (i==0 and isFirst or i==1 and isLast): |
---|
978 | x = rere[i].match( t[3][i] ) |
---|
979 | lok &= self.test( x != None, 'Cannot match time range %s: %s [%s/%s]' % (i,fn,j,n), part=True, appendLogfile=(self.fLogDict.get(fn,None),fn) ) |
---|
980 | if not lok: |
---|
981 | ### print 'Cannot match time range %s:' % t[1] |
---|
982 | if self.recorder != None: |
---|
983 | self.recorder.modify( t[1], 'ERROR: time range' ) |
---|
984 | if self.monitor != None: |
---|
985 | nofh9 = self.monitor.get_open_fds() |
---|
986 | if nofh9 > nofh0: |
---|
987 | print 'Open file handles: %s --- %s [%s]' % (nofh0, nofh9, j ) |
---|
988 | |
---|
989 | ### http://stackoverflow.com/questions/2023608/check-what-files-are-open-in-python |
---|
990 | class sysMonitor: |
---|
991 | |
---|
992 | def __init__(self): |
---|
993 | self.fhCountMax = 0 |
---|
994 | |
---|
995 | def get_open_fds(self): |
---|
996 | ''' |
---|
997 | return the number of open file descriptors for current process |
---|
998 | .. warning: will only work on UNIX-like os-es. |
---|
999 | ''' |
---|
1000 | import subprocess |
---|
1001 | import os |
---|
1002 | |
---|
1003 | pid = os.getpid() |
---|
1004 | self.procs = subprocess.check_output( |
---|
1005 | [ "lsof", '-w', '-Ff', "-p", str( pid ) ] ) |
---|
1006 | |
---|
1007 | self.ps = filter( |
---|
1008 | lambda s: s and s[ 0 ] == 'f' and s[1: ].isdigit(), |
---|
1009 | self.procs.split( '\n' ) ) |
---|
1010 | self.fhCountMax = max( self.fhCountMax, len(self.ps) ) |
---|
1011 | return len( self.ps ) |
---|