3535class Usermod ():
3636 """Class to hold information about a history usermod directory"""
3737
38- def __init__ (self , name , frequencies , dependencies = None ,
39- include_cosp = False , include_aerocom = False ):
38+ def __init__ (self , name , dirname , frequencies , usermods_dir ,
39+ dependencies = None , include_cosp = False , include_aerocom = False ):
4040 """Initialize a history usermod section"""
4141 self .__name = name
42+ self .__dirname = os .path .normpath (os .path .join (usermods_dir , dirname ))
4243 self .__freqset = set ([x .strip () for x in frequencies .split (',' )])
4344 if dependencies :
4445 self .__depends = [x .strip () for x in dependencies .split (',' )]
@@ -50,27 +51,32 @@ def __init__(self, name, frequencies, dependencies=None,
5051
5152 def unique_frequencies (self , usermod_dict ):
5253 """Return the output frequencies not covered by this section's dependencies.
53- It is an error if any dependency is not in usermod_dict.
54- It is an error if any frequency in a dependency is not in this section's dependencies."""
54+ """
5555 my_freqs = self .frequencies
5656 for dependency in self .dependencies :
57- if dependency in usermod_dict :
58- depmod = usermod_dict [dependency ]
59- if depmod .frequencies - self .frequencies :
60- raise ValueError (f"Section, '{ dependency } ' has frequencies not in '{ self .name } '" )
61- # end if
62- my_freqs -= depmod .frequencies
63- else :
64- raise ValueError (f"Dependency, '{ dependency } ', from { self .name } , not found" )
65- # end if
57+ depmod = usermod_dict [dependency ]
58+ my_freqs -= depmod .frequencies
6659 # end for
67- return my_freqs
60+ return sorted (my_freqs , key = lambda x : _HIST_FILEORDER .index (x ))
61+
62+ def namelist_file (self ):
63+ """Construct and return the namelist filename for this object"""
64+ return os .path .join (self .dirname , "user_nl_cam" )
65+
66+ def include_file (self ):
67+ """Construct and return the include usermods filename for this object"""
68+ return os .path .join (self .dirname , "include_user_mods" )
6869
6970 @property
7071 def name (self ):
7172 """Return the name for this object"""
7273 return self .__name
7374
75+ @property
76+ def dirname (self ):
77+ """Return the usermod subdirectory name for this object"""
78+ return self .__dirname
79+
7480 @property
7581 def frequencies (self ):
7682 """Return the frequencies for this object"""
@@ -127,13 +133,17 @@ def command_line(args):
127133 help = """Stop processing if any missing fields found.
128134 Default is to produce fieldlist files by ignoring any
129135 missing fields.""" )
136+ parser .add_argument ("--max-line" , type = int , default = 80 ,
137+ help = "Maximum line length for namelist files" )
130138 pargs = parser .parse_args (args )
131139 return (pargs .CMIP_file , pargs .CAM_file , pargs .usermods , pargs .cfgfile ,
132- pargs .overwrite , pargs .error_on_missing )
140+ pargs .overwrite , pargs .error_on_missing , pargs . max_line )
133141
134- def read_config_file (filename ):
142+ def read_config_file (filename , usermods_dir , overwrite ):
135143 """Read a fincl group configuration (ini-style) file.
136- Returns a dictionary of usermods sections with the section name as the key"""
144+ Returns a dictionary of usermods sections with the section name as the key.
145+ If any errors are found, print and return None"""
146+ errors = False
137147 usermod_dict = {}
138148 config = configparser .ConfigParser ()
139149 config .read (filename )
@@ -143,6 +153,7 @@ def read_config_file(filename):
143153 use_cosp = None
144154 include_aerocom = None
145155 frequencies = cfg_sect ['frequencies' ]
156+ dirname = cfg_sect ['usermod_dir' ]
146157 if 'include_usermods' in cfg_sect :
147158 depends = cfg_sect ['include_usermods' ]
148159 # end if
@@ -152,29 +163,66 @@ def read_config_file(filename):
152163 if 'use_aercom' in cfg_sect :
153164 include_aerocom = cfg_sect ['use_aercom' ]
154165 # end if
155- if section in usermod_dict :
156- raise ValueError ("Duplicate section, '{section}'" )
157- # end if
158- usermod_dict [section ] = Usermod (section , frequencies ,
166+ usermod_dict [section ] = Usermod (section , dirname , frequencies ,
167+ usermods_dir ,
159168 dependencies = depends ,
160169 include_cosp = use_cosp ,
161170 include_aerocom = include_aerocom )
162171 # end for
163- return usermod_dict
172+ # Check for errors
173+ for name , usermod in usermod_dict .items ():
174+ if name in usermod_dict :
175+ print (f"Duplicate section, '{ name } '" )
176+ errors = True
177+ # end if
178+ if not overwrite :
179+ path = usermod .namelist_file ()
180+ if os .path .exists (path ):
181+ print (f"namelist file, '{ path } ' exists and overwrite = False" )
182+ errors = True
183+ # end if
184+ # end if
185+ if not overwrite and usermod .dependencies :
186+ path = usermod .include_file ()
187+ if os .path .exists (path ):
188+ print (f"include file, '{ path } ' exists and overwrite = False" )
189+ errors = True
190+ # end if
191+ # end if
192+ for depend in usermod .dependencies :
193+ if depend == usermod .name :
194+ print (f"Invalid dependency, { depend } in section { depend } " )
195+ errors = True
196+ # end if
197+ # It is an error if any dependency is not in usermod_dict.
198+ if depend not in usermod_dict :
199+ print (f"Invalid dependency, '{ depend } ' not in config file" )
200+ errors = True
201+ # end if
202+ # end for
203+ # Check frequencies
204+ if not usermod .frequencies :
205+ print (f"Section, '{ name } ', contains no output frequencies" )
206+ errors = True
207+ elif if any ([x not in _HIST_FILEORDER for x in usermod .frequencies ]):
208+ unknown = list (set (usermod .frequencies ) - set (_HIST_FILEORDER ))
209+ freq = ', ' .join (unknown )
210+ print (f"Section, '{ section } ', contains unknown frequencies: { freq } " )
211+ errors = True
212+ else :
213+ for dependency in self .dependencies :
214+ depmod = usermod_dict [dependency ]
215+ if depmod .frequencies - usermod .frequencies :
216+ print (f"Section, '{ dependency } ' has frequencies not in '{ name } '" )
217+ errors = True
218+ # end if
164219
165- @contextlib .contextmanager
166- def flex_open (filename = None , mode = 'w' ):
167- if filename and filename != '-' :
168- fh = open (filename , mode )
169- else :
170- fh = sys .stdout
220+ # end if
221+ # end for
222+ if errors :
223+ return None
171224 # end if
172-
173- try :
174- yield fh
175- finally :
176- if fh is not sys .stdout :
177- fh .close ()
225+ return usermod_dict
178226
179227def read_fieldname_file (filename ):
180228 """Read a fieldname file and return all fieldnames as a list."""
@@ -260,10 +308,11 @@ def parse_spreadsheet(csvfile, model_name="atmos"):
260308 # end for
261309 return cmip_dict
262310
263- def check_for_missing_fieldnames (masterlist , data_request ):
311+ def check_for_missing_fieldnames (masterlist , data_request , request_name ):
264312 """Given a data request dictionary (<data_request>),
265313 check to see if any are not in <masterlist>.
266- Return a list of missing fields names (an empty list means none).
314+ Return a set of missing fields names (an empty list means none).
315+ Print out any missing fields.
267316 Clean <data_request> to remove missing field entries (side effect)."""
268317 # Gather the set of all fields (combine different frequencies)
269318 all_reqfields = set ()
@@ -281,65 +330,86 @@ def check_for_missing_fieldnames(masterlist, data_request):
281330 for field in sorted (missing ):
282331 print (f" { field } " )
283332 # end for
333+ print (f"These fields were found in the { request_name } data request spreadsheet" )
284334 # end if
285335 return missing
286336
287- def generate_namelist_entries (data_request , nl_filename , maxline = 125 , hist_files = _HIST_FILEORDER ):
288- """Write the set of namelist entries represented by <data_request> to
289- <nl_filename> (which may be standard output)."""
290- lbreak = ''
291- with flex_open (nl_filename , mode = "w" ) as outfile :
292- for index , freq in enumerate (hist_files ):
293- if freq in data_request :
294- if freq == 'subhr' :
295- avgflag = 'I'
296- else :
297- avgflag = 'A'
298- # end if
299- # Write history file config info
300- outfile .write (f"{ lbreak } { _HIST_TITLES [freq ]} \n " )
301- outfile .write (f"nhtfrq({ index + 1 } ) = { _HIST_FRQCODES [freq ]} \n " )
302- outfile .write (f"mfilt({ index + 1 } ) = { _HIST_MFILT [freq ]} \n " )
303- outfile .write (f"empty_htapes({ index + 1 } ) = .true.\n " )
304- fields = data_request [freq ]
305- fldstring = ', ' .join ([f"{ x } :{ avgflag } " for x in fields ])
306- nlstr = f"fincl{ index + 1 } = { fldstring } "
307- # Write the fincl string with appropriate line breaks
308- begpos = 0
309- strlen = len (nlstr )
310- while begpos < strlen :
311- endpos = strlen
312- if endpos - begpos > maxline :
313- endpos = nlstr [0 :begpos + maxline ].rfind (' ' )
314- if endpos < begpos :
315- endpos = strlen
316- # end if
337+ def generate_namelist_entries (data_request , usermod_config , maxline ):
338+ """Write the sets of namelist entries represented by <data_request> to
339+ the usermods files defined in <usermod_config>."""
340+ for usermod in usermod_config .values ():
341+ lbreak = ''
342+ if not os .path .exists (usemod .dirname ):
343+ os .makedirs (usemod .dirname )
344+ # end if
345+ with open (usermod .namelist_file (), mode = "w" ) as outfile :
346+ for freq in usermod .unique_frequencies (usermod_config )
347+ if freq in data_request :
348+ # index is the fincl number for this frequency
349+ index = _HIST_FILEORDER .index (freq ) + 1
350+ if freq == 'subhr' :
351+ avgflag = 'I'
352+ else :
353+ avgflag = 'A'
317354 # end if
318- outfile .write (f"{ nlstr [begpos :endpos ]} \n " )
319- begpos = endpos
320- # end while
355+ # Write history file config info
356+ outfile .write (f"{ lbreak } { _HIST_TITLES [freq ]} \n " )
357+ outfile .write (f"nhtfrq({ index } ) = { _HIST_FRQCODES [freq ]} \n " )
358+ outfile .write (f"mfilt({ index } ) = { _HIST_MFILT [freq ]} \n " )
359+ outfile .write (f"empty_htapes({ index } ) = .true.\n " )
360+ fields = data_request [freq ]
361+ fldstring = ', ' .join ([f"{ x } :{ avgflag } " for x in fields ])
362+ nlstr = f"fincl{ index } = { fldstring } "
363+ # Write the fincl string with appropriate line breaks
364+ begpos = 0
365+ strlen = len (nlstr )
366+ while begpos < strlen :
367+ endpos = strlen
368+ if endpos - begpos > maxline :
369+ endpos = nlstr [0 :begpos + maxline ].rfind (' ' )
370+ if endpos < begpos :
371+ endpos = strlen
372+ # end if
373+ # end if
374+ outfile .write (f"{ nlstr [begpos :endpos ]} \n " )
375+ begpos = endpos
376+ # end while
377+ # end if
378+ lbreak = '\n '
379+ # end for
321380 # end if
322- lbreak = '\n '
323- # end for
324- # Now, write out combined namelist items
325- # end with
326-
381+ # end with (open file)
382+ # Write the include usermods file (if any)
383+ if usermod .dependencies :
384+ with open (username .include_file , mode = "w" ) as outfile :
385+ for depend in usermod .dependencies :
386+ depdir = os .path .basename (usermod_config [depend ].dirname )
387+ outfile .write (f"{ os .path .join (os .pardir , depdir )} \n " )
388+ # end for
389+ # end for
390+ # end if
391+ # end for (sections)
327392
328393###############################################################################
329394
330395if __name__ == "__main__" :
331396 arglist = command_line (sys .argv [1 :])
332- cmipfile , camfile , usermods_dir , configfile , overwrite , error = arglist
397+ cmipfile , camfile , usermods , configfile , overwrite , error , maxline = arglist
333398 # read configuration
334- usermod_dict = read_config_file (configfile )
335- all_fieldnames , cosp_fieldnames , aerocom_fieldnames = read_diagnostic_fieldnames ()
336- cmip7_request = parse_spreadsheet (cmipfile )
337- missing7 = check_for_missing_fieldnames (all_fieldnames , cmip7_request , "CMIP7" )
338- cam_request = parse_spreadsheet (camfile )
339- missingc = check_for_missing_fieldnames (all_fieldnames , cmip7_request , "CAM" )
340- if error and (missing7 or missingc ):
341- print ("Missing fields found, not producing any namelist usermods files" )
342- # else:
343- # generate_namelist_entries(data_request, , maxline=80)
399+ usermod_dict = read_config_file (configfile , usermods , overwrite )
400+ if usermod_dict :
401+ fieldnames = read_diagnostic_fieldnames ()
402+ all_fieldnames , cosp_fieldnames , aerocom_fieldnames = fieldnames
403+ cmip7_request = parse_spreadsheet (cmipfile )
404+ missing7 = check_for_missing_fieldnames (all_fieldnames , cmip7_request ,
405+ "CMIP7" )
406+ cam_request = parse_spreadsheet (camfile )
407+ missingc = check_for_missing_fieldnames (all_fieldnames , cmip7_request ,
408+ "CAM" )
409+ if error and (missing7 or missingc ):
410+ print ("Missing fields found, not producing any namelist usermods files" )
411+ else :
412+ generate_namelist_entries (data_request , usermod_dict , maxline )
413+ # end if
344414 # end if
345415 sys .exit (0 )
0 commit comments