source: CMIP6dreq/trunk/dreqPy/scope.py @ 1189

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CMIP6dreq/trunk/dreqPy/scope.py@1189
Revision 1189, 71.8 KB checked in by mjuckes, 20 months ago (diff)

01.00.23

Line 
1"""Date Request Scoping module
2------------------------------
3The scope.py module contains the dreqQuery class and a set of ancilliary functions. The dreqQuery class contains methods for analysing the data request.
4"""
5
6class exYr(object):
7  def __init__(self):
8    pass
9
10try:
11  import dreq
12  imm=1
13except:
14  import dreqPy.dreq  as dreq
15  imm=2
16
17if imm == 1:
18  from utilities import cmvFilter, gridOptionSort
19  import misc_utils
20  import fgrid
21  import volsum
22else:
23  import dreqPy.volsum as volsum
24  import dreqPy.fgrid as fgrid
25  from dreqPy.utilities import cmvFilter, gridOptionSort
26  import dreqPy.misc_utils as misc_utils
27
28import collections, string, operator
29import sys, os
30
31def intdict():
32    return collections.defaultdict( int )
33
34if sys.version_info >= (2,7):
35  oldpython = False
36else:
37  oldpython = True
38
39gridSorter = gridOptionSort( oldpython )
40
41class timeSlice( object ):
42  def __init__(self,tsl):
43    self.tsl = tsl
44
45  def sort(self):
46    tsl = self.tsl
47    s = set()
48    ee = dict()
49    for ts in tsl:
50      if ts[0] == None:
51        return (1,ts,'Taking unsliced option')
52      s.add( ts[0][1] )
53      ee[ts[0][0]] = ts
54    tst = s.pop()
55    p = min( [ee[k][1] for k in ee.keys()] )
56    if len(s) > 0 or tst == 'dayList':
57      if sorted(ee.keys()) in [['piControl030a','piControl200'],['piControl030', 'piControl030a', 'piControl200']]:
58        return (1,(ee['piControl200'][0],p), 'Taking larger slice (possible alignment issues)')
59      elif sorted(ee.keys()) in [['piControl030', 'piControl030a']]:
60        return (1,(ee['piControl030'][0],p), 'Taking preferred slice (possible alignment issues)')
61      elif sorted(ee.keys()) == ['RFMIP','RFMIP2']:
62        return (1,(('RFMIP-union', 'dayList', None, None),p), 'Taking ad-hoc union')
63      elif sorted(ee.keys()) == ['RFMIP', 'RFMIP2', 'hist55']:
64        return (1,(('hist55plus', 'rangeplus', 1960, 2014),p), 'Taking ad-hoc union with extra ...')
65      elif sorted(ee.keys()) == ['RFMIP', 'hist55']:
66        return (1,(('hist55plus', 'rangeplus', 1960, 2014),p), 'Taking ad-hoc union with extra ...')
67      elif sorted(ee.keys()) == ['RFMIP2', 'hist55']:
68        return (1,(ee['hist55'][0],p), 'Taking larger containing slice')
69      elif sorted(ee.keys()) == ['DAMIP20','DAMIP40']:
70        return (1,(ee['DAMIP40'][0],p), 'Taking larger containing slice')
71      return (-1,None,'Multiple slice types: %s' % sorted(ee.keys()))
72
73    if not ( tst in ['simpleRange','relativeRange'] or (len(tst) > 13 and tst[:13] == 'branchedYears') ):
74      return (-2,None,'slice type aggregation not supported')
75    if len(tsl) == 2:
76      tsll = list( tsl )
77      sa,ea = tsll[0][0][2:]
78      sb,eb = tsll[1][0][2:]
79      if sa <= sb and ea >= eb:
80        return (1,tsll[0], 'Taking largest slice')
81      if sb <= sa and eb >= ea:
82        return (1,tsll[1], 'Taking largest slice')
83      if ea < sb or eb < sa:
84        return (2,tsll, 'Slices are disjoint')
85      return (-3,None, 'Overlapping slices')
86    else:
87##
88## sort by end year and length .. if longest of last ending is also the first starting, we can sort ...
89##
90      tsll = sorted( list(tsl), key=lambda x: (x[0][3],x[0][3]-x[0][2]) )
91      if min( [x[0][2] for x in tsll] ) == tsll[-1][0][2]:
92        return (1,tsll[-1], 'Taking largest slice')
93      return (-4,None, 'Cannot sort slices')
94
95def sortTimeSlice( tsl ):
96 
97  s = set()
98  for ts in tsl:
99    if ts[0] == None:
100      return (1,ts,'Taking unsliced option')
101    s.add( ts[0][1] )
102  if len(s) > 1:
103    return (-1,None,'Multiple slice types')
104  tst = s.pop()
105  if not ( tst in ['simpleRange','relativeRange'] or (len(tst) > 13 and tst[:13] == 'branchedYears') ):
106    return (-2,None,'slice type aggregation not supported')
107  if len(tsl) == 2:
108    tsll = list( tsl )
109    sa,ea = tsll[0][0][2:]
110    sb,eb = tsll[1][0][2:]
111    if sa <= sb and ea >= eb:
112      return (1,tsll[0], 'Taking largest slice')
113    if sb <= sa and eb >= ea:
114      return (1,tsll[1], 'Taking largest slice')
115    if ea < sb or eb < sa:
116      return (2,tsll, 'Slices are disjoint')
117    return (-3,None, 'Overlapping slices')
118  else:
119##
120## sort by end year and length .. if longest of last ending is also the first starting, we can sort ...
121##
122    tsll = sorted( list(tsl), key=lambda x: (x[0][3],x[0][3]-x[0][2]) )
123    if min( [x[0][2] for x in tsll] ) == tsll[-1][0][2]:
124        return (1,tsll[-1], 'Taking largest slice')
125    return (-4,None, 'Cannot sort slices')
126
127odsz = {'landUse':(5,'free'), 'tau':7, 'scatratio':15, 'effectRadLi|tau':(28,'query pending'), 'vegtype':(8,'free'), 'sza5':5, 'site':(119,'73 for aquaplanet .. '), 'iceband':(5,'free'), 'dbze':15, 'spectband':(10,'free'), 'misrBands':(7,'query pending'), 'effectRadIc|tau':(28,'query pending')}
128
129python2 = True
130if sys.version_info[0] == 3:
131  python2 = False
132  from functools import reduce
133  try: 
134    from utilP3 import mlog3
135  except:
136    from dreqPy.utilP3 import mlog3
137  mlg = mlog3()
138else:
139  from utilP2 import util
140  mlg = util.mlog()
141
142class c1(object):
143  def __init__(self):
144    self.a = collections.defaultdict( int )
145class c1s(object):
146  def __init__(self):
147    self.a = collections.defaultdict( set )
148
149NT_txtopts = collections.namedtuple( 'txtopts', ['mode'] )
150
151def vfmt(ss):
152  stb = ss*1.e-12
153  if stb < .099:
154    return '%7.2fGb' % (stb*100)
155  else:
156    return '%7.2fTb' % stb
157
158class baseException(Exception):
159  """Basic exception for general use in code."""
160
161  def __init__(self,msg):
162    self.msg = 'scope:: %s' % msg
163
164  def __str__(self):
165    return repr( self.msg )
166
167  def __repr__(self):
168    return self.msg
169
170nt_mcfg = collections.namedtuple( 'mcfg', ['nho','nlo','nha','nla','nlas','nls','nh1'] )
171class cmpd(object):
172  def __init__(self,dct):
173    self.d = dct
174  def cmp(self,x,y,):
175    return cmp( self.d[x], self.d[y] )
176
177
178def filter1( a, b ):
179  if b < 0:
180    return a
181  else:
182    return min( [a,b] )
183
184def filter2( a, b, tt, tm ):
185## largest tier less than or equal to tm
186  ll = [t for t in tt if t <= tm]
187  if len( ll ) > 0:
188    t1 = [t for t in tt if t <= tm][-1]
189    it1 = tt.index(t1)
190    aa = a[it1]
191    if b < 0:
192      return aa
193    else:
194      return min( [aa,b] )
195  else:
196    return 0
197
198npy = {'1hrClimMon':24*12, 'daily':365, u'Annual':1, u'fx':0.01, u'1hr':24*365, u'3hr':8*365,
199       u'monClim':12, u'Timestep':100, u'6hr':4*365, u'day':365, u'1day':365, u'mon':12, u'yr':1,
200       u'1mon':12, 'month':12, 'year':1, 'monthly':12, 'hr':24*365, 'other':24*365,
201        'subhr':24*365, 'Day':365, '6h':4*365, '3 hourly':8*365, '':1, 'dec':0.1,
202        '1hrCM':24*12, '1hrPt':24*365, '3hrPt':8*365, '6hrPt':4*365, 'monPt':12, 'monC':12, 'subhrPt':24*365, 'yrPt':1 }
203
204def vol01( sz, v, npy, freq, inx ):
205  n1 = npy[freq]
206  s = sz[inx.uid[v].stid]
207  assert type(s) == type(1), 'Non-integer size found for %s' % v
208  assert type(n1) in (type(1),type(0.)), 'Non-number "npy" found for %s, [%s]' % (v,freq)
209  return s*n1
210
211class col_list(object):
212  def __init__(self):
213    self.a = collections.defaultdict(list)
214
215class col_count(object):
216  def __init__(self):
217    self.a = collections.defaultdict(int)
218
219class dreqQuery(object):
220  __doc__ = """Methods to analyse the data request, including data volume estimates"""
221  errorLog = collections.defaultdict( set )
222  def __init__(self,dq=None,tierMax=1):
223    if dq == None:
224      self.dq = dreq.loadDreq()
225    else:
226      self.dq = dq
227    self.rlu = {}
228    for i in self.dq.coll['objective'].items:
229      k = '%s.%s' % (i.mip,i.label)
230      ##assert not k in self.rlu, 'Duplicate label in objectives: %s' % k
231      if k in self.rlu:
232        print ( 'SEVERE: Duplicate label in objectives: %s' % k )
233      self.rlu[k] = i.uid
234
235    self.odsz = odsz
236    self.npy = npy
237    self.strSz = dict()
238    self.cmvFilter = cmvFilter( self )
239    self.tierMax = tierMax
240    self.gridPolicyDefaultNative = False
241    self.gridOceanStructured = True
242    self.gridPolicyForce = None
243    self.retainRedundantRank = False
244    self.intersection = False
245    self.gridPolicyTopOnly = True
246    self.exptFilter = None
247    self.exptFilterBlack = None
248    self.uniqueRequest = False
249
250    self.mips = set( [x.label for x in self.dq.coll['mip'].items ] )
251    self.mips = ['CMIP','AerChemMIP', 'C4MIP', 'CFMIP', 'DAMIP', 'DCPP', 'FAFMIP', 'GeoMIP', 'GMMIP', 'HighResMIP', 'ISMIP6', 'LS3MIP', 'LUMIP', 'OMIP', 'PMIP', 'RFMIP', 'ScenarioMIP', 'VolMIP', 'CORDEX', 'DynVar', 'SIMIP', 'VIACSAB']
252    self.mipsp = self.mips[:-4]
253    self.cmvGridId, i4 = fgrid.fgrid( self.dq )
254    assert len(i4) == 0
255
256    self.experiments = set( [x.uid for x in self.dq.coll['experiment'].items ] )
257    self.exptByLabel = {}
258    self.rqLinkByExpt = self._setRqLinkByExpt()
259    for x in self.dq.coll['experiment'].items:
260      if x.label in self.exptByLabel:
261        print ( 'ERROR: experiment label duplicated: %s' % x.label )
262      self.exptByLabel[x.label] = x.uid
263    self.mipls = sorted( list( self.mips ) )
264
265    self.default_mcfg = nt_mcfg._make( [259200,60,64800,40,20,5,100] )
266    self.mcfg = self.default_mcfg._asdict()
267    self.mcfgNote = None
268    self.szcfg()
269    self.requestItemExpAll(  )
270
271  def _setRqLinkByExpt(self):
272    dq = self.dq
273    ee = {}
274#
275# loop over experiment records
276##
277    for e in dq.coll['experiment'].items:
278      eu = e.uid
279      ss = set()
280
281## loop over request link records
282      for l in dq.coll['requestLink'].items:
283        lu = l.uid
284
285## check to see if any request items associated with the record link to current experiment.
286        for u in dq.inx.iref_by_sect[lu].a['requestItem']:
287            esid = dq.inx.uid[u].esid
288            if esid == eu or 'experiment' in dq.inx.iref_by_sect[esid].a and eu in dq.inx.iref_by_sect[esid].a['experiment']:
289              ss.add( lu )
290      ee[eu] = ss
291    return ee
292
293  def showOpts(self):
294    print ( ( self.tierMax, self.gridPolicyDefaultNative, self.gridOceanStructured, self.gridPolicyForce,
295    self.retainRedundantRank, self.gridPolicyTopOnly, self.exptFilter, self.exptFilterBlack,
296    self.uniqueRequest ))
297
298  def setMcfg(self, ll, msg=None ):
299    assert len(ll) == 7, 'Model config must be of length 7: %s' % str(ll)
300    assert all( [type(x) == type(1) for x in ll] )
301    self.mcfg = nt_mcfg._make( ll )._asdict()
302    if msg == None:
303      self.mcfgNote = 'User supplied model configuration: %s' % str(ll)
304    else:
305      self.mcfgNote = msg
306    self.szcfg()
307
308  def szcfg(self):
309    szr = {'100km':64800, '1deg':64800, '2deg':16200 }
310    self.szss = {}
311    self.sz = {}
312    self.szg = collections.defaultdict( dict )
313    self.szgss = collections.defaultdict( dict )
314    self.isLatLon = {}
315    self.szSrf = collections.defaultdict( dict )
316    self.szssSrf = collections.defaultdict( dict )
317    for i in self.dq.coll['spatialShape'].items:
318      gtype = 'a'
319      if i.levelFlag == False:
320        ds =  i.dimensions.split( '|' )
321        if ds[-1] in ['site', 'basin']:
322          vd = ds[-2]
323        else:
324          vd = ds[-1]
325 
326        if vd[:4] == 'olev' or vd == 'rho':
327          gtype = 'o'
328          nz = self.mcfg['nlo']
329        elif vd[:4] == 'alev':
330          nz = self.mcfg['nla']
331        elif vd in ['slevel']:
332          nz = self.mcfg['nls']
333        elif vd in ['snowdepth','sdepth']:
334          nz = 5
335        elif vd == 'aslevel':
336          nz = self.mcfg['nlas']
337        else:
338          mlg.prnt( 'Failed to parse dimensions %s: %s' % (i.label,i.dimensions) )
339          raise
340      else:
341        nz = i.levels
342
343      dims = set( i.dimensions.split( '|' ) )
344      if 'latitude' in dims and 'longitude' in dims:
345        if gtype == 'o':
346          nh = self.mcfg['nho']
347          self.isLatLon[i.uid] = 'o'
348        else:
349          nh = self.mcfg['nha']
350          self.isLatLon[i.uid] = 'a'
351      else:
352        nh = 10
353        self.isLatLon[i.uid] = False
354
355      self.szss[i.uid] = nh*nz
356      if self.isLatLon[i.uid] != False and len(dims) == 2:
357        self.szssSrf[i.uid] = { 'a':self.mcfg['nha']*nz, 'o':self.mcfg['nho']*nz }
358
359      for k in szr:
360        if self.isLatLon[i.uid] != False:
361          self.szgss[k][i.uid] = szr[k]*nz
362        else:
363          self.szgss[k][i.uid] = nh*nz
364
365    for i in self.dq.coll['structure'].items:
366      s = 1
367      knownAtmos = False
368      if i.__dict__.get('odims','')  != '':
369        if i.odims in odsz:
370           sf = odsz[i.odims]
371        else:
372           ## print 'SEVERE.odims.00001: no information on dimension size: %s' % i.odims
373           sf = 5
374        if type( sf ) == type( () ):
375          sf = sf[0]
376        s = s*sf
377        if i.odims not in ['iceband']:
378          knownAtmos = True
379      if i.spid in self.szss:
380        self.sz[i.uid] = self.szss[i.spid]*s
381
382        if i.uid in self.szssSrf:
383          if knownAtmos:
384            self.sz[i.uid] = self.szssSrf[i.spid]['a']*s
385          else:
386            for k in ['a','o']:
387               self.szSrf[i.uid][k] = self.szssSrf[i.spid][k]*s
388
389        for k in szr:
390          self.szg[k][i.uid] = self.szgss[k][i.spid]*s
391      else:
392        print ('WARNING: spid has no size info: %s [%s]' % (i.spid,i.uid) )
393        self.sz[i.uid] = 0.
394        for k in szr:
395          self.szg[k][i.uid] = 0.
396
397  def getRequestLinkByMip( self, mipSel ):
398    """Return the set of request links which are associated with specified MIP"""
399
400    if type(mipSel) == type( {} ):
401      return self.getRequestLinkByMipObjective(self,mipSel)
402
403    if type(mipSel) == type(''):
404      t1 = lambda x: x == mipSel
405    elif type(mipSel) == type(set()):
406      t1 = lambda x: x in mipSel
407
408    s = set()
409    for i in self.dq.coll['requestLink'].items:
410      if t1(i.mip):
411        if 'requestItem' in self.dq.inx.iref_by_sect[i.uid].a:
412          if any( [ self.rqiExp[x][3] > 0 for  x in self.dq.inx.iref_by_sect[i.uid].a['requestItem'] if x in self.rqiExp ] ):
413            s.add( i )
414
415    self.rqs = list( s )
416    return self.rqs
417
418  def getRequestLinkByMipObjective( self, mipSel ):
419    """Return the set of request links which are associated with specified MIP and its objectives"""
420
421    assert type(mipSel) == type( {} ),'Argument must be a dictionary, listing objectives for each MIP'
422
423    s = set()
424    for i in self.dq.coll['requestLink'].items:
425      if i.mip in mipSel:
426        if len(mipSel[i.mip]) == 0:
427          s.add( i )
428        elif 'objectiveLink' in self.dq.inx.iref_by_sect[i.uid].a:
429          ss = set( [self.dq.inx.uid[k].label for k in self.dq.inx.iref_by_sect[i.uid].a['objectiveLink'] ] )
430          if any( [x in mipSel[i.mip] for x in ss] ):
431            s.add( i )
432##
433## filter requestLinks by tierMax: check to see whether they link to experiments with tier below or equal to tiermax.
434##
435    s1 = set()
436    for i in s:
437      if 'requestItem' in self.dq.inx.iref_by_sect[i.uid].a:
438        if any( [ self.rqiExp[x][-1] > 0 for  x in self.dq.inx.iref_by_sect[i.uid].a['requestItem'] if x in self.rqiExp ] ):
439            s1.add( i )
440
441    self.rqs = list( s1 )
442    return self.rqs
443
444  def varGroupXexpt(self, rqList ):
445    """For a list of request links, return a list of variable group IDs for each experiment"""
446    self.cc = collections.defaultdict( list )
447    ## dummy = {self.cc[i.expt].append(i.rlid) for i in self.dq.coll['requestItem'].items if i.rlid in {j.uid for j in rqList} }
448    return self.cc
449
450  def yearsInRequest(self, rql ):
451    self.ntot = sum( [i.ny for i in self.dq.coll['requestItem'].items if i.rlid == rql.uid] )
452    return self.ntot
453
454  def rqlByExpt( self, l1, ex, pmax=2, expFullEx=False ):
455    """rqlByExpt: return a set of request links for an experiment"""
456##
457    inx = self.dq.inx
458
459    if ex != None:
460   
461      exi = self.dq.inx.uid[ex]
462      if exi._h.label == 'experiment':
463        exset = set( [ex,exi.egid,exi.mip] )
464      else:
465        exset = set( self.esid_to_exptList(ex,deref=False,full=expFullEx) )
466##
467## rql is the set of all request links which are associated with a request item for this experiment set
468##
469   
470      l1p = set()
471      for i in l1:
472        if i.preset < 0 or i.preset <= pmax:
473          if i.esid in exset:
474            l1p.add(i)
475    else:
476      exset = None
477      l1p = l1
478
479    rql0 = set()
480    for i in l1p:
481       rql0.add(i.rlid)
482
483    rqlInv = set()
484    for u in rql0:
485      if inx.uid[u]._h.label == 'remarks':
486        rqlInv.add( u )
487    if len(rqlInv) != 0:
488      mlg.prnt ( 'WARNING.001.00002: %s invalid request links from request items ...' % len(rqlInv) )
489    rql = set()
490    for u in rql0:
491       if inx.uid[u]._h.label != 'remarks':
492         rql.add( u ) 
493
494    return rql, l1p, exset
495
496  def varsByRql( self, rql, pmax=2, intersection=False, asDict=False): 
497      """The complete set of variables associated with a set of request links."""
498      inx = self.dq.inx
499      cc1 = collections.defaultdict( set )
500      for i in rql:
501        o = inx.uid[i]
502        if o.opt == 'priority':
503          p = int( float( o.opar ) )
504          assert p in [1,2,3], 'Priority incorrectly set .. %s, %s, %s' % (o.label,o.title, o.uid)
505          cc1[inx.uid[i].mip].add( (inx.uid[i].refid,p) )
506        else:
507          cc1[inx.uid[i].mip].add( inx.uid[i].refid )
508
509      if intersection:
510        ccv = {}
511#
512# set of request variables for each MIP
513#
514        for k in cc1:
515          thisc = reduce( operator.or_, [set( inx.iref_by_sect[vg].a['requestVar'] ) for vg in cc1[k] ] )
516          rqvgs = collections.defaultdict( set )
517          for x in cc1[k]:
518            if type(x) == type( () ):
519              rqvgs[x[0]].add( x[1] )
520            else:
521              rqvgs[x].add( 3 )
522         
523          s = set()
524          for vg in rqvgs:
525            for l in inx.iref_by_sect[vg].a['requestVar']:
526              if inx.uid[l].priority <= min(pmax,max(rqvgs[vg])):
527                s.add( inx.uid[l].vid )
528          ccv[k] = s
529
530        if len( ccv.keys() ) < len( list(imips) ):
531          vars = set()
532        else:
533          vars =  reduce( operator.and_, [ccv[k] for k in ccv] )
534      else:
535        rqvgs = collections.defaultdict( set )
536        for k in cc1:
537          for x in cc1[k]:
538            if type(x) == type( () ):
539              rqvgs[x[0]].add( x[1] )
540            else:
541              rqvgs[x].add( 3 )
542         
543###To obtain a set of variables associated with this collection of variable groups:
544
545        if asDict:
546          vars = collections.defaultdict( list )
547        else:
548          vars = set()
549        for vg in rqvgs:
550          for l in inx.iref_by_sect[vg].a['requestVar']:
551            if inx.uid[l].priority <= min(pmax,max(rqvgs[vg])):
552               if asDict:
553                 vars[inx.uid[l].vid].append( vg )
554               else:
555                 vars.add(inx.uid[l].vid)
556
557        ##col1 = reduce( operator.or_, [set( inx.iref_by_sect[vg].a['requestVar'] ) for vg in rqvg ] )
558### filter out cases where the request does not point to a CMOR variable.
559    ##vars = {vid for vid in vars if inx.uid[vid][0] == u'CMORvar'}
560
561      if asDict:
562        thisvars = {}
563        for vid in vars:
564           if inx.uid[vid]._h.label == u'CMORvar':
565             thisvars[vid] = vars[vid]
566      else:
567        thisvars = set()
568        for vid in vars:
569           if inx.uid[vid]._h.label == u'CMORvar':
570             thisvars.add(vid)
571
572      return thisvars
573
574  def exptYears( self, rqll, ex=None, exBlack=None):
575    """Parse a set of request links, and get years requested for each (varGroup, expt, grid) tuple """
576     
577    self.tsliceDict = collections.defaultdict( dict )
578    ccts = collections.defaultdict( dict )
579    ccts2 = collections.defaultdict( set )
580    cc = collections.defaultdict( set )
581    for rl in rqll:
582      if 'requestItem' not in self.dq.inx.iref_by_sect[rl.uid].a:
583        self.errorLog['WARN.001.00001'].add( 'no request items for: %s, %s' % (rl.uid, rl.title) )
584        ##print ( 'WARN.001.00001: no request items for: %s, %s' % (rl.uid, rl.title) )
585      else:
586
587        ##print rl.uid, rl.title, rl.grid, rl.gridreq
588        if self.gridPolicyForce != None:
589          grd = self.gridPolicyForce
590        elif rl.grid in ['1deg','2deg','100km']:
591          if rl.grid == '100km':
592            grd = '1deg'
593          else:
594            grd = rl.grid
595        else:
596          ## note that naming of "gridreq" is unfortunate ... "No" means that native grid is required
597          if rl.gridreq in ['No', 'no']:
598             #or self.gridPolicyDefaultNative:
599            grd = 'native'
600          elif rl.gridreq in ['no*1']:
601             #or self.gridPolicyDefaultNative:
602            grd = 'native:01'
603          else:
604            ##print ( 'INFO.grd.00001: defaulting to grid ..%s, %s, %s' % (rl.label,rl.title, rl.uid) )
605            grd = 'DEF'
606
607        for iu in self.dq.inx.iref_by_sect[rl.uid].a['requestItem']:
608          i = self.dq.inx.uid[iu]
609
610##
611## apply "treset" filter to request items linked to this group.
612##
613          if self.tierMax < 0 or 'treset' not in i.__dict__ or i.treset <= self.tierMax:
614            if iu in self.rqiExp:
615              for e in self.rqiExp[iu][1]:
616                if (ex == None or e in ex) and (exBlack == None or e not in exBlack):
617                  this = self.rqiExp[iu][1][e]
618                  if this != None:
619                    thisns = this[-3]
620                    thisny = this[-2]
621                    thisne = this[-1]
622                    ##cc[ (rl.refid,e,grd) ].add( filter1( thisns*thisny*thisne, i.nymax) )
623                    cc[ (rl.refid,e,grd) ].add( thisns*thisny*thisne )
624                    if self.rqiExp[iu][4] != None:
625                      ccts[(rl.refid,e)][thisns*thisny*thisne] = self.rqiExp[iu][4]
626                      ccts2[(rl.refid,e)].add( self.rqiExp[iu][4] )
627
628    ee = collections.defaultdict( dict )
629
630    revertToLast = True
631    ey = exYr()
632    if revertToLast:
633      for g,e,grd in cc:
634        ee[g][(e,grd)] = max( cc[( g,e,grd) ] )
635        ##if (g,e) in ccts and ee[g][(e,grd)] in ccts[(g,e)]:
636#
637# possible corner cut here ... as max length may not include all years where there is a non-overlap ...
638#
639           ##self.tsliceDict[g][e] = ccts[(g,e)][ ee[g][(e,grd)] ]
640## change to a set of slices
641        self.tsliceDict[g][e] = ccts2[(g,e)]
642      ey.exptYears = ee
643      return ey
644    ff = collections.defaultdict( dict )
645##
646## this needs to be done separately for ocean and atmosphere, because of the default logic workflow ...
647    for g,e,grd in cc:
648      ee[(g,e)][grd] = max( cc[( g,e,grd) ] )
649
650    xx = collections.defaultdict( dict )
651    for g,e in ee:
652      ddef = ee[(g,e)].get( 'DEF', 0 )
653      for grd in ee[(g,e)]:
654        if grd != 'DEF':
655          xx[(g,'a')][(e, grd)] = ee[(g,e)][grd]
656          xx[(g,'o')][(e, grd)] = ee[(g,e)][grd]
657          xx[(g,'')][(e, grd)] = ee[(g,e)][grd]
658        if grd == 'native' and ddef != 0:
659          xx[(g,'a')][(e, 'native')] = max( [xx[(g,'a')][(e, 'native')],ddef] )
660          xx[(g,'')][(e, 'native')] = max( [xx[(g,'')][(e, 'native')],ddef] )
661        if grd == '1deg' and ddef != 0:
662          xx[(g,'o')][(e, '1deg')] = max( [xx[(g,'o')][(e, '1deg')],ddef] )
663
664    for grp,flg in xx:
665      ff[grp][flg] = xx[(grp,flg)]
666         
667    ## return dict[<variable group>]{dict[<experiment><grid>]{<years>}}
668    ## return dict[<variable group>][grid flag]{dict[<experiment>,<grid>]{<years>}}
669    return ff
670
671  def volByExpt( self, l1, ex, pmax=1, cc=None, intersection=False,expFullEx=False, adsCount=False ):
672    """volByExpt: calculates the total data volume associated with an experiment/experiment group and a list of request items.
673          The calculation has some approximations concerning the number of years in each experiment group.
674          cc: an optional collector, to accumulate indexed volumes. """
675##
676    inx = self.dq.inx
677    imips = set()
678    for i in l1:
679      imips.add(i.mip)
680   
681    rql, l1p, exset = self.rqlByExpt( l1, ex, pmax=pmax, expFullEx=expFullEx )
682    verbose = False
683    if verbose:
684      for i in rql:
685        r = inx.uid[i]
686        print ( '%s, %s, %s' % (r.label, r.title, r.uid) )
687
688    if ex != None:
689     
690      exi = self.dq.inx.uid[ex]
691      if exi._h.label == 'experiment':
692        exset = set( [ex,exi.egid,exi.mip] )
693#####
694    if len( rql ) == 0:
695      self.vars = set()
696      return (0,{},{} )
697
698## The complete set of variables associated with these requests:
699    vars = self.varsByRql( rql, pmax=pmax, intersection=intersection, asDict=True) 
700##
701## filter by configuration option and rank
702##
703    if not self.retainRedundantRank:
704      len1 = len(vars.keys())
705      cmv = self.cmvFilter.filterByChoiceRank(cmv=vars.keys())
706      vars = cmv
707   
708    self.vars = vars
709
710    e = {}
711    for u in rql:
712### for request variables which reference the variable group attached to the link, add the associate CMOR variables, subject to priority
713      i = inx.uid[u]
714      e[i.uid] = set()
715      si = collections.defaultdict( list )
716      for x in inx.iref_by_sect[i.refid].a['requestVar']:
717           if inx.uid[x].priority <= pmax:
718              e[i.uid].add( inx.uid[x].vid )
719
720              if verbose:
721                cmv = inx.uid[inx.uid[x].vid]
722                if cmv._h.label == 'CMORvar':
723                  si[ cmv.mipTable ].append( inx.uid[x].label )
724#
725# for each variable, calculate the maximum number of years across all the request links which reference that variable.
726##
727## for each request item we have nymax, nenmax, nexmax.
728##
729    nymg = collections.defaultdict( dict )
730##
731## if dataset count rather than volume is wanted, use item 3 from rqiExp tuple.
732    if adsCount:
733      irqi = 3
734    else:
735      irqi = 2
736
737    sgg = set()
738    for v in vars:
739      s = set()
740      sg = collections.defaultdict( set )
741      cc2 = collections.defaultdict( set )
742      cc2s = collections.defaultdict( c1s )
743      for i in l1p:
744##################
745        if (exset == None or i.esid in exset) and v in e[i.rlid]:
746          ix = inx.uid[i.esid]
747          rl = inx.uid[i.rlid]
748          sgg.add( rl.grid )
749          if rl.grid in ['100km','1deg','2deg']:
750            grd = rl.grid
751          else:
752            grd = 'native'
753
754          this = None
755          if exset == None:
756            thisz = 100
757##
758## for a single experiment, look up n years, and n ensemble.
759## should have nstart????
760##
761          elif exi._h.label == 'experiment' or ix._h.label == 'experiment':
762            this = None
763            if ex in self.rqiExp[i.uid][1]:
764              this = self.rqiExp[i.uid][1][ex]
765            elif ix.uid in self.rqiExp[i.uid][1]:
766              this = self.rqiExp[i.uid][1][ix.uid]
767            if this != None:
768              thisns = this[-3]
769              thisny = this[-2]
770              thisne = this[-1]
771              cc2s[grd].a[u].add( filter1( thisns*thisny*thisne, i.nymax) )
772          else:
773            thisz = None
774            if 'experiment' in inx.iref_by_sect[i.esid].a:
775              for u in inx.iref_by_sect[i.esid].a['experiment']:
776                if u in self.rqiExp[i.uid][1]:
777                  this = self.rqiExp[i.uid][1][u]
778                  thisns = this[-3]
779                  thisny = this[-2]
780                  thisne = this[-1]
781##
782###   aggregate year count for each experiment and output grid
783## clarify definition and usage of nymax -- should be redundant ... could be replaced by inward references from "timeSlice"
784                  cc2s[grd].a[u].add( filter1( thisns*thisny*thisne, i.nymax) )
785
786          if exset != None:
787            sg[grd].add( self.rqiExp[i.uid][irqi] )
788     
789###
790### sum over experiments of maximum within each experiment
791###
792      for g in sg:
793        nymg[v][g] = sum( [max( cc2s[g].a[k] ) for k in cc2s[g].a] )
794
795    szv = {}
796    ov = []
797    for v in vars:
798      if 'requestVar' not in inx.iref_by_sect[v].a:
799         print ( 'Variable with no request ....: %s, %s' % (inx.uid[v].label, inx.uid[v].mipTable) )
800      try:
801        szv[v] = self.sz[inx.uid[v].stid]*npy[inx.uid[v].frequency]
802      except:
803        if inx.uid[v].stid not in self.sz:
804          print ('ERROR: size not found for stid %s (v=%s, %s)' % (inx.uid[v].stid,v,inx.uid[v].label) )
805        if inx.uid[v].frequency not in npy:
806          print ('ERROR: npy not found for frequency %s (v=%s, %s)' % (inx.uid[v].frequency,v,inx.uid[v].label) )
807        szv[v] = 0
808      ov.append( self.dq.inx.uid[v] )
809
810    ff = {}
811    for v in vars:
812      if adsCount:
813        ff[v] = 1
814      else:
815        if 'native' in nymg[v]:
816          ff[v] = szv[v]
817          ny = nymg[v]['native']
818        else:
819          ks0 = nymg[v].keys()
820          if len(ks0) == 0:
821            ff[v] = 0.
822            ny = 0.
823          else:
824            ks = gridSorter.sort( nymg[v].keys() )[0]
825            ##ks = list( nymg[v].keys() )[0]
826            ny = nymg[v][ks]
827            if inx.uid[v].stid in self.szg[ks]:
828              ff[v] = self.szg[ks][ inx.uid[v].stid ] * npy[inx.uid[v].frequency]
829            else:
830              ff[v] = 0.
831
832        if inx.uid[v].frequency not in ['monClim','monC']:
833          ff[v] = ff[v]*ny
834
835    ee = self.listIndexDual( ov, 'mipTable', 'label', acount=None, alist=None, cdict=ff, cc=cc )
836    self.ngptot = sum( [  ff[v]  for v in vars] )
837    return (self.ngptot, ee, ff )
838
839  def esid_to_exptList(self,esid,deref=False,full=False):
840    if not esid in self.dq.inx.uid:
841      mlg.prnt ( 'Attempt to dereferece invalid uid: %s' % esid )
842      raise
843
844    if self.dq.inx.uid[esid]._h.label == 'experiment':
845      expts = [esid,]
846    elif self.dq.inx.uid[esid]._h.label != 'remarks':
847      if esid in self.dq.inx.iref_by_sect and 'experiment' in self.dq.inx.iref_by_sect[esid].a:
848        expts = list( self.dq.inx.iref_by_sect[esid].a['experiment'][:] )
849      else:
850        expts = []
851
852## add in groups and mips for completeness
853##
854      if full:
855        if self.dq.inx.uid[esid]._h.label == 'mip':
856          s = set()
857          for e in expts:
858            if self.dq.inx.uid[e]._h.label != 'experiment':
859              mlg.prnt ( 'ERROR: %s, %s, %s ' % (esid,e, self.dq.inx.uid[e].title ) )
860            s.add( self.dq.inx.uid[e].egid )
861          for i in s:
862            expts.append( i )
863        expts.append( esid )
864    else:
865      return None
866
867    if self.tierMax > 0:
868      expts1 = []
869      for i in expts:
870        if self.dq.inx.uid[i]._h.label == 'experiment':
871          if self.dq.inx.uid[i].tier[0] <= self.tierMax:
872            expts1.append( i )
873        elif self.dq.inx.uid[i]._h.label == 'exptgroup':
874          if self.dq.inx.uid[i].tierMin <= self.tierMax:
875            expts1.append( i )
876        else:
877            expts1.append( i )
878    else:
879      expts1 = expts
880
881    if deref:
882      return [self.dq.inx.uid[e] for e in expts1]
883    else:
884      return expts1
885##
886## need to call this on load
887## then use instead of i.ny etc below
888##
889  def requestItemExpAll( self ):
890    self.rqiExp = {}
891    for rqi in self.dq.coll['requestItem'].items:
892      a,b,c,d,e = self.requestItemExp( rqi )
893      if a != None:
894        self.rqiExp[rqi.uid] = (a,b,c,d,e)
895
896  def requestItemExp( self, rqi ):
897    assert rqi._h.label == "requestItem", 'Argument to requestItemExp must be a requestItem'
898    tsl = None
899    if 'tslice' in rqi.__dict__:
900      ts = self.dq.inx.uid[ rqi.tslice ]
901      if ts._h.label == 'timeSlice':
902        if ts.type in ['simpleRange','relativeRange']:
903          tsl = (ts.label, ts.type, ts.start,ts.end)
904        elif ts.type == 'branchedYears':
905          tsl = (ts.label,'%s:%s' % (ts.type,ts.child), ts.start,ts.end)
906        else:
907          tsl = (ts.label, ts.type, None, None )
908
909     
910    u = rqi.esid
911    if self.dq.inx.uid[u]._h.label == 'experiment':
912      expts = [u,]
913    elif self.dq.inx.uid[u]._h.label != 'remarks':
914      if u in self.dq.inx.iref_by_sect and 'experiment' in self.dq.inx.iref_by_sect[u].a:
915        expts = self.dq.inx.iref_by_sect[u].a['experiment']
916      else:
917        expts = []
918    else:
919      return (None, None, None, None,None)
920
921    if self.tierMax > 0:
922      expts = [i for i in expts if self.dq.inx.uid[i].tier[0] <= self.tierMax]
923
924    self.multiTierOnly = False
925    if self.multiTierOnly:
926      expts = [i for i in expts if len(self.dq.inx.uid[i].tier) > 1]
927      print ('Len expts: %s' % len(expts) )
928
929    if len(expts) > 0:
930      e = [self.dq.inx.uid[i] for i in expts]
931      for i in e:
932        if i._h.label != 'experiment':
933          mlg.prnt ( 'ERROR: %s, %s, %s ' % ( u,i._h.label, i.label, i.title ) )
934      dat2 = {}
935      for i in e:
936        dat2[i.uid] = (i.ntot, i.yps, i.ensz, i.tier, i.nstart, filter1(i.yps,rqi.nymax), filter2(i.ensz,rqi.nenmax,i.tier,self.tierMax) )
937
938      nytot = sum( [dat2[x][-2]*dat2[x][-3] for x in dat2 ] )
939      netot = sum( [dat2[x][-1] for x in dat2 ] )
940    else:
941      dat2 = {}
942      nytot = 0
943      netot = 0
944   
945##
946## to get list of years per expt for each requestLink ... expts is union of all dat2 keys,
947## and want max of dat2[x][0] for each experiment x.
948##
949    return (expts, dat2, nytot, netot, tsl )
950
951  def setTierMax( self, tierMax ):
952    """Set the maxium tier and recompute request sizes"""
953    if tierMax != self.tierMax:
954      self.tierMax = tierMax
955      self.requestItemExpAll(  )
956
957  def summaryByMip( self, pmax=1 ):
958    bytesPerFloat = 2.
959    for m in self.mipls:
960      v = self.volByMip( m, pmax=pmax )
961      mlg.prnt ( '%12.12s: %6.2fTb' % (m,v*bytesPerFloat*1.e-12) )
962
963  def rqlByMip( self, mip):
964    if mip == 'TOTAL':
965        mip = self.mips
966
967    if type(mip) in [type( '' ),type( u'') ]:
968      if mip not in self.mips:
969        mlg.prnt ( self.mips )
970        raise baseException( 'rqiByMip: Name of mip not recognised: %s' % mip )
971      l1 = [i for i in  self.dq.coll['requestLink'].items if i.mip == mip]
972    elif type(mip) in [ type( set()), type( [] ) ]:
973      nf = [ m for m in mip if m not in self.mips]
974      if len(nf) > 0:
975          raise baseException( 'rqlByMip: Name of mip(s) not recognised: %s' % str(nf) )
976      l1 = [i for i in  self.dq.coll['requestLink'].items if i.mip in mip]
977    elif type(mip) == type( dict()):
978      nf = [ m for m in mip if m not in self.mips]
979      if len(nf) > 0:
980        raise baseException( 'rqlByMip: Name of mip(s) not recognised: %s' % str(nf) )
981      l1 = []
982      for i in  self.dq.coll['requestLink'].items:
983        if i.mip in mip:
984          ok = False
985          if len( mip[i.mip] ) == 0:
986            ok = True
987          else:
988            for ol in self.dq.inx.iref_by_sect[i.uid].a['objectiveLink']:
989              o = self.dq.inx.uid[ol]
990              if self.dq.inx.uid[o.oid].label in mip[i.mip]:
991                ok = True
992          if ok:
993              l1.append( i )
994    else:
995      raise baseException( 'rqiByMip: "mip" (1st explicit argument) should be type string or set: %s -- %s' % (mip, type(mip))   )
996
997    return l1
998
999  def rqiByMip( self, mip):
1000    l1 = self.rqlByMip( mip )
1001    if len(l1) == 0:
1002       return []
1003    l2 = [] 
1004    for i in l1:
1005       if 'requestItem' in self.dq.inx.iref_by_sect[i.uid].a:
1006          for u in self.dq.inx.iref_by_sect[i.uid].a['requestItem']:
1007               l2.append( self.dq.inx.uid[u] )
1008
1009    l20 = self.rqiByMip0( mip )
1010    for i in l20:
1011      assert i in l2
1012    return l2
1013   
1014   
1015  def rqiByMip0( self, mip):
1016
1017    if mip == 'TOTAL':
1018        mip = self.mips
1019    if type(mip) in [type( '' ),type( u'') ]:
1020      if mip not in self.mips:
1021        mlg.prnt ( self.mips )
1022        raise baseException( 'rqiByMip: Name of mip not recognised: %s' % mip )
1023      l1 = [i for i in  self.dq.coll['requestItem'].items if i.mip == mip]
1024    elif type(mip) in [ type( set()), type( [] ) ]:
1025      nf = [ m for m in mip if m not in self.mips]
1026      if len(nf) > 0:
1027          raise baseException( 'rqiByMip: Name of mip(s) not recognised: %s' % str(nf) )
1028      l1 = [i for i in  self.dq.coll['requestItem'].items if i.mip in mip]
1029    elif type(mip) == type( dict()):
1030      nf = [ m for m in mip if m not in self.mips]
1031      if len(nf) > 0:
1032        raise baseException( 'rqiByMip: Name of mip(s) not recognised: %s' % str(nf) )
1033      l1 = []
1034      for i in  self.dq.coll['requestLink'].items:
1035        if i.mip in mip:
1036          ok = False
1037          if len( mip[i.mip] ) == 0:
1038            ok = True
1039          else:
1040            for ol in self.dq.inx.iref_by_sect[i.uid].a['objectiveLink']:
1041              o = self.dq.inx.uid[ol]
1042              if self.dq.inx.uid[o.oid].label in mip[i.mip]:
1043                ok = True
1044          if ok:
1045              if 'requestItem' in self.dq.inx.iref_by_sect[i.uid].a:
1046                for u in self.dq.inx.iref_by_sect[i.uid].a['requestItem']:
1047                  l1.append( self.dq.inx.uid[u] )
1048    else:
1049      raise baseException( 'rqiByMip: "mip" (1st explicit argument) should be type string or set: %s -- %s' % (mip, type(mip))   )
1050
1051    return l1
1052
1053  def checkDir(self,odir,msg):
1054      if not os.path.isdir( odir ):
1055         try:
1056            os.mkdir( odir )
1057         except:
1058            print ('\n\nFailed to make directory "%s" for: %s: make necessary subdirectories or run where you have write access' % (odir,msg) )
1059            print ( '\n\n' )
1060            raise
1061         print ('Created directory %s for: %s' % (odir,msg) )
1062
1063  def xlsByMipExpt(self,m,ex,pmax,odir='xls',xls=True,txt=False,txtOpts=None):
1064    import scope_utils
1065    mxls = scope_utils.xlsTabs(self,tiermax=self.tierMax,pmax=pmax,xls=xls, txt=txt, txtOpts=txtOpts,odir=odir)
1066    mlab = misc_utils.setMlab( m )
1067    mxls.run( m, mlab=mlab )
1068
1069  def cmvByInvMip( self, mip,pmax=1,includeYears=False, exptFilter=None,exptFilterBlack=None ):
1070    mips = set( self.mips[:] )
1071    if type(mip) == type( '' ):
1072        mips.discard( mip )
1073    else:
1074      for m in mip:
1075        mips.discard( m )
1076
1077    return self.cmvByMip( mips,pmax=pmax,includeYears=includeYears, exptFilter=exptFilter, exptFilterBlack=exptFilterBlack )
1078
1079  def cmvByMip( self, mip,pmax=1,includeYears=False, exptFilter=None, exptFilterBlack=None ):
1080    if exptFilter != None:
1081      assert type(exptFilter) == type( set() ), 'Argument exptFilter must be None or a set: %s' % str(exptFilter)
1082    if exptFilterBlack != None:
1083      assert type(exptFilterBlack) == type( set() ), 'Argument exptFilterBlack must be None or a set: %s' % str(exptFilterBlack)
1084      if exptFilter != None:
1085        assert len( exptFilter.difference( exptFilterBlack ) ) > 0, 'If exptFilter and exptFilterBlack are both set, exptFilter must have non-black listed elements' 
1086
1087    l1,ee = self.rvgByMip( mip, includePreset=True, returnLinks=True )
1088    if includeYears:
1089      expys = self.exptYears( l1, ex=exptFilter, exBlack=exptFilterBlack )
1090      cc = collections.defaultdict( set )
1091      ccts = collections.defaultdict( set )
1092
1093    mipsByVar = collections.defaultdict( set )
1094    ss = set()
1095    for pr in ee:
1096### loop over request  var groups.
1097      for i in ee[pr]:
1098        if 'requestVar' in self.dq.inx.iref_by_sect[i.uid].a:
1099#
1100# loop over request vars in group
1101#
1102          for x in self.dq.inx.iref_by_sect[i.uid].a['requestVar']:
1103            i1 = self.dq.inx.uid[x]
1104
1105            thisp = i1.priority
1106            if pr != -1:
1107              thisp = pr
1108             
1109            if thisp <= pmax:
1110              if includeYears and i1.vid in self.cmvGridId:
1111                ##assert i.uid in expys, 'No experiment info found for requestVarGroup: %s' % i.uid
1112                ## may have no entry as a consequence of tierMin being set in the requestLink(s).
1113                assert i1.vid in self.cmvGridId, 'No grid identification lookup found for %s: %s' % (i1.label,i1.vid)
1114                assert self.cmvGridId[i1.vid] in ['a','o','si','li'], 'Unexpected grid id: %s: %s:: %s' % (i1.label,i1.vid, self.cmvGridId[i1.vid])
1115                gflg = {'si':'','li':''}.get( self.cmvGridId[i1.vid], self.cmvGridId[i1.vid] )
1116                rtl = True
1117
1118                if i.uid in expys.exptYears:
1119                  mipsByVar[i1.vid].add( i.mip )
1120                  if rtl:
1121                    for e,grd in expys.exptYears[i.uid]:
1122                        if exptFilter == None or e in exptFilter:
1123                          if grd == 'DEF':
1124                            if gflg == 'o' and not self.gridPolicyDefaultNative:
1125                            ##if gflg == 'o':
1126                              grd1 = '1deg'
1127                            else:
1128                              grd1 = 'native'
1129                          else:
1130                            grd1 = grd
1131                          cc[(i1.vid,e,grd1)].add( expys.exptYears[i.uid][e,grd] )
1132                          if i.uid in self.tsliceDict and e in self.tsliceDict[i.uid]:
1133                            for thisSlice in self.tsliceDict[i.uid][e]:
1134                              ccts[(i1.vid,e)].add( (thisSlice,thisp) )
1135                          else:
1136                            ccts[(i1.vid,e)].add( (None,thisp) )
1137
1138                  else:
1139
1140                   for gf in expys.exptYears[i.uid]:
1141                    for e,grd in expys.exptYears[i.uid][gf]:
1142                      if grd in ["1deg",'2deg'] or gf == gflg:
1143                        if exptFilter == None or e in exptFilter:
1144                          cc[(i1.vid,e,grd)].add( expys.exptYears[i.uid][gf][e,grd] )
1145              else:
1146                print ( 'SKIPPING %s: %s' % (i1.label,i1.vid) )
1147                ss.add( i1.vid )
1148
1149    if self.intersection and type(mip) == type( set() ) and len(mip) > 1:
1150      sint = set( [k for k in mipsByVar if len( mipsByVar[k] ) == len(mip)] )
1151      print ( 'INTERSECTION: %s out of %s variables [%s]' % (len(sint),len(mipsByVar.keys()),str(mip)) )
1152      xxx = [t for t in cc if t[0] not in sint]
1153      for t in xxx:
1154          del cc[t]
1155    if includeYears:
1156      l2 = collections.defaultdict( dict )
1157      l2x = collections.defaultdict( dict )
1158##
1159## this removes lower ranked grids .... but for some groups want different grids for different variable categories
1160##
1161      if self.gridPolicyTopOnly:
1162        for v,e,g in cc:
1163          l2x[(v,e)][g] = max( list( cc[(v,e,g)] ) )
1164        for v,e in l2x:
1165          if len( l2x[(v,e)].keys() ) == 1:
1166             g,val = list( l2x[(v,e)].items() )[0]
1167          else:
1168            kk = gridSorter.sort( l2x[(v,e)].keys() )
1169            gflg = {'si':'','li':''}.get( self.cmvGridId[v], self.cmvGridId[v] )
1170            g = kk[0]
1171            if g not in l2x[(v,e)]:
1172              print ( '%s not found in %s (%s):' % (g,str(l2x[(v,e)].keys()),str(kk)) )
1173            val = l2x[(v,e)][g]
1174               
1175          l2[v][(e,g)] = val
1176      else:
1177        for v,e,g in cc:
1178          l2[v][(e,g)] = max( list( cc[(v,e,g)] ) )
1179
1180      l2ts = collections.defaultdict( dict )
1181      for v in l2:
1182        for e,g in l2[v]:
1183          if (v,e) in ccts:
1184            ccx = collections.defaultdict( set )
1185            for x in ccts[(v,e)]:
1186              ccx[x[0]].add( x[1] )
1187            if len( ccx.keys() ) > 1:
1188              tslp = [ (k,min(ccx[k])) for k in ccx ]
1189              thisTimeSlice = timeSlice( tslp )
1190              rc, ts, msg = thisTimeSlice.sort()
1191              ##rc, ts, msg = sortTimeSlice( tslp )
1192              if rc == 1:
1193                l2ts[v][e] = tuple( list(ts) + [g,] )
1194              elif rc == 2:
1195                try:
1196##(('abrupt5', 'simpleRange', 0, 5), 1), (('abrupt30', 'simpleRange', 121, 150), 1)]
1197                  yl = list( range( ts[0][0][2], ts[0][0][3] + 1) ) + list( range( ts[1][0][2], ts[1][0][3] + 1) )
1198                except:
1199                  print ( 'FAILED TO GENERATE YEARLIST' )
1200                  print ( str((v,e) ) )
1201                  print ( 'range( ts[0][0][2], ts[0][0][3] + 1) + range( ts[1][0][2], ts[1][0][3] + 1)' )
1202                  print ( str(ts) )
1203                  raise
1204### tslab,tsmode,a,b,priority,grid
1205                l2ts[v][e] = ('_union', 'YEARLIST', len(yl), str(yl), ts[1], g )
1206              else:
1207                print ('TIME SLICE MULTIPLE OPTIONS FOR : %s, %s, %s, %s' % (v,e,str(ccts[(v,e)]), msg ) )
1208            else:
1209              a = ccx.keys()[0]
1210              b = min( [x[1] for x in ccts[(v,e)] ] )
1211              if type(a) == type( [] ):
1212                l2ts[v][e] = a + [b,g,]
1213              elif type(a) == type( () ):
1214                l2ts[v][e] = list(a) + [b,g,]
1215              elif a == None:
1216                l2ts[v][e] = [None,b,g]
1217              else:
1218                assert False, 'Bad type for ccts record: %s' % type( a)
1219      return l2, l2ts
1220    else:
1221      l2 = sorted( [i for i in [self.dq.inx.uid[i] for i in ss] if i._h.label != 'remarks'], key=lambda x: x.label )
1222      return l2
1223
1224  def exptFilterList(self,val,option,ret='uid'):
1225    if type( val ) not in [[],()]:
1226      val = [val,]
1227
1228    if option == 'lab':
1229      v0 = val[:]
1230      val = []
1231      mm = []
1232      for v in v0:
1233        if v not in self.exptByLabel:
1234          mm.append( v )
1235        else:
1236          val.append( self.exptByLabel[v] )
1237
1238      assert len(mm) == 0, 'Experiment names not all recognised: %s' % str(mm)
1239
1240    oo = set()
1241    for v in val:
1242      i = self.dq.inx.uid[v]
1243      if i._h.label in ['exptgroup','mip']:
1244        if 'experiment' in self.dq.inx.iref_by_sect[i.uid].a:
1245          for u in self.dq.inx.iref_by_sect[i.uid].a['experiment']:
1246            oo.add( u )
1247      elif i._h.label == 'experiment':
1248            oo.add( i.uid )
1249      else:
1250        print ('WARNING .. skipping request for experiment which links to record of type %s' % i._h.label )
1251    return oo
1252   
1253  def getFreqStrSummary(self,mip,pmax=1):
1254##
1255## get a dictionary keyed on CMORvar uid, containing dictionary keyed on (experiment, grid) with value as number of years.
1256##
1257    if not self.uniqueRequest:
1258      cmv, self.cmvts = self.cmvByMip(mip,pmax=pmax,includeYears=True,exptFilter=self.exptFilter,exptFilterBlack=self.exptFilterBlack)
1259    else:
1260      cmv1, cmvts1 = self.cmvByInvMip(mip,pmax=pmax,includeYears=True,exptFilter=self.exptFilter,exptFilterBlack=self.exptFilterBlack)
1261      cmv2, cmvts2 = self.cmvByMip('TOTAL',pmax=pmax,includeYears=True,exptFilter=self.exptFilter,exptFilterBlack=self.exptFilterBlack)
1262      cmv = self.differenceSelectedCmvDict(  cmv1, cmv2 )
1263
1264    if not self.retainRedundantRank:
1265      len1 = len(cmv)
1266      self.cmvFilter.filterByChoiceRank(cmv=cmv,asDict=True)
1267      len2 = len(cmv)
1268      ##print 'INFO.redundant.0001: length %s --> %s' % (len1,len2)
1269 
1270    self.selectedCmv = cmv
1271    return self.cmvByFreqStr( cmv )
1272
1273  def differenceSelectedCmvDict( self, cmv1, cmv2 ):
1274      """Return the diffence between two dictionaries of cmor variables returned by self.cmvByMip.
1275         The dictionaries contain dictionaries of values. Differences may be subdictionaries not present,
1276         elements of sub-dictionaries not present, or elements of sub-dictionaries present with different values.
1277         A one sided difference is returned."""
1278
1279      cmv = {}
1280      for i in cmv2:
1281        if i not in cmv1:
1282          cmv[i] = cmv2[i]
1283        else:
1284          eei = {}
1285          for t in cmv2[i]:
1286            if t not in cmv1[i]:
1287              eei[t] = cmv2[i][t]
1288            else:
1289              if cmv2[i][t] > cmv1[i][t]:
1290                 eei[t] = cmv2[i][t] - cmv1[i][t]
1291          if len( eei.keys() ) != 0:
1292            cmv[i] = eei
1293      return cmv
1294
1295  def cmvByFreqStr(self,cmv,asDict=True,exptFilter=None,exptFilterBlack=None):
1296    if exptFilter != None:
1297      assert type(exptFilter) == type( set() ), 'Argument exptFilter must be None or a set: %s' % str(exptFilter)
1298    if exptFilterBlack != None:
1299      assert type(exptFilterBlack) == type( set() ), 'Argument exptFilterBlack must be None or a set: %s' % str(exptFilterBlack)
1300      if exptFilter != None:
1301        assert len( exptFilter.difference( exptFilterBlack ) ) > 0, 'If exptFilter and exptFilterBlack are both set, exptFilter must have non-black listed elements' 
1302
1303    cc = collections.defaultdict( list )
1304    for i in cmv:
1305      if asDict:
1306        ii = self.dq.inx.uid[i]
1307        if ii._h.label != 'remarks':
1308         st = self.dq.inx.uid[ ii.stid ]
1309         if st._h.label != 'remarks':
1310          cc0 = collections.defaultdict( float )
1311          cc1 = collections.defaultdict( int )
1312          se = collections.defaultdict( set )
1313          for e,g in cmv[i]:
1314            cc0[g] += cmv[i][(e,g)]
1315            cc1[g] += 1
1316            se[g].add(e)
1317          for g in cc0:
1318            g1 = g
1319            if self.isLatLon[st.spid] != False:
1320              g1 = g
1321              if g1 == 'DEF' and self.isLatLon[st.spid] == 'o':
1322                  if self.gridPolicyDefaultNative:
1323                     g1 = 'native'
1324                  else:
1325                     g1 = '1deg'
1326              elif g == 'native:01':
1327                gflg = {'si':'','li':''}.get( self.cmvGridId[i], self.cmvGridId[i] )
1328                if gflg == 'o' and not self.gridOceanStructured:
1329                  g1 = '1deg'
1330                else:
1331                  g1 = 'native'
1332              elif g1 in ['1deg','2deg','native']:
1333                pass
1334              else:
1335                print ( 'WARNING --- blind default to native: %s' % g )
1336                g1 = 'native'
1337            elif g == 'native:01':
1338                g1 = 'native'
1339
1340            cc[ (st.spid,st.__dict__.get('odims',''),ii.frequency,g1) ].append( (i,cc0[g],cc1[g],se[g]) )
1341
1342      else:
1343        st = self.dq.inx.uid[ i.stid ]
1344        cc[ (st.spid,st.__dict__.get('odims',''),i.frequency) ].append( i.label )
1345
1346    self.thiscmvset = set()
1347    c2 = collections.defaultdict( dict )
1348    sf = set()
1349    if asDict:
1350      for s,o,f,g in cc.keys():
1351        c2[(s,o,g)][f] = cc[ (s,o,f,g) ]
1352        sf.add( f )
1353    else:
1354      for s,o,f in cc.keys():
1355        c2[(s,o)][f] = cc[ (s,o,f) ]
1356        sf.add( f )
1357    lf = sorted( list(sf) )
1358    c3 = collections.defaultdict( dict )
1359
1360    for tt in sorted( c2.keys() ):
1361      if asDict:
1362        s,o,g = tt
1363      else:
1364        s,o = tt
1365        g = 'native'
1366      i = self.dq.inx.uid[ s ]
1367
1368      if asDict:
1369        for f in c2[tt]:
1370            isClim = f.lower().find( 'clim' ) != -1
1371            ny = 0
1372            expts = set()
1373            labs = []
1374            labs = collections.defaultdict( int )
1375            ccx = collections.defaultdict( list )
1376            for cmvi, ny1, ne, eset in c2[tt][f]:
1377              ccx[cmvi].append( (ny1, ne, eset) )
1378            net = 0
1379            for cmvi in ccx:
1380              if len( ccx[cmvi] ) == 1:
1381                 ny1, ne, eset = ccx[cmvi][0]
1382              else:
1383                 ny1, ne, eset = ( 0,0,set() )
1384                 for a,b,s in ccx[cmvi]:
1385                   ny1 += a
1386                   ne += b
1387                   eset = eset.union(  s )
1388             
1389              net += ne
1390              if len(eset) != ne:
1391                print ( 'WARNING: inconsistency in volume estimate ... possible duplication for %s,%s' % (cmvi,f) )
1392              for e in eset:
1393                elab = self.dq.inx.uid[e].label
1394                expts.add(elab)
1395
1396              if exptFilter != None:
1397                expts = exptFilter.intersection( expts )
1398              if exptFilterBlack != None:
1399                expts = expts.difference( exptFilterBlack )
1400
1401              if len(expts) > 0:
1402                lab = self.dq.inx.uid[cmvi].label
1403                self.thiscmvset.add( cmvi )
1404                ny += ny1
1405                labs[cmvi] += ny1
1406            ne = len( expts )
1407            nn = len( labs.keys() )
1408             
1409            if isClim:
1410              ny = net/float(nn)
1411            else:
1412              ny = ny/float(nn)
1413            assert tt[2] in ['native','1deg','2deg','native:01'], 'BAD grid identifier: %s' % str(tt)
1414            c3[tt][f] = (nn,ny,ne, labs,expts)
1415    return (sf,c3)
1416
1417  def getStrSz( self, g, stid=None, s=None, o=None, tt=False, cmv=None ):
1418    assert stid == None or (s==None and o==None), 'Specify either stid or s and o'
1419    assert stid != None or (s!=None and o!=None), 'Specify either stid or s and o'
1420
1421    if stid != None:
1422      st = self.dq.inx.uid[stid]
1423      if st._h.label != 'remarks':
1424        s = st.spid
1425        o = st.__dict__.get( 'odims', '' )
1426      else:
1427        self.strSz[ (stid,g) ] = (False,0)
1428        if tt:
1429          return (self.strSz[ (stid,g) ], None)
1430        else:
1431          return self.strSz[ (stid,g) ]
1432
1433    g1 = g
1434    if g1 == 'DEF':
1435          if self.isLatLon[s] == 'o':
1436             g1 = '1deg'
1437          else:
1438             g1 = 'native'
1439    elif g1 == 'native:01':
1440      assert cmv != None, 'Need a valid cmor variable id  .... '
1441      gflg = {'si':'','li':''}.get( self.cmvGridId[cmv], self.cmvGridId[cmv] )
1442      if gflg == 'o' and not self.gridOceanStructured:
1443                  g1 = '1deg'
1444      else:
1445                  g1 = 'native'
1446    if (s,o,g) not in self.strSz:
1447
1448        if o == '':
1449           sf = 1
1450        elif o in self.odsz:
1451           sf = self.odsz[o]
1452        else:
1453           sf = 5
1454
1455        if type( sf ) == type( () ):
1456           sf = sf[0]
1457
1458        try:
1459          if g1 != 'native' and self.isLatLon[s] != False:
1460            szg = self.szgss[g1][s]
1461          else:
1462            szg = self.szss[s]
1463        except:
1464          print ( 'Failed to get size for: %s, %s, %s' % (g,g1,s ) )
1465          raise
1466
1467        szg = szg * sf
1468        self.strSz[ (s,o,g) ] = (True,szg)
1469
1470    if tt:
1471      return (self.strSz[ (s,o,g) ], (s,o,g1) )
1472    else:
1473      return self.strSz[ (s,o,g) ]
1474
1475  def rvgByMip( self, mip, years=False, includePreset=False, returnLinks=False ):
1476    l1 = self.rqlByMip( mip )
1477    if includePreset:
1478      cc = collections.defaultdict( set )
1479      ss = set()
1480      for i in l1:
1481        if 'requestItem' in self.dq.inx.iref_by_sect[i.uid].a:
1482          prs = set()
1483          for x in self.dq.inx.iref_by_sect[i.uid].a['requestItem']:
1484             prs.add(self.dq.inx.uid[x].preset)
1485
1486          for p in prs:
1487            assert p in [-1,1,2,3], 'Bad preset value'
1488            cc[p].add( i.refid )
1489      ee = {}
1490      for p in cc:
1491        l2 = sorted( [self.dq.inx.uid[i] for i in cc[p]], key=lambda x: x.label )
1492        ee[p] = l2
1493      if returnLinks:
1494        return (l1,ee)
1495      else:
1496        return ee
1497    else:
1498      ss = set( [i.refid for i in l1] )
1499      l2 = sorted( [self.dq.inx.uid[i] for i in ss], key=lambda x: x.label )
1500      if returnLinks:
1501        return (l1,l2)
1502      else:
1503        return l2
1504
1505  def volByMip2( self, mip, pmax=2, intersection=False, adsCount=False, exptid=None,makeTabs=False, odir='xls'):
1506      vs = volsum.vsum( self, odsz, npy )
1507      rqf = 'dummy'
1508      vsmode='short'
1509      if makeTabs:
1510        mlab = misc_utils.setMlab( mip )
1511        rqf = '%s/requestVol_%s_%s_%s' % (odir,mlab,self.tierMax,pmax)
1512        vsmode='full'
1513      vs.run( mip, rqf, pmax=pmax, doxlsx=makeTabs ) 
1514      vs.anal(olab='dummy', doUnique=False, mode=vsmode, makeTabs=makeTabs)
1515      self.vf = vs.res['vf'].copy()
1516      for f in sorted( vs.res['vf'].keys() ):
1517           mlg.prnt ( 'Frequency: %s: %s' % (f, vs.res['vf'][f]*2.*1.e-12 ) )
1518      ttl = sum( [x for k,x in vs.res['vu'].items()] )
1519      self.res = vs.res
1520      self.indexedVol = collections.defaultdict( dict )
1521      for u in vs.res['vu']:
1522        cmv = self.dq.inx.uid[u]
1523        self.indexedVol[cmv.frequency]['%s.%s' % (cmv.mipTable,cmv.label)] = vs.res['vu'][u]
1524      return ttl
1525
1526  def volByMip( self, mip, pmax=2, intersection=False, adsCount=False, exptid=None):
1527
1528    l1 = self.rqiByMip( mip )
1529     
1530    #### The set of experiments/experiment groups:
1531    if exptid == None:
1532      exps = self.experiments
1533    elif type( exptid ) == type(''):
1534      exps = set( [exptid,] )
1535    else:
1536      assert type( exptid ) == type( set() ),'exptid arg to volByMip must be None, string or set: %s' % type( exptid )
1537      exps = exptid
1538   
1539    self.volByE = {}
1540    vtot = 0
1541    cc = collections.defaultdict( col_count )
1542    self.allVars = set()
1543    for e in exps:
1544      expts = self.esid_to_exptList(e,deref=True,full=False)
1545      if expts not in  [None,[]]:
1546        for ei in expts:
1547          self.volByE[ei.label] = self.volByExpt( l1, ei.uid, pmax=pmax, cc=cc, intersection=intersection, adsCount=adsCount )
1548          vtot += self.volByE[ei.label][0]
1549        self.allVars = self.allVars.union( self.vars )
1550    self.indexedVol = cc
1551
1552    return vtot
1553
1554  def listIndexDual(self, ll, a1, a2, acount=None, alist=None, cdict=None, cc=None ):
1555    do_count = acount != None
1556    do_list = alist != None
1557    assert not (do_count and do_list), 'It is an error to request both list and count'
1558    if not (do_count or do_list):
1559      acount = '__number__'
1560      do_count = True
1561
1562    if cc == None:
1563      if do_count:
1564        cc = collections.defaultdict( col_count )
1565      elif do_list:
1566        cc = collections.defaultdict( col_list )
1567
1568    if do_count:
1569      for l in ll:
1570        if cdict != None:
1571          v = cdict[l.uid]
1572        elif acount == '__number__':
1573          v = 1
1574        else:
1575          v = l.__dict__[acount]
1576
1577        cc[ l.__dict__[a1] ].a[ l.__dict__[a2] ] += v
1578    elif do_list:
1579      for l in ll:
1580        if cdict != None:
1581          v = cdict[l.uid]
1582        elif alist == '__item__':
1583          v = l
1584        else:
1585          v = l.__dict__[alist]
1586        cc[ l.__dict__[a1] ].a[ l.__dict__[a2] ].append( v )
1587
1588    od = {}
1589    for k in cc.keys():
1590      d2 = {}
1591      for k2 in cc[k].a.keys():
1592        d2[k2] = cc[k].a[k2]
1593      od[k] = d2
1594    return od
1595
1596class dreqUI(object):
1597  """Data Request Command line.
1598-------------------------
1599      -v : print version and exit;
1600      --unitTest : run some simple tests;
1601      -m <mip>:  MIP of list of MIPs (comma separated; for objective selection see note [1] below);
1602      -l <options>: List for options:
1603              o: objectives
1604              e: experiments
1605      -q <options>: List information about the schema:
1606              s: sections
1607              <section>: attributes for a section
1608              <section:attribute>: definition of an attribute.
1609      -h :       help: print help text;
1610      -e <expt>: experiment;
1611      -t <tier> maxmum tier;
1612      -p <priority>  maximum priority;
1613      --xls : Create Excel file with requested variables;
1614      --sf : Print summary of variable count by structure and frequency [default];
1615      --legacy : Use legacy approach to volume estimation (deprecated);
1616      --xfr : Output variable lists in sheets organised by frequency and realm instead of by MIP table;
1617      --SF : Print summary of variable count by structure and frequency for all MIPs;
1618      --grdpol <native|1deg> :  policy for default grid, if MIPs have not expressed a preference;
1619      --grdforce <native|1deg> :  force a specific grid option, independent of individual preferences;
1620      --ogrdunstr : provide volume estimates for unstructured ocean grid (interpolation requirements of OMIP data are different in this case);
1621      --omitCmip : omit the CMIP core data request (included by default);
1622      --allgrd :  When a variable is requested on multiple grids, archive all grids requested (default: only the finest resolution);
1623      --unique :  List only variables which are requested uniquely by this MIP, for at least one experiment;
1624      --esm :  include ESM experiments (default is to omit esm-hist etc from volume estimates);
1625      --txt : Create text file with requested variables;
1626      --mcfg : Model configuration: 7 integers, comma separated, 'nho','nlo','nha','nla','nlas','nls','nh1'
1627                 default: 259200,60,64800,40,20,5,100
1628      --txtOpts : options for content of text file: (v|c)[(+|-)att1[,att2[...]]]
1629      --xlsDir <directory> : Directory in which to place variable listing [xls];
1630      --printLinesMax <n>  : Maximum number of lines to be printed (default 20)
1631      --printVars    : If present, a summary of the variables (see --printLinesMax) fitting the selection options will be printed
1632      --intersection : Analyse the intersection of requests rather than union.
1633
1634NOTES
1635-----
1636[1] A set of objectives within a MIP can be specified in the command line. The extended syntax of the "-m" argument is:
1637-m <mip>[:objective[.obj2[.obj3 ...]]][,<mip2]...]
1638
1639e.g.
1640drq -m HighResMIP:Ocean.DiurnalCycle
1641"""
1642  def __init__(self,args):
1643    self.adict = {}
1644    self.knownargs = {'-m':('m',True), '-p':('p',True), '-e':('e',True), '-t':('t',True), \
1645                      '-h':('h',False), '--printLinesMax':('plm',True), \
1646                      '-l':('l',True),
1647                      '-q':('q',True),
1648                      '--printVars':('vars',False), '--intersection':('intersection',False), \
1649                      '--count':('count',False), \
1650                      '--txt':('txt',False), \
1651                      '--sf':('sf',False), \
1652                      '--legacy':('legacy',False), \
1653                      '--xfr':('xfr',False), \
1654                      '--SF':('SF',False), \
1655                      '--esm':('esm',False), \
1656                      '--grdpol':('grdpol',True), \
1657                      '--ogrdunstr':('ogrdunstr',False), \
1658                      '--grdforce':('grdforce',True), \
1659                      '--omitCmip':('omitcmip',False), \
1660                      '--allgrd':('allgrd',False), \
1661                      '--unique':('unique',False), \
1662                      '--mcfg':('mcfg',True), \
1663                      '--txtOpts':('txtOpts',True), \
1664                      '--xlsDir':('xlsdir',True), '--xls':('xls',False) \
1665                       } 
1666    aa = args[:]
1667    notKnownArgs = []
1668    while len(aa) > 0:
1669      a = aa.pop(0)
1670      if a in self.knownargs:
1671        b = self.knownargs[a][0]
1672        if self.knownargs[a][1]:
1673          v = aa.pop(0)
1674          self.adict[b] = v
1675        else:
1676          self.adict[b] = True
1677      else:
1678        notKnownArgs.append(a)
1679
1680    assert self.checkArgs( notKnownArgs ), 'FATAL ERROR 001: Arguments not recognised: %s' % (str(notKnownArgs) )
1681
1682    if 'm' in self.adict:
1683      if self.adict['m'] == '_all_':
1684        pass
1685      elif self.adict['m'].find( ':' ) != -1:
1686        ee = {}
1687        for i in self.adict['m'].split(','):
1688          bits =  i.split( ':' )
1689          if len( bits ) == 1:
1690             ee[bits[0]] = []
1691          else:
1692             assert len(bits) == 2, 'Cannot parse %s' % self.adict['m']
1693             ee[bits[0]] = bits[1].split( '.' )
1694        self.adict['m'] = ee
1695      else:
1696        self.adict['m'] = set(self.adict['m'].split(',') )
1697        if 'omitcmip' not in self.adict and 'CMIP' not in self.adict['m']:
1698          self.adict['m'].add( 'CMIP' )
1699
1700    if 'grdpol' in self.adict:
1701      assert self.adict['grdpol'] in ['native','1deg'], 'Grid policy argument --grdpol must be native or 1deg : %s' % self.adict['grdpol']
1702
1703    if 'grdforce' in self.adict:
1704      assert self.adict['grdforce'] in ['native','1deg'], 'Grid policy argument --grdforce must be native or 1deg : %s' % self.adict['grdforce']
1705
1706    integerArgs = set( ['p','t','plm'] )
1707    for i in integerArgs.intersection( self.adict ):
1708      self.adict[i] = int( self.adict[i] )
1709
1710    self.intersection = self.adict.get( 'intersection', False )
1711
1712 
1713  def checkArgs( self, notKnownArgs ):
1714    if len( notKnownArgs ) == 0:
1715      return True
1716    print ('--------------------------------------')
1717    print ('------------  %s Arguments Not Recognised ------------' % len(notKnownArgs) )
1718    k = 0
1719    for x in notKnownArgs:
1720      k += 1
1721      if x[1:] in self.knownargs:
1722        print ( '%s PERHAPS %s instead of %s' % (k, x[1:],x) )
1723      elif '-%s' % x in self.knownargs:
1724        print ( '%s PERHAPS -%s instead of %s' % (k, x,x) )
1725      elif x[0] == '\xe2':
1726        print ( '%s POSSIBLY -- (double hyphen) instead of long dash in %s' % (k, x) )
1727    print ('--------------------------------------')
1728
1729    return len( notKnownArgs ) == 0
1730     
1731  def run(self, dq=None):
1732    if 'h' in self.adict:
1733      mlg.prnt ( self.__doc__ )
1734      return
1735
1736    if 'q' in self.adict:
1737      if dq == None:
1738        dq = dreq.loadDreq(configOnly=True)
1739      s = self.adict['q']
1740      if self.adict['q'] == 's':
1741        ss = sorted( [(i.title,i.label) for i in dq.coll['__sect__'].items] )
1742        for s in ss:
1743          mlg.prnt( '%16s:: %s' % (s[1],s[0]) )
1744      else:
1745        ss = [i.label for i in dq.coll['__sect__'].items]
1746        if s.find( ':' ) != -1:
1747          s,a = s.split( ':' )
1748        else:
1749          a = None
1750        if s not in ss:
1751          mlg.prnt( 'ERROR: option must be a section; use "-q s" to list sections' )
1752        elif a == None:
1753          x = [i for i in dq.coll['__sect__'].items if i.label == s]
1754          s1 = [i for i in  dq.coll['__main__'].items if 'ATTRIBUTE::%s' % s in i.uid]
1755          mlg.prnt( x[0].title )
1756          mlg.prnt( ' '.join( sorted  ([i.label for i in s1] ) ))
1757        else:
1758          x = [i for i in dq.coll['__main__'].items if i.uid == 'ATTRIBUTE::%s.%s' % (s,a) ]
1759          if len(x) == 0:
1760            mlg.prnt( 'ERROR: attribute not found' )
1761            s1 = [i for i in  dq.coll['__main__'].items if 'ATTRIBUTE::%s' % s in i.uid]
1762            mlg.prnt( 'ATTRIBUTES: ' + ' '.join( sorted  ([i.label for i in s1] ) ))
1763          else:
1764            mlg.prnt( 'Section %s, attribute %s' % (s,a) )
1765            mlg.prnt( x[0].title )
1766            mlg.prnt( x[0].description )
1767      return
1768
1769    if not ('m' in self.adict or 'SF' in self.adict):
1770      mlg.prnt ( 'Current version requires -m or --SF argument'  )
1771      mlg.prnt ( self.__doc__ )
1772      sys.exit(0)
1773
1774    if dq == None:
1775      self.dq = dreq.loadDreq()
1776    else:
1777      self.dq = dq
1778
1779    if 'l' in self.adict:
1780      self.printList()
1781      return
1782
1783    if 'mcfg' in self.adict:
1784      ll = string.split( self.adict['mcfg'], ',' )
1785      assert len(ll) == 7, 'Length of model configuration argument must be 7 comma separated integers: %s' %  self.adict['mcfg']
1786      lli = [ int(x) for x in ll]
1787
1788    self.sc = dreqQuery( dq=self.dq )
1789    self.sc.intersection = self.intersection
1790
1791    if 'grdforce' in self.adict:
1792      self.sc.gridPolicyForce = self.adict['grdforce']
1793    if 'grdpol' in self.adict:
1794      self.sc.gridPolicyDefaultNative = self.adict['grdpol'] == 'native'
1795      print ( 'SETTING grid policy: %s' % self.sc.gridPolicyDefaultNative )
1796    if 'allgrd' in self.adict:
1797      self.sc.gridPolicyTopOnly = False
1798      print ( 'SETTING grid policy for multiple preferred grids: %s' % self.sc.gridPolicyTopOnly )
1799    if 'unique' in self.adict:
1800      self.sc.uniqueRequest = True
1801    self.sc.gridOceanStructured = not self.adict.get( 'ogrdunstr', False )
1802
1803    if 'mcfg' in self.adict:
1804      self.sc.setMcfg( lli )
1805
1806    tierMax = self.adict.get( 't', 1 )
1807    self.sc.setTierMax(  tierMax )
1808    pmax = self.adict.get( 'p', 1 )
1809
1810    makeXls = self.adict.get( 'xls', False )
1811    makeTxt = self.adict.get( 'txt', False )
1812    ##doSf = 'SF' in self.adict or 'sf' in self.adict
1813    doSf = 'legacy' not in self.adict
1814    if doSf:
1815      self.adict['sf'] = True
1816    assert not ('legacy' in self.adict and 'sf' in self.adict), "Conflicting command line argument, 'legacy' and 'sf': use only one of these"
1817    if makeXls or makeTxt or doSf:
1818      xlsOdir = self.adict.get( 'xlsdir', 'xls' )
1819      self.sc.checkDir( xlsOdir, 'xls files' )
1820
1821    tabByFreqRealm = self.adict.get( 'xfr', False )
1822    if 'SF' in self.adict:
1823      self.sc.gridPolicyDefaultNative = True
1824      vs = volsum.vsum( self.sc, odsz, npy, odir=xlsOdir, tabByFreqRealm=tabByFreqRealm )
1825      vs.analAll(pmax)
1826
1827      self.sc.gridPolicyDefaultNative = False
1828      vs = volsum.vsum( self.sc, odsz, npy, odir=xlsOdir, tabByFreqRealm=tabByFreqRealm )
1829      vs.analAll(pmax)
1830
1831      self.sc.setTierMax( 3 )
1832      vs = volsum.vsum( self.sc, odsz, npy, odir=xlsOdir, tabByFreqRealm=tabByFreqRealm )
1833      vs.analAll(3)
1834      return
1835
1836    ok = True
1837    if self.adict['m'] == '_all_':
1838      self.adict['m'] = set(self.sc.mips )
1839      mlab = 'TOTAL'
1840    else:
1841      for i in self.adict['m']:
1842        if i not in self.sc.mips:
1843          ok = False
1844          tt = misc_utils.mdiff().diff( i,self.sc.mips )
1845          assert not tt[0], 'Bad logic ... unexpected return from misc_utils.mdiff'
1846          ##cms = difflib.get_close_matches(i,self.sc.mips )
1847          if tt[1] == 0:
1848            mlg.prnt ( 'NOT FOUND: %s' % i )
1849          else:
1850            msg = []
1851            for ix in tt[2]:
1852              msg.append( '%s [%4.1f]' % (','.join( ix[1] ),ix[0]) ) 
1853
1854            mlg.prnt( '----------------------------------------' )
1855            if tt[1] == 1 and len(tt[2][0][1]) == 1:
1856              mlg.prnt ( 'NOT FOUND: %s:  SUGGESTION: %s' % (i,msg[0]) )
1857            else:
1858              mlg.prnt ( 'NOT FOUND: %s:  SUGGESTIONS: %s' % (i,'; '.join( msg ) ) ) 
1859            mlg.prnt( '----------------------------------------' )
1860      mlab = misc_utils.setMlab( self.adict['m'] )
1861    assert ok,'Available MIPs: %s' % str(self.sc.mips)
1862
1863    eid = None
1864    ex = None
1865    if 'e' in self.adict:
1866      ex = self.adict['e']
1867      if ex in self.sc.mipsp:
1868        eid = set( self.dq.inx.iref_by_sect[ex].a['experiment'] )
1869        self.sc.exptFilter = eid
1870      elif self.adict['e'] in self.sc.exptByLabel:
1871        eid = self.sc.exptByLabel[ self.adict['e'] ]
1872        self.sc.exptFilter = set( [eid,] )
1873      else:
1874        ns = 0
1875        md =  misc_utils.mdiff()
1876        ttm = md.diff( self.adict['e'],self.sc.mipsp )
1877        tte = md.diff( self.adict['e'],self.sc.exptByLabel.keys() )
1878        if ttm[1] > 0 and tte[1] == 0 or (ttm[2][0][0] > 0.6*tte[2][0][0]):
1879          oo =  md.prntprep( self.adict['e'], ttm )
1880          for l in oo:
1881            mlg.prnt( l )
1882        if tte[1] > 0 and ttm[1] == 0 or (tte[2][0][0] > 0.6*ttm[2][0][0]):
1883          oo =  md.prntprep( self.adict['e'], tte )
1884          for l in oo:
1885            mlg.prnt( l )
1886        assert False, 'Experiment/MIP %s not found' % self.adict['e']
1887
1888    if not self.adict.get( 'esm', False ):
1889      ss = set()
1890      for e in ['esm-hist','esm-hist-ext','esm-piControl','piControl-spinup','esm-piControl-spinup']:
1891        ss.add( self.sc.exptByLabel[ e ] )
1892      self.sc.exptFilterBlack = ss
1893    makeXls = self.adict.get( 'xls', False )
1894
1895    if 'sf' in self.adict:
1896      vs = volsum.vsum( self.sc, odsz, npy, odir=xlsOdir, tabByFreqRealm=tabByFreqRealm )
1897      vs.run( self.adict['m'], '%s/requestVol_%s_%s_%s' % (xlsOdir,mlab,tierMax,pmax), pmax=pmax, doxlsx=makeXls ) 
1898      totalOnly = False
1899      if len( self.adict['m'] ) == 1 or totalOnly:
1900        if makeXls:
1901          vsmode='full'
1902        else:
1903          vsmode='short'
1904        vs.anal(olab=mlab,doUnique=False, mode=vsmode, makeTabs=makeXls)
1905        for f in sorted( vs.res['vf'].keys() ):
1906           mlg.prnt ( 'Frequency: %s: %s' % (f, vs.res['vf'][f]*2.*1.e-12 ) )
1907        ttl = sum( [x for k,x in vs.res['vu'].items()] )*2.*1.e-12
1908        mlg.prnt( 'TOTAL volume: %8.2fTb' % ttl )
1909        self.printListCc(vs.res['vu'])
1910        return
1911     
1912      mips = self.adict['m']
1913      if type(mips) in [type(set()),type(dict())]:
1914          mips = self.adict['m'].copy()
1915          if len(mips) > 1:
1916            if type(mips) == type(set()):
1917               mips.add( '*TOTAL' )
1918            else:
1919               mips['*TOTAL'] = ''
1920
1921      vs.analAll(pmax,mips=mips,html=False,makeTabs=makeXls)
1922      thisd = {}
1923      for m in sorted( self.adict['m'] ) + ['*TOTAL',]:
1924        for f in sorted( vs.rres[m].keys() ):
1925           mlg.prnt ( '%s:: Frequency: %s: %s' % (m,f, vs.rres[m][f]*2.*1.e-12 ) )
1926      for m in sorted( self.adict['m'] ) + ['*TOTAL',]:
1927        thisd[m] = sum( [x for k,x in vs.rres[m].items()] )
1928        mlg.prnt( '%s:: TOTAL volume: %8.2fTb' % (m, thisd[m]*2.*1.e-12 )  )
1929      self.printListCc(vs.rresu['*TOTAL'])
1930      return
1931
1932    adsCount = self.adict.get( 'count', False )
1933
1934    self.getVolByMip(pmax,eid,adsCount)
1935    makeTxt = self.adict.get( 'txt', False )
1936    if makeXls or makeTxt:
1937      mips = self.adict['m']
1938
1939      if 'txtOpts' in self.adict:
1940        if self.adict['txtOpts'][0] == 'v':
1941          txtOpts = NT_txtopts( 'var' )
1942        else:
1943          txtOpts = NT_txtopts( 'cmv' )
1944      else:
1945        txtOpts=None
1946
1947      self.sc.xlsByMipExpt(mips,eid,pmax,odir=xlsOdir,xls=makeXls,txt=makeTxt,txtOpts=txtOpts)
1948
1949  def printListCc(self,cc):
1950    if self.adict.get( 'vars', False ):
1951      if python2:
1952            vl = sorted( cc.keys(), cmp=cmpd(cc).cmp, reverse=True )
1953      else:
1954            vl = sorted( cc.keys(), key=lambda x: cc[x], reverse=True )
1955      printLinesMax = self.adict.get( 'plm', 20 )
1956      if printLinesMax > 0:
1957        mx = min( [printLinesMax,len(vl)] )
1958      else:
1959        mx = len(vl)
1960
1961      for k in vl[:mx]:
1962            cmv = self.dq.inx.uid[k]
1963            print ('%s.%s::   %sTb' % (cmv.mipTable, cmv.label, cc[k]*2.*1.e-12) )
1964
1965  def printList(self):
1966    mips = self.adict['m']
1967    ee = {}
1968    for i in self.dq.coll['mip'].items:
1969      if i.label in mips:
1970        ee[i.label] = i
1971    if self.adict['l'] in ['o','e']:
1972      targ = {'o':'objective', 'e':'experiment' }[self.adict['l']]
1973      for k in sorted( ee.keys() ):
1974        if targ in self.dq.inx.iref_by_sect[ee[k].uid].a:
1975          for u in self.dq.inx.iref_by_sect[ee[k].uid].a[targ]:
1976            print ( '%s: %s' % (ee[k].label, self.dq.inx.uid[u].label) )
1977    else:
1978      print ('list objective *%s* not recognised (should be e or o)' % self.adict['l'] )
1979     
1980  def getVolByMip(self,pmax,eid,adsCount):
1981
1982    v0 = self.sc.volByMip( self.adict['m'], pmax=pmax, intersection=self.intersection, adsCount=adsCount, exptid=eid )
1983    mlg.prnt ( 'getVolByMip: %s [%s]' % (v0,misc_utils.vfmt(v0*2.)) )
1984    cc = collections.defaultdict( int )
1985    for e in self.sc.volByE:
1986      for v in self.sc.volByE[e][2]:
1987          cc[v] += self.sc.volByE[e][2][v]
1988    x = 0
1989    for v in cc:
1990      x += cc[v]
1991   
1992    if python2:
1993      vl = sorted( cc.keys(), cmp=cmpd(cc).cmp, reverse=True )
1994    else:
1995      vl = sorted( cc.keys(), key=lambda x: cc[x], reverse=True )
1996    if self.adict.get( 'vars', False ):
1997      printLinesMax = self.adict.get( 'plm', 20 )
1998      if printLinesMax > 0:
1999        mx = min( [printLinesMax,len(vl)] )
2000      else:
2001        mx = len(vl)
2002
2003      for v in vl[:mx]:
2004        mlg.prnt ( '%s.%s: %s' % (self.dq.inx.uid[v].mipTable,self.dq.inx.uid[v].label, misc_utils.vfmt( cc[v]*2. ) ) )
2005      if mx < len(vl):
2006        mlg.prnt ( '%s variables not listed (use --printLinesMax to print more)' % (len(vl)-mx) )
2007
Note: See TracBrowser for help on using the repository browser.