예제 #1
0
def capgen(run_env):
    ###############################################################################
    """Parse indicated host, scheme, and suite files.
    Generate code to allow host model to run indicated CCPP suites."""
    ## A few sanity checks
    ## Make sure output directory is legit
    if os.path.exists(run_env.output_dir):
        if not os.path.isdir(run_env.output_dir):
            errmsg = "output-root, '{}', is not a directory"
            raise CCPPError(errmsg.format(run_env.output_root))
        # end if
        if not os.access(run_env.output_dir, os.W_OK):
            errmsg = "Cannot write files to output-root ({})"
            raise CCPPError(errmsg.format(run_env.output_root))
        # end if (output_dir is okay)
    else:
        # Try to create output_dir (let it crash if it fails)
        os.makedirs(run_env.output_dir)
    # end if
    host_files = run_env.host_files
    host_name = run_env.host_name
    scheme_files = run_env.scheme_files
    # We need to create three lists of files, hosts, schemes, and SDFs
    host_files = create_file_list(run_env.host_files, ['meta'], 'Host',
                                  run_env.logger)
    scheme_files = create_file_list(run_env.scheme_files, ['meta'], 'Scheme',
                                    run_env.logger)
    sdfs = create_file_list(run_env.suites, ['xml'], 'Suite', run_env.logger)
    check_for_writeable_file(run_env.datatable_file, "Cap output datatable")
    ##XXgoldyXX: Temporary warning
    if run_env.generate_docfiles:
        raise CCPPError("--generate-docfiles not yet supported")
    # end if
    # First up, handle the host files
    host_model = parse_host_model_files(host_files, host_name, run_env)
    # Next, parse the scheme files
    scheme_headers, scheme_tdict = parse_scheme_files(scheme_files, run_env)
    ddts = host_model.ddt_lib.keys()
    if ddts and run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG):
        run_env.logger.debug("DDT definitions = {}".format(ddts))
    # end if
    plist = host_model.prop_list('local_name')
    if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG):
        run_env.logger.debug("{} variables = {}".format(
            host_model.name, plist))
        run_env.logger.debug("schemes = {}".format(
            [x.title for x in scheme_headers]))
    # Finally, we can get on with writing suites
    # Make sure to write to temporary location if files exist in <output_dir>
    if not os.path.exists(run_env.output_dir):
        # Try to create output_dir (let it crash if it fails)
        os.makedirs(run_env.output_dir)
        # Nothing here, use it for output
        outtemp_dir = run_env.output_dir
    elif not os.listdir(run_env.output_dir):
        # Nothing here, use it for output
        outtemp_dir = run_env.output_dir
    else:
        # We need to create a temporary staging area, create it here
        outtemp_name = "ccpp_temp_scratch_dir"
        outtemp_dir = os.path.join(run_env.output_dir, outtemp_name)
        if os.path.exists(outtemp_dir):
            remove_dir(outtemp_dir, force=True)
        # end if
        os.makedirs(outtemp_dir)
    # end if
    ccpp_api = API(sdfs, host_model, scheme_headers, run_env)
    cap_filenames = ccpp_api.write(outtemp_dir, run_env)
    if run_env.generate_host_cap:
        # Create a cap file
        host_files = [
            write_host_cap(host_model, ccpp_api, outtemp_dir, run_env)
        ]
    else:
        host_files = list()
    # end if
    # Create the kinds file
    kinds_file = create_kinds_file(run_env, outtemp_dir)
    # Move any changed files to output_dir and remove outtemp_dir
    move_modified_files(outtemp_dir,
                        run_env.output_dir,
                        overwrite=run_env.force_overwrite,
                        remove_src=True)
    # We have to rename the files we created
    if outtemp_dir != run_env.output_dir:
        replace_paths(cap_filenames, outtemp_dir, run_env.output_dir)
        replace_paths(host_files, outtemp_dir, run_env.output_dir)
        kinds_file = kinds_file.replace(outtemp_dir, run_env.output_dir)
    # end if
    # Finally, create the database of generated files and caps
    # This can be directly in output_dir because it will not affect dependencies
    src_dir = os.path.join(__FRAMEWORK_ROOT, "src")
    generate_ccpp_datatable(run_env, host_model, ccpp_api, scheme_headers,
                            scheme_tdict, host_files, cap_filenames,
                            kinds_file, src_dir)
예제 #2
0
def parse_preamble_data(statements, pobj, spec_name, endmatch, logger):
    """Parse module variables or DDT definitions from a module preamble
    or parse program variables from the beginning of a program.
    """
    inspec = True
    mheaders = list()
    var_dict = VarDictionary(spec_name)
    psrc = ParseSource(spec_name, 'MODULE', pobj)
    active_table = None
    if logger is not None:
        ctx = context_string(pobj, nodir=True)
        msg = "Parsing preamble variables of {}{}"
        logger.debug(msg.format(spec_name, ctx))
    # End if
    while inspec and (statements is not None):
        while len(statements) > 0:
            statement = statements.pop(0)
            # End program or module
            pmatch = endmatch.match(statement)
            asmatch = _ARG_TABLE_START_RE.match(statement)
            type_def = fortran_type_definition(statement)
            if asmatch is not None:
                active_table = asmatch.group(1)
            elif (pmatch is not None) or is_contains_statement(statement,
                                                               inspec):
                # We are done with the specification
                inspec = False
                # Put statement back so caller knows where we are
                statements.insert(0, statement)
                # Add the header (even if we found no variables)
                mheader = MetadataTable(table_name_in=spec_name,
                                        table_type_in='module',
                                        module=spec_name,
                                        var_dict=var_dict, logger=logger)
                mheaders.append(mheader)
                if logger is not None:
                    ctx = context_string(pobj, nodir=True)
                    msg = 'Adding header {}{}'
                    logger.debug(msg.format(mheader.table_name, ctx))
                break
            elif ((type_def is not None) and
                  (type_def[0].lower() == active_table.lower())):
                # Put statement back so caller knows where we are
                statements.insert(0, statement)
                statements, ddt = parse_type_def(statements, type_def,
                                                 spec_name, pobj, logger)
                if ddt is None:
                    ctx = context_string(pobj, nodir=True)
                    msg = "No DDT found at '{}'{}"
                    raise CCPPError(msg.format(statement, ctx))
                # End if
                mheaders.append(ddt)
                if logger is not None:
                    ctx = context_string(pobj, nodir=True)
                    msg = 'Adding DDT {}{}'
                    logger.debug(msg.format(ddt.table_name, ctx))
                # End if
                active_table = None
            elif active_table is not None:
                # We should have a variable definition to add
                if ((not is_comment_statement(statement)) and
                    (not parse_use_statement(statement, logger)) and
                    (active_table.lower() == spec_name.lower())):
                    dvars = parse_fortran_var_decl(statement, psrc,
                                                   logger=logger)
                    for var in dvars:
                        var_dict.add_variable(var)
                    # End for
                # End if
            # End if (else we are not in an active table so just skip)
        # End while
        if inspec and (len(statements) == 0):
            statements = read_statements(pobj)
        # End if
    # End while
    return statements, mheaders
예제 #3
0
def write_host_cap(host_model, api, output_dir, run_env):
###############################################################################
    """Write an API to allow <host_model> to call any configured CCPP suite"""
    module_name = "{}_ccpp_cap".format(host_model.name)
    cap_filename = os.path.join(output_dir, '{}.F90'.format(module_name))
    if run_env.logger is not None:
        msg = 'Writing CCPP Host Model Cap for {} to {}'
        run_env.logger.info(msg.format(host_model.name, cap_filename))
    # End if
    header = _HEADER.format(host_model=host_model.name)
    with FortranWriter(cap_filename, 'w', header, module_name) as cap:
        # Write module use statements
        maxmod = len(KINDS_MODULE)
        cap.write('   use {kinds}'.format(kinds=KINDS_MODULE), 1)
        modules = host_model.variable_locations()
        if modules:
            mlen = max([len(x[0]) for x in modules])
            maxmod = max(maxmod, mlen)
        # End if
        mlen = max([len(x.module) for x in api.suites])
        maxmod = max(maxmod, mlen)
        maxmod = max(maxmod, len(CONST_DDT_MOD))
        for mod in modules:
            mspc = (maxmod - len(mod[0]))*' '
            cap.write("use {}, {}only: {}".format(mod[0], mspc, mod[1]), 1)
        # End for
        mspc = ' '*(maxmod - len(CONST_DDT_MOD))
        cap.write("use {}, {}only: {}".format(CONST_DDT_MOD, mspc,
                                              CONST_DDT_NAME), 1)
        cap.write_preamble()
        max_suite_len = 0
        for suite in api.suites:
            max_suite_len = max(max_suite_len, len(suite.module))
        # End for
        cap.write("! Public Interfaces", 1)
        # CCPP_STATE_MACH.transitions represents the host CCPP interface
        for stage in CCPP_STATE_MACH.transitions():
            stmt = "public :: {host_model}_ccpp_physics_{stage}"
            cap.write(stmt.format(host_model=host_model.name, stage=stage), 1)
        # End for
        API.declare_inspection_interfaces(cap)
        # Write the host-model interfaces for constituents
        reg_name = constituent_register_subname(host_model)
        cap.write("public :: {}".format(reg_name), 1)
        numconsts_name = constituent_num_consts_funcname(host_model)
        cap.write("public :: {}".format(numconsts_name), 1)
        copyin_name = constituent_copyin_subname(host_model)
        cap.write("public :: {}".format(copyin_name), 1)
        copyout_name = constituent_copyout_subname(host_model)
        cap.write("public :: {}".format(copyout_name), 1)
        cap.write("", 0)
        cap.write("! Private module variables", 1)
        const_dict = add_constituent_vars(cap, host_model, api.suites, run_env)
        cap.end_module_header()
        for stage in CCPP_STATE_MACH.transitions():
            # Create a dict of local variables for stage
            host_local_vars = VarDictionary("{}_{}".format(host_model.name,
                                                           stage), run_env)
            # Create part call lists
            # Look for any loop-variable mismatch
            for suite in api.suites:
                spart_list = suite_part_list(suite, stage)
                for spart in spart_list:
                    spart_args = spart.call_list.variable_list()
                    for sp_var in spart_args:
                        stdname = sp_var.get_prop_value('standard_name')
                        hvar = const_dict.find_variable(standard_name=stdname,
                                                        any_scope=True)
                        if hvar is None:
                            errmsg = 'No host model variable for {} in {}'
                            raise CCPPError(errmsg.format(stdname, spart.name))
                        # End if
                    # End for (loop over part variables)
                # End for (loop of suite parts)
            # End for (loop over suites)
            run_stage = stage == 'run'
            # All interfaces need the suite name
            apivars = [_SUITE_NAME_VAR]
            if run_stage:
                # Only the run phase needs a suite part name
                apivars.append(_SUITE_PART_VAR)
            # End if
            # Create a list of dummy arguments with correct intent settings
            callvars = host_model.call_list(stage) # Host interface dummy args
            hdvars = list()
            subst_dict = {}
            for hvar in callvars:
                protected = hvar.get_prop_value('protected')
                stdname = hvar.get_prop_value('standard_name')
                if stdname in CCPP_LOOP_VAR_STDNAMES:
                    protected = True # Cannot modify a loop variable
                # End if
                if protected:
                    subst_dict['intent'] = 'in'
                else:
                    subst_dict['intent'] = 'inout'
                # End if
                hdvars.append(hvar.clone(subst_dict,
                                         source_name=_API_SRC_NAME))
            # End for
            lnames = [x.get_prop_value('local_name') for x in apivars + hdvars]
            api_vlist = ", ".join(lnames)
            cap.write(_SUBHEAD.format(api_vars=api_vlist,
                                      host_model=host_model.name,
                                      stage=stage), 1)
            # Write out any suite part use statements
            for suite in api.suites:
                mspc = (max_suite_len - len(suite.module))*' '
                spart_list = suite_part_list(suite, stage)
                for spart in spart_list:
                    stmt = "use {}, {}only: {}"
                    cap.write(stmt.format(suite.module, mspc, spart.name), 2)
                # End for
            # End for
            # Write out any host model DDT input var use statements
            host_model.ddt_lib.write_ddt_use_statements(hdvars, cap, 2,
                                                        pad=max_suite_len)

            cap.write("", 1)
            # Write out dummy arguments
            for var in apivars:
                var.write_def(cap, 2, host_model)
            # End for
            for var in hdvars:
                var.write_def(cap, 2, host_model)
            # End for
            for var in host_local_vars.variable_list():
                var.write_def(cap, 2, host_model)
            # End for
            cap.write('', 0)
            # Write out the body clauses
            errmsg_name, errflg_name = api.get_errinfo_names()
            # Initialize err variables
            cap.write('{errflg} = 0'.format(errflg=errflg_name), 2)
            cap.write('{errmsg} = ""'.format(errmsg=errmsg_name), 2)
            else_str = ''
            for suite in api.suites:
                stmt = "{}if (trim(suite_name) == '{}') then"
                cap.write(stmt.format(else_str, suite.name), 2)
                if stage == 'run':
                    el2_str = ''
                    spart_list = suite_part_list(suite, stage)
                    for spart in spart_list:
                        pname = spart.name[len(suite.name)+1:]
                        stmt = "{}if (trim(suite_part) == '{}') then"
                        cap.write(stmt.format(el2_str, pname), 3)
                        call_str = suite_part_call_list(host_model, const_dict,
                                                        spart, True)
                        cap.write("call {}({})".format(spart.name, call_str), 4)
                        el2_str = 'else '
                    # End for
                    cap.write("else", 3)
                    emsg = "write({errmsg}, '(3a)')".format(errmsg=errmsg_name)
                    emsg += '"No suite part named ", '
                    emsg += 'trim(suite_part), '
                    emsg += '" found in suite {sname}"'.format(sname=suite.name)
                    cap.write(emsg, 4)
                    cap.write("{errflg} = 1".format(errflg=errflg_name), 4)
                    cap.write("end if", 3)
                else:
                    spart = suite.phase_group(stage)
                    call_str = suite_part_call_list(host_model, const_dict,
                                                    spart, False)
                    stmt = "call {}_{}({})"
                    cap.write(stmt.format(suite.name, stage, call_str), 3)
                # End if
                else_str = 'else '
            # End for
            cap.write("else", 2)
            emsg = "write({errmsg}, '(3a)')".format(errmsg=errmsg_name)
            emsg += '"No suite named ", '
            emsg += 'trim(suite_name), "found"'
            cap.write(emsg, 3)
            cap.write("{errflg} = 1".format(errflg=errflg_name), 3)
            cap.write("end if", 2)
            cap.write(_SUBFOOT.format(host_model=host_model.name,
                                      stage=stage), 1)
        # End for
        # Write the API inspection routines (e.g., list of suites)
        api.write_inspection_routines(cap)
        # Write the constituent initialization interfaces
        err_vars = host_model.find_error_variables()
        const_obj_name = constituent_model_object_name(host_model)
        cap.write("", 0)
        const_names_name = constituent_model_const_stdnames(host_model)
        const_indices_name = constituent_model_const_indices(host_model)
        ConstituentVarDict.write_host_routines(cap, host_model, reg_name,
                                               numconsts_name, copyin_name,
                                               copyout_name, const_obj_name,
                                               const_names_name,
                                               const_indices_name,
                                               api.suites, err_vars)
    # End with
    return cap_filename
예제 #4
0
def compare_fheader_to_mheader(meta_header, fort_header, logger):
    ###############################################################################
    """Compare a metadata header against the header generated from the
    corresponding code in the associated Fortran file.
    Return a string with any errors found (empty string is no errors).
    """
    errors_found = ''
    title = meta_header.title
    mht = meta_header.header_type
    fht = fort_header.header_type
    if mht != fht:
        # Special case, host metadata can be in a Fortran module or scheme
        if (mht != 'host') or (fht not in ('module', SCHEME_HEADER_TYPE)):
            errmsg = 'Metadata table type mismatch for {}, {} != {}{}'
            ctx = meta_header.start_context()
            raise CCPPError(
                errmsg.format(title, meta_header.header_type,
                              fort_header.header_type, ctx))
        # end if
    else:
        # The headers should have the same variables in the same order
        # The exception is that a Fortran module can have variable declarations
        # after all the metadata variables.
        mlist = meta_header.variable_list()
        mlen = len(mlist)
        flist = fort_header.variable_list()
        flen = len(flist)
        # Remove array references from mlist before checking lengths
        for mvar in mlist:
            if is_arrayspec(mvar.get_prop_value('local_name')):
                mlen -= 1
            # end if
        # end for
        list_match = mlen == flen
        # Check for optional Fortran variables that are not in metadata
        if flen > mlen:
            for find, fvar in enumerate(flist):
                lname = fvar.get_prop_value('local_name')
                _, mind = find_var_in_list(lname, mlist)
                if mind < 0:
                    if fvar.get_prop_value('optional'):
                        # This is an optional variable
                        flen -= 1
                    # end if
                # end if
            # end for
            list_match = mlen == flen
        # end if
        if not list_match:
            if fht in _EXTRA_VARIABLE_TABLE_TYPES:
                if flen > mlen:
                    list_match = True
                else:
                    etype = 'Fortran {}'.format(fht)
                # end if
            elif flen > mlen:
                etype = 'metadata header'
            else:
                etype = 'Fortran {}'.format(fht)
            # end if
        # end if
        if not list_match:
            errmsg = 'Variable mismatch in {}, variables missing from {}.'
            errors_found = add_error(errors_found, errmsg.format(title, etype))
        # end if
        for mind, mvar in enumerate(mlist):
            lname = mvar.get_prop_value('local_name')
            arrayref = is_arrayspec(lname)
            fvar, find = find_var_in_list(lname, flist)
            if mind >= flen:
                if arrayref:
                    # Array reference, variable not in Fortran table
                    pass
                elif fvar is None:
                    errmsg = 'No Fortran variable for {} in {}'
                    errors_found = add_error(errors_found,
                                             errmsg.format(lname, title))
                # end if (no else, we already reported an out-of-place error
                # Do not break to collect all missing variables
                continue
            # end if
            # At this point, we should have a Fortran variable
            if (not arrayref) and (fvar is None):
                errmsg = 'Variable mismatch in {}, no Fortran variable {}.'
                errors_found = add_error(errors_found,
                                         errmsg.format(title, lname))
                continue
            # end if
            # Check order dependence
            if fht in _ORDERED_TABLE_TYPES:
                if find != mind:
                    errmsg = 'Out of order argument, {} in {}'
                    errors_found = add_error(errors_found,
                                             errmsg.format(lname, title))
                    continue
                # end if
            # end if
            if arrayref:
                # Array reference, do not look for this in Fortran table
                continue
            # end if
            errs = var_comp('local_name', mvar, fvar, title)
            if errs:
                errors_found = add_error(errors_found, errs)
            else:
                errs = var_comp('type', mvar, fvar, title)
                if errs:
                    errors_found = add_error(errors_found, errs)
                # end if
                errs = var_comp('kind', mvar, fvar, title)
                if errs:
                    errors_found = add_error(errors_found, errs)
                # end if
                if meta_header.header_type == SCHEME_HEADER_TYPE:
                    errs = var_comp('intent', mvar, fvar, title)
                    if errs:
                        errors_found = add_error(errors_found, errs)
                    # end if
                # end if
                # Compare dimensions
                errs = dims_comp(meta_header, mvar, fvar, title, logger)
                if errs:
                    errors_found = add_error(errors_found, errs)
                # end if
            # end if
        # end for
    # end if
    return errors_found
예제 #5
0
def add_constituent_vars(cap, host_model, suite_list, run_env):
###############################################################################
    """Create a DDT library containing array reference variables
    for each constituent field for all suites in <suite_list>.
    Create and return a dictionary containing an index variable for each of the
    constituents as well as the variables from the DDT object.
    Also, write declarations for these variables to <cap>.
    Since the constituents are in a DDT (ccpp_constituent_properties_t),
    create a metadata table with the required information, then parse it
    to create the dictionary.
    """
    # First create a MetadataTable for the constituents DDT
    stdname_layer = "ccpp_constituents_num_layer_consts"
    stdname_interface = "ccpp_constituents_num_interface_consts"
    stdname_2d = "ccpp_constituents_num_2d_consts"
    horiz_dim = "horizontal_dimension"
    vert_layer_dim = "vertical_layer_dimension"
    vert_interface_dim = "vertical_interface_dimension"
    array_layer = "vars_layer"
    array_interface = "vars_interface"
    array_2d = "vars_2d"
    # Table preamble (leave off ccpp-table-properties header)
    ddt_mdata = [
        #"[ccpp-table-properties]",
        " name = {}".format(CONST_DDT_NAME), " type = ddt",
        "[ccpp-arg-table]",
        " name = {}".format(CONST_DDT_NAME), " type = ddt",
        "[ num_layer_vars ]",
        " standard_name = {}".format(stdname_layer),
        " units = count", " dimensions = ()", " type = integer",
        "[ num_interface_vars ]",
        " standard_name = {}".format(stdname_interface),
        " units = count", " dimensions = ()", " type = integer",
        "[ num_2d_vars ]",
        " standard_name = {}".format(stdname_2d),
        " units = count", " dimensions = ()", " type = integer",
        "[ {} ]".format(array_layer),
        " standard_name = ccpp_constituents_array_of_layer_consts",
        " units = none",
        " dimensions = ({}, {}, {})".format(horiz_dim, vert_layer_dim,
                                            stdname_layer),
        " type = real", " kind = kind_phys",
        "[ {} ]".format(array_interface),
        " standard_name = ccpp_constituents_array_of_interface_consts",
        " units = none",
        " dimensions = ({}, {}, {})".format(horiz_dim,
                                            vert_interface_dim,
                                            stdname_interface),
        " type = real", " kind = kind_phys",
        "[ {} ]".format(array_2d),
        " standard_name = ccpp_constituents_array_of_2d_consts",
        " units = none",
        " dimensions = ({}, {})".format(horiz_dim, stdname_2d),
        " type = real", " kind = kind_phys"]
    # Add entries for each constituent (once per standard name)
    const_stdnames = set()
    for suite in suite_list:
        if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG):
            lmsg = "Adding constituents from {} to {}"
            run_env.logger.debug(lmsg.format(suite.name, host_model.name))
        # end if
        scdict = suite.constituent_dictionary()
        for cvar in scdict.variable_list():
            std_name = cvar.get_prop_value('standard_name')
            if std_name not in const_stdnames:
                # Add a metadata entry for this constituent
                # Check dimensions and figure vertical dimension
                # Currently, we only support variables with first dimension,
                #   horizontal_dimension, and second (optional) dimension,
                #   vertical_layer_dimension or vertical_interface_dimension
                dims = cvar.get_dimensions()
                if (len(dims) < 1) or (len(dims) > 2):
                    emsg = "Unsupported constituent dimensions, '{}'"
                    dimstr = "({})".format(", ".join(dims))
                    raise CCPPError(emsg.format(dimstr))
                # end if
                hdim = dims[0].split(':')[-1]
                if hdim != 'horizontal_dimension':
                    emsg = "Unsupported first constituent dimension, '{}', "
                    emsg += "must be 'horizontal_dimension'"
                    raise CCPPError(emsg.format(hdim))
                # end if
                if len(dims) > 1:
                    vdim = dims[1].split(':')[-1]
                    if vdim == vert_layer_dim:
                        cvar_array_name = array_layer
                    elif vdim == vert_interface_dim:
                        cvar_array_name = array_interface
                    else:
                        emsg = "Unsupported vertical constituent dimension, "
                        emsg += "'{}', must be '{}' or '{}'"
                        raise CCPPError(emsg.format(vdim, vert_layer_dim,
                                                    vert_interface_dim))
                    # end if
                else:
                    cvar_array_name = array_2d
                # end if
                # First, create an index variable for <cvar>
                ind_std_name = "index_of_{}".format(std_name)
                loc_name = "{}(:,:,{})".format(cvar_array_name, ind_std_name)
                ddt_mdata.append("[ {} ]".format(loc_name))
                ddt_mdata.append(" standard_name = {}".format(std_name))
                units = cvar.get_prop_value('units')
                ddt_mdata.append(" units = {}".format(units))
                dimstr = "({})".format(", ".join(dims))
                ddt_mdata.append(" dimensions = {}".format(dimstr))
                vtype = cvar.get_prop_value('type')
                vkind = cvar.get_prop_value('kind')
                ddt_mdata.append(" type = {} | kind = {}".format(vtype, vkind))
                const_stdnames.add(std_name)
            # end if
        # end for
    # end for
    # Parse this table using a fake filename
    parse_obj = ParseObject("{}_constituent_mod.meta".format(host_model.name),
                            ddt_mdata)
    ddt_table = MetadataTable(run_env, parse_object=parse_obj)
    ddt_name = ddt_table.sections()[0].title
    ddt_lib = DDTLibrary('{}_constituent_ddtlib'.format(host_model.name),
                         run_env, ddts=ddt_table.sections())
    # A bit of cleanup
    del parse_obj
    del ddt_mdata
    # Now, create the "host constituent module" dictionary
    const_dict = VarDictionary("{}_constituents".format(host_model.name),
                               run_env, parent_dict=host_model)
    # Add in the constituents object
    prop_dict = {'standard_name' : "ccpp_model_constituents_object",
                 'local_name' : constituent_model_object_name(host_model),
                 'dimensions' : '()', 'units' : "None", 'ddt_type' : ddt_name}
    const_var = Var(prop_dict, _API_SOURCE, run_env)
    const_var.write_def(cap, 1, const_dict)
    ddt_lib.collect_ddt_fields(const_dict, const_var, run_env)
    # Declare variable for the constituent standard names array
    max_csname = max([len(x) for x in const_stdnames]) if const_stdnames else 0
    num_const_fields = len(const_stdnames)
    cs_stdname = constituent_model_const_stdnames(host_model)
    const_list = sorted(const_stdnames)
    if const_list:
        const_strs = ['"{}{}"'.format(x, ' '*(max_csname - len(x)))
                      for x in const_list]
        cs_stdame_initstr = " = (/ " + ", ".join(const_strs) + " /)"
    else:
        cs_stdame_initstr = ""
    # end if
    cap.write("character(len={}) :: {}({}){}".format(max_csname, cs_stdname,
                                                     num_const_fields,
                                                     cs_stdame_initstr), 1)
    # Declare variable for the constituent standard names array
    array_name = constituent_model_const_indices(host_model)
    cap.write("integer :: {}({}) = -1".format(array_name, num_const_fields), 1)
    # Add individual variables for each index var to the const_dict
    for index, std_name in enumerate(const_list):
        ind_std_name = "index_of_{}".format(std_name)
        ind_loc_name = "{}({})".format(array_name, index + 1)
        prop_dict = {'standard_name' : ind_std_name,
                     'local_name' : ind_loc_name, 'dimensions' : '()',
                     'units' : 'index', 'protected' : "True",
                     'type' : 'integer', 'kind' : ''}
        ind_var = Var(prop_dict, _API_SOURCE, run_env)
        const_dict.add_variable(ind_var, run_env)
    # end for
    # Add vertical dimensions for DDT call strings
    pver = host_model.find_variable(standard_name=vert_layer_dim,
                                    any_scope=False)
    if pver is not None:
        prop_dict = {'standard_name' : vert_layer_dim,
                     'local_name' : pver.get_prop_value('local_name'),
                     'units' : 'count', 'type' : 'integer',
                     'protected' : 'True', 'dimensions' : '()'}
        if const_dict.find_variable(standard_name=vert_layer_dim,
                                    any_scope=False) is None:
            ind_var = Var(prop_dict, _API_SOURCE, _API_DUMMY_RUN_ENV)
            const_dict.add_variable(ind_var, run_env)
        # end if
    # end if
    pver = host_model.find_variable(standard_name=vert_interface_dim,
                                    any_scope=False)
    if pver is not None:
        prop_dict = {'standard_name' : vert_interface_dim,
                     'local_name' : pver.get_prop_value('local_name'),
                     'units' : 'count', 'type' : 'integer',
                     'protected' : 'True', 'dimensions' : '()'}
        if const_dict.find_variable(standard_name=vert_interface_dim,
                                    any_scope=False) is None:
            ind_var = Var(prop_dict, _API_SOURCE, run_env)
            const_dict.add_variable(ind_var, run_env)
        # end if
    # end if

    return const_dict
def parse_module(pobj, statements, run_env):
    """Parse a Fortran MODULE and return any leftover statements
    and metadata tables encountered in the MODULE."""
    # The first statement should be a module statement, grab the name
    pmatch = _MODULE_RE.match(statements[0])
    if pmatch is None:
        raise ParseSyntaxError('MODULE statement', statements[0])
    # End if
    mod_name = pmatch.group(1)
    pobj.enter_region('MODULE', region_name=mod_name, nested_ok=False)
    if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG):
        ctx = context_string(pobj, nodir=True)
        msg = "Parsing Fortran module, {}{}"
        run_env.logger.debug(msg.format(mod_name, ctx))
    # End if
    # After the module name is the specification part
    statements, mtables = parse_specification(pobj, statements[1:], run_env,
                                              mod_name=mod_name)
    # Look for metadata tables
    statements = read_statements(pobj, statements)
    inmodule = pobj.in_region('MODULE', region_name=mod_name)
    active_table = None
    while inmodule and (statements is not None):
        while statements:
            statement = statements.pop(0)
            # End module
            pmatch = _ENDMODULE_RE.match(statement)
            asmatch = _ARG_TABLE_START_RE.match(statement)
            if asmatch is not None:
                active_table = asmatch.group(1)
            elif pmatch is not None:
                mod_name = pmatch.group(1)
                pobj.leave_region('MODULE', region_name=mod_name)
                inmodule = False
                break
            elif active_table is not None:
                statements, mheader = parse_scheme_metadata(statements, pobj,
                                                            mod_name,
                                                            active_table,
                                                            run_env)
                if mheader is not None:
                    title = mheader.table_name
                    if title in mtables:
                        errmsg = duplicate_header(mtables[title], mheader)
                        raise CCPPError(errmsg)
                    # end if
                    if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG):
                        mtype = mheader.table_type
                        ctx = mheader.start_context()
                        msg = "Adding metadata from {}, {}{}"
                        run_env.logger.debug(msg.format(mtype, title, ctx))
                    # End if
                    mtables.append(mheader)
                # End if
                active_table = None
                inmodule = pobj.in_region('MODULE', region_name=mod_name)
                break
            # End if
        # End while
        if inmodule and (statements is not None) and (len(statements) == 0):
            statements = read_statements(pobj)
        # End if
    # End while
    return statements, mtables
def parse_specification(pobj, statements, run_env, mod_name=None,
                        prog_name=None):
    """Parse specification part of a module or (sub)program"""
    if (mod_name is not None) and (prog_name is not None):
        raise ParseInternalError("<mod_name> and <prog_name> cannot both be used")
    # end if
    if mod_name is not None:
        spec_name = mod_name
        endmatch = _ENDMODULE_RE
        inmod = True
    elif prog_name is not None:
        spec_name = prog_name
        endmatch = _ENDPROGRAM_RE
        inmod = False
    else:
        raise ParseInternalError("One of <mod_name> or <prog_name> must be used")
    # End if
    if run_env.logger is not None:
        ctx = context_string(pobj, nodir=True)
        msg = "Parsing specification of {}{}"
        run_env.logger.debug(msg.format(spec_name, ctx))
    # End if

    inspec = True
    mtables = list()
    while inspec and (statements is not None):
        while len(statements) > 0:
            statement = statements.pop(0)
            # End program or module
            pmatch = endmatch.match(statement)
            asmatch = _ARG_TABLE_START_RE.match(statement)
            if pmatch is not None:
                # We never found a contains statement
                inspec = False
                break
            elif asmatch is not None:
                # Put table statement back to re-read
                statements.insert(0, statement)
                statements, new_tbls = parse_preamble_data(statements,
                                                           pobj, spec_name,
                                                           endmatch, run_env)
                for tbl in new_tbls:
                    title = tbl.table_name
                    if title in mtables:
                        errmsg = duplicate_header(mtables[title], tbl)
                        raise CCPPError(errmsg)
                    # end if
                    if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG):
                        ctx = tbl.start_context()
                        mtype = tbl.table_type
                        msg = "Adding metadata from {}, {}{}"
                        run_env.logger.debug(msg.format(mtype, title, ctx))
                    # End if
                    mtables.append(tbl)
                # End if
                inspec = pobj.in_region('MODULE', region_name=mod_name)
                break
            elif is_contains_statement(statement, inmod):
                inspec = False
                break
            # End if
        # End while
        if inspec and (len(statements) == 0):
            statements = read_statements(pobj)
        # End if
    # End while
    return statements, mtables
def parse_scheme_metadata(statements, pobj, spec_name, table_name, run_env):
    "Parse dummy argument information from a subroutine"
    psrc = None
    mheader = None
    var_dict = None
    scheme_name = None
    # Find the subroutine line, should be first executable statement
    inpreamble = False
    insub = True
    if run_env.logger and run_env.logger.isEnabledFor(logging.DEBUG):
        ctx = context_string(pobj, nodir=True)
        msg = "Parsing specification of {}{}"
        run_env.logger.debug(msg.format(table_name, ctx))
    # End if
    ctx = context_string(pobj) # Save initial context with directory
    vdict = None # Initialized when we parse the subroutine arguments
    while insub and (statements is not None):
        while statements:
            statement = statements.pop(0)
            smatch = _SUBROUTINE_RE.match(statement)
            esmatch = _END_SUBROUTINE_RE.match(statement)
            pmatch = _ENDMODULE_RE.match(statement)
            asmatch = _ARG_TABLE_START_RE.match(statement)
            if asmatch is not None:
                # We have run off the end of something, hope that is okay
                # Put this statement back for the caller to deal with
                statements.insert(0, statement)
                insub = False
                break
            # End if
            if pmatch is not None:
                # We have run off the end of the module, hope that is okay
                pobj.leave_region('MODULE', region_name=spec_name)
                insub = False
                break
            # End if
            if smatch is not None:
                scheme_name = smatch.group(1)
                inpreamble = scheme_name.lower() == table_name.lower()
                if inpreamble:
                    if smatch.group(2) is not None:
                        smstr = smatch.group(2).strip()
                        if len(smstr) > 0:
                            smlist = smstr.strip().split(',')
                        else:
                            smlist = list()
                        # End if
                        scheme_args = [x.strip().lower() for x in smlist]
                    else:
                        scheme_args = list()
                    # End if
                    # Create a dict template with all the scheme's arguments
                    # in the correct order
                    vdict = OrderedDict()
                    for arg in scheme_args:
                        if len(arg) == 0:
                            errmsg = 'Empty argument{}'
                            raise ParseInternalError(errmsg.format(pobj))
                        # End if
                        if arg in vdict:
                            errmsg = 'Duplicate dummy argument, {}'
                            raise ParseSyntaxError(errmsg.format(arg),
                                                   context=pobj)
                        # End if
                        vdict[arg] = None
                    # End for
                    psrc = ParseSource(scheme_name, 'scheme', pobj)
                # End if
            elif inpreamble:
                # Process a preamble statement (use or argument declaration)
                if esmatch is not None:
                    inpreamble = False
                    insub = False
                elif ((not is_comment_statement(statement)) and
                      (not parse_use_statement(statement, run_env)) and
                      is_dummy_argument_statement(statement)):
                    dvars = parse_fortran_var_decl(statement, psrc, run_env)
                    for var in dvars:
                        lname = var.get_prop_value('local_name').lower()
                        if lname in vdict:
                            if vdict[lname] is not None:
                                emsg = "Error: duplicate dummy argument, {}"
                                raise ParseSyntaxError(emsg.format(lname),
                                                       context=pobj)
                            # End if
                            vdict[lname] = var
                        else:
                            raise ParseSyntaxError('dummy argument',
                                                   token=lname, context=pobj)
                        # End if
                    # End for
                # End if
            # End if
        # End while
        if insub and (len(statements) == 0):
            statements = read_statements(pobj)
        # End if
    # End while
    # Check for missing declarations
    missing = list()
    if vdict is None:
        errmsg = 'Subroutine, {}, not found{}'
        raise CCPPError(errmsg.format(scheme_name, ctx))
    # End if
    for lname in vdict.keys():
        if vdict[lname] is None:
            missing.append(lname)
        # End if
    # End for
    if len(missing) > 0:
        errmsg = 'Missing local_variables, {} in {}'
        raise CCPPError(errmsg.format(missing, scheme_name))
    # End if
    var_dict = VarDictionary(scheme_name, run_env, variables=vdict)
    if (scheme_name is not None) and (var_dict is not None):
        mheader = MetadataTable(run_env, table_name_in=scheme_name,
                                table_type_in='scheme', module=spec_name,
                                var_dict=var_dict)
    # End if
    return statements, mheader
예제 #9
0
 def __init__(self, meta_tables, name_in, run_env):
     """Initialize this HostModel object.
     <meta_tables> is a list of parsed host metadata tables.
     <name_in> is the name for this host model.
     <run_env> is the CCPPFrameworkEnv object for this framework run.
     """
     self.__name = name_in
     self.__var_locations = {}  # Local name to module map
     self.__loop_vars = None  # Loop control vars in interface calls
     self.__used_variables = None  # Local names which have been requested
     self.__deferred_finds = None  # Used variables that were missed at first
     self.__run_env = run_env
     # First, process DDT headers
     meta_headers = list()
     for sect in [x.sections() for x in meta_tables.values()]:
         meta_headers.extend(sect)
     # end for
     # Initialize our dictionaries
     # Initialize variable dictionary
     super().__init__(self.name, run_env)
     self.__ddt_lib = DDTLibrary(
         '{}_ddts'.format(self.name),
         run_env,
         ddts=[d for d in meta_headers if d.header_type == 'ddt'])
     self.__ddt_dict = VarDictionary("{}_ddt_vars".format(self.name),
                                     run_env,
                                     parent_dict=self)
     # Now, process the code headers by type
     self.__metadata_tables = meta_tables
     for header in [h for h in meta_headers if h.header_type != 'ddt']:
         title = header.title
         if run_env.logger is not None:
             msg = 'Adding {} {} to host model'
             run_env.logger.debug(msg.format(header.header_type, title))
         # End if
         if header.header_type == 'module':
             # Set the variable modules
             modname = header.title
             for var in header.variable_list():
                 self.add_variable(var, run_env)
                 lname = var.get_prop_value('local_name')
                 self.__var_locations[lname] = modname
                 self.ddt_lib.check_ddt_type(var, header, lname=lname)
                 if var.is_ddt():
                     self.ddt_lib.collect_ddt_fields(
                         self.__ddt_dict, var, run_env)
                 # End if
             # End for
         elif header.header_type == 'host':
             if self.__name is None:
                 # Grab the first host name we see
                 self.__name = header.name
             # End if
             for var in header.variable_list():
                 self.add_variable(var, run_env)
                 self.ddt_lib.check_ddt_type(var, header)
                 if var.is_ddt():
                     self.ddt_lib.collect_ddt_fields(
                         self.__ddt_dict, var, run_env)
                 # End if
             # End for
             loop_vars = header.variable_list(std_vars=False,
                                              loop_vars=True,
                                              consts=False)
             if loop_vars:
                 # loop_vars are part of the host-model interface call
                 # at run time. As such, they override the host-model
                 # array dimensions.
                 self.__loop_vars = VarDictionary(self.name, run_env)
             # End if
             for hvar in loop_vars:
                 std_name = hvar.get_prop_value('standard_name')
                 if std_name not in self.__loop_vars:
                     self.__loop_vars.add_variable(hvar, run_env)
                 else:
                     ovar = self.__loop_vars[std_name]
                     ctx1 = context_string(ovar.context)
                     ctx2 = context_string(hvar.context)
                     lname1 = ovar.get_prop_value('local_name')
                     lname2 = hvar.get_prop_value('local_name')
                     errmsg = ("Duplicate host loop var for {n}:\n"
                               "  Dup:  {l1}{c1}\n  Orig: {l2}{c2}")
                     raise CCPPError(
                         errmsg.format(n=self.name,
                                       l1=lname1,
                                       c1=ctx1,
                                       l2=lname2,
                                       c2=ctx2))
                 # End if
             # End for
         else:
             errmsg = "Invalid host model metadata header type, {} ({}){}"
             errmsg += "\nType must be 'module' or 'host'"
             ctx = context_string(header.context)
             raise CCPPError(
                 errmsg.format(header.title, header.header_type, ctx))
         # End if
     # End while
     if self.name is None:
         errmsg = 'No name found for host model, add a host metadata entry'
         raise CCPPError(errmsg)
     # End if
     # Finally, turn on the use meter so we know which module variables
     #    to 'use' in a host cap.
     self.__used_variables = set()  # Local names which have been requested
     self.__deferred_finds = set(
     )  # Used variables that were missed at first