Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add script to check fortran vs metadata without a host model #558

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions scripts/ccpp_capgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,19 +312,30 @@ def compare_fheader_to_mheader(meta_header, fort_header, logger):
# end if
for mind, mvar in enumerate(mlist):
lname = mvar.get_prop_value('local_name')
mname = mvar.get_prop_value('standard_name')
arrayref = is_arrayspec(lname)
fvar, find = find_var_in_list(lname, flist)
# Check for consistency between optional variables in metadata and
# optional variables in fortran. Error if optional attribute is
# missing from fortran declaration.
# first check: if metadata says the variable is optional, does the fortran match?
mopt = mvar.get_prop_value('optional')
if find and mopt:
fopt = fvar.get_prop_value('optional')
if (not fopt):
errmsg = 'Missing optional attribute in fortran declaration for variable {}, in file {}'
errmsg = 'Missing "optional" attribute in fortran declaration for variable {}, for {}'
gold2718 marked this conversation as resolved.
Show resolved Hide resolved
errors_found = add_error(errors_found, errmsg.format(mname,title))
# end if
# end if
# now check: if fortran says the variable is optional, does the metadata match?
if fvar:
fopt = fvar.get_prop_value('optional')
mopt = mvar.get_prop_value('optional')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Harmless, but the line is not needed with line 322.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch - removed!

if (fopt and not mopt):
errmsg = 'Missing "optional" metadata property for variable {}, for {}'
gold2718 marked this conversation as resolved.
Show resolved Hide resolved
errors_found = add_error(errors_found, errmsg.format(mname, title))
# end if
# end if
if mind >= flen:
if arrayref:
# Array reference, variable not in Fortran table
Expand Down Expand Up @@ -511,15 +522,17 @@ def parse_host_model_files(host_filenames, host_name, run_env):
return host_model

###############################################################################
def parse_scheme_files(scheme_filenames, run_env):
def parse_scheme_files(scheme_filenames, run_env, known_ddts=None):
###############################################################################
"""
Gather information from scheme files (e.g., init, run, and finalize
methods) and return resulting dictionary.
"""
table_dict = {} # Duplicate check and for dependencies processing
header_dict = {} # To check for duplicates
known_ddts = list()
if not known_ddts:
known_ddts = list()
# end if
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orphan put out of his misery.

(removed)

logger = run_env.logger
for filename in scheme_filenames:
logger.info('Reading CCPP schemes from {}'.format(filename))
Expand Down
101 changes: 101 additions & 0 deletions scripts/fortran_tools/offline_check_fortran_vs_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/usr/bin/env python3
gold2718 marked this conversation as resolved.
Show resolved Hide resolved

"""
Recursively compare all fortran and metadata files in user-supplied directory, and report any problems
USAGE: set PYTHONPATH environment variable to the the scripts directory, then:
python offline_check_fortran_vs_metadata.py --directory <full path to directory with scheme files> (--debug)
"""


import sys
import os
import logging
import argparse

# CCPP framework imports
gold2718 marked this conversation as resolved.
Show resolved Hide resolved
from framework_env import CCPPFrameworkEnv
from fortran_tools import parse_fortran_file
from metadata_table import parse_metadata_file
from ccpp_capgen import find_associated_fortran_file
from ccpp_capgen import check_fortran_against_metadata, parse_scheme_files
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You aren't using check_fortran_against_metadata.
parse_scheme_files calls check_fortran_against_metadata.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed!

from parse_tools import init_log, set_log_level
from parse_tools import register_fortran_ddt_name
from parse_tools import CCPPError, ParseInternalError

_LOGGER = init_log(os.path.basename(__file__))
_DUMMY_RUN_ENV = CCPPFrameworkEnv(_LOGGER, ndict={'host_files':'',
'scheme_files':'',
'suites':''})
_CCPP_FRAMEWORK_DDT_TYPES = ["ccpp_hash_table_t",
"ccpp_hashable_t",
"ccpp_hashable_char_t",
"ccpp_constituent_prop_ptr_t"]



def find_files_to_compare(directory):
metadata_files = []
for root, _, files in os.walk(directory, topdown=True):
gold2718 marked this conversation as resolved.
Show resolved Hide resolved
for name in files:
if os.path.splitext(name)[1] == '.meta':
metadata_files.append(os.path.join(root, name))
# end if
# end for
# end for
return metadata_files

def compare_fortran_and_metadata(scheme_directory, run_env):
## Check for files
metadata_files = find_files_to_compare(scheme_directory)
# Pre-register base CCPP DDT types:
for ddt_name in _CCPP_FRAMEWORK_DDT_TYPES:
register_fortran_ddt_name(ddt_name)
# end for
# Perform checks
parse_scheme_files(metadata_files, run_env, known_ddts=['ccpp_constituent_prop_ptr_t'])

def parse_command_line(arguments, description):
"""Parse command-line arguments"""
parser = argparse.ArgumentParser(description=description,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("--directory", type=str, required=True,
metavar='top-level directory to analyze - REQUIRED',
help="""Full path to scheme directory""")
parser.add_argument("--debug", action='store_true', default=False,
help="""turn on debug mode for additional verbosity""")
pargs = parser.parse_args(arguments)
return pargs

def _main_func():
"""Parse command line, then parse indicated host, scheme, and suite files.
Finally, generate code to allow host model to run indicated CCPP suites."""
pargs = parse_command_line(sys.argv[1:], __doc__)
logger = _LOGGER
if pargs.debug:
set_log_level(logger, logging.DEBUG)
else:
set_log_level(logger, logging.INFO)
# end if
compare_fortran_and_metadata(pargs.directory, _DUMMY_RUN_ENV)
print('All checks passed!')

###############################################################################

if __name__ == "__main__":
try:
_main_func()
sys.exit(0)
except ParseInternalError as pie:
_LOGGER.exception(pie)
sys.exit(-1)
except CCPPError as ccpp_err:
if _LOGGER.getEffectiveLevel() <= logging.DEBUG:
_LOGGER.exception(ccpp_err)
else:
_LOGGER.error(ccpp_err)
# end if
sys.exit(1)
finally:
logging.shutdown()
# end try

6 changes: 2 additions & 4 deletions test/capgen_test/temp_adjust.F90
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ subroutine temp_adjust_run(foo, timestep, temp_prev, temp_layer, qv, ps, &
REAL(kind_phys), intent(in) :: temp_prev(:)
REAL(kind_phys), intent(inout) :: temp_layer(foo)
character(len=512), intent(out) :: errmsg
integer, optional, intent(out) :: errflg
integer, intent(out) :: errflg
real(kind_phys), optional, intent(in) :: innie
real(kind_phys), optional, intent(out) :: outie
real(kind_phys), optional, intent(inout) :: optsie
Expand All @@ -36,9 +36,7 @@ subroutine temp_adjust_run(foo, timestep, temp_prev, temp_layer, qv, ps, &
integer :: col_index

errmsg = ''
if (present(errflg)) then
errflg = 0
end if
errflg = 0

do col_index = 1, foo
temp_layer(col_index) = temp_layer(col_index) + temp_prev(col_index)
Expand Down
Loading