1515import argparse
1616import sys
1717import logging
18+ import math
1819
1920# Find and include the ccpp-framework scripts directory
2021# Assume we are in <CAMROOT>/cime_config and SPIN is in <CAMROOT>/ccpp_framework
3637from fortran_tools import FortranWriter
3738# pylint: enable=wrong-import-position
3839
40+ #Names for possible auto-generated loop variables:
41+ _LOOP_VARS = ["i" , "j" , "k" , "m" , "l" , "x" , "y" , "z" ]
42+
3943###############################################################################
4044def is_int (token ):
4145###############################################################################
@@ -239,6 +243,16 @@ def __init__(self, var_xml):
239243 self .__type = typ
240244 args = self ._parse_array_desc (alen , self .var_name )
241245 self .__array_lengths , self .__array_names = args
246+
247+ # Check if there are too many dimensions for the namelist array:
248+ if len (self .array_len ) > len (_LOOP_VARS ):
249+ emsg = f"Namelist variable '{ self .var_name } ' has "
250+ emsg += f"{ len (self .array_len )} dimensions,\n "
251+ emsg += "which is more than the limit of "
252+ emsg += f"{ len (_LOOP_VARS )} dimensions "
253+ emsg += "that is currently supported."
254+ raise IndexError (emsg )
255+
242256 if self .var_type == "character" :
243257 # character arguments at least require a length
244258 self .__valid &= self .kind is not None
@@ -648,10 +662,8 @@ def _write_metadata_file(self, nlvars, outdir, logger):
648662 # end for
649663 # end with
650664
651- def _write_nlread_file (self , nlvars , outdir , log_info ,
652- indent , mpi_obj , logger ):
653- """Write the namelist reading Fortran module to <outdir>
654- """
665+ def _write_nlread_file (self , nlvars , outdir , indent , mpi_obj , logger ):
666+ """Write the namelist reading Fortran module to <outdir>"""
655667 file_desc = f"Module to read namelist variables for { self .scheme } "
656668 # Collect all the kinds used in the file
657669 file_kinds = set ()
@@ -666,6 +678,7 @@ def _write_nlread_file(self, nlvars, outdir, log_info,
666678 if logger :
667679 logger .info (f"Writing Fortran module, { self .__nlread_file } " )
668680 # end if
681+
669682 with FortranWriter (self .__nlread_file , "w" , file_desc ,
670683 self .nlread_module , indent = indent ) as ofile :
671684 # Write out any kinds needed
@@ -689,10 +702,7 @@ def _write_nlread_file(self, nlvars, outdir, log_info,
689702 ofile .end_module_header ()
690703 ofile .blank_line ()
691704 # Write out the definition of the namelist reading function
692- nl_args = ["nl_unit" , "mpicomm" , "mpiroot" , "mpi_isroot" ]
693- if log_info :
694- nl_args .append ("logunit" )
695- # end if
705+ nl_args = ["nl_unit" , "mpicomm" , "mpiroot" , "mpi_isroot" , "logunit" ]
696706 args = f"({ ', ' .join (nl_args )} )"
697707 ofile .write (f"subroutine { self .nlread_func } { args } " , 1 )
698708 mpi = mpi_obj .mpi_module
@@ -701,6 +711,10 @@ def _write_nlread_file(self, nlvars, outdir, log_info,
701711 ofile .write (f"use { mpi } , { spc } only: { mpi_types } " , 2 )
702712 spc = ' ' * (len ("cam_abortutils" ) - len ("shr_nl_mod" ))
703713 ofile .write (f"use shr_nl_mod, { spc } only: shr_nl_find_group_name" , 2 )
714+ spc = ' ' * (len ("cam_abortutils" ) - len ("cam_logfile" ))
715+ ofile .write (f"use cam_logfile, { spc } only: debug_output, DEBUGOUT_INFO" , 2 )
716+ spc = ' ' * (len ("cam_abortutils" ) - len ("shr_kind_mod" ))
717+ ofile .write (f"use shr_kind_mod, { spc } only: cl=>shr_kind_cl" , 2 )
704718 ofile .write ("use cam_abortutils, only: endrun" , 2 )
705719 ofile .blank_line ()
706720 ofile .comment ("Dummy arguments" , 2 )
@@ -710,12 +724,33 @@ def _write_nlread_file(self, nlvars, outdir, log_info,
710724 ofile .write (f"{ comm_type } , intent(in) :: mpicomm" , 2 )
711725 ofile .write (f"integer,{ spc } intent(in) :: mpiroot" , 2 )
712726 ofile .write (f"logical,{ spc } intent(in) :: mpi_isroot" , 2 )
713- if log_info :
714- ofile .write (f"integer,{ spc } intent(in) :: logunit" , 2 )
715- # end if
727+ ofile .write (f"integer,{ spc } intent(in) :: logunit" , 2 )
716728 ofile .blank_line ()
717729 ofile .comment ("Local variables" , 2 )
718730 ofile .write ("integer :: ierr" , 2 )
731+ ofile .write ("character(len=cl) :: errmsg" , 2 )
732+
733+ # Add loop control variables if needed for pretty
734+ # printing array variables to the log file:
735+ #-----------------------------------------
736+
737+ # Determine what the largest number of dimensions
738+ # is for a given variable, as that is how many
739+ # variables we'll need to declare).
740+ max_dims = 0
741+ for grpvars in self .__groups .values ():
742+ for grpvar in grpvars :
743+ if grpvar .array_len :
744+ max_dims = max (max_dims , len (grpvar .array_len ))
745+
746+ # Write variable declarations needed for looping:
747+ if max_dims > 0 :
748+ loop_var_str = ', ' .join (_LOOP_VARS [:max_dims ])
749+ ofile .blank_line ()
750+ ofile .comment ("Used for namelist variable logging: " , 2 )
751+ ofile .write (f"integer :: { loop_var_str } " , 2 )
752+ #-----------------------------------------
753+
719754 substr = "character(len=*), parameter :: subname = '{}'"
720755 ofile .write (substr .format (self .nlread_func ), 2 )
721756 # Declare the namelists
@@ -735,34 +770,54 @@ def _write_nlread_file(self, nlvars, outdir, log_info,
735770 args = f"(nl_unit, '{ grpname } ', status=ierr)"
736771 ofile .write (f"call shr_nl_find_group_name{ args } " , 3 )
737772 ofile .write ("if (ierr == 0) then" , 3 )
738- ofile .write (f"read(nl_unit, { grpname } , iostat=ierr)" , 4 )
773+ ofile .write (f"read(nl_unit, { grpname } , iostat=ierr, iomsg=errmsg )" , 4 )
739774 ofile .write ("if (ierr /= 0) then" , 4 )
740- errmsg = f"ERROR reading namelist, { grpname } "
741- ofile .write (f"call endrun(subname//':: { errmsg } ')" , 5 )
775+ errmsg = f"ERROR reading namelist, { grpname } , with following error: "
776+ ofile .write (f"call endrun(subname//':: { errmsg } '//errmsg )" , 5 )
742777 ofile .write ("end if" , 4 )
743778 ofile .write ("else" , 3 )
744779 emsg = f"ERROR: Did not find namelist group, { grpname } ."
745780 ofile .write (f"call endrun(subname//':: { emsg } ')" , 4 )
746781 ofile .write ("end if" , 3 )
747- if log_info :
748- ofile .comment ("Print out namelist values" , 3 )
749- msg = f"Namelist values from { grpname } for { self .scheme } "
750- ofile .write (f"write(logunit, *) '{ msg } '" , 3 )
751- for grpvar in self .__groups [grpname ]:
782+
783+ ofile .comment ("Print out namelist values" , 3 )
784+ ofile .write ("if (debug_output >= DEBUGOUT_INFO) then" , 3 )
785+ msg = f"Namelist values from group '{ grpname } ' for scheme '{ self .scheme } '"
786+ ofile .write (f'write(logunit, *) "{ msg } "' , 4 )
787+ for grpvar in self .__groups [grpname ]:
788+ if grpvar .array_len :
789+ # do loop syntax:
790+ for dim_count , alen in enumerate (grpvar .array_len ):
791+ ofile .write (f"do { _LOOP_VARS [dim_count ]} =1,{ alen } " , 4 + dim_count )
792+
793+ # array printing syntax:
794+ loop_var_prt_str = ",','," .join (_LOOP_VARS [:(dim_count + 1 )])
795+ loop_fmt_str = "a" + ",i0,a" * (dim_count + 1 )
796+
797+ # generate actual Fortran write calls
798+ msg = f"'{ grpvar .var_name } (',{ loop_var_prt_str } ,') = '"
799+ ofile .write (f"write(logunit,'({ loop_fmt_str } )') { msg } " , 4 + (dim_count + 1 ))
800+
801+ loop_var_str = ',' .join (_LOOP_VARS [:(dim_count + 1 )])
802+ msg = f"{ grpvar .var_name } ({ loop_var_str } )"
803+ ofile .write (f"write(logunit, *) { msg } " ,4 + (dim_count + 1 ))
804+
805+ # 'end do' syntax (must be done backwards for correct indenting):
806+ for dim_count in range (len (grpvar .array_len ), 0 , - 1 ):
807+ ofile .write (f"end do" , 4 + (dim_count - 1 ))
808+ else :
752809 msg = f"'{ grpvar .var_name } = ', { grpvar .var_name } "
753- ofile .write (f"write(logunit, *) { msg } " , 3 )
754- # end for
755- # end if
756- ofile .write ("end if" , 2 )
810+ ofile .write (f"write(logunit, *) { msg } " , 4 )
811+ # end for
812+ ofile .write ("end if" , 3 ) #log level check
813+ ofile .write ("end if" , 2 ) #root task check
814+
757815 ofile .comment ("Broadcast the namelist variables" , 2 )
758816 for grpvar in self .__groups [grpname ]:
759817 arglist = [grpvar .var_name ]
760818 dimsize = 1
761819 if grpvar .array_len :
762- # XXgoldyXX: Can be replaced math.prod in python 3.8+
763- for alen in grpvar .array_len :
764- dimsize *= alen
765- # end for
820+ dimsize = math .prod (grpvar .array_len )
766821 dimstr = f"*{ dimsize } "
767822 else :
768823 dimstr = ""
@@ -787,13 +842,12 @@ def _write_nlread_file(self, nlvars, outdir, log_info,
787842 ofile .write (f"end subroutine { self .nlread_func } " , 1 )
788843 # end with
789844
790- def process_namelist_def_file (self , outdir , log_info , indent ,
791- mpi_obj , logger ):
845+ def process_namelist_def_file (self , outdir , indent , mpi_obj , logger ):
792846 """Read the namelist variables from <nlxml> and produce both a module
793847 with a routine that reads these variables into module variables and
794848 an associated CCPP metadata file.
795849 <outdir> is the directory where the output files are written.
796- If <log_info > is True, output statments to log namelist values
850+ <indent > is the number of whitespaces used when doing scope indentation
797851 <mpi_obj> is an MpiModuleInfo object used to generate the correct
798852 MPI Fortran statements.
799853 <logger> is a Python logger.
@@ -831,8 +885,7 @@ def process_namelist_def_file(self, outdir, log_info, indent,
831885 self ._write_metadata_file (nlvars , outdir , logger )
832886 # end if
833887 if not self .errors :
834- self ._write_nlread_file (nlvars , outdir , log_info ,
835- indent , mpi_obj , logger )
888+ self ._write_nlread_file (nlvars , outdir , indent , mpi_obj , logger )
836889 # end if
837890
838891 def group_names (self ):
@@ -933,7 +986,6 @@ def __init__(self, args, description, schema_paths=None, logger=None):
933986 # end if
934987 self .__mpi_obj = MpiModuleInfo (args .mpi_comm_arg , args .mpi_root_arg ,
935988 args .mpi_is_root_arg , args .mpi_module )
936- self .__logunit_arg = args .logunit_arg
937989 self .__indent = args .indent
938990 self .__xml_schema_dir = args .xml_schema_dir
939991 self .__namelist_read_mod = args .namelist_read_mod
@@ -976,10 +1028,6 @@ def _parse_command_line(self, args, description):
9761028 parser .add_argument ("--mpi-is-root-arg" , type = str , default = "mpi_isroot" ,
9771029 help = """Name of the logical communicator root
9781030 (.true. if the current task is root) input argument""" )
979- parser .add_argument ("--logunit-arg" , type = str , default = "logunit" ,
980- help = """Name of the output log input argument.
981- If this argument is not used, namelist schemes
982- will not log output.""" )
9831031 parser .add_argument ("--indent" , type = int , default = 3 ,
9841032 help = "Indent level for Fortran source code" )
9851033 parser .add_argument ("--xml-schema-dir" , type = str , default = _XML_SCHEMAS ,
@@ -1093,10 +1141,7 @@ def _write_active_scheme_reader(self, outdir, logger):
10931141 # Standard arguments
10941142 arglist = [self .nlfile_arg , self .active_schemes_arg ,
10951143 self .mpi_comm_arg , self .mpi_root_arg ,
1096- self .mpi_is_root_arg ]
1097- if self .log_values ():
1098- arglist .append (self .logunit_arg )
1099- # end if
1144+ self .mpi_is_root_arg , "logunit" ]
11001145 # Host-level namelist parameters
11011146 #XXgoldyXX: Need a process for this
11021147 args = ", " .join (arglist )
@@ -1125,9 +1170,7 @@ def _write_active_scheme_reader(self, outdir, logger):
11251170 ofile .write (f"{ int_input } :: { self .mpi_comm_arg } " , 2 )
11261171 ofile .write (f"{ int_input } :: { self .mpi_root_arg } " , 2 )
11271172 ofile .write (f"{ logical_input } :: { self .mpi_is_root_arg } " , 2 )
1128- if self .log_values ():
1129- ofile .write (f"{ int_input } :: { self .logunit_arg } " , 2 )
1130- # end if
1173+ ofile .write (f"{ int_input } :: logunit" , 2 )
11311174 ofile .blank_line ()
11321175 ofile .comment ("Local variable" , 2 )
11331176 ofile .write ("integer :: nl_unit" , 2 )
@@ -1136,10 +1179,8 @@ def _write_active_scheme_reader(self, outdir, logger):
11361179 open_args = f"newunit=nl_unit, file=trim({ self .nlfile_arg } )"
11371180 ofile .write (f"open({ open_args } , status='old')" , 2 )
11381181 nlarglist = ["nl_unit" , self .mpi_comm_arg ,
1139- self .mpi_root_arg , self .mpi_is_root_arg ]
1140- if self .log_values ():
1141- nlarglist .append (self .logunit_arg )
1142- # end if
1182+ self .mpi_root_arg , self .mpi_is_root_arg ,
1183+ "logunit" ]
11431184 nlargs = ", " .join (nlarglist )
11441185 for scheme_name in self .schemes ():
11451186 func_name = self .__scheme_nl_files [scheme_name ].nlread_func
@@ -1168,8 +1209,8 @@ def write_nl_files(self, outdir, logger):
11681209 errors = []
11691210 for scheme_name in self .schemes ():
11701211 scheme = self .__scheme_nl_files [scheme_name ]
1171- scheme .process_namelist_def_file (outdir , self .log_values () ,
1172- self .indent , self . mpi_obj , logger )
1212+ scheme .process_namelist_def_file (outdir , self .indent ,
1213+ self .mpi_obj , logger )
11731214 if scheme .errors :
11741215 errors .extend (scheme .errors )
11751216 # endif
@@ -1228,10 +1269,6 @@ def groups(self, schemes=None):
12281269 # end if
12291270 return sorted (groups )
12301271
1231- def log_values (self ):
1232- """Return True if namelist values should be written"""
1233- return isinstance (self .__logunit_arg , str ) and self .__logunit_arg
1234-
12351272 @property
12361273 def nlfile_arg (self ):
12371274 """Return the name of the input namelist file argument"""
@@ -1289,13 +1326,6 @@ def namelist_read_subname(self):
12891326 """
12901327 return self .__namelist_read_subname
12911328
1292- @property
1293- def logunit_arg (self ):
1294- """Return the dummy argument name for write statements used to log
1295- namelist variable values.
1296- """
1297- return self .__logunit_arg
1298-
12991329 @property
13001330 def indent (self ):
13011331 """Return the indent unit size (in spaces) for this NamelistFiles object
0 commit comments