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

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

add table_utils

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
164      if self.xls:
165        for i in range(len(hrec)):
166          self.sht.write( j,i, hrec[i], self.hdr_cell_format )
167
168      if self.txt:
169        for i in range(len(hrec)):
170          self.oo.write( hrec[i] + '\t' )
171        self.oo.write( '\n' )
172
173  def cmvtab(self,t,addMips,mode='c',tslice=False,byFreqRealm=False):
174      if self.xls:
175        self.sht = self.newSheet( t )
176      j = 0
177      ncga = 'NetCDF Global Attribute'
178      if mode == 'c':
179        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']
180        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','','','','','']
181        if self.xls:
182          self.sht.set_column(1,1,40)
183          self.sht.set_column(1,3,50)
184          self.sht.set_column(1,4,30)
185          self.sht.set_column(1,5,50)
186          self.sht.set_column(1,6,30)
187          self.sht.set_column(1,9,40)
188          self.sht.set_column(1,18,40)
189          self.sht.set_column(1,19,40)
190      else:
191        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']
192      if addMips:
193        hrec.append( 'MIPs (requesting)' )
194        hrec.append( 'MIPs (by experiment)' )
195
196      if byFreqRealm:
197        hrec = ['Table',] + hrec
198        hcmt = ['CMOR table',] + hcmt
199      if tslice:
200          hrec += ['Number of Years','Slice Type','Years','Grid']
201          hcmt += ['','','','']
202
203      if self.xls:
204        for i in range(len(hrec)):
205          self.sht.write( j,i, hrec[i], self.hdr_cell_format )
206          if hcmt[i] != '':
207            self.sht.write_comment( j,i,hcmt[i])
208
209      if self.txt:
210        self.oo.write( 'MIP table\t' )
211        for i in range(len(hrec)):
212          self.oo.write( hrec[i] + '\t' )
213        self.oo.write( '\n' )
214        self.oo.write( t + '\t' )
215        for i in range(len(hrec)):
216          if hcmt[i] != '':
217            self.oo.write( hcmt[i] + '\t')
218          else:
219            self.oo.write( '\t')
220        self.oo.write( '\n' )
221
222  def newSheet(self,name):
223    self.worksheet = self.wb.add_worksheet(name=name)
224    return self.worksheet
225
226  def close(self):
227    if self.xls:
228      self.wb.close()
229    if self.txt:
230      self.oo.close()
231
232class makeTab(object):
233  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):
234    """txtOpts: gives option to list MIP variables instead of CMOR variables"""
235    if subset != None:
236      cmv = [x for x in dq.coll['CMORvar'].items if x.uid in subset]
237    else:
238      cmv = dq.coll['CMORvar'].items
239    self.byFreqRealm=byFreqRealm
240
241    ixt = collections.defaultdict(list)
242    if not byFreqRealm:
243      for i in cmv:
244        ixt[i.mipTable].append( i.uid )
245    else:
246      for i in cmv:
247        ixt['%s.%s' % (i.frequency,realmFlt( i.modeling_realm) )].append( i.uid )
248
249    if oldpython:
250        tables = sorted( ixt.keys(), cmp=cmpAnnex )
251    else:
252        tables = sorted( ixt.keys(), key=kAnnex )
253
254    addMips = True
255    if addMips:
256      c = vrev.checkVar(dq)
257    mode = 'c'
258    tableNotes = [
259       ('Request Version',str(dq.version)),
260       ('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')]
261
262    wb = xlsx( dest, xls=xls, txt=txt )
263    if mcfgNote != None:
264      wb.mcfgNote = mcfgNote
265    wb.header( tableNotes, collected)
266
267    if txtOpts != None and txtOpts.mode == 'var':
268      vl =  list( set( [v.vid for v in cmv] )  )
269      vli = [dq.inx.uid[i] for i in vl]
270      if oldpython:
271        thisvli =  sorted( vli, cmp=cmpdn(['sn','label']).cmp )
272      else:
273        thisvli = sorted( vli, key=kCmpdn )
274      wb.var()
275     
276      j = 0
277      for v in thisvli:
278      ###hrec = ['Long name', 'units', 'description', 'Variable Name', 'CF Standard Name' ]
279         orec = [v.title, v.units, v.description, v.label, v.sn]
280         j += 1
281         wb.varrec( j,orec )
282    else:
283      withoo = False
284      for t in tables:
285        if withoo:
286          oo = open( 'tables/test_%s.csv' % t, 'w' )
287        wb.cmvtab(t,addMips,mode='c',tslice=tslice != None,byFreqRealm=byFreqRealm)
288
289        j = 0
290        if oldpython:
291          thiscmv =  sorted( [dq.inx.uid[u] for u in ixt[t]], cmp=cmpdn(['prov','rowIndex','label']).cmp )
292        else:
293          thiscmv = sorted( [dq.inx.uid[u] for u in ixt[t]], key=kCmpdnPrl )
294
295        for v in thiscmv:
296          cv = dq.inx.uid[ v.vid ]
297          strc = dq.inx.uid[ v.stid ]
298          if strc._h.label == 'remarks':
299            print ( 'ERROR: structure not found for %s: %s .. %s (%s)' % (v.uid,v.label,v.title,v.mipTable) )
300            ok = False
301          else:
302            sshp = dq.inx.uid[ strc.spid ]
303            tshp = dq.inx.uid[ strc.tmid ]
304            ok = all( [i._h.label != 'remarks' for i in [cv,strc,sshp,tshp]] )
305          #[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']
306
307          if not ok:
308            if (t,v.label) not in skipped:
309              ml = []
310              for i in range(4):
311                 ii = [cv,strc,sshp,tshp][i]
312                 if ii._h.label == 'remarks':
313                   ml.append( ['var','struct','time','spatial'][i] )
314              print ( 'makeTables: skipping %s %s: %s' % (t,v.label,','.join( ml)) )
315              skipped.add( (t,v.label) )
316          else:
317            dims = []
318            dims +=  sshp.dimensions.split( '|' )
319            dims +=  tshp.dimensions.split( '|' )
320            dims +=  strc.odims.split( '|' )
321            dims +=  strc.coords.split( '|' )
322            dims = ' '.join( dims )
323            if "qcranges" in dq.inx.iref_by_sect[v.uid].a:
324              u = dq.inx.iref_by_sect[v.uid].a['qcranges'][0]
325              qc = dq.inx.uid[u]
326              ll = []
327              for k in ['valid_min', 'valid_max', 'ok_min_mean_abs', 'ok_max_mean_abs']:
328                if qc.hasattr(k):
329                  ll.append( '%s %s' % (qc.__dict__[k],qc.__dict__['%s_status' % k][0]) )
330                else:
331                  ll.append( '' )
332              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = tuple( ll )
333            else:
334              valid_min, valid_max, ok_min_mean_abs, ok_max_mean_abs = ('','','','')
335               
336            if mode == 'c':
337              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]
338            else:
339              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]
340
341            if byFreqRealm:
342              orec = [v.mipTable,] + orec
343
344            if addMips:
345              thismips = c.chkCmv( v.uid )
346              thismips2 = c.chkCmv( v.uid, byExpt=True )
347              orec.append( ','.join( sorted( list( thismips) ) ) )
348              orec.append( ','.join( sorted( list( thismips2) ) ) )
349
350            if tslice != None:
351              if v.uid not in tslice:
352                orec += ['All', '','','']
353              elif type( tslice[v.uid] ) == type( 0 ):
354                print ( 'ERROR: unexpected tslice type: %s, %s' % (v.uid, tslice[v.uid] ) )
355              elif len(  tslice[v.uid] ) == 3:
356                x,priority,grid = tslice[v.uid]
357                orec[0] = priority
358                orec += ['','','',grid]
359              else:
360                tslab,tsmode,a,b,priority,grid = tslice[v.uid]
361                if type( priority ) != type(1):
362                  thisp = priority
363                  priority = thisp[1]
364                  ##print 'ERROR in priority type[2]: ',priority, tslice[v.uid]
365                orec[0] = priority
366                     
367                if tsmode[:4] in ['simp','bran']:
368                   nys = b + 1 - a
369                   ys = range(a,b+1)
370                   orec += [str(nys), '',str(ys)]
371                elif tsmode[:4] in ['YEAR']:
372                   nys = a
373                   ys = b
374                   orec += [str(nys), '',str(ys)]
375                else:
376                   orec += ['slice', tslab,'']
377                orec.append( grid )
378               
379            if withoo:
380              oo.write( '\t'.join(orec ) + '\n' )
381            j+=1
382            wb.cmvtabrec( j,t,orec )
383
384        if withoo:
385          oo.close()
386    wb.close()
387
388class tables(object):
389  def __init__(self,sc, odir='xls',xls=True,txt=False,txtOpts=None):
390      self.sc = sc
391      self.dq = sc.dq
392      ##self.mips = mips
393      self.odir = odir
394      self.accReset()
395      self.doXls = xls
396      self.doTxt = txt
397      self.txtOpts = txtOpts
398
399  def accReset(self):
400    self.acc = [0.,collections.defaultdict(int),collections.defaultdict( float ) ]
401
402  def accAdd(self,x):
403    self.acc[0] += x[0]
404    for k in x[2]:
405       self.acc[2][k] += x[2][k]
406
407
408  def doTable(self,m,l1,m2,pmax,collector,acc=True, mlab=None,exptids=None,cc=None):
409      """*acc* allows accumulation of values to be switched off when called in single expt mode"""
410     
411      self.verbose = False
412      if mlab == None:
413        mlab = misc_utils.setMlab( m )
414
415      cc0 = misc_utils.getExptSum( self.dq, mlab, l1 )
416      ks = sorted( list( cc0.keys() ) )
417      if self.verbose:
418        print ('Experiment summary: %s %s' % (mlab,', '.join( ['%s: %s' % (k,len(cc0[k])) for k in ks] ) ) )
419
420      if m2 in [None, 'TOTAL']:
421        x = self.acc
422      else:
423        x = self.sc.volByExpt( l1, m2, pmax=pmax )
424
425##self.volByExpt( l1, e, pmax=pmax, cc=cc, retainRedundantRank=retainRedundantRank, intersection=intersection, adsCount=adsCount )
426        v0 = self.sc.volByMip( m, pmax=pmax,  exptid=m2 )
427####
428        if cc==None:
429          cc = collections.defaultdict( int )
430        for e in self.sc.volByE:
431          if self.verbose:
432             print ('INFO.mlab.... %s: %s: %s' % ( mlab, e, len( self.sc.volByE[e][2] ) ) )
433          for v in self.sc.volByE[e][2]:
434             cc[v] += self.sc.volByE[e][2][v]
435        xxx = 0
436        for v in cc:
437          xxx += cc[v]
438####
439        if acc:
440          for e in self.sc.volByE:
441            self.accAdd(self.sc.volByE[e])
442
443      if m2 not in [ None, 'TOTAL']:
444          im2 = self.dq.inx.uid[m2]
445          ismip = im2._h.label == 'mip'
446          mlab2 = im2.label
447
448          x0 = 0
449          for e in self.sc.volByE:
450            if exptids == None or e in exptids:
451              x = self.sc.volByE[e]
452              if x[0] > 0:
453                collector[mlab].a[mlab2] += x[0]
454                x0 += x[0]
455      else:
456          ismip = False
457          mlab2 = 'TOTAL'
458          x0 = x[0]
459
460      if mlab2 == 'TOTAL' and x0 == 0:
461        print ( 'no data detected for %s' % mlab )
462
463      if x0 > 0:
464#
465# create sum for each table
466#
467        xs = 0
468        kkc = '_%s_%s' % (mlab,mlab2)
469        kkct = '_%s_%s' % (mlab,'TOTAL')
470        if m2 in [None, 'TOTAL']:
471          x = self.acc
472          x2 = set(x[2].keys() )
473          for k in x[2].keys():
474           i = self.dq.inx.uid[k]
475           xxx =  x[2][k]
476           xs += xxx
477        else:
478          x2 = set()
479          for e in self.sc.volByE:
480            if exptids == None or e in exptids:
481              x = self.sc.volByE[e]
482              x2 = x2.union( set( x[2].keys() ) )
483              for k in x[2].keys():
484               i = self.dq.inx.uid[k]
485               xxx =  x[2][k]
486               xs += xxx
487               if xxx > 0:
488                collector[kkc].a[i.mipTable] += xxx
489                if ismip:
490                  collector[kkct].a[i.mipTable] += xxx
491
492##
493## One user was getting false error message here, with ('%s' % x0) == ('%s' % xs)
494##
495        if abs(x0 -xs) > 1.e-8*( abs(x0) + abs(xs) ):
496          print ( 'ERROR.0088: consistency problem %s  %s %s %s' % (m,m2,x0,xs) )
497        if x0 == 0:
498          print ( 'Zero size: %s, %s' % (m,m2) )
499          if len( x[2].keys() ) > 0:
500             print ( 'ERROR:zero: %s, %s: %s' % (m,m2,str(x[2].keys()) ) )
501
502        if acc and m2 not in [ None, 'TOTAL']:
503          collector[mlab].a['TOTAL'] += x0
504
505        dd = collections.defaultdict( list )
506        lll = set()
507        for v in x2:
508          vi = self.sc.dq.inx.uid[v]
509          if vi._h.label != 'remarks':
510            f,t,l,tt,d,u = (vi.frequency,vi.mipTable,vi.label,vi.title,vi.description,vi.uid)
511            lll.add(u)
512            dd[t].append( (f,t,l,tt,d,u) )
513
514        if len( dd.keys() ) > 0:
515          collector[mlab].dd[mlab2] = dd
516          if m2 not in [ None, 'TOTAL']:
517            if im2._h.label == 'experiment':
518              dothis = self.sc.tierMax >= min( im2.tier )
519###
520### BUT ... there is a treset in the request item .... it may be that some variables are excluded ...
521###         need the variable list itself .....
522###
523          makeTab( self.sc.dq, subset=lll, dest='%s/%s-%s_%s_%s' % (self.odir,mlab,mlab2,self.sc.tierMax,pmax), collected=collector[kkc].a,
524              mcfgNote=self.sc.mcfgNote,
525              txt=self.doTxt, xls=self.doXls, txtOpts=self.txtOpts )
Note: See TracBrowser for help on using the repository browser.