source: CMIP6dreqbuild/trunk/src/framework/audit.py @ 861

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreqbuild/trunk/src/framework/audit.py@861
Revision 861, 15.3 KB checked in by mjuckes, 4 years ago (diff)

candidate

Line 
1
2from dreqPy import dreq
3from dreqPy import misc_utils
4import collections, sys, os
5
6
7python2 = True
8if sys.version_info[0] == 3:
9  python2 = False
10
11if len(sys.argv) > 1:
12  lname = sys.argv[1]
13else:
14  lname = 'audit'
15logFarm = misc_utils.dreqLog(dir='logs')
16log = logFarm.getLog(lname)
17
18class c1(object):
19  def __init__(self):
20    self.a = collections.defaultdict( list )
21
22class checkUnits(object):
23
24  def __init__(self, cc):
25    assert python2, 'The checkUnits module requires cf, which is not available in python 3'
26    import cf
27    for k in sorted( cc.keys() ):
28      try:
29        a = cf.units.Units( k )
30      except:
31        log.warn( 'Bad CF units: %s' % k )
32        a = None
33
34
35      if a != None:
36        for k2 in cc[k].a:
37          try:
38            b = cf.units.Units( k2 )
39          except:
40            b = None
41            log.warn( 'WARN.001.0003: unit invalid : %s .... %s: %s' % (k,k2,str( cc[k].a[k2]) ) )
42          if b != None and not a.equivalent(b):
43            log.warn( 'WARN.001.0002: unit mismatch: %s .... %s: %s' % (k,k2,str( cc[k].a[k2]) ) )
44
45def splitStr( s ):
46  if s == '':
47    return ['']
48  if s.find( ' ') == -1:
49    return [s,]
50  return s.split()
51
52class checkDims(object):
53
54  def __init__(self, dq):
55    """Generate list of used dimensions in ss;
56       Check against defined dimenions [todo]"""
57    ss = set()
58    self.cellm = set()
59    cc = collections.defaultdict(list)
60    duds = ('', '****', '?',)
61    for i in dq.coll['spatialShape'].items:
62      if i.dimensions != '':
63        for x in i.dimensions.split( '|' ):
64          if x in duds:
65            cc[x].append( i.uid )
66          else:
67            ss.add(x)
68    for i in dq.coll['temporalShape'].items:
69      if i.dimensions != '':
70        for x in i.dimensions.split( '|' ):
71          if x in duds:
72            cc[x].append( i.uid )
73          else:
74            ss.add(x)
75    for i in dq.coll['structure'].items:
76      self.cellm.add( i.cell_methods )
77      if i.odims != '':
78        for x in i.odims.split( '|' ):
79          if x in duds:
80            cc[x].append( i.uid )
81          else:
82            ss.add(x)
83      if i.coords != '':
84        for x in i.coords.split( '|' ):
85          if x in duds:
86            cc[x].append( i.uid )
87          else:
88            ss.add(x)
89
90    cellm = sorted( list( self.cellm ) )
91    for c in cellm:
92      log.info( 'cell_methods\t %s' % c )
93    log.info( str( sorted( list(ss) ) ) )
94    ll = set( [i.label for i in dq.coll['grids'].items] )
95    for x in ss:
96      if x not in ll:
97        log.error( 'ERROR: dimension not found: %s' % x )
98
99class auditor(object):
100  def __init__(self,dq):
101    self.dq = dq
102    self.run()
103
104  def run(self):
105    self.audit_var()
106    self.audit_sn()
107    self.audit_cmv()
108    self.audit_rqv()
109    self.audit_rql()
110    self.audit_rqg()
111    self.audit_str()
112    self.audit_cm()
113
114  def audit_cm(self):
115    log.info( 'Running Cell Methods Audit' )
116    l1 = [i for i in self.dq.coll['structure'].items if i.cell_methods == ""]
117    ss = set()
118    if len( l1 ) > 0:
119      log.error( 'ERROR.cm.00010: %s blank cell methods in structure records: ' % len(l1) )
120      for i in l1:
121        log.error( 'ERROR.cm.00011: %s: %s' % ( i.label, i.title )  )
122    for i in l1:
123      if 'CMORvar' in self.dq.inx.iref_by_sect[i.uid].a:
124        for u in dq.inx.iref_by_sect[i.uid].a['CMORvar']:
125           ss.add(u)
126    if len(ss) > 0:
127      log.error( 'ERROR.cm.00020: %s CMOR vars affected ' % len(ss) )
128
129  def audit_var(self):
130    cc = collections.defaultdict( list )
131    ss = collections.defaultdict( c1 )
132    umap = { 'Wm-2':'W m-2', 'string':''}
133    for i in self.dq.coll['var'].items:
134      cc[i.label].append(i)
135      isn = self.dq.inx.uid[i.sn]
136      if isn._h.label != 'remarks':
137        u = umap.get( isn.units, isn.units )
138        ss[u].a[i.units].append( i.label )
139
140    ii = [k for k in cc if len(cc[k]) > 1]
141    log.warn( 'var.0001: %48s [%s]: %s' % ('Duplicate variable names',len( self.dq.coll['var'].items ),len(ii)) )
142    showDupVar=False
143    showDupVar=True
144    showDupVarBrief=False
145    if showDupVar:
146      for i in ii:
147        log.info(  '----- %s -----' % i )
148        for x in cc[i]:
149          log.info( '%s, %s, %s, %s' % ( x.label,x.title,x.sn,x.prov ))
150    elif showDupVarBrief:
151      lg.info( str( ii ))
152    self.cc = cc
153
154    if not python2:
155      log.warn( 'Skipping the units check .. not available in python3' )
156    else:
157      checkUnits( ss )
158   
159  def audit_str(self):
160    nms = 0
161    ss = set()
162    cc = collections.defaultdict( list )
163    for i in self.dq.coll['structure'].items:
164      cc[ (i.spid, i.tmid,i.odims,i.coords,i.cmid) ].append( i )
165      if i.cmid != '__unset__' and self.dq.inx.uid[i.cmid]._h.label == 'remarks' and i.cell_methods != '':
166        ss.add(i.cmid)
167        nms += 1
168    if nms > 0:
169      log.error( 'str.00010: missing cell methods records for %s records (%s)' % (nms, len(ss) ) )
170      log.error( 'str.00011: %s' % str( sorted(list(ss)) ) )
171    else:
172      log.info( 'str.00010: all structure methods have valid cmid' )
173
174    ks = [k for k in cc if len(cc[k]) > 1]
175    log.info( 'str.00020: count of repeated space/time structure: %s' % len(ks) )
176    if len(ks) > 0:
177      oo = open( 'structureRepeats.csv', 'w' )
178      for k in ks:
179        oo.write( '\t'.join( ['NEXT', k[0], k[1], k[2], k[3], self.dq.inx.uid[k[0]].label, self.dq.inx.uid[k[1]].label] ) + '\n' )
180        for i in cc[k]:
181          oo.write( '\t'.join( ['+',i.uid, i.label, i.title, i.odims, i.coords, i.cell_methods, i.cell_measures, i.flag_meanings, i.spid ] ) + '\n' )
182      oo.close()
183
184  def audit_cmv(self):
185    ccmv = collections.defaultdict( list )
186    ccmv2 = collections.defaultdict( list )
187    ccmv4 = collections.defaultdict( list )
188    nms = 0
189    ss = set()
190    badRealm = set()
191    for i in self.dq.coll['CMORvar'].items:
192      ccmv[i.label].append(i)
193      ccmv4[ (i.frequency,i.label,i.stid)].append(i)
194      ccmv2[ (i.mipTable,i.label)].append(i)
195      if self.dq.inx.uid[i.stid]._h.label == 'remarks':
196        ss.add( i.stid )
197        nms += 1
198      if i.modeling_realm in ['','__unset__']:
199        badRealm.add( (i.mipTable, i.label ) )
200    if len( badRealm ) > 0:
201      log.error( 'cmv.00030: bad modeling realms set in %s cmv records' % len( badRealm ) )
202      cc1 = collections.defaultdict( list )
203      for tab, lab in badRealm:
204        cc1[tab].append(lab)
205      for tab in sorted( cc1.keys() ):
206        log.error( 'cmv.00031: table %s: %s' % (tab,str(sorted(cc1[tab]))) )
207    else:
208      log.info( 'cmv.00030: modeling realms set ok cmv records' )
209
210    if nms > 0:
211      log.error( 'cmv.00010: missing variable structures for %s CMORvar records, bad stids: %s' % (nms,len(ss)) )
212      log.error( 'cmv.00011: bad stids: %s' % str( sorted( list( ss ) ) ) )
213    else:
214      log.info( 'cmv.00010: variable structures for all CMORvar records present' )
215
216    ii0 = [i for i in self.dq.coll['CMORvar'].items if i.mipTable == '']
217    if len(ii0) > 0:
218      log.error( 'cmv.00020: %48s [%s]: %s' % ('Empty MIP table in CMORvar',len(self.dq.coll['CMORvar'].items),len(ii0)) )
219    else:
220      log.info( 'cmv.00020: all CMORvar records have mip table defined' )
221
222    ii = [i for i in self.dq.coll['CMORvar'].items if self.dq.inx.uid[i.vid]._h.label == 'remarks']
223    if len(ii) == 0:
224      log.info( 'cmv.00200: all CMORvar records have valid vid' )
225    else:
226      ii1 = [i for i in ii if 'requestVar' in self.dq.inx.iref_by_sect[i.uid].a]
227      log.error( 'cmv.00200: %48s [%s]: %s (requested: %s)' % ('CMORvar records with bad vid',len(self.dq.coll['CMORvar'].items),len(ii),len(ii1)) )
228
229      iix2 = []
230      iix3 = []
231      for i in ii1:
232        ok = False
233        ok3 = False
234        for u in self.dq.inx.iref_by_sect[i.uid].a['requestVar']:
235          vg = self.dq.inx.uid[ self.dq.inx.uid[u].vgid ]
236          if vg._h.label != 'remarks':
237           ok = True
238           iix2.append(i)
239           if 'requestLink' in self.dq.inx.iref_by_sect[vg.uid].a:
240             iix3.append(i)
241      log.error( 'cmv.0021: of these: valid requestVarGroup: %s; valid requestLink(s): %s' %  (len(iix2),len(iix3)) )
242      for i in iix3:
243        log.error( 'cmv.0022: %s, %s, %s' % (i.uid,i.label,i.title) )
244         
245    ii2 = [k for k in ccmv2 if len(ccmv2[k]) > 1]
246    if len(ii2) == 0:
247      log.info( 'cmv.0030: unique CMOR variable names OK' )
248    else:
249      log.error( 'cmv.0030: non-unique CMOR variable naming for %s records' % len(ii2) )
250      log.error( 'cmv.0031: %s' % str(ii2) )
251
252    ii4 = [k for k in ccmv4 if len(ccmv4[k]) > 1]
253    ii4b = list()
254    for k in ii4:
255      if len(ccmv4[k]) == 2 and set( [i.mipTable[-3:] for i in ccmv4[k]] ) == set(['ant','gre']):
256        pass
257      else:
258        ii4b.append(k)
259
260    if len(ii4b) == 0:
261      log.info( 'cmv.0040: unique CMOR variable usage OK' )
262    else:
263      log.error( 'cmv.0040: non-unique CMOR variable usage possible for %s records' % len(ii4b) )
264      log.error( 'cmv.0041: %s' % str(ii4b) )
265      kk=0
266      oo = open( 'cmv_probDuplication.csv', 'w' )
267      for k in ii4b:
268        kk += 1
269        rec = ('NEXT\t%s\t' % kk ) + str( '\t'.join( k ) )
270        print 'INFO.cmvdup.00001: ',rec
271        oo.write( rec + '\n' )
272        vid = None
273        for i in ccmv4[k]:
274          assert vid == None or i.vid == vid, 'Unexpected vid variation ...: %s%s %s' % (i.label, i.vid, vid)
275          vid = i.vid
276          rec = '+\t%s\t%s\t%s\t%s\t%s' % (i.uid,i.mipTable,i.title,i.description,i.prov)
277          oo.write( rec + '\n' )
278          print 'INFO.cmvdup.00001: ',rec
279      oo.close()
280
281    ccmv3 = collections.defaultdict( list )
282    for t in ccmv2:
283      s = set()
284      for i in ccmv2[t]:
285        id = i.uid
286        if 'varRelLnk' in self.dq.inx.iref_by_sect[id].a:
287          for x in self.dq.inx.iref_by_sect[id].a['varRelLnk']:
288            s.add( x )
289      s = list(s)
290      if len(s) > 0:
291        ccmv3[t] = s
292
293    ii5 = [k for k in ii2 if k not in ccmv3 or len(ccmv2[k]) > len(ccmv3[k])]
294    cc4 = collections.defaultdict( list )
295    for t,v in ii2:
296      cc4[t].append( v )
297
298    log.warn( '%48s [%s]: %s [%s tables]' % ('Duplicate variables in CMORvar',len(self.dq.coll['CMORvar'].items),len(ii2), len( cc4.keys() ) ) )
299    showAllCmvErrors=False
300    showAllCmvErrors=True
301    if showAllCmvErrors:
302       rats = ['uid','label','title','description','frequency','mipTable','stid','prov','provNote']
303       oocm = open( 'cmvDup.csv', 'w' )
304       c1 = collections.defaultdict( list )
305       ##for i in ii2:
306         ##c1[i.prov].append( i.label )
307       ##log.warn( 'No MIP var: %s ... %s' % (str( sorted( [i.label for i in ii if i.label in self.cc] ) ), str(c1)))
308       for k in sorted( cc4.keys() ):
309         log.warn(  '%16s::  %s' % (k,str(sorted(cc4[k]))) )
310         for v in sorted(cc4[k]):
311             sv = set()
312             for i in ccmv2[ (k,v) ]:
313                sv.add(i.vid)
314
315             if len(list(sv)) == 1:
316               vun = True
317               vv = self.dq.inx.uid[ list(sv)[0] ]
318               oocm.write( '@VAR:\t\t\t%s\t%s\t%s\n' % (vv.label,vv.title,vv.description) )
319             else:
320               vun = False
321
322             for i in ccmv2[ (k,v) ]:
323                stt = self.dq.inx.uid[i.stid].title
324                if 'Temporal mean' in stt:
325                   if i.frequency == '3hr':
326                     i.title += ' [3 hour mean]'
327                xtra = ''
328                oocm.write( '\t\t' + '\t'.join( [str(i.__dict__[x]) for x in rats] ) + '\t%s%s\n' % (stt,xtra) )
329                if not vun:
330                  vv = self.dq.inx.uid[i.vid]
331                  xtra = '\t\t\t\t%s\t%s\t%s\n' % (vv.label,vv.title,vv.description)
332                  oocm.write(xtra)
333             oocm.write( '####\n' )
334       oocm.close()
335    t2_ii = ii[:]
336    self.ccmv = ccmv
337
338  def audit_sn(self):
339    cc = collections.defaultdict( list )
340    nrem = 0
341    snm = set()
342    for i in self.dq.coll['var'].items:
343      kk = i.sn
344      if self.dq.inx.uid[kk]._h.label == 'remarks':
345        nrem += 1
346        snm.add(kk)
347      if i.procnote != []:
348        ll = sorted( i.procnote )
349        kk += ':' + '|'.join(ll )
350      cc[kk].append( i.uid )
351
352    log.warn( 'sn.001: Number of missing standard names: %s [for %s variables]' % (len(snm),nrem) )
353    ll = []
354    for k in cc:
355      if len( cc[k] ) > 1:
356        ll.append( k )
357
358    log.warn( 'sn.002: Duplicate use of standard names: %s' % len(ll) )
359    oo = open( 'audit_sn_repeats.csv', 'w' )
360    for k in sorted( ll ):
361      orc = [k,]
362      l1 = cc[k][:]
363      l2 = []
364      l1b = []
365      for u in cc[k]:
366        i = self.dq.inx.uid[u]
367        if i.prov == "CMIP6 endorsement [SIMIP]":
368          l2.append(u)
369        else:
370          l1b.append(u)
371      if len( l2) > 1:
372        log.error( 'ERROR: identified multiple priority choices: %s:: %s' % (k,str(l2)) )
373      if len(l2) == 1:
374        orc.append( '*' )
375      else:
376        orc.append( '' )
377
378      for u in l2 +l1b:
379        i = self.dq.inx.uid[u]
380        orc += [i.label, i.title]
381         
382      oo.write( '\t'.join( orc ) + '\n' )
383    oo.close()
384     
385  def audit_rqv(self):
386    ii = [i for i in self.dq.coll['requestVar'].items if self.dq.inx.uid[i.vid]._h.label == 'remarks']
387    ii2 = [i for i in ii if i.label not in self.cc]
388    ii3 = [i.vgid for i in ii if self.dq.inx.uid[i.vgid]._h.label != 'remarks']
389##ii4: set of invalid requestVarGroup records
390    ii4 = [i for i in ii3 if 'requestLink' not in self.dq.inx.iref_by_sect[i].]
391######
392    ii5Info = 'Set of bad requestVar records linking to a valid requestVarGroup records.'
393    ii5 = [i for i in ii if i.vgid not in ii4]
394##ii6: set of bad requestVar records linking to a valid requestVarGroup records and no variable name match.
395    ii6 = [i for i in ii5 if i.label not in self.cc]
396    log.error( '%48s [%s]: %s [%s, %s, %s; %s]' % ('Bad variable links in requestVar',len(self.dq.coll['requestVar'].items),len(ii),len(ii2), len(ii5), len(ii6), len(ii4)) )
397    log.info ( str( ii5Info ))
398    svg = set()
399    for i in ii5:
400      log.info( 'ii5: %s' % str([i.label, i.title, i.uid, i.mip, self.dq.inx.uid[i.vgid].title] ) )
401      svg.add( i.vgid )
402    for i in ii2:
403      log.info( 'ii2: %s' % str([i.label, i.title, i.uid] ) )
404
405    log.warn( 'rvg.0010: requestVarGroups with bad request vars:' )
406    for u in sorted( list(svg) ):
407      log.warn( 'rvg.0011: %s: %s' % (u, self.dq.inx.uid[u].title ))
408
409    log.info( '    [# records]: # broken [var name not known, link to valid group, valid group and no var name; valid request groups' )
410    showAllRqvErrors=False
411    if showAllRqvErrors:
412      log.info( '%s\n%s' % (str( [i.label for i in ii if i.label in self.cc]), str( [i.label for i in ii2] ) ) )
413
414    ii1 = [i for i in ii if i.label in self.ccmv]
415    listBadRequestVar=False
416    if listBadRequestVar:
417      for i in ii1:
418        log.info( str( i.label,i.mip,i.table,[x.frequency for x in self.ccmv[i.label]] ) )
419
420  def audit_rql(self):
421    ii = [i for i in self.dq.coll['requestLink'].items if self.dq.inx.uid[i.refid]._h.label == 'remarks']
422    log.info( '%48s: %s (from %s)' % ('Bad request group links in requestLink',len(ii), len(self.dq.coll['requestLink'].items)) )
423
424  def audit_rqg(self):
425    ii = [i for i in self.dq.coll['requestVarGroup'].items if len( dq.inx.iref_by_sect[i.uid].a['requestVar'])  == 0]
426    ii1 = [i for i in ii if len( dq.inx.iref_by_sect[i.uid].a['requestLink'])  != 0]
427    log.info( '%48s: %s/%s (from %s)' % ('Empty request groups',len(ii) - len(ii1),len(ii1), len(self.dq.coll['requestVarGroup'].items)) )
428    listEmptyRequestVarGroups=False
429    if listEmptyRequestVarGroups:
430      for i in ii1:
431        log.info ( '%s: %s, %s' % (i.label, i.title, i.mip) )
432
433dq = dreq.loadDreq()
434a = auditor( dq )
435cd = checkDims( dq )
Note: See TracBrowser for help on using the repository browser.