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

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

cleaned examples and import structure

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