source: CMIP6dreqbuild/trunk/src/framework/dreqPy/table_utils.py @ 1037

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreqbuild/trunk/src/framework/dreqPy/table_utils.py@1037
Revision 1037, 20.2 KB checked in by mjuckes, 3 years ago (diff)

upadte

Line 
1import collections, os, sys
2
3try:
4    import xlsxwriter
5except:
6    print ('No xlsxwrite: will not make tables ...')
7
8try:
9  import vrev
10  import misc_utils
11except:
12  import dreqPy.vrev as vrev
13  import dreqPy.misc_utils as misc_utils
14
15def realmFlt( ss ):
16  if ss == '':
17    return ss
18  if ss.find( ' ' ) == -1:
19    return ss
20  return ss.split( ' ' )[0]
21
22python2 = True
23if sys.version_info[0] == 3:
24  python2 = False
25  def cmp(x,y):
26    if x == y:
27      return 0
28    elif x > y:
29      return 1
30    else:
31      return -1
32
33if sys.version_info >= (2,7):
34  from functools import cmp_to_key
35  oldpython = False
36else:
37  oldpython = True
38
39class cmpdn(object):
40  def __init__(self,kl):
41    self.kl = kl
42  def cmp(self,x,y):
43    for k in self.kl:
44      if x.__dict__[k] != y.__dict__[k]:
45        return cmp( x.__dict__[k], y.__dict__[k] )
46
47    return cmp( 0,0 )
48
49def cmpAnnex( x, y ):
50  ax = len(x) > 2 and x[:2] == 'em'
51  ay = len(y) > 2 and y[:2] == 'em'
52  bx = len(x) > 5 and x[:5] in ['CMIP5','CORDE','SPECS']
53  by = len(y) > 5 and y[:5] in ['CMIP5','CORDE','SPECS']
54  if ax  == ay and bx == by:
55    return cmp(x,y)
56  elif ax:
57    if by:
58      return cmp(0,1)
59    else:
60      return cmp(1,0)
61  elif ay:
62    if bx:
63      return cmp(1,0)
64    else:
65      return cmp(0,1)
66  elif bx:
67      return cmp(1,0)
68  else:
69    return cmp(0,1)
70
71if not oldpython:
72  kAnnex = cmp_to_key( cmpAnnex )
73  kCmpdn = cmp_to_key( cmpdn(['sn','label']).cmp )
74  kCmpdnPrl = cmp_to_key( cmpdn(['prov','rowIndex','label']).cmp )
75
76
77class xlsx(object):
78  def __init__(self,fn,xls=True,txt=False,txtOpts=None):
79    self.xls=xls
80    self.txt=txt
81    self.txtOpts = txtOpts
82    self.mcfgNote = 'Reference Volume (1 deg. atmosphere, 0.5 deg. ocean)'
83    if xls:
84      self.wb = xlsxwriter.Workbook('%s.xlsx' % fn)
85      self.hdr_cell_format = self.wb.add_format({'text_wrap': True, 'font_size': 14, 'font_color':'#0000ff', 'bold':1, 'fg_color':'#aaaacc'})
86      self.hdr_cell_format.set_text_wrap()
87      self.sect_cell_format = self.wb.add_format({'text_wrap': True, 'font_size': 14, 'font_color':'#0000ff', 'bold':1, 'fg_color':'#ccccbb'})
88      self.sect_cell_format.set_text_wrap()
89      self.cell_format = self.wb.add_format({'text_wrap': True, 'font_size': 11})
90      self.cell_format.set_text_wrap()
91
92    if txt:
93      self.oo = open( '%s.csv' % fn, 'w' )
94
95  def header(self,tableNotes,collected):
96    if self.xls:
97      sht = self.newSheet( 'Notes' )
98      sht.write( 0,0, '', self.hdr_cell_format )
99      sht.write( 0,1, 'Notes on tables', self.hdr_cell_format )
100      ri = 0
101      sht.set_column(0,0,30)
102      sht.set_column(1,1,60)
103      self.sht = sht
104      for t in tableNotes:
105        ri += 1
106        for i in range(2):
107          sht.write( ri,i, t[i], self.cell_format )
108
109      if collected != None:
110        ri += 2
111        sht.write( ri, 0, 'Table', self.sect_cell_format )
112        sht.write( ri, 1, self.mcfgNote, self.sect_cell_format )
113        ttl = 0.
114        for k in sorted( collected.keys() ):
115          ri += 1
116          sht.write( ri, 0, k )
117          sht.write( ri, 1, misc_utils.vfmt( collected[k]*2. ) )
118          ttl += collected[k]
119
120        ri += 1
121        sht.write( ri, 0, 'TOTAL' )
122        sht.write( ri, 1, misc_utils.vfmt( ttl*2. ) )
123
124    if self.txt:
125      self.oo.write( '\t'.join( ['Notes','','Notes on tables']) + '\n' )
126      for t in tableNotes:
127        self.oo.write( '\t'.join( ['Notes',] + list(t)) + '\n' )
128
129      if collected != None:
130        self.oo.write( '\t'.join( ['Notes','Table','Reference Volume (1 deg. atmosphere, 0.5 deg. ocean)']) + '\n')
131        for k in sorted( collected.keys() ):
132          self.oo.write( '\t'.join( ['Notes',k,misc_utils.vfmt( collected[k]*2. )]) + '\n' )
133
134  def cmvtabrec(self,j,t,orec):
135     if self.xls:
136        for i in range(len(orec)):
137           if str( type(orec[i]) ) == "<class 'dreq.dreqItem_CoreAttributes'>":
138             self.sht.write( j,i, '', self.cell_format )
139           else:
140             ##print i, orec[i], type( orec[i] )
141             try:
142                self.sht.write( j,i, orec[i], self.cell_format )
143             except:
144               print ('FAILED TO WRITE RECORD: %s' % str(orec))
145               print ('FAILED TO WRITE RECORD: %s' % str(orec[i]))
146               raise
147
148     if self.txt:
149        self.oo.write( '\t'.join( [t,] + orec) + '\n' )
150
151  def varrec(self,j,orec):
152     if self.xls:
153        for i in range(len(orec)):
154           self.sht.write( j,i, orec[i], self.cell_format )
155
156     if self.txt:
157        self.oo.write( '\t'.join( orec, '\t') + '\n' )
158
159  def var(self):
160      if self.xls:
161        self.sht = self.newSheet( 'var' )
162      j = 0
163      hrec = ['Long name', 'units', 'description', 'Variable Name', 'CF Standard Name' ]
164      if self.xls:
165          self.sht.set_column(1,1,40)
166          self.sht.set_column(1,2,30)
167          self.sht.set_column(1,3,60)
168          self.sht.set_column(1,4,40)
169          self.sht.set_column(1,5,40)
170          self.sht.set_column(1,20,40)
171          self.sht.set_column(1,21,40)
172
173      if self.xls:
174        for i in range(len(hrec)):
175          self.sht.write( j,i, hrec[i], self.hdr_cell_format )
176
177      if self.txt:
178        for i in range(len(hrec)):
179          self.oo.write( hrec[i] + '\t' )
180        self.oo.write( '\n' )
181
182  def cmvtab(self,t,addMips,mode='c',tslice=False,byFreqRealm=False):
183      if self.xls:
184        self.sht = self.newSheet( t )
185      j = 0
186      ncga = 'NetCDF Global Attribute'
187      if mode == 'c':
188        hrec = ['Priority','Long name', 'units', 'description', 'comment', 'Variable Name', 'CF Standard Name', 'cell_methods', 'positive', 'type', 'dimensions', 'CMOR Name', 'modeling_realm', 'frequency', 'cell_measures', 'prov', 'provNote','rowIndex','UID','vid','stid','Structure Title','valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs']
189        hcmt = ['Default priority (generally overridden by settings in "requestVar" record)',ncga,'','','Name of variable in file','','','CMOR directive','','','CMOR name, unique within table','','','','','','','','','','CMOR variable identifier','MIP variable identifier','Structure identifier','','','','','']
190        if self.xls:
191          self.sht.set_column(1,1,40)
192          self.sht.set_column(1,3,50)
193          self.sht.set_column(1,4,30)
194          self.sht.set_column(1,5,50)
195          self.sht.set_column(1,6,30)
196          self.sht.set_column(1,9,40)
197          self.sht.set_column(1,18,40)
198          self.sht.set_column(1,19,40)
199          self.sht.set_column(1,20,40)
200          self.sht.set_column(1,21,40)
201          self.sht.set_column(1,26,40)
202          self.sht.set_column(1,27,40)
203          self.sht.set_column(1,30,40)
204      else:
205        hrec = ['','Long name', 'units', 'description', '', 'Variable Name', 'CF Standard Name', '','', 'cell_methods', 'valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs', 'positive', 'type', 'dimensions', 'CMOR name', 'modeling_realm', 'frequency', 'cell_measures', 'flag_values', 'flag_meanings', 'prov', 'provNote','rowIndex','UID']
206      if addMips:
207        hrec.append( 'MIPs (requesting)' )
208        hrec.append( 'MIPs (by experiment)' )
209
210      if byFreqRealm:
211        hrec = ['Table',] + hrec
212        hcmt = ['CMOR table',] + hcmt
213      if tslice:
214          hrec += ['Number of Years','Slice Type','Years','Grid']
215          hcmt += ['','','','']
216
217      if self.xls:
218        for i in range(len(hrec)):
219          self.sht.write( j,i, hrec[i], self.hdr_cell_format )
220          if hcmt[i] != '':
221            self.sht.write_comment( j,i,hcmt[i])
222
223      if self.txt:
224        self.oo.write( 'MIP table\t' )
225        for i in range(len(hrec)):
226          self.oo.write( hrec[i] + '\t' )
227        self.oo.write( '\n' )
228        self.oo.write( t + '\t' )
229        for i in range(len(hrec)):
230          if hcmt[i] != '':
231            self.oo.write( hcmt[i] + '\t')
232          else:
233            self.oo.write( '\t')
234        self.oo.write( '\n' )
235
236  def newSheet(self,name):
237    self.worksheet = self.wb.add_worksheet(name=name)
238    return self.worksheet
239
240  def close(self):
241    if self.xls:
242      self.wb.close()
243    if self.txt:
244      self.oo.close()
245
246class makeTab(object):
247  def __init__(self, dq, subset=None, mcfgNote=None, dest='tables/test', skipped=set(), collected=None,xls=True,txt=False,txtOpts=None,byFreqRealm=False, tslice=None):
248    """txtOpts: gives option to list MIP variables instead of CMOR variables"""
249    if subset != None:
250      cmv = [x for x in dq.coll['CMORvar'].items if x.uid in subset]
251    else:
252      cmv = dq.coll['CMORvar'].items
253    self.byFreqRealm=byFreqRealm
254
255    ixt = collections.defaultdict(list)
256    if not byFreqRealm:
257      for i in cmv:
258        ixt[i.mipTable].append( i.uid )
259    else:
260      for i in cmv:
261        ixt['%s.%s' % (i.frequency,realmFlt( i.modeling_realm) )].append( i.uid )
262
263    if oldpython:
264        tables = sorted( ixt.keys(), cmp=cmpAnnex )
265    else:
266        tables = sorted( ixt.keys(), key=kAnnex )
267
268    addMips = True
269    if addMips:
270      c = vrev.checkVar(dq)
271    mode = 'c'
272    tableNotes = [
273       ('Request Version',str(dq.version)),
274       ('MIPs (...)','The last two columns in each row list MIPs associated with each variable. The first column in this pair lists the MIPs which are requesting the variable in one or more experiments. The second column lists the MIPs proposing experiments in which this variable is requested. E.g. If a variable is requested in a DECK experiment by HighResMIP, then HighResMIP appears in the first column and DECK in the second')]
275
276    wb = xlsx( dest, xls=xls, txt=txt )
277    if mcfgNote != None:
278      wb.mcfgNote = mcfgNote
279    wb.header( tableNotes, collected)
280
281    if txtOpts != None and txtOpts.mode == 'var':
282      vl =  list( set( [v.vid for v in cmv] )  )
283      vli = [dq.inx.uid[i] for i in vl]
284      if oldpython:
285        thisvli =  sorted( vli, cmp=cmpdn(['sn','label']).cmp )
286      else:
287        thisvli = sorted( vli, key=kCmpdn )
288      wb.var()
289     
290      j = 0
291      for v in thisvli:
292      ###hrec = ['Long name', 'units', 'description', 'Variable Name', 'CF Standard Name' ]
293         orec = [v.title, v.units, v.description, v.label, v.sn]
294         j += 1
295         wb.varrec( j,orec )
296    else:
297      withoo = False
298      for t in tables:
299        if withoo:
300          oo = open( 'tables/test_%s.csv' % t, 'w' )
301        wb.cmvtab(t,addMips,mode='c',tslice=tslice != None,byFreqRealm=byFreqRealm)
302
303        j = 0
304        if oldpython:
305          thiscmv =  sorted( [dq.inx.uid[u] for u in ixt[t]], cmp=cmpdn(['prov','rowIndex','label']).cmp )
306        else:
307          thiscmv = sorted( [dq.inx.uid[u] for u in ixt[t]], key=kCmpdnPrl )
308
309        for v in thiscmv:
310          cv = dq.inx.uid[ v.vid ]
311          strc = dq.inx.uid[ v.stid ]
312          if strc._h.label == 'remarks':
313            print ( 'ERROR: structure not found for %s: %s .. %s (%s)' % (v.uid,v.label,v.title,v.mipTable) )
314            ok = False
315          else:
316            sshp = dq.inx.uid[ strc.spid ]
317            tshp = dq.inx.uid[ strc.tmid ]
318            ok = all( [i._h.label != 'remarks' for i in [cv,strc,sshp,tshp]] )
319          #[u'shuffle', u'ok_max_mean_abs', u'vid', '_contentInitialised', u'valid_min', u'frequency', u'uid', u'title', u'rowIndex', u'positive', u'stid', u'mipTable', u'label', u'type', u'description', u'deflate_level', u'deflate', u'provNote', u'ok_min_mean_abs', u'modeling_realm', u'prov', u'valid_max']
320
321          if not ok:
322            if (t,v.label) not in skipped:
323              ml = []
324              for i in range(4):
325                 ii = [cv,strc,sshp,tshp][i]
326                 if ii._h.label == 'remarks':
327                   ml.append( ['var','struct','time','spatial'][i] )
328              print ( 'makeTables: skipping %s %s: %s' % (t,v.label,','.join( ml)) )
329              skipped.add( (t,v.label) )
330          else:
331            dims = []
332            dims +=  sshp.dimensions.split( '|' )
333            if 'odims' in strc.__dict__:
334              dims +=  strc.odims.split( '|' )
335            dims +=  tshp.dimensions.split( '|' )
336            if 'coords' in strc.__dict__:
337              dims +=  strc.coords.split( '|' )
338            dims = ' '.join( dims )
339            if "qcranges" in dq.inx.iref_by_sect[v.uid].a:
340              u = dq.inx.iref_by_sect[v.uid].a['qcranges'][0]
341              qc = dq.inx.uid[u]
342              ll = []
343              for k in ['valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs']:
344                if qc.hasattr(k):
345                  ll.append( '%s %s' % (qc.__dict__[k],qc.__dict__['%s_status' % k][0]) )
346                else:
347                  ll.append( '' )
348              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = tuple( ll )
349            else:
350              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = ('','','','')
351               
352            if mode == 'c':
353              try:
354                orec = [str(v.defaultPriority),cv.title, cv.units, cv.description, v.description, cv.label, cv.sn, strc.cell_methods, v.positive, v.type, dims, v.label, v.modeling_realm, v.frequency, strc.cell_measures, v.prov,v.provNote,str(v.rowIndex),v.uid,v.vid,v.stid,strc.title, valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs]
355              except:
356                print ('FAILED TO CONSTRUCT RECORD: %s [%s], %s [%s]' % (v.uid,v.label,cv.uid,cv.label) )
357                raise
358            else:
359              orec = ['',cv.title, cv.units, v.description, '', cv.label, cv.sn, '','', strc.cell_methods, valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs, v.positive, v.type, dims, v.label, v.modeling_realm, v.frequency, strc.cell_measures, strc.flag_values, strc.flag_meanings,v.prov,v.provNote,str(v.rowIndex),cv.uid]
360
361            if byFreqRealm:
362              orec = [v.mipTable,] + orec
363
364            if addMips:
365              thismips = c.chkCmv( v.uid )
366              thismips2 = c.chkCmv( v.uid, byExpt=True )
367              orec.append( ','.join( sorted( list( thismips) ) ) )
368              orec.append( ','.join( sorted( list( thismips2) ) ) )
369
370            if tslice != None:
371              msgLevel = 0
372              if v.uid in tslice and msgLevel > 1:
373                print ( 'INFO.table_utils.01001: slice 3: %s : %s' % ( str( tslice[v.uid] ), v.label ) )
374              if v.uid not in tslice:
375                orec += ['All', '','','']
376              elif type( tslice[v.uid] ) == type( 0 ):
377                print ( 'ERROR: unexpected tslice type: %s, %s' % (v.uid, tslice[v.uid] ) )
378              elif len(  tslice[v.uid] ) == 3:
379                x,priority,grid = tslice[v.uid]
380                orec[0] = priority
381                if x != None:
382                   tslab,tsmode,a,b = x
383                   orec += [tslab,tsmode,'',grid]
384                else:   
385                   print ( 'WARN.table_utils.01001: slice 3: %s : %s' % ( str( tslice[v.uid] ), v.label ) )
386                   orec += ['*unknown*','','',grid]
387              else:
388                tslab,tsmode,a,b,priority,grid = tslice[v.uid]
389                if type( priority ) != type(1):
390                  thisp = priority
391                  priority = thisp[1]
392                  ##print 'ERROR in priority type[2]: ',priority, tslice[v.uid]
393                orec[0] = priority
394                     
395                if tsmode[:4] in ['simp','bran']:
396                   nys = b + 1 - a
397                   ys = range(a,b+1)
398                   orec += [str(nys), '',str(ys)]
399                elif tsmode in ['rangeplus']:
400                   nys = b + 1 - a + 0.01
401                   ys = [1850,] + range(a,b+1)
402                   orec += [str(nys), 'Partial 1850 + %s to %s' % (a,b),'%6.2f' % ys]
403                elif tsmode[:4] in ['YEAR']:
404                   nys = a
405                   ys = b
406                   orec += [str(nys), '',str(ys)]
407                else:
408                   orec += ['slice', tslab,'']
409                orec.append( grid )
410               
411            if withoo:
412              oo.write( '\t'.join(orec ) + '\n' )
413            j+=1
414            wb.cmvtabrec( j,t,orec )
415
416        if withoo:
417          oo.close()
418    wb.close()
419
420class tables(object):
421  def __init__(self,sc, odir='xls',xls=True,txt=False,txtOpts=None):
422      self.sc = sc
423      self.dq = sc.dq
424      ##self.mips = mips
425      self.odir = odir
426      self.accReset()
427      self.doXls = xls
428      self.doTxt = txt
429      self.txtOpts = txtOpts
430
431  def accReset(self):
432    self.acc = [0.,collections.defaultdict(int),collections.defaultdict( float ) ]
433
434  def accAdd(self,x):
435    self.acc[0] += x[0]
436    for k in x[2]:
437       self.acc[2][k] += x[2][k]
438
439
440  def doTable(self,m,l1,m2,pmax,collector,acc=True, mlab=None,exptids=None,cc=None):
441      """*acc* allows accumulation of values to be switched off when called in single expt mode"""
442     
443      self.verbose = False
444      if mlab == None:
445        mlab = misc_utils.setMlab( m )
446
447      cc0 = misc_utils.getExptSum( self.dq, mlab, l1 )
448      ks = sorted( list( cc0.keys() ) )
449      if self.verbose:
450        print ('Experiment summary: %s %s' % (mlab,', '.join( ['%s: %s' % (k,len(cc0[k])) for k in ks] ) ) )
451
452      if m2 in [None, 'TOTAL']:
453        x = self.acc
454      else:
455        x = self.sc.volByExpt( l1, m2, pmax=pmax )
456
457##self.volByExpt( l1, e, pmax=pmax, cc=cc, retainRedundantRank=retainRedundantRank, intersection=intersection, adsCount=adsCount )
458        v0 = self.sc.volByMip( m, pmax=pmax,  exptid=m2 )
459####
460        if cc==None:
461          cc = collections.defaultdict( int )
462        for e in self.sc.volByE:
463          if self.verbose:
464             print ('INFO.mlab.... %s: %s: %s' % ( mlab, e, len( self.sc.volByE[e][2] ) ) )
465          for v in self.sc.volByE[e][2]:
466             cc[v] += self.sc.volByE[e][2][v]
467        xxx = 0
468        for v in cc:
469          xxx += cc[v]
470####
471        if acc:
472          for e in self.sc.volByE:
473            self.accAdd(self.sc.volByE[e])
474
475      if m2 not in [ None, 'TOTAL']:
476          im2 = self.dq.inx.uid[m2]
477          ismip = im2._h.label == 'mip'
478          mlab2 = im2.label
479
480          x0 = 0
481          for e in self.sc.volByE:
482            if exptids == None or e in exptids:
483              x = self.sc.volByE[e]
484              if x[0] > 0:
485                collector[mlab].a[mlab2] += x[0]
486                x0 += x[0]
487      else:
488          ismip = False
489          mlab2 = 'TOTAL'
490          x0 = x[0]
491
492      if mlab2 == 'TOTAL' and x0 == 0:
493        print ( 'no data detected for %s' % mlab )
494
495      if x0 > 0:
496#
497# create sum for each table
498#
499        xs = 0
500        kkc = '_%s_%s' % (mlab,mlab2)
501        kkct = '_%s_%s' % (mlab,'TOTAL')
502        if m2 in [None, 'TOTAL']:
503          x = self.acc
504          x2 = set(x[2].keys() )
505          for k in x[2].keys():
506           i = self.dq.inx.uid[k]
507           xxx =  x[2][k]
508           xs += xxx
509        else:
510          x2 = set()
511          for e in self.sc.volByE:
512            if exptids == None or e in exptids:
513              x = self.sc.volByE[e]
514              x2 = x2.union( set( x[2].keys() ) )
515              for k in x[2].keys():
516               i = self.dq.inx.uid[k]
517               xxx =  x[2][k]
518               xs += xxx
519               if xxx > 0:
520                collector[kkc].a[i.mipTable] += xxx
521                if ismip:
522                  collector[kkct].a[i.mipTable] += xxx
523
524##
525## One user was getting false error message here, with ('%s' % x0) == ('%s' % xs)
526##
527        if abs(x0 -xs) > 1.e-8*( abs(x0) + abs(xs) ):
528          print ( 'ERROR.0088: consistency problem %s  %s %s %s' % (m,m2,x0,xs) )
529        if x0 == 0:
530          print ( 'Zero size: %s, %s' % (m,m2) )
531          if len( x[2].keys() ) > 0:
532             print ( 'ERROR:zero: %s, %s: %s' % (m,m2,str(x[2].keys()) ) )
533
534        if acc and m2 not in [ None, 'TOTAL']:
535          collector[mlab].a['TOTAL'] += x0
536
537        dd = collections.defaultdict( list )
538        lll = set()
539        for v in x2:
540          vi = self.sc.dq.inx.uid[v]
541          if vi._h.label != 'remarks':
542            f,t,l,tt,d,u = (vi.frequency,vi.mipTable,vi.label,vi.title,vi.description,vi.uid)
543            lll.add(u)
544            dd[t].append( (f,t,l,tt,d,u) )
545
546        if len( dd.keys() ) > 0:
547          collector[mlab].dd[mlab2] = dd
548          if m2 not in [ None, 'TOTAL']:
549            if im2._h.label == 'experiment':
550              dothis = self.sc.tierMax >= min( im2.tier )
551###
552### BUT ... there is a treset in the request item .... it may be that some variables are excluded ...
553###         need the variable list itself .....
554###
555          makeTab( self.sc.dq, subset=lll, dest='%s/%s-%s_%s_%s' % (self.odir,mlab,mlab2,self.sc.tierMax,pmax), collected=collector[kkc].a,
556              mcfgNote=self.sc.mcfgNote,
557              txt=self.doTxt, xls=self.doXls, txtOpts=self.txtOpts )
Note: See TracBrowser for help on using the repository browser.