1 | # Python script to check namelist.input consistency with a given WRF version |
---|
2 | # L. Fita, LMD, December 2014 |
---|
3 | import numpy as np |
---|
4 | import os |
---|
5 | from optparse import OptionParser |
---|
6 | import sys |
---|
7 | import nc_var_tools as ncvar |
---|
8 | |
---|
9 | ## e.g. # WRF_namelist_check.py -w /home/lluis/DATA/WRF/WRFV3.3/serial -n /home/lluis/DATA/WRF/WRFV3.3/serial/WRFV3/run/namelist.input |
---|
10 | #* WRF structure |
---|
11 | # - module_config_rec definition on: |
---|
12 | # .- 'inc/namelist_defines.inc' definition of variables from 'namelist.input' called from 'frame/module_configure.F' |
---|
13 | # .- 'include/config_assigns.inc' assigment to the grid structure called from 'frame/module_configure.F' |
---|
14 | |
---|
15 | # - grid definition on: |
---|
16 | # .- 'inc/state_struct.inc' types of the grid content |
---|
17 | # .- 'frame/module_domain_type.F' definition of the grid |
---|
18 | # .- 'dyn_em/start_em.F' filling the grid |
---|
19 | |
---|
20 | # - namelist reading in include: config_assigns.inc |
---|
21 | |
---|
22 | main = 'WRF_namelist_check.py' |
---|
23 | |
---|
24 | errormsg = 'ERROR -- error -- ERROR -- error' |
---|
25 | warnmsg = 'WARNING -- waring -- WARNING -- warning' |
---|
26 | |
---|
27 | def searchInlist(listname, nameFind): |
---|
28 | """ Function to search a value within a list |
---|
29 | listname = list |
---|
30 | nameFind = value to find |
---|
31 | >>> searInlist(['1', '2', '3', '5'], '5') |
---|
32 | True |
---|
33 | """ |
---|
34 | for x in listname: |
---|
35 | if x == nameFind: |
---|
36 | return True |
---|
37 | return False |
---|
38 | |
---|
39 | def reduce_spaces(string): |
---|
40 | """ Function to give words of a line of text removing any extra space |
---|
41 | """ |
---|
42 | values = string.replace('\n','').split(' ') |
---|
43 | vals = [] |
---|
44 | for val in values: |
---|
45 | if len(val) > 0: |
---|
46 | vals.append(val) |
---|
47 | |
---|
48 | return vals |
---|
49 | |
---|
50 | def numVector_String(vec,char): |
---|
51 | """ Function to transform a vector of numbers to a single string [char] separated |
---|
52 | numVector_String(vec,char) |
---|
53 | vec= vector with the numerical values |
---|
54 | char= single character to split the values |
---|
55 | >>> print numVector_String(np.arange(10),' ') |
---|
56 | 0 1 2 3 4 5 6 7 8 9 |
---|
57 | """ |
---|
58 | fname = 'numVector_String' |
---|
59 | |
---|
60 | if vec == 'h': |
---|
61 | print fname + '_____________________________________________________________' |
---|
62 | print numVector_String.__doc__ |
---|
63 | quit() |
---|
64 | |
---|
65 | Nvals = len(vec) |
---|
66 | |
---|
67 | string='' |
---|
68 | for i in range(Nvals): |
---|
69 | if i == 0: |
---|
70 | string = str(vec[i]) |
---|
71 | else: |
---|
72 | string = string + char + str(vec[i]) |
---|
73 | |
---|
74 | return string |
---|
75 | |
---|
76 | def get_namelist_vars(values, namelist): |
---|
77 | """ Function to get namelist-like values ([varname] = [value]) |
---|
78 | get_namelist_vars(namelist) |
---|
79 | values= [sectionname],[kout] |
---|
80 | [sectionname]: name of the section from which one want the values ('all', for all) |
---|
81 | [kout]: kind of output |
---|
82 | 'tex3': printed output as LaTeX table of three columns \verb+namelist_name+ & value |
---|
83 | 'column': printed output as namelist_name value |
---|
84 | 'dict': as two python dictionary object (namelistname, value and namelistname, sectionname) |
---|
85 | namelist= namelist_like file to retrieve values |
---|
86 | >>> get_namelist_vars('geogrid,dic','/home/lluis/etudes/domains/medic950116/namelist.wps') |
---|
87 | {'e_sn': '32, 97,', 'stand_lon': '0.', 'opt_geogrid_tbl_path': "'./'", 'geog_data_path': "'/home/lluis/DATA/WRF/geog'", 'pole_lat': '90.0', 'ref_lat': '35.0,', 'map_proj': "'lat-lon',", 'parent_id': '1, 1,', 'geog_data_res': "'2m','2m',", 'e_we': '32, 112,', 'dx': '0.35,', 'dy': '0.35,', 'parent_grid_ratio': '1, 3,', 'pole_lon': '0.0', 'ref_lon': '20,', 'j_parent_start': '1, 17,', 'i_parent_start': '1, 31,'} |
---|
88 | """ |
---|
89 | |
---|
90 | fname = 'get_namelist_vars' |
---|
91 | |
---|
92 | # List of characters which split pairs name/value |
---|
93 | valuessep = ['='] |
---|
94 | # List of characters which indicate a comment |
---|
95 | commentchars = ['#'] |
---|
96 | |
---|
97 | if values == 'h': |
---|
98 | print fname + '_____________________________________________________________' |
---|
99 | print get_namelist_vars.__doc__ |
---|
100 | quit() |
---|
101 | |
---|
102 | expectargs = '[sectionname],[kout]' |
---|
103 | check_arguments(fname,len(expectargs.split(',')),values,',',expectargs) |
---|
104 | |
---|
105 | secname = values.split(',')[0] |
---|
106 | kout = values.split(',')[1] |
---|
107 | |
---|
108 | if not os.path.isfile(namelist): |
---|
109 | print errormsg |
---|
110 | print ' ' + fname + ": namelist file '" + namelist + "' does not exist !!" |
---|
111 | quit(-1) |
---|
112 | |
---|
113 | ncml = open(namelist, 'r') |
---|
114 | |
---|
115 | sections = {} |
---|
116 | namelistvals = {} |
---|
117 | namelistsecs = {} |
---|
118 | sectionnames = [] |
---|
119 | namessec = [] |
---|
120 | allnames = [] |
---|
121 | namelistvalssec = {} |
---|
122 | namelistsecssec = {} |
---|
123 | nmlname = '' |
---|
124 | sectionname = '' |
---|
125 | |
---|
126 | for line in ncml: |
---|
127 | linevals = reduce_spaces(line) |
---|
128 | Nvals = len(linevals) |
---|
129 | |
---|
130 | if Nvals >= 1 and linevals[0][0:1] == '&': |
---|
131 | if len(sectionnames) > 1: |
---|
132 | sections[sectionname] = namessec |
---|
133 | |
---|
134 | sectionname = linevals[0][1:len(linevals[0])+1] |
---|
135 | # print ' ' + fname + ": new section '" + sectionname + "' !!!" |
---|
136 | sectionnames.append(sectionname) |
---|
137 | namessec = [] |
---|
138 | nmlname = '' |
---|
139 | elif Nvals >= 1 and not searchInlist(commentchars,linevals[0][0:1]): |
---|
140 | if Nvals >= 3 and searchInlist(valuessep,linevals[1]): |
---|
141 | nmlname = linevals[0] |
---|
142 | nmlval = numVector_String(linevals[2:Nvals],' ') |
---|
143 | elif Nvals == 1: |
---|
144 | for valsep in valuessep: |
---|
145 | if linevals[0].find(valsep) != -1: |
---|
146 | nmlname = linevals[0].split(valsep)[0] |
---|
147 | nmlval = linevals[0].split(valsep)[1] |
---|
148 | break |
---|
149 | elif Nvals == 2: |
---|
150 | for valsep in valuessep: |
---|
151 | if linevals[0].find(valsep) != -1: |
---|
152 | nmlname = linevals[0].split(valsep)[0] |
---|
153 | nmlval = linevals[1] |
---|
154 | break |
---|
155 | elif linevals[1].find(valsep) != -1: |
---|
156 | nmlname = linevals[0] |
---|
157 | nmlval = linevals[1].split(valsep)[0] |
---|
158 | break |
---|
159 | else: |
---|
160 | print warnmsg |
---|
161 | print ' ' + fname + ': wrong number of values', Nvals, \ |
---|
162 | 'in the namelist line!' |
---|
163 | print ' line: ',line |
---|
164 | print ' line values:',linevals |
---|
165 | # quit(-1) |
---|
166 | |
---|
167 | namelistvals[nmlname] = nmlval |
---|
168 | namelistsecs[nmlname] = sectionname |
---|
169 | |
---|
170 | namessec.append(nmlname) |
---|
171 | allnames.append(nmlname) |
---|
172 | |
---|
173 | if len(sectionname) > 1: |
---|
174 | sections[sectionname] = namessec |
---|
175 | |
---|
176 | if secname != 'all': |
---|
177 | if not searchInlist(sections.keys(),secname): |
---|
178 | print errormsg |
---|
179 | print ' ' + fname + ": section '" + values + "' does not exist !!" |
---|
180 | print ' only found:',sectionnames |
---|
181 | quit(-1) |
---|
182 | |
---|
183 | namestouse = [] |
---|
184 | for nml in allnames: |
---|
185 | for nnml in sections[secname]: |
---|
186 | namelistvalssec[nnml] = namelistvals[nnml] |
---|
187 | namelistsecssec[nnml] = secname |
---|
188 | if nml == nnml: namestouse.append(nml) |
---|
189 | else: |
---|
190 | namestouse = allnames |
---|
191 | namelistvalssec = namelistvals |
---|
192 | namelistsecssec = namelistsecs |
---|
193 | |
---|
194 | if kout == 'tex3': |
---|
195 | ofile='get_namelist_vars_3col.tex' |
---|
196 | fobj = open(ofile, 'w') |
---|
197 | |
---|
198 | vals = namestouse |
---|
199 | Nvals = len(vals) |
---|
200 | Nvals3 = int(Nvals/3) |
---|
201 | latextab = '\\begin{center}\n\\begin{tabular}{lclclc}\n' |
---|
202 | for il in range(2): |
---|
203 | latextab = latextab + '{\\bfseries{name}} & {\\bfseries{value}} & ' |
---|
204 | latextab= latextab+ '{\\bfseries{name}} & {\\bfseries{value}} \\\\ \\hline\n' |
---|
205 | |
---|
206 | if np.mod(Nvals,3) != 0: |
---|
207 | Nvals0 = Nvals - np.mod(Nvals,3) |
---|
208 | else: |
---|
209 | Nvals0 = Nvals |
---|
210 | |
---|
211 | for ival in range(0,Nvals0,3): |
---|
212 | line = '' |
---|
213 | print ' ival:',ival |
---|
214 | for il in range(2): |
---|
215 | line = line + '\\verb+' + vals[ival+il] + '+ & ' + \ |
---|
216 | namelistvalssec[vals[ival+il]].replace('_','\\_') +' & ' |
---|
217 | line = line + '\\verb+' + vals[ival+2] + '+ & ' + \ |
---|
218 | namelistvalssec[vals[ival+2]].replace('_','\\_') + ' \\\\\n' |
---|
219 | latextab = latextab + line |
---|
220 | |
---|
221 | latextab = latextab + '%not multiple of three!!!!\n' |
---|
222 | print 'mod:', np.mod(Nvals,3),Nvals0,Nvals |
---|
223 | if np.mod(Nvals,3) != 0: |
---|
224 | ival = Nvals0 |
---|
225 | line = '' |
---|
226 | for il in range(np.mod(Nvals,3)): |
---|
227 | print 'ival:',ival + il |
---|
228 | line = line + '\\verb+' + vals[ival+il] + '+ & ' + \ |
---|
229 | namelistvalssec[vals[ival+il]].replace('_','\\_') + ' & ' |
---|
230 | for il in range(2-np.mod(Nvals,3)): |
---|
231 | line = line + ' & & ' |
---|
232 | latextab = latextab + line + ' & \\\\\n' |
---|
233 | latextab = latextab + '\\end{tabular}\n\\end{center}\n' |
---|
234 | |
---|
235 | # print latextab |
---|
236 | fobj.write(latextab) |
---|
237 | |
---|
238 | fobj.close() |
---|
239 | print fname + ": successful writen '" + ofile + "' LaTeX tale file !!" |
---|
240 | |
---|
241 | return |
---|
242 | elif kout == 'column': |
---|
243 | for dictv in namestouse: |
---|
244 | print dictv + ' = ' + namelistvalssec[dictv] |
---|
245 | return |
---|
246 | elif kout == 'dict': |
---|
247 | return namelistvalssec, namelistsecssec |
---|
248 | else: |
---|
249 | print errormsg |
---|
250 | print ' ' + fname + ": kind of output '" + kout + "' not ready!!!" |
---|
251 | quit(-1) |
---|
252 | |
---|
253 | return |
---|
254 | |
---|
255 | def check_arguments(funcname,Nargs,args,char,expectargs): |
---|
256 | """ Function to check the number of arguments if they are coincident |
---|
257 | check_arguments(funcname,Nargs,args,char) |
---|
258 | funcname= name of the function/program to check |
---|
259 | Nargs= theoretical number of arguments |
---|
260 | args= passed arguments |
---|
261 | char= character used to split the arguments |
---|
262 | """ |
---|
263 | |
---|
264 | fname = 'check_arguments' |
---|
265 | |
---|
266 | Nvals = len(args.split(char)) |
---|
267 | if Nvals != Nargs: |
---|
268 | print errormsg |
---|
269 | print ' ' + fname + ': wrong number of arguments:',Nvals," passed to '", \ |
---|
270 | funcname, "' which requires:",Nargs,'!!' |
---|
271 | print ' given arguments:',args.split(char) |
---|
272 | print ' expected arguments:',expectargs |
---|
273 | quit(-1) |
---|
274 | |
---|
275 | return |
---|
276 | |
---|
277 | def check_conversion(value, trykind): |
---|
278 | """ Function to check a variable conversion to a python type |
---|
279 | from: http://stackoverflow.com/questions/4690600/python-exception-message-capturing |
---|
280 | value= value to convert |
---|
281 | trykind: kind to try to convert to |
---|
282 | integer: 'INTEGER', 'integer', 'int' |
---|
283 | float: 'REAL', 'real', 'float' |
---|
284 | intl: 'LOGICAL', 'logical', 'bool' |
---|
285 | >>> check_conversion('1.3','integer') |
---|
286 | ERROR -- error -- ERROR -- error |
---|
287 | check_conversion: Failed to convert '1.3' to 'int': invalid literal for int() with base 10: '1.3' |
---|
288 | """ |
---|
289 | import sys, traceback |
---|
290 | fname = 'check_conversion' |
---|
291 | |
---|
292 | intk = ['INTEGER', 'integer', 'int'] |
---|
293 | intr = ['REAL', 'real', 'float'] |
---|
294 | intl = ['LOGICAL', 'logical', 'boolean', 'bool'] |
---|
295 | # Accepted values as boolean values: |
---|
296 | intlv = ['y', 'n', 'yes', 'no', '.TRUE.', '.FALSE.', '.true.', '.false.', \ |
---|
297 | '.T.', '.F.', '.t.', '.f.'] |
---|
298 | |
---|
299 | # All kinds |
---|
300 | allkinds = intk + intr + intl |
---|
301 | if not searchInlist(allkinds, trykind): |
---|
302 | print errormsg |
---|
303 | print ' ' + fname + ": kind '" + trykind + "' not ready !!" |
---|
304 | quit(-1) |
---|
305 | |
---|
306 | try: |
---|
307 | if searchInlist(intk,trykind): |
---|
308 | conv = int(value) |
---|
309 | elif searchInlist(intr,trykind): |
---|
310 | conv = np.float(value) |
---|
311 | elif searchInlist(intl,trykind): |
---|
312 | if not searchInlist(intlv, value): |
---|
313 | print errormsg |
---|
314 | print ' ' + fname + ": Failed to convert '" + value + "' to '" + \ |
---|
315 | trykind + "' !!" |
---|
316 | return -1 |
---|
317 | except: |
---|
318 | exc_type, exc_value, exc_traceback = sys.exc_info() |
---|
319 | print errormsg |
---|
320 | print ' ' + fname + ": Failed to convert '" + value + "' to '" + trykind + \ |
---|
321 | "': " + str(exc_value) |
---|
322 | return -1 |
---|
323 | |
---|
324 | return 0 |
---|
325 | |
---|
326 | ####### ###### ##### #### ### ## # |
---|
327 | |
---|
328 | parser = OptionParser() |
---|
329 | parser.add_option("-w", "--WRF_folder", dest="wfold", |
---|
330 | help="compiled WRF folder to use", metavar="FOLDER") |
---|
331 | parser.add_option("-n", "--WRF_namelist", dest="namelist", |
---|
332 | help="'namelist.input' to use", metavar="FILE") |
---|
333 | |
---|
334 | (opts, args) = parser.parse_args() |
---|
335 | |
---|
336 | ####### ####### |
---|
337 | ## MAIN |
---|
338 | ####### |
---|
339 | |
---|
340 | if not os.path.isfile(opts.wfold + '/WRFV3/main/wrf.exe'): |
---|
341 | print errormsg |
---|
342 | print ' ' + main + ": compiled WRF '" + opts.wfold + "/WRFV3/main/wrf.exe'" + \ |
---|
343 | 'does not exist !!' |
---|
344 | quit(-1) |
---|
345 | |
---|
346 | if not os.path.isfile(opts.namelist): |
---|
347 | print errormsg |
---|
348 | print ' ' + main + ": WRF namelist '" + opts.namelist + "' does not exist !!" |
---|
349 | quit(-1) |
---|
350 | |
---|
351 | infold = opts.wfold + '/WRFV3' |
---|
352 | |
---|
353 | # Reading namelist characteristics |
---|
354 | ## |
---|
355 | nmldef = infold + '/inc/namelist_defines.inc' |
---|
356 | |
---|
357 | if not os.path.isfile(nmldef): |
---|
358 | print errormsg |
---|
359 | print ' ' + main + ": included WRF '" + nmldef + "' does not exist !!" |
---|
360 | quit(-1) |
---|
361 | |
---|
362 | onmldef = open(nmldef, 'r') |
---|
363 | |
---|
364 | nmlpar = [] |
---|
365 | nmlpark = {} |
---|
366 | nmlpard = {} |
---|
367 | |
---|
368 | for line in onmldef: |
---|
369 | if line[0:1] != '!': |
---|
370 | values = line.split(' ') |
---|
371 | par = line.split('::')[1].replace('\n','').replace(' ','') |
---|
372 | nmlpar.append(par) |
---|
373 | nmlpark[par] = values[0].replace(' ','') |
---|
374 | if len(line.split(',')) > 1: |
---|
375 | dim = line.split(',')[1].split('::')[0].split('(')[1].replace(')','') |
---|
376 | nmlpard[par] = dim.replace(' ','') |
---|
377 | |
---|
378 | # if nmlpard.has_key(par): |
---|
379 | # print par,':',nmlpark[par],'=',nmlpard[par] |
---|
380 | # else: |
---|
381 | # print par,':',nmlpark[par] |
---|
382 | |
---|
383 | onmldef.close() |
---|
384 | |
---|
385 | # Reading namelist sections |
---|
386 | ## |
---|
387 | nmlsec = infold + '/inc/namelist_statements.inc' |
---|
388 | |
---|
389 | if not os.path.isfile(nmlsec): |
---|
390 | print errormsg |
---|
391 | print ' ' + main + ": included WRF '" + nmlsec + "' does not exist !!" |
---|
392 | quit(-1) |
---|
393 | |
---|
394 | onmlsec = open(nmlsec, 'r') |
---|
395 | |
---|
396 | nmlpars = {} |
---|
397 | nmlsecs = [] |
---|
398 | |
---|
399 | for line in onmlsec: |
---|
400 | # print 'line:',line |
---|
401 | if line[0:1] != '!': |
---|
402 | values = line.split('/') |
---|
403 | par = values[2].replace('\n','').replace(' ','') |
---|
404 | sec = values[1].replace(' ','') |
---|
405 | |
---|
406 | nmlpars[par] = sec |
---|
407 | if not searchInlist(nmlsecs,sec): |
---|
408 | nmlsecs.append(sec) |
---|
409 | |
---|
410 | onmlsec.close() |
---|
411 | |
---|
412 | # Getting values from 'namelist.input |
---|
413 | ## |
---|
414 | readnmlvals, readnmlsecs = get_namelist_vars('all,dict', opts.namelist) |
---|
415 | |
---|
416 | # Checking values |
---|
417 | ## |
---|
418 | readparam = set(readnmlvals.keys()) |
---|
419 | configparam = set(nmlpark.keys()) |
---|
420 | |
---|
421 | coinread = list(readparam & configparam) |
---|
422 | Wrongread = list(readparam - configparam) |
---|
423 | |
---|
424 | # Wrong pramater name |
---|
425 | ## |
---|
426 | Nwrong = len(Wrongread) |
---|
427 | |
---|
428 | if Nwrong > 0: |
---|
429 | print errormsg |
---|
430 | print ' ' + main + ': some readed parameters are wrong!' |
---|
431 | print ' wrong parameters: ', Wrongread |
---|
432 | |
---|
433 | # Wrong section |
---|
434 | ## |
---|
435 | for readpar in readnmlsecs.keys(): |
---|
436 | if nmlpars.has_key(readpar) and readnmlsecs[readpar] != nmlpars[readpar]: |
---|
437 | print errormsg |
---|
438 | print ' ' + main + ": readed parameter '" + readpar + "' in section '" + \ |
---|
439 | readnmlsecs[readpar] + "' should be in section '" + nmlpars[readpar] + \ |
---|
440 | "' !!" |
---|
441 | |
---|
442 | # Wrong type of value |
---|
443 | ## |
---|
444 | for readpar in readnmlvals.keys(): |
---|
445 | if nmlpark.has_key(readpar): |
---|
446 | pkind = nmlpark[readpar] |
---|
447 | readvals = readnmlvals[readpar].replace(' ','').split(',') |
---|
448 | Nreadvals = len(readvals) |
---|
449 | |
---|
450 | if Nreadvals > 1: |
---|
451 | for ival in range(Nreadvals): |
---|
452 | if len(readvals[ival]) > 0: |
---|
453 | ier = check_conversion(readvals[ival], pkind) |
---|
454 | if ier != 0: |
---|
455 | print errormsg |
---|
456 | print ' ' + main + ": wrong readed '" + readpar + \ |
---|
457 | "' value: '" + readvals[ival] + "' is not of type '" + \ |
---|
458 | pkind + "' !!" |
---|
459 | else: |
---|
460 | if readpar == 'max_dom': |
---|
461 | Ndomains = int(readvals[0]) |
---|
462 | |
---|
463 | else: |
---|
464 | ier = check_conversion(readnmlvals[readpar], pkind) |
---|
465 | if ier != 0: |
---|
466 | print errormsg |
---|
467 | print ' ' + main + ": wrong readed '" + readpar + "' value: '" + \ |
---|
468 | readvals + "' is not of type '", pkind, "' !!" |
---|
469 | |
---|
470 | # Checking number of values |
---|
471 | ## |
---|
472 | for readpar in readnmlvals.keys(): |
---|
473 | if nmlpard.has_key(readpar): |
---|
474 | if nmlpard[readpar] == 'max_domains': |
---|
475 | readvals = readnmlvals[readpar].replace(' ','').split(',') |
---|
476 | Nreadvals = len(readvals) |
---|
477 | # Removing spurious value due to ',' at the end of the parameter values in namelist |
---|
478 | if len(readvals[Nreadvals - 1]) == 0: Nreadvals = Nreadvals - 1 |
---|
479 | |
---|
480 | if Nreadvals < Ndomains: |
---|
481 | print errormsg |
---|
482 | print ' ' + main + ': wrong number of values: ', Nreadvals, '=', \ |
---|
483 | readnmlvals[readpar]," for parameter '" + readpar + "' should be:",\ |
---|
484 | Ndomains,'!!' |
---|