source: CMIP6dreq/trunk/dreqPy/table_utils.py @ 1005

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreq/trunk/dreqPy/table_utils.py@1005
Revision 1005, 19.4 KB checked in by mjuckes, 3 years ago (diff)

01.00.10

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            if 'odims' in strc.__dict__:
327              dims +=  strc.odims.split( '|' )
328            dims +=  tshp.dimensions.split( '|' )
329            if 'coords' in strc.__dict__:
330              dims +=  strc.coords.split( '|' )
331            dims = ' '.join( dims )
332            if "qcranges" in dq.inx.iref_by_sect[v.uid].a:
333              u = dq.inx.iref_by_sect[v.uid].a['qcranges'][0]
334              qc = dq.inx.uid[u]
335              ll = []
336              for k in ['valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs']:
337                if qc.hasattr(k):
338                  ll.append( '%s %s' % (qc.__dict__[k],qc.__dict__['%s_status' % k][0]) )
339                else:
340                  ll.append( '' )
341              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = tuple( ll )
342            else:
343              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = ('','','','')
344               
345            if mode == 'c':
346              try:
347                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]
348              except:
349                print ('FAILED TO CONSTRUCT RECORD: %s [%s], %s [%s]' % (v.uid,v.label,cv.uid,cv.label) )
350                raise
351            else:
352              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]
353
354            if byFreqRealm:
355              orec = [v.mipTable,] + orec
356
357            if addMips:
358              thismips = c.chkCmv( v.uid )
359              thismips2 = c.chkCmv( v.uid, byExpt=True )
360              orec.append( ','.join( sorted( list( thismips) ) ) )
361              orec.append( ','.join( sorted( list( thismips2) ) ) )
362
363            if tslice != None:
364              if v.uid not in tslice:
365                orec += ['All', '','','']
366              elif type( tslice[v.uid] ) == type( 0 ):
367                print ( 'ERROR: unexpected tslice type: %s, %s' % (v.uid, tslice[v.uid] ) )
368              elif len(  tslice[v.uid] ) == 3:
369                x,priority,grid = tslice[v.uid]
370                orec[0] = priority
371                orec += ['','','',grid]
372              else:
373                tslab,tsmode,a,b,priority,grid = tslice[v.uid]
374                if type( priority ) != type(1):
375                  thisp = priority
376                  priority = thisp[1]
377                  ##print 'ERROR in priority type[2]: ',priority, tslice[v.uid]
378                orec[0] = priority
379                     
380                if tsmode[:4] in ['simp','bran']:
381                   nys = b + 1 - a
382                   ys = range(a,b+1)
383                   orec += [str(nys), '',str(ys)]
384                elif tsmode[:4] in ['YEAR']:
385                   nys = a
386                   ys = b
387                   orec += [str(nys), '',str(ys)]
388                else:
389                   orec += ['slice', tslab,'']
390                orec.append( grid )
391               
392            if withoo:
393              oo.write( '\t'.join(orec ) + '\n' )
394            j+=1
395            wb.cmvtabrec( j,t,orec )
396
397        if withoo:
398          oo.close()
399    wb.close()
400
401class tables(object):
402  def __init__(self,sc, odir='xls',xls=True,txt=False,txtOpts=None):
403      self.sc = sc
404      self.dq = sc.dq
405      ##self.mips = mips
406      self.odir = odir
407      self.accReset()
408      self.doXls = xls
409      self.doTxt = txt
410      self.txtOpts = txtOpts
411
412  def accReset(self):
413    self.acc = [0.,collections.defaultdict(int),collections.defaultdict( float ) ]
414
415  def accAdd(self,x):
416    self.acc[0] += x[0]
417    for k in x[2]:
418       self.acc[2][k] += x[2][k]
419
420
421  def doTable(self,m,l1,m2,pmax,collector,acc=True, mlab=None,exptids=None,cc=None):
422      """*acc* allows accumulation of values to be switched off when called in single expt mode"""
423     
424      self.verbose = False
425      if mlab == None:
426        mlab = misc_utils.setMlab( m )
427
428      cc0 = misc_utils.getExptSum( self.dq, mlab, l1 )
429      ks = sorted( list( cc0.keys() ) )
430      if self.verbose:
431        print ('Experiment summary: %s %s' % (mlab,', '.join( ['%s: %s' % (k,len(cc0[k])) for k in ks] ) ) )
432
433      if m2 in [None, 'TOTAL']:
434        x = self.acc
435      else:
436        x = self.sc.volByExpt( l1, m2, pmax=pmax )
437
438##self.volByExpt( l1, e, pmax=pmax, cc=cc, retainRedundantRank=retainRedundantRank, intersection=intersection, adsCount=adsCount )
439        v0 = self.sc.volByMip( m, pmax=pmax,  exptid=m2 )
440####
441        if cc==None:
442          cc = collections.defaultdict( int )
443        for e in self.sc.volByE:
444          if self.verbose:
445             print ('INFO.mlab.... %s: %s: %s' % ( mlab, e, len( self.sc.volByE[e][2] ) ) )
446          for v in self.sc.volByE[e][2]:
447             cc[v] += self.sc.volByE[e][2][v]
448        xxx = 0
449        for v in cc:
450          xxx += cc[v]
451####
452        if acc:
453          for e in self.sc.volByE:
454            self.accAdd(self.sc.volByE[e])
455
456      if m2 not in [ None, 'TOTAL']:
457          im2 = self.dq.inx.uid[m2]
458          ismip = im2._h.label == 'mip'
459          mlab2 = im2.label
460
461          x0 = 0
462          for e in self.sc.volByE:
463            if exptids == None or e in exptids:
464              x = self.sc.volByE[e]
465              if x[0] > 0:
466                collector[mlab].a[mlab2] += x[0]
467                x0 += x[0]
468      else:
469          ismip = False
470          mlab2 = 'TOTAL'
471          x0 = x[0]
472
473      if mlab2 == 'TOTAL' and x0 == 0:
474        print ( 'no data detected for %s' % mlab )
475
476      if x0 > 0:
477#
478# create sum for each table
479#
480        xs = 0
481        kkc = '_%s_%s' % (mlab,mlab2)
482        kkct = '_%s_%s' % (mlab,'TOTAL')
483        if m2 in [None, 'TOTAL']:
484          x = self.acc
485          x2 = set(x[2].keys() )
486          for k in x[2].keys():
487           i = self.dq.inx.uid[k]
488           xxx =  x[2][k]
489           xs += xxx
490        else:
491          x2 = set()
492          for e in self.sc.volByE:
493            if exptids == None or e in exptids:
494              x = self.sc.volByE[e]
495              x2 = x2.union( set( x[2].keys() ) )
496              for k in x[2].keys():
497               i = self.dq.inx.uid[k]
498               xxx =  x[2][k]
499               xs += xxx
500               if xxx > 0:
501                collector[kkc].a[i.mipTable] += xxx
502                if ismip:
503                  collector[kkct].a[i.mipTable] += xxx
504
505##
506## One user was getting false error message here, with ('%s' % x0) == ('%s' % xs)
507##
508        if abs(x0 -xs) > 1.e-8*( abs(x0) + abs(xs) ):
509          print ( 'ERROR.0088: consistency problem %s  %s %s %s' % (m,m2,x0,xs) )
510        if x0 == 0:
511          print ( 'Zero size: %s, %s' % (m,m2) )
512          if len( x[2].keys() ) > 0:
513             print ( 'ERROR:zero: %s, %s: %s' % (m,m2,str(x[2].keys()) ) )
514
515        if acc and m2 not in [ None, 'TOTAL']:
516          collector[mlab].a['TOTAL'] += x0
517
518        dd = collections.defaultdict( list )
519        lll = set()
520        for v in x2:
521          vi = self.sc.dq.inx.uid[v]
522          if vi._h.label != 'remarks':
523            f,t,l,tt,d,u = (vi.frequency,vi.mipTable,vi.label,vi.title,vi.description,vi.uid)
524            lll.add(u)
525            dd[t].append( (f,t,l,tt,d,u) )
526
527        if len( dd.keys() ) > 0:
528          collector[mlab].dd[mlab2] = dd
529          if m2 not in [ None, 'TOTAL']:
530            if im2._h.label == 'experiment':
531              dothis = self.sc.tierMax >= min( im2.tier )
532###
533### BUT ... there is a treset in the request item .... it may be that some variables are excluded ...
534###         need the variable list itself .....
535###
536          makeTab( self.sc.dq, subset=lll, dest='%s/%s-%s_%s_%s' % (self.odir,mlab,mlab2,self.sc.tierMax,pmax), collected=collector[kkc].a,
537              mcfgNote=self.sc.mcfgNote,
538              txt=self.doTxt, xls=self.doXls, txtOpts=self.txtOpts )
Note: See TracBrowser for help on using the repository browser.