source: CCCC/trunk/ceda_cc/extractMipInfo.py @ 241

Subversion URL: http://proj.badc.rl.ac.uk/svn/exarch/CCCC/trunk/ceda_cc/extractMipInfo.py@241
Revision 241, 20.1 KB checked in by mjuckes, 5 years ago (diff)

added associated expt

Line 
1
2import collections, glob, string, re, json
3from fcc_utils2 import mipTableScan, snlist, tupsort
4from config_c4 import CC_CONFIG_DIR
5
6def uniquify( ll ):
7  ll.sort()
8  l0 = [ll[0],]
9  for l in ll[1:]:
10    if l != l0[-1]:
11      l0.append(l)
12  return l0
13 
14
15ms = mipTableScan()
16snc = snlist()
17
18snl, snla = snc.gen_sn_list( )
19NT_mip = collections.namedtuple( 'mip',['label','dir','pattern'] )
20NT_canvari = collections.namedtuple( 'canonicalVariation',['conditions','text', 'ref'] )
21vlist = [
22('uas',
23"eastward_wind",
24'Eastward Near-Surface Wind Speed',
25'Eastward Near-Surface Wind',
26'WRONG LONG NAME (*speed* included)'),
27('vas',
28"northward_wind",
29'Northward Near-Surface Wind Speed',
30'Northward Near-Surface Wind',
31'WRONG LONG NAME (*speed* included)'),
32("snc",
33"surface_snow_area_fraction",
34"Snow Area Fraction",
35"Surface Snow Area Fraction",
36"WRONG LONG NAME (*surface* omitted)"),
37("prsn",
38"snowfall_flux", 
39"Solid Precipitation",
40"Snowfall Flux",
41"WRONG LONG NAME"),
42("tntscpbl",
43"tendency_of_air_temperature_due_to_stratiform_cloud_and_precipitation_and_boundary_layer_mixing",
44"Tendency of Air Temperature due to Stratiform Cloud Condensation and Evaporation",
45"Tendency of Air Temperature Due to Stratiform Cloud and Precipitation and Boundary Layer Mixing",
46'WRONG LONG NAME'),
47("tas",
48"air_temperature",
49"Air Temperature",
50"Near-Surface Air Temperature",
51'WRONG LONG NAME'),
52("cfadDbze94",
53"histogram_of_equivalent_reflectivity_factor_over_height_above_reference_ellipsoid",
54"CloudSat Radar Reflectivity CFAD",
55"CloudSat Radar Reflectivity",
56"INCONSISTENT LONG NAME"),
57("cfadLidarsr532",
58"histogram_of_backscattering_ratio_over_height_above_reference_ellipsoid",
59"CALIPSO Scattering Ratio CFAD",
60"CALIPSO Scattering Ratio",
61"INCONSISTENT LONG NAME"),
62("cl",
63"cloud_area_fraction_in_atmosphere_layer",
64"Cloud Area Fraction",
65"Cloud Area Fraction in Atmosphere Layer",
66"INCONSISTENT LONG NAME"),
67("clcalipso",
68"cloud_area_fraction_in_atmosphere_layer",
69"CALIPSO Cloud Fraction",
70"CALIPSO Cloud Area Fraction",
71'WRONG LONG NAME'),
72("cltisccp",
73"cloud_area_fraction",
74"ISCCP Total Total Cloud Fraction",
75"ISCCP Total Cloud Fraction",
76'WRONG LONG NAME'),
77("reffclwc",
78"effective_radius_of_convective_cloud_liquid_water_particle",
79"Convective Cloud Droplet Effective Radius",
80"Hydrometeor Effective Radius of Convective Cloud Liquid Water",
81'INCONSISTENT LONG NAMES'),
82("reffclws",
83"effective_radius_of_stratiform_cloud_liquid_water_particle",
84'Stratiform Cloud Droplet Effective Radius',
85'Hydrometeor Effective Radius of Stratiform Cloud Liquid Water',
86'INCONSISTENT LONG NAMES' ) ]
87
88
89class helper:
90
91  def __init__(self):
92    self.applycv = True
93    self.re1 = re.compile( '"(.*)"=="(.*)"' )
94
95    self.cmip5Tables= ['CMIP5_3hr', 'CMIP5_6hrPlev', 'CMIP5_Amon', 'CMIP5_cfDay', 'CMIP5_cfOff', 'CMIP5_day', 'CMIP5_grids', 'CMIP5_Lmon', 'CMIP5_OImon', 'CMIP5_Oyr',
96  'CMIP5_6hrLev', 'CMIP5_aero', ' CMIP5_cf3hr', 'CMIP5_cfMon', 'CMIP5_cfSites', 'CMIP5_fx', 'CMIP5_LImon', 'CMIP5_Oclim', 'CMIP5_Omon' ]
97    self.cmip5DefPoint = ['CMIP5_3hr', 'CMIP5_6hrPlev', 'CMIP5_cfOff', 'CMIP5_6hrLev', ' CMIP5_cf3hr', 'CMIP5_cfSites' ]
98
99    self.canonvar = [ NT_canvari( (('table','CMIP5_3hr'),), 'This is sampled synoptically.', '' ),
100                      NT_canvari( (), 'The flux is computed as the mass divided by the area of the grid cell.', 'This is calculated as the convective mass flux divided by the area of the whole grid cell (not just the area of the cloud).' ),
101            ]
102
103    self.canonvar = []
104    for l in open( 'config/canonicalVariations.txt' ).readlines():
105      if l[0] != '#':
106        ix = l.index(':')
107        s = string.strip( l[ix:] )
108        r = self.re1.findall( s )
109        assert len(r) == 1, 'Cannot parse: %s' % s
110        self.canonvar.append( NT_canvari( (), r[0][0], r[0][1] ) )
111
112  def match(self,a,b):
113      if type(a) == type( 'X' ) and type(b) == type( 'X' ):
114        a0,b0 = map( lambda x: string.replace(x, '__ABSENT__',''), [a,b] )
115        return string.strip( string.replace(a0, '  ', ' '), '"') == string.strip( string.replace(b0, '  ', ' '), '"')
116      else:
117        return a == b
118
119  def checkCond( self, table, var, conditions ):
120    val = True
121    for ck, cv in conditions:
122      if ck == 'table':
123        val &= table == cv
124      elif ck == 'var':
125        val &= var == cv
126
127    return val
128       
129     
130
131class snsub:
132
133  def __init__(self):
134    pass
135
136  def isFalseSn(self,var,sn):
137    if sn == 'mole_concentration_of_molecular_oxygen_in_sea_water':
138      return (True,'mole_concentration_of_dissolved_molecular_oxygen_in_sea_water', 'INVALID STANDARD NAME')
139    elif var == 'rldscs' and sn == 'downwelling_longwave_flux_in_air_assuming_clear_sky':
140      return (True,'surface_downwelling_longwave_flux_in_air_assuming_clear_sky','WRONG STANDARD NAME (should be surface)' )
141    elif var == 'clisccp' and sn == 'cloud_area_fraction_in_atmosphere_layer':
142      return (True, 'isccp_cloud_area_fraction', 'WRONG STANDARD NAME (should be isccp)' )
143    return (False,'no match','')
144
145  def isFalseLn(self,var,ln):
146    for tt in vlist:
147       if var == tt[0] and ln == tt[2]:
148         return (True, tt[3], tt[4] )
149    return (False,'no match','')
150
151snsubber = snsub()
152
153
154cmip5_ignore = ['pfull','phalf','depth','depth_c','eta','nsigma','vertices_latitude','vertices_longitude','ztop','ptop','p0','z1','z2','href','k_c','a','a_bnds','ap','ap_bnds','b','b_bnds','sigma','sigma_bnds','zlev','zlev_bnds','zfull','zhalf']
155
156class mipCo:
157
158  def __init__(self,mips,helper=None):
159    self.vl0 = []
160    self.al0 = []
161    self.tl = []
162    self.dl = []
163    self.td = {}
164    self.dd = {}
165    self.helper = helper
166    for mip in mips:
167      self._scan(mip)
168
169  def _scan(self,mip):
170   
171 ## dl = glob.glob( '%s%s' % (mip.dir,mip.pattern) )
172    dl = glob.glob( '%s/%s%s' % (CC_CONFIG_DIR, mip.dir,mip.pattern) )
173    dl.sort()
174    for d in dl:
175     if d[-5:] != 'grids':
176      tab = string.split( d, '/')[-1]
177      isoceanic = tab[:7] == "CMIP5_O"
178      l2 = ms.scan_table( open( d ).readlines(), None, asDict=True, lax=True, tag="x", warn=True)
179      l2k = []
180      usedDims = []
181      for k in l2.keys():
182        if k not in cmip5_ignore:
183          l2k.append(k)
184          if l2[k][0] != 'scalar':
185            usedDims += l2[k][0]
186      l2k.sort()
187      usedDims.sort()
188      usedDims = uniquify( usedDims )
189     
190      ##self.al0 += ms.adict.keys()
191      self.vl0 += l2k
192      self.tl.append( [tab,l2, l2k,isoceanic] )
193      self.td[tab] = l2
194      ##self.dd[tab] = ms.adict.copy()
195      self.dd[tab] = {}
196      for k in ms.adict.keys():
197        if k not in usedDims:
198          print "WARNING: axis %s declared and not used in table %s" % (k,tab)
199      for u in usedDims:
200        if ms.adict.has_key(u):
201           self.dd[tab][u] = ms.adict[u]
202        else:
203           print 'USED DIMENSION %s not in table %s' % (u,tab)
204      ##self.dl.append( [tab, ms.adict.copy()] )
205      self.dl.append( [tab, self.dd[tab].copy() ] )
206      self.al0 += self.dd[tab].keys()
207
208    self.vl0.sort()
209    self.vl = []
210    self.vl.append( self.vl0[0] )
211    self.vdict = { self.vl[0]:[] }
212    for v in self.vl0[1:]:
213      if v != self.vl[-1]:
214        self.vl.append(v)
215        self.vdict[v] = []
216    self.al0.sort()
217    self.al = [self.al0[0],]
218    self.adict = { self.al[0]:[] }
219    for v in self.al0[1:]:
220      if v != self.al[-1]:
221        self.al.append(v)
222        self.adict[v] = []
223
224    for t in self.tl:
225      for k in t[2]:
226        self.vdict[k].append(t[0])
227
228## create list of tables for each dimension.
229    for a in self.dl:
230      for k in a[1].keys():
231        self.adict[k].append(a[0])
232
233    self.dims = self.adict.keys()
234    self.dims.sort()
235    self.vars = self.vdict.keys()
236    self.vars.sort()
237    ##for v in self.vars:
238      ##l = self.vdict[v]
239      ##l.sort()
240##  print v, l, td[l[0]][v][1].get('standard_name','__NO_STANDARD_NAME__')
241
242class runcheck1:
243  def __init__( self, m, thisatts, isAxes=False):
244    self.errors = []
245    if not isAxes:
246      vars = m.vars
247      vdict = m.vdict
248      td = m.td
249      ix = 1
250      xxx = ''
251    else:
252      vars = m.dims
253      vdict = m.adict
254      td = m.dd
255      ix = 0
256      xxx = 'dim: '
257    vd2 = {}
258    for v in vars:
259     l = vdict[v]
260     l.sort()
261     if len(l) > 1:
262       for att in thisatts:
263       ##for att in ['standard_name','units']:
264         if att == '__dimensions__':
265           atl = map( lambda x: string.join( td[x][v][0] ), l )
266         else:
267           atl = map( lambda x: td[x][v][ix].get(att,'__ABSENT__'), l )
268         atl.sort()
269         av = [atl[0],]
270         for a in atl[1:]:
271           if a != av[-1]:
272             av.append(a)
273         if att == 'standard_name':
274           for a in av:
275             if a not in snl and a not in snla:
276               print xxx,"INVALID STANDARD NAME: ",a,v
277               self.errors.append( "INVALID STANDARD NAME: %s [%s]" % (a,v) )
278         if len(av) > 1:
279           ee = {}
280   
281           for a in [True,False]:
282             ee[a] = []
283   
284           isol = []
285           for x in l:
286             a = td[x][v][ix].get(att,'__ABSENT__')
287             try:
288              if att == 'standard_name' or ( att == 'long_name' and vd2[v][0] == 2):
289               iso = x[:7] == 'CMIP5_O'
290               tt = snsubber.isFalseSn( v, a )
291              elif att == 'long_name':
292               tt = snsubber.isFalseLn( v, a )
293               dims = td[x][v][0]
294               iso = 'depth0m' in dims
295              else:
296               iso = False
297               tt = (False,)
298         ##    iso = False
299             except:
300               print att,v
301               raise
302             isol.append((iso,x))
303             if tt[0]:
304               print 'Substituting ',v,a,tt
305               ee[iso].append( tt[1] )
306             else:
307               ee[iso].append( a )
308   
309           for a in [True,False]:
310             ok = True
311             if len(ee[a]) > 1 :
312               for x in ee[a][1:]:
313                 if x != ee[a][0]:
314                   ok = False
315   
316             if not ok:
317                print xxx,'E001: Multiple values : ',att,v
318                for t in isol:
319                  if t[0] == a:
320                    tab = t[1]
321                    if att in ['standard_name','long_name']:
322                      print xxx,tab,td[tab][v][ix].get('standard_name','__ABSENT__'),td[tab][v][ix].get('long_name','__ABSENT__')
323                    else:
324                      print xxx,tab,td[tab][v][ix].get(att,'__ABSENT__')
325                   
326           if att == "standard_name":
327             vd2[v] = (2,[ee[True],ee[False]] )
328         else:
329           if att == "standard_name":
330             tt = snsubber.isFalseSn( v, av[0] )
331             if tt[0]:
332               print 'Substituting ',v,av[0],tt
333               vd2[v] = (1, tt[1])
334             else:
335               vd2[v] = (1, av)
336     elif len(l) == 1:
337           tab = vdict[v][0]
338           a = td[tab][v][ix].get('standard_name','__ABSENT__')
339           tt = snsubber.isFalseSn( v, a )
340           if tt[0]:
341             print 'Substituting ',v,a,tt
342             vd2[v] = (1, tt[1])
343           else:
344             vd2[v] = (1, a)
345           ##print 'MULTIPLE VALUES: ',v,att,av
346     else:
347      print xxx, 'Zero length element: %s' % v
348   
349class typecheck1:
350  def __init__( self, m, thisatts,helper=None):
351    self.type2Atts = ['positive','comment', 'long_name', 'modeling_realm', 'out_name', 'standard_name', 'type', 'units', 'flag_meanings', 'flag_values']
352    self.type3Atts = ['positive','long_name','modeling_realm', 'out_name', 'standard_name', 'type', 'units', 'flag_meanings', 'flag_values']
353    self.type4Atts = ['positive','modeling_realm', 'out_name', 'standard_name', 'type', 'units', 'flag_meanings', 'flag_values']
354    self.type2Atts = ['positive','comment', 'long_name', 'modeling_realm', 'out_name', 'standard_name', 'type', 'units']
355    self.type3Atts = ['positive','long_name','modeling_realm', 'out_name', 'standard_name', 'type', 'units']
356    self.type4Atts = ['positive','modeling_realm', 'out_name', 'standard_name', 'type', 'units']
357    self.m = m
358    vars = m.vars
359    vdict = m.vdict
360    self.helper=helper
361    td = m.td
362    vd2 = {}
363    type1, type2, type3, type4, type5 = [[],[],[],[],[],] 
364    vd2 = {}
365    for v in vars:
366     l = vdict[v]
367     dl = map( lambda x: string.join( td[x][v][0] ), l )
368     vd2[v] = str( uniquify( dl ) )
369    json.dump( vd2, open( 'mipInfo.json', 'w' ) )
370    for v in vars:
371     l = vdict[v]
372     l.sort()
373     if len(l) == 1: 
374       type1.append(v)
375     elif len(l) > 1:
376       adict = {}
377       for att in thisatts:
378         if att == '__dimensions__':
379           atl = map( lambda x: (string.join( td[x][v][0] ),x), l )
380         else:
381           atl = map( lambda x: (td[x][v][1].get(att,'__ABSENT__'),x), l )
382         atl.sort( tupsort(0).cmp )
383         a0 = atl[0][0]
384         if a0 == None:
385           a0 = ""
386         av = [a0,]
387         for a,tab in atl[1:]:
388           if a == None:
389             a = ""
390           if a != av[-1]:
391             if self.helper != None and self.helper.applycv:
392               thisok=False
393               pmatch = False
394               for cond,src,targ in self.helper.canonvar:
395                 if string.find(a,src) != -1 or  string.find(av[-1],src) != -1:
396                   ##print 'Potential match ---- ',a
397                   ##print src,'###',targ
398                   ##print av[-1]
399                   pmatch = True
400                 if self.helper.checkCond( tab, v, cond ):
401                   if self.helper.match(string.replace( a, src, targ ), av[-1]) or self.helper.match(string.replace( av[-1], src, targ ), a):
402                     thisok = True
403               if thisok:
404                 print '############### conditional match found', tab, v
405               else:
406                 if pmatch:
407                   ##print '########### no matvh found'
408                   pass
409                 av.append(a)
410             else:
411               av.append(a)
412         adict[att] = av
413         ##if v == "snd":
414           ##print adict
415       
416## check for type 2
417       tval = None
418       ##if adict[ 'positive' ] == ['__ABSENT__']:
419       if all( map( lambda x: len(adict[x]) == 1, self.type2Atts )):
420           tval = 2
421       elif all( map( lambda x: len(adict[x]) == 1, self.type3Atts )):
422           tval = 3
423       elif all( map( lambda x: len(adict[x]) == 1, self.type4Atts )):
424           tval = 4
425       else:
426           l = map( lambda x: '%s:%s, ' % (x,len(adict[x]) ), self.type2Atts )
427       if tval == 2:
428         type2.append( v)
429       elif tval == 3:
430         type3.append( v)
431       elif tval == 4:
432         type4.append( v)
433       else:
434         type5.append(v)
435    xx = float( len(vars) )
436    print string.join( map( lambda x: '%s (%5.1f%%);' % (x,x/xx*100), [len(type1), len(type2), len(type3), len(type4), len(type5)] ) )
437    self.type1 = type1
438    self.type2 = type2
439    self.type3 = type3
440    self.type4 = type4
441    self.type5 = type5
442
443  def exportHtml( self, typecode ):
444
445    allAtts = ['__dimensions__', 'cell_methods', 'comment', 'long_name', 'modeling_realm', 'out_name', 'standard_name', 'type', 'units', 'valid_max', 'valid_min', 'positive', 'ok_max_mean_abs', 'ok_min_mean_abs', 'flag_meanings', 'flag_values']
446    fixedType1Tmpl = """:%(standard_name)s [%(units)s]</h3>
447        %(__dimensions__)s<br/>
448        %(long_name)s<br/>
449       realm: %(modeling_realm)s; out_name: %(out_name)s; type: %(type)s, <br/>
450       cell_methods: %(cell_methods)s; comment: %(comment)s<br/>
451"""
452    fixedType2TmplA = """:%(standard_name)s [%(units)s]</h3>
453        %(long_name)s<br/>
454       realm: %(modeling_realm)s; out_name: %(out_name)s; type: %(type)s, <br/>
455       comment: %(comment)s<br/>
456"""
457    fixedType2TmplB = "<li>%s [%s]: %s -- (%s,%s,%s,%s)</li>\n"
458       
459    fixedType3TmplA = """:%(standard_name)s [%(units)s]</h3>
460       realm: %(modeling_realm)s; out_name: %(out_name)s; type: %(type)s <br/>
461"""
462    fixedType3TmplB = "<li>%s [%s]: %s: %s [%s]</li>\n"
463    fixedType4TmplB = "<li>%s [%s]: %s [%s]</li>\n"
464    fixedType5TmplA = """ [%(units)s]</h3>
465       out_name: %(out_name)s; type: %(type)s <br/>
466"""
467    fixedType5TmplB = "<li>%s [%s]: %s, %s [%s]: %s</li>\n"
468       
469    if typecode == 1:
470      oo = open( 'type1.html', 'w' )
471      self.type1.sort()
472      ee = {}
473      for v in self.type1:
474        tab = self.m.vdict[v][0]
475        if not ee.has_key(tab):
476          ee[tab] = []
477        ee[tab].append( v )
478      keys = ee.keys()
479      keys.sort()
480      for k in keys:
481         oo.write( '<h2>Table %s</h2>\n' % k )
482         for v in ee[k]:
483            try:
484              etmp = {}
485              for a in allAtts:
486                etmp[a] = self.m.td[k][v][1].get( a, 'unset' )
487              etmp['__dimensions__'] = string.join( self.m.td[k][v][0] )
488              oo.write( '<h3>' + v + (fixedType1Tmpl % etmp) )
489            except:
490              print k, self.m.td[k][v][1].keys()
491              raise
492      oo.close()
493    elif typecode == 2:
494      oo = open( 'type2.html', 'w' )
495      self.type2.sort()
496      oo.write( '<h2>Variables with fixed attributes</h2>\n' )
497      for v in self.type2:
498            l = self.m.vdict[v]
499            etmp = {}
500            for a in allAtts:
501                etmp[a] = self.m.td[l[0]][v][1].get( a, 'unset' )
502            oo.write( '<h3>' + v + (fixedType2TmplA % etmp) )
503            oo.write( '<ul>\n' )
504            for t in l:
505              dims = string.join( self.m.td[t][v][0] )
506              sa = tuple( [t,dims,] + map( lambda x: self.m.td[t][v][1].get( x, 'unset' ), ['cell_methods','valid_max', 'valid_min', 'ok_max_mean_abs', 'ok_min_mean_abs'] ) )
507              oo.write( fixedType2TmplB % sa )
508            oo.write( '</ul>\n' )
509      oo.close()
510           
511    elif typecode in [3,4,5]:
512      oo = open( 'type%s.html' % typecode, 'w' )
513      thistype,h2,al,tmplA,tmplB = { 3:(self.type3,"Variables with varying comment",['long_name','comment','cell_methods'], fixedType3TmplA, fixedType3TmplB),
514                      4:(self.type4,"Variables with varying long_name",['long_name','cell_methods'],fixedType3TmplA, fixedType4TmplB),
515                      5:(self.type5,"Remaining variables",['standard_name','long_name','cell_methods','realm'],fixedType5TmplA, fixedType5TmplB) }[typecode]
516      thistype.sort()
517      oo.write( '<h2>%s</h2>\n' % h2 )
518      for v in thistype:
519            l = self.m.vdict[v]
520            etmp = {}
521            for a in allAtts:
522                etmp[a] = self.m.td[l[0]][v][1].get( a, 'unset' )
523            oo.write( '<h3>' + v + (tmplA % etmp) )
524            oo.write( '<ul>\n' )
525            for t in l:
526              dims = string.join( self.m.td[t][v][0] )
527              sa = tuple( [t,dims,] + map( lambda x: self.m.td[t][v][1].get( x, 'unset' ), al ) )
528              oo.write( tmplB % sa )
529            oo.write( '</ul>\n' )
530      oo.close()
531           
532
533mips = ( NT_mip( 'cmip5','cmip5_vocabs/mip/', 'CMIP5_*' ),
534         NT_mip( 'ccmi', 'ccmi_vocabs/mip/', 'CCMI1_*')  )
535cordex_mip = NT_mip( 'cordex', 'cordex_vocabs/mip/', 'CORDEX_*')
536specs_mip = NT_mip( 'specs', 'specs_vocabs/mip/', 'SPECS_*')
537mips = ( cordex_mip, NT_mip( 'ccmi', 'ccmi_vocabs/mip/', 'CCMI1_*'), NT_mip( 'cmip5','cmip5_vocabs/mip/', 'CMIP5_*' ), )
538mips = ( cordex_mip, )
539mips = ( NT_mip( 'ccmi', 'ccmi_vocabs/mip/', 'CCMI1_*'),  )
540mips = ( specs_mip, )
541mips = ( NT_mip( 'cmip5','cmip5_vocabs/mip/', 'CMIP5_*' ), )
542m = mipCo( mips ) 
543h = helper()
544
545keys = m.adict.keys()
546keys.sort()
547fh = open( 'axes_json.txt', 'w' )
548for k in keys:
549  ee = m.dd[m.adict[k][0]][k][0]
550  ee["__name__"] = k
551  fh.write( json.dumps( ee ) + '\n' )
552fh.close()
553
554al = []
555for k0 in m.dd.keys():
556  for k1 in m.dd[k0].keys():
557    al += m.dd[k0][k1][0].keys()
558ald = uniquify( al )
559ald.sort()
560i = ald.index('standard_name')
561ald.pop(i)
562ald = ['standard_name', ] + ald
563
564cmip5AxesAtts = ['axis', 'bounds_values', 'climatology', 'coords_attrib', 'formula', 'index_only', 'long_name', 'must_call_cmor_grid', 'must_have_bounds', 'out_name', 'positive', 'requested', 'requested_bounds', 'standard_name', 'stored_direction', 'tolerance', 'type', 'units', 'valid_max', 'valid_min', 'value', 'z_bounds_factors', 'z_factors']
565
566## check consistency of dimensions
567r = runcheck1( m, ald, isAxes=True )
568for e in r.errors:
569  print e
570
571allatts = ms.al
572thisatts = ['standard_name','units','long_name','__dimensions__']
573## need to have standard name first.
574for a in allatts:
575  if a not in thisatts:
576    thisatts.append(a)
577s =typecheck1( m, thisatts, helper=h)
578s.exportHtml( 1 )
579s.exportHtml( 2 )
580s.exportHtml( 3 )
581s.exportHtml( 4 )
582s.exportHtml( 5 )
Note: See TracBrowser for help on using the repository browser.