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

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

release cand

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