Developers/Namelist Checker: nemo_nml_check.py

File nemo_nml_check.py, 4.5 KB (added by acc, 4 months ago)
Line 
1import re
2from argparse import ArgumentParser
3from difflib import SequenceMatcher
4#
5def read_nmls( fin, tolower ):
6  #
7  # function to read namelist files and construct lists of named namelists
8  # and their constituent variables (the latter being a list of lists)
9  # If tolower is True then all names are converted to lowercase.
10  # NEMO conventions are assumed here. In particular:
11  # 1. namelists are started with &name_of_namelist appearing as the first non-whitespace
12  #    on a line (and not followed by anything other than an optional comment)
13  # 2. Only one variable is set on each line
14  # 3. namelists are closed with / appearing as the first non-whitespace on a line
15  # Compliance with these conventions is not checked.
16  #
17  try:
18   f = open(fin,'r')
19  except:
20   print('Unable to open ',fin)
21   exit()
22  nmls   = []
23  nvars  = []
24  linnos = []
25  inside = 0
26  blank = 0
27  stripa=re.compile('\s*&')       # match whitespace up to leading ampersand
28  stripc=re.compile('\s*!.*$')    # match whitespace up to leading comment marker
29  stripe=re.compile('\s*=.*$')    # match = (with preceding whitespace) and anything following
30  lc=0
31  for line in f:
32    lc = lc + 1
33    nm = re.match(r'^\S*&',line)
34    if nm != None:                # Found a new namelist; extract and store name
35      #print( stripa.sub('',stripc.sub('',line) ) )
36      inside = 1
37      if tolower: line = line.lower()
38      nmls.append(stripa.sub('',stripc.sub('',line.strip()) ))
39      newl = []                   # new temporary list
40      lins = []                   # new temporary list
41    if nm == None and inside == 1 and re.match(r'^\s*!', line) == None :
42      # still inside namelist and not a comment
43      if re.match(r'^\s*$', line) != None :
44        # ignore blank lines
45        blank = blank + 1
46      elif re.match(r'^\s*/', line) == None :
47        # It is not the closing slash, therefore extract and store variable name
48        if tolower: line = line.lower()
49        #print( stripe.sub('',stripc.sub('',line) ) )
50        newl.append(stripe.sub('',stripc.sub('',line.strip()) ))
51        lins.append(lc)
52      else :
53        # Reached the end of this particular namelist. Append temporary list to list of lists
54        inside = 0
55        nvars.append(newl)
56        linnos.append(lins)
57  f.close()
58  return nmls, nvars, linnos
59
60def main():
61    parser = ArgumentParser(description=
62    """
63    Check for consistency between cfg namelists and ref versions
64     e.g. python nemo_nml_check.py --cfg <cfg namelist> --ref  <ref namelist> --match <closeness factor>
65    """)
66    parser.add_argument('--cfg', '-c',dest='cfgfile',help='configuration namelist', nargs= '*',default='namelist_cfg')
67    parser.add_argument('--ref', '-r',dest='reffile',help='reference namelist', nargs= '*',default='namelist_ref')
68    parser.add_argument('--match', '-m',dest='mfactor',help='closeness factor (0-1)', nargs= '*',default='0.75')
69    args = parser.parse_args()
70    #
71    mfact = float(''.join(args.mfactor))
72    nmls, nvars, lnos    = read_nmls(''.join(args.reffile), True )    # Construct lists from the   reference   namelist
73    cnmls, cnvars, clnos = read_nmls(''.join(args.cfgfile), False)    # Construct lists from the configuration namelist
74    #
75    # for each non-empty, named namelist in the configuration namelist file: find the equivalent namelist in the
76    # reference set and confirm that a match for each variable exists.
77    # Checks are performed on strings converted to lowercase. This was done on read for the reference set but
78    # the configuration set has been keep in original form so that reports match the file contents.
79    #
80    nomatch = 0
81    for k, nl in enumerate(cnmls):
82      #print(k, nl)
83      if len(cnvars[k]) > 0:
84        for rk, rnl in enumerate(nmls):
85          if rnl == nl.lower() :
86           for nv, v in enumerate(cnvars[k]):
87             #print(k,v,rnl)
88             if v.lower() not in nvars[rk] :
89               if nomatch == 0 :
90                 print('No match in matching blocks for:                           |  Possible close match:')
91                 nomatch = 1
92               print( '{:<22} in {:<12} (at line no. {:4d})'.format(v,nl,clnos[k][nv]), end='  |  ')
93               rapprox=0.0
94               for nk2, nv2 in enumerate(nvars[rk]):
95                 r=SequenceMatcher(None, v, nv2).ratio()
96                 if r >= rapprox:
97                   (rapprox, vapprox) = (r, nv2)
98               if rapprox > mfact : 
99                 print( vapprox )
100               else :
101                 print(" ")
102   
103#
104
105if __name__ == '__main__':
106    main()