Source code for pypec.namelist

"""
:mod:`pypec.namelist` -- IO for FORTRAN Namelists
=================================================

This module provides pythonic tools for reading and writing 
fortran namelists.

Examples
--------

This module can be used to convert FORTRAN namelist files into 
python ordered dictionary type objects. The individual namelists are 
capitalized and their paramters are left as written. The actual
parameter is thus stored two levels deep in the dictionary.

>>> nl=read('examples/example_namelist.in')
>>> nl.keys()
['GPEC_INPUT', 'GPEC_CONTROL', 'GPEC_OUTPUT', 'GPEC_DIAGNOSE']
>>> nl['GPEC_CONTROL'].keys()
['resp_index', 'sing_spot', 'reg_flag', 'reg_spot', 'chebyshev_flag', 'nche']
>>> nl['GPEC_CONTROL']['reg_spot']
0.05

The parameters can be changed, added, or deleted as in any python
dictionary.

>>> nl['GPEC_CONTROL']['reg_spot'] = 0.01
>>> nl['GPEC_CONTROL']['reg_spot']
0.01
>>> del nl['GPEC_CONTROL']['nche']
>>> nl['GPEC_CONTROL'].keys()
['resp_index', 'sing_spot', 'reg_flag', 'reg_spot', 'chebyshev_flag']
>>> nl['GPEC_CONTROL']['nche'] = 30

The resulting namelists can then be re-written in ASCII format
for use with the FORTAN codes.

>>> write(nl,'examples/example_namelist_edit.in')
True

.. note:: 
 These examples can be tested by developers using ipython as follows:

   In [1]: import pypec.namelist,doctest

   In [2]: doctest.testmod(pypec.namelist,verbose=True)

"""
"""
    @package gpec
    @author NC Logan
    @email nlogan@pppl.gov
"""

from collections import OrderedDict

[docs]class Objectify(object): """ Class base to convert iterable to instance. """ def __init__(self, d): """ Recursively convert iterable to instance. :param d: iterable. Dictionary that will be converted. """ for a, b in d.items(): if isinstance(b, (list, tuple)): setattr(self, a, [Objectify(x) if isinstance(x, dict) else x for x in b]) else: setattr(self, a, Objectify(b) if isinstance(b, dict) else b) def _todict(self): """ Converts instance back to dictionary. """ d = OrderedDict() for attr in dir(self): if not attr.startswith('__') and not attr=='_todict': val = getattr(self, attr) if isinstance(val, (list, tuple)): d[attr] = [x._todict() if isinstance(x, Objectify) else x for x in val] else: d[attr] = val._todict() if isinstance(val, Objectify) else val return d
def _string_to_type(str): """ Convert a string representing a fortran namelist value to the appropriate python type object. :param str : str. String to convert :returns: obj. Python object of appropriate type. """ try: pyobj=int(str) except ValueError: try: pyobj=float(str) except ValueError: if str.lower().startswith('.t') or str.lower().startswith('t'): pyobj=True elif str.lower().startswith('.f') or str.lower().startswith('f'): pyobj=False #complex written as tuple elif str.startswith('(') and str.endswith(')'): fpair = list(map(float,str.translate(str.maketrans(dict.fromkeys('() '))).split(','))) pyobj=complex(*fpair) #lists written using spaces or repetition elif ('*' in str or ' ' in str or ',' in str) and str.translate(str.maketrans(dict.fromkeys(',.* '))).isalnum(): pyobj=[] str = str.replace(',',' ') for i in str.split(): if '*' in i: n,v = i.split('*') pyobj+=int(n)*[_string_to_type(v)] else: pyobj+=[_string_to_type(i)] #default to string else: pyobj=str.replace("'","").replace('"','') return pyobj
[docs]def read(filename,out_type='dict',comment='!',old=False): """ Read in a file. Must have namelists in series, not embedded. Assumes name of list preceded by '&' and list ends with a single backslash. :param filename: str. Path to input namelist. :param out_type: str. Can be either 'dict' or 'obj'. :param comment: str. Lines starting with any of these characters are considered annotations. :returns: dict (object). Top keys (attributes) are namelists with sub-key(-attribute) parameter values. .. note:: The returned dict is actually "OrderedDict" type from collections module. If returning an object, the object is un-ordered. """ if not out_type in ['dict','obj']: raise ValueError("Must output 'dict' or 'obj' type.") nldict = OrderedDict() if old: grp = '$' end = '/' # $END gets mixed up with the $ group labeling else: grp = '&' end = '/' with open(filename,'r') as f: filestr = f.read() #handle $END block specification filestr = filestr.replace('$end','$END').replace('$END','/') #handle unnamed groups if grp not in filestr: print('No group names found: placing all in blank group.') filestr=grp+' \n'+filestr+'\n/' if '=' in filestr[0:filestr.index(grp)]: print('Group with no name found in begining: placing in blank group.') filestr = grp+' \n'+filestr[0:filestr.index(grp)]+'\n'+end+'\n'+filestr[filestr.index(grp):] if '=' in filestr[filestr.rfind(end):]: print('Group with no name found in end: placing in blank group.') filestr = filestr[0:filestr.rfind(end)+1]+'\n'+grp+' \n'+filestr[filestr.rfind(end)+1:]+'\n'+end #remove tabs and split lines nlist = filestr.translate(str.maketrans(dict.fromkeys('\t'))).replace(grp,grp+'\n').split('\n') #prevent errors in searching for & or /, without condensing list values nlist=[nlistkey.strip() for nlistkey in nlist] start=0 stop=0 it=0 while stop < len(nlist): it+=1 start = nlist.index(grp,stop)+1 stop = nlist.index(end,stop+2)-1 name = nlist[start].strip().upper() nldict[name] = OrderedDict() #loop through each list, skiping empty lines #for item in [line for line in nlist[start+1:stop+1] if line.strip()]: for item in nlist[start+1:stop+1]: if (item.lstrip()+' ')[0] in comment: # comment iname,ival = item,None nldict[name][item]=None elif '=' in item: # parameter iname = str.strip(item.split('=')[0]) ival = item.split('=')[1] # note there could be another '=' in the comments for c in comment: ival = str.strip(ival.split(c)[0]) # allow same line comments try: nldict[name][iname]=_string_to_type(ival) except: raise ValueError("Failed to read "+name+" due to line: "+item) else: continue try: test=nlist.index(grp,stop) except ValueError: stop=len(nlist) if it>100: stop = len(nlist) #prevent inf loop if bug if out_type=='obj': return Objectify(nldict) return nldict
[docs]def write(nl,filename,old=False): """ Write namelist object to file for fortran interface. :param nl: object. namelist object from read(). Can be dictionary or Objectify type class. :param filename: str. Path of file to be written. :returns: bool. True. """ if type(nl) == Objectify: print('Warning: lists from objects are un-ordered.') nl = nl._todict() if old: start = '$' end = '$END' else: start = '&' end = '/' with open(filename,'w') as f: f.write('\n') #vacuum code requires first line in vac.in for key in nl: f.write(start*bool(key)+key+'\n') for subkey in nl[key]: if type(nl[key][subkey])==bool: f.write(' '+subkey+' = .'+str(nl[key][subkey]).upper()+'.') elif type(nl[key][subkey])==complex: fortranfmt = str(nl[key][subkey]).replace('+',',').replace('j','') f.write(' '+subkey+' = '+fortranfmt+'') elif type(nl[key][subkey])==str: f.write(' '+subkey+" = '"+str(nl[key][subkey])+"'") elif type(nl[key][subkey])==list: spacedlist = str(nl[key][subkey]).translate(str.maketrans(dict.fromkeys(',[]'))) f.write(' '+subkey+' = '+spacedlist+'') elif type(nl[key][subkey]) is None: # comments f.write(' '+subkey) else: f.write(' '+subkey+' = '+str(nl[key][subkey])) f.write('\n') f.write(end*bool(key)+'\n') return True
def _write_rst(nl,name='',filename='namelist.rst'): """ Write namelist object to rst file for sphinx documentation including tooltips. :param nl : object. namelist object from read. :param name : str. Name of namelist file. :param filename : str. Path of file to be written. :returns: bool. True. """ import _tooltips_ reload(_tooltips_) if type(nl) == Objectify: print('Warning: lists from objects are un-ordered.') nl = nl._todict() with open(filename,'w') as f: f.write(name.upper()+ ' Namelist Inputs\n') #Title f.write('*'*len(name)+'****************\n\n') for key in nl: f.write(key+'\n') f.write('='*len(key)+'\n\n') for subkey in nl[key]: if type(nl[key][subkey])==bool: f.write('**'+subkey+'**'+' = .'+str(nl[key][subkey]).upper()+'.\n') elif type(nl[key][subkey])==complex: fortranfmt = str(nl[key][subkey]).replace('+',',').replace('j','') f.write('**'+subkey+'**'+' = '+fortranfmt+'\n') elif type(nl[key][subkey])==str: f.write('**'+subkey+'**'+' = "'+str(nl[key][subkey])+'"\n') elif type(nl[key][subkey])==list: spacedlist = str(nl[key][subkey]).translate(str.maketrans(dict.fromkeys(',[]'))) f.write('**'+subkey+'**'+' = '+spacedlist+'\n') else: f.write('**'+subkey+'**'+' = '+str(nl[key][subkey])+'\n') if subkey in _tooltips_.alltips: f.write(' '+_tooltips_.alltips[subkey]+'\n')#.replace('\n','')+'\n') else: f.write('\n') f.write('\n') f.write('\n\n') return True