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

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

sync

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