diff --git a/CIME/XML/component.py b/CIME/XML/component.py index b3834a17c91..abd09c86fd0 100644 --- a/CIME/XML/component.py +++ b/CIME/XML/component.py @@ -327,7 +327,7 @@ def print_values(self): for entry in entries: name = self.get(entry, "id") text = self.text(self.get_child("desc", root=entry)) - logger.info(" {:20s} : {}".format(name, text.encode("utf-8"))) + logger.info(" {:20s} : {}".format(name, text)) def return_values(self): """ diff --git a/CIME/XML/grids.py b/CIME/XML/grids.py index 7b274587323..60bc1ff3d02 100644 --- a/CIME/XML/grids.py +++ b/CIME/XML/grids.py @@ -121,25 +121,13 @@ def _valid_lname(self, name): break return valid - def _read_config_grids(self, name, compset, atmnlev, lndnlev): + def _read_config_grids(self, name, compset, atmnlev=None, lndnlev=None): """ read config_grids.xml with version 2.0 schema Returns a grid long name given the alias ('name' argument) """ - model_grid = {} - for comp_gridname in self._comp_gridnames: - model_grid[comp_gridname] = None - - # (1) set array of component grid defaults that match current compset grids_node = self.get_child("grids") - grid_defaults_node = self.get_child("model_grid_defaults", root=grids_node) - for grid_node in self.get_children("grid", root=grid_defaults_node): - name_attrib = self.get(grid_node, "name") - compset_attrib = self.get(grid_node, "compset") - compset_match = re.search(compset_attrib, compset) - if compset_match is not None: - model_grid[name_attrib] = self.text(grid_node) # (2)loop over all of the "model grid" nodes and determine is there an alias match with the # input grid name - if there is an alias match determine if the "compset" and "not_compset" @@ -200,37 +188,65 @@ def _read_config_grids(self, name, compset, atmnlev, lndnlev): foundcompset, "grid alias {} not valid for compset {}".format(name, compset) ) - # for the match - find all of the component grid settings + return self.get_grid_longname( + grids_node, model_gridnode, compset, atmnlev, lndnlev + ) + + def get_grid_longname( + self, grids_node, model_gridnode, compset=None, atmnlev=None, lndnlev=None + ): + model_grid = {} + + for comp_gridname in self._comp_gridnames: + model_grid[comp_gridname] = None + + if compset is not None: + grid_defaults_node = self.get_child("model_grid_defaults", root=grids_node) + + for grid_node in self.get_children("grid", root=grid_defaults_node): + name_attrib = self.get(grid_node, "name") + compset_attrib = self.get(grid_node, "compset") + compset_match = re.search(compset_attrib, compset) + + if compset_match is not None: + model_grid[name_attrib] = self.text(grid_node) + grid_nodes = self.get_children("grid", root=model_gridnode) + for grid_node in grid_nodes: name = self.get(grid_node, "name") value = self.text(grid_node) + if model_grid[name] != "null": model_grid[name] = value + mask_node = self.get_optional_child("mask", root=model_gridnode) + if mask_node is not None: model_grid["mask"] = self.text(mask_node) else: model_grid["mask"] = model_grid["ocnice"] lname = "" + for component_gridname in self._comp_gridnames: if lname: lname = lname + "_" + grid_prefix[component_gridname] else: lname = grid_prefix[component_gridname] + if model_grid[component_gridname] is not None: lname += model_grid[component_gridname] + if component_gridname == "atm" and atmnlev is not None: if not ("a{:n}ull" in lname): lname += "z" + atmnlev - elif component_gridname == "lnd" and lndnlev is not None: if not ("l{:n}ull" in lname): lname += "z" + lndnlev - else: lname += "null" + return lname def _get_domains(self, component_grids, atmlevregex, lndlevregex, driver): @@ -539,7 +555,7 @@ def _get_gridmaps_for_one_grid_pair( for name, value in these_gridmaps.items(): _add_grid_info(gridmaps, name, value) - def print_values(self, long_output=None): + def print_values(self, long=False): # write out help message helptext = self.get_element_text("help") logger.info("{} ".format(helptext)) @@ -573,7 +589,7 @@ def print_values(self, long_output=None): ) domains = {} - if long_output is not None: + if long: domain_nodes = self.get_children("domain", root=self.get_child("domains")) for domain_node in domain_nodes: name = self.get(domain_node, "name") @@ -599,7 +615,8 @@ def print_values(self, long_output=None): desc, files ) - model_grid_nodes = self.get_children("model_grid", root=self.get_child("grids")) + grids_node = self.get_child("grids") + model_grid_nodes = self.get_children("model_grid", root=grids_node) for model_grid_node in model_grid_nodes: alias = self.get(model_grid_node, "alias") compset = self.get(model_grid_node, "compset") @@ -616,6 +633,8 @@ def print_values(self, long_output=None): grid_nodes = self.get_children("grid", root=model_grid_node) grids = "" gridnames = [] + lname = self.get_grid_longname(grids_node, model_grid_node) + logger.info("\n{:<7}longname: {}".format(" ", lname)) for grid_node in grid_nodes: gridnames.append(self.text(grid_node)) grids += self.get(grid_node, "name") + ":" + self.text(grid_node) + " " @@ -623,11 +642,18 @@ def print_values(self, long_output=None): mask_nodes = self.get_children("mask", root=model_grid_node) for mask_node in mask_nodes: logger.info(" mask is: {}".format(self.text(mask_node))) - if long_output is not None: + if long: gridnames = set(gridnames) for gridname in gridnames: if gridname != "null": - logger.info(" {}".format(domains[gridname])) + try: + logger.info(" {}".format(domains[gridname])) + except KeyError: + logger.info( + " Could not provide domains for gridname {!r}".format( + gridname + ) + ) # ------------------------------------------------------------------------ diff --git a/CIME/scripts/query_config.py b/CIME/scripts/query_config.py index 37309ace5c2..7f4240c668c 100755 --- a/CIME/scripts/query_config.py +++ b/CIME/scripts/query_config.py @@ -6,106 +6,199 @@ information will be listed for each. """ +import os +import sys +import logging +import argparse + from CIME.Tools.standard_script_setup import * -import re -from CIME.utils import expect, get_cime_default_driver, deprecate_action +from CIME import utils from CIME.XML.files import Files from CIME.XML.component import Component from CIME.XML.compsets import Compsets from CIME.XML.grids import Grids +from CIME.XML.machines import Machines from CIME.config import Config -# from CIME.XML.machines import Machines -import CIME.XML.machines -from argparse import RawTextHelpFormatter - logger = logging.getLogger(__name__) -customize_path = os.path.join(CIME.utils.get_src_root(), "cime_config", "customize") +customize_path = os.path.join(utils.get_src_root(), "cime_config", "customize") config = Config.load(customize_path) -supported_comp_interfaces = list(config.driver_choices) +def _main_func(description=__doc__): + kwargs = parse_command_line(description) -def query_grids(files, long_output, xml=False): - """ - query all grids. - """ - config_file = files.get_value("GRIDS_SPEC_FILE") - expect( + if kwargs["grids"]: + query_grids(**kwargs) + + if kwargs["compsets"] is not None: + query_compsets(**kwargs) + + if kwargs["components"] is not None: + query_component_settings(**kwargs) + + if kwargs["machines"] is not None: + query_machines(**kwargs) + + +def parse_command_line(description): + files = {x: Files(x) for x in list(config.driver_choices)} + + compset_active_components = get_compset_active_components(files) + compset_active_components.extend(["all"]) + + components = get_components(files) + components.extend(["all"]) + + default_driver = config.driver_default + config_file = files[default_driver].get_value("MACHINES_SPEC_FILE") + utils.expect( os.path.isfile(config_file), "Cannot find config_file {} on disk".format(config_file), ) + machines = Machines(config_file, machine="Query") + machine_names = ["all", "current"] + machine_names.extend(machines.list_available_machines()) - grids = Grids(config_file) - if xml: - print("{}".format(grids.get_raw_record().decode("UTF-8"))) - elif long_output: - grids.print_values(long_output=long_output) - else: - grids.print_values() + parser = argparse.ArgumentParser( + description=description, formatter_class=argparse.RawDescriptionHelpFormatter + ) + config_group = parser.add_argument_group("Config options") -def query_machines(files, machine_name="all", xml=False): - """ - query machines. Defaule: all - """ - config_file = files.get_value("MACHINES_SPEC_FILE") - expect( + config_group.add_argument( + "--compsets", + nargs="?", + const="all", + choices=sorted(set(compset_active_components)), + help="Query compsets for active component. If no value is passed, compsets for all active components will be printed.", + ) + + config_group.add_argument( + "--components", + nargs="?", + const="all", + choices=sorted(set(components)), + help="Query settings for component. If not value is passed, settings for all components will be printed.", + ) + + config_group.add_argument( + "--grids", action="store_true", help="Query grids for model." + ) + + config_group.add_argument( + "--machines", + nargs="?", + const="all", + choices=machine_names, + help="Query machines for model. If not value is passed, all machines will be printed.", + ) + + output_group = parser.add_argument_group("Output options") + + output_group.add_argument( + "--long", action="store_true", help="Print extended output for queries." + ) + + output_group.add_argument("--xml", action="store_true", help="Print output in xml.") + + filter_group = parser.add_argument_group("Filter options") + + default_driver = config.driver_default + + filter_group.add_argument( + "--driver", + choices=config.driver_choices, + default=default_driver, + help=f"Filter by driver, defaults to {default_driver!r}.", + ) + + filter_group.add_argument( + "--comp_interface", + choices=config.driver_choices, + default="mct", + action=utils.deprecate_action(", use --driver argument"), + help="DEPRECATED: Use --driver argument", + ) + + utils.setup_standard_logging_options(parser) + + kwargs = vars(parser.parse_args()) + + utils.configure_logging(**kwargs) + + # make sure at least one argument has been passed + if not any([kwargs[x] for x in ["grids", "compsets", "components", "machines"]]): + parser.print_help(sys.stderr) + + kwargs["files"] = files[kwargs["driver"]] + + return kwargs + + +def get_compset_active_components(files): + values = [] + + for file in files.values(): + active_components = file.get_components("COMPSETS_SPEC_FILE") + + values.extend([x for x in active_components if x is not None]) + + return values + + +def get_components(files): + values = [] + + for file in files.values(): + classes = get_component_classes(file) + + for c in classes: + components = file.get_components(f"COMP_ROOT_DIR_{c}") + + values.extend([x for x in components if x is not None]) + + return values + + +def get_component_classes(files): + infile = files.get_value("CONFIG_CPL_FILE") + + config_drv = Component(infile, "CPL") + + return config_drv.get_valid_model_components() + + +def query_grids(files, long, xml, **_): + config_file = files.get_value("GRIDS_SPEC_FILE") + + utils.expect( os.path.isfile(config_file), "Cannot find config_file {} on disk".format(config_file), ) - # Provide a special machine name indicating no need for a machine name - machines = Machines(config_file, machine="Query") - if xml: - if machine_name == "all": - print("{}".format(machines.get_raw_record().decode("UTF-8"))) - else: - machines.set_machine(machine_name) - print( - "{}".format( - machines.get_raw_record(root=machines.machine_node).decode("UTF-8") - ) - ) - else: - machines.print_values(machine_name=machine_name) + grids = Grids(config_file) -def query_compsets(files, name, xml=False): - """ - query compset definition give a compset name - """ - # Determine valid component values by checking the value attributes for COMPSETS_SPEC_FILE - components = get_compsets(files) - match_found = None - all_components = False - if re.search("^all$", name): # print all compsets - match_found = name - all_components = True + if xml: + print("{}".format(grids.get_raw_record().decode("UTF-8"))) else: - for component in components: - if component == name: - match_found = name - break + grids.print_values(long=long) - # If name is not a valid argument - exit with error - expect( - match_found is not None, - "Invalid input argument {}, valid input arguments are {}".format( - name, components - ), - ) - if all_components: # print all compsets - for component in components: +def query_compsets(files, compsets, **kwargs): + if compsets == "all": + active_components = files.get_components("COMPSETS_SPEC_FILE") + + for component in active_components: # the all_components flag will only print available components - print_compset(component, files, all_components=all_components, xml=xml) + print_compset(component, files, all_components=True, **kwargs) else: - print_compset(name, files, xml=xml) + print_compset(compsets, files, **kwargs) -def print_compset(name, files, all_components=False, xml=False): +def print_compset(name, files, xml=False, all_components=False, **_): """ print compsets associated with the component name, but if all_components is true only print the details if the associated component is available @@ -115,13 +208,13 @@ def print_compset(name, files, all_components=False, xml=False): config_file = files.get_value("COMPSETS_SPEC_FILE", attribute={"component": name}) # only error out if we aren't printing all otherwise exit quitely if not all_components: - expect( + utils.expect( (config_file), "Cannot find any config_component.xml file for {}".format(name), ) # Check that file exists on disk - expect( + utils.expect( os.path.isfile(config_file), "Cannot find config_file {} on disk".format(config_file), ) @@ -142,50 +235,49 @@ def print_compset(name, files, all_components=False, xml=False): compsets.print_values(arg_help=False) -def query_all_components(files, xml=False): - """ - query all components - """ - components = get_components(files) - # Loop through the elements for each component class (in config_files.xml) - for comp in components: - string = "CONFIG_{}_FILE".format(comp) +def query_component_settings(components, files, **kwargs): + classes = get_component_classes(files) - # determine all components in string - components = files.get_components(string) - for item in components: - query_component(item, files, all_components=True, xml=xml) + if components == "all": + # Loop through the elements for each component class (in config_files.xml) + for comp in classes: + string = "CONFIG_{}_FILE".format(comp) + # determine all components in string + components = files.get_components(string) -def query_component(name, files, all_components=False, xml=False): - """ - query a component by name - """ + for item in components: + _query_component_settings(item, files, all_components=True, **kwargs) + else: + _query_component_settings(components, files, **kwargs) + + +def _query_component_settings(component, files, xml=False, all_components=False, **_): # Determine the valid component classes (e.g. atm) for the driver/cpl # These are then stored in comps_array - components = get_components(files) + classes = get_component_classes(files) # Loop through the elements for each component class (in config_files.xml) # and see if there is a match for the the target component in the component attribute match_found = False valid_components = [] config_exists = False - for comp in components: + for comp in classes: string = "CONFIG_{}_FILE".format(comp) config_file = None # determine all components in string root_dir_node_name = "COMP_ROOT_DIR_{}".format(comp) - components = files.get_components(root_dir_node_name) - if components is None: - components = files.get_components(string) - for item in components: + _components = files.get_components(root_dir_node_name) + if _components is None: + _components = files.get_components(string) + for item in _components: valid_components.append(item) logger.debug("{}: valid_components {}".format(comp, valid_components)) # determine if config_file is on disk - if name is None: + if component is None: config_file = files.get_value(string) - elif name in valid_components: - config_file = files.get_value(string, attribute={"component": name}) + elif component in valid_components: + config_file = files.get_value(string, attribute={"component": component}) logger.debug("query {}".format(config_file)) if config_file is not None: match_found = True @@ -193,21 +285,24 @@ def query_component(name, files, all_components=False, xml=False): break if not all_components and not config_exists: - expect(config_exists, "Cannot find config_file {} on disk".format(config_file)) + utils.expect( + config_exists, "Cannot find config_file {} on disk".format(config_file) + ) elif all_components and not config_exists: print("WARNING: Couldn't find config_file {} on disk".format(config_file)) return # If name is not a valid argument - exit with error - expect( + utils.expect( match_found, "Invalid input argument {}, valid input arguments are {}".format( - name, valid_components + component, valid_components ), ) # Check that file exists on disk, if not exit with error - expect( - (config_file), "Cannot find any config_component.xml file for {}".format(name) + utils.expect( + (config_file), + "Cannot find any config_component.xml file for {}".format(component), ) # determine component xml content @@ -218,280 +313,90 @@ def query_component(name, files, all_components=False, xml=False): component.print_values() -def parse_command_line(args, description): - """ - parse command line arguments - """ - cime_model = CIME.utils.get_model() - - parser = ArgumentParser( - description=description, formatter_class=RawTextHelpFormatter - ) - - CIME.utils.setup_standard_logging_options(parser) - - valid_components = ["all"] - - parser.add_argument("--xml", action="store_true", help="Output in xml format.") - - files = {} - for comp_interface in supported_comp_interfaces: - files[comp_interface] = Files(comp_interface=comp_interface) - components = files[comp_interface].get_components("COMPSETS_SPEC_FILE") - for item in components: - valid_components.append(item) - - parser.add_argument( - "--compsets", - nargs="?", - const="all", - choices=valid_components, - help="Query compsets corresponding to the target component for the {} model." - " If no component is given, lists compsets defined by all components".format( - cime_model - ), - ) - - # Loop through the elements for each component class (in config_files.xml) - valid_components = ["all"] - tmp_comp_interfaces = supported_comp_interfaces - for comp_interface in tmp_comp_interfaces: - try: - components = get_components(files[comp_interface]) - except Exception: - supported_comp_interfaces.remove(comp_interface) - - for comp in components: - string = config.xml_component_key.format(comp) - - # determine all components in string - components = files[comp_interface].get_components(string) - if components: - for item in components: - valid_components.append(item) - - parser.add_argument( - "--components", - nargs="?", - const="all", - choices=valid_components, - help="Query component settings corresponding to the target component for {} model." - "\nIf the option is empty, then the lists settings defined by all components is output".format( - cime_model - ), - ) - - parser.add_argument( - "--grids", - action="store_true", - help="Query supported model grids for {} model.".format(cime_model), - ) - # same for all comp_interfaces - config_file = files[config.driver_default].get_value("MACHINES_SPEC_FILE") - expect( +def query_machines(files, machines, xml, **_): + config_file = files.get_value("MACHINES_SPEC_FILE") + utils.expect( os.path.isfile(config_file), "Cannot find config_file {} on disk".format(config_file), ) - machines = Machines(config_file, machine="Query") - machine_names = ["all", "current"] - machine_names.extend(machines.list_available_machines()) - - parser.add_argument( - "--machines", - nargs="?", - const="all", - choices=machine_names, - help="Query supported machines for {} model." - "\nIf option is left empty then all machines are listed," - "\nIf the option is 'current' then only the current machine details are listed.".format( - cime_model - ), - ) - - parser.add_argument( - "--long", action="store_true", help="Provide long output for queries" - ) - - parser.add_argument( - "--comp_interface", - choices=supported_comp_interfaces, # same as config.driver_choices - default="mct", - action=deprecate_action(", use --driver argument"), - help="DEPRECATED: Use --driver argument", - ) - - parser.add_argument( - "--driver", - choices=config.driver_choices, - default=get_cime_default_driver(), - help="Coupler/Driver interface", - ) - - args = CIME.utils.parse_args_and_handle_standard_logging_options(args, parser) - - # make sure at least one argument has been passed - if not (args.grids or args.compsets or args.components or args.machines): - parser.print_help(sys.stderr) - - return ( - args.grids, - args.compsets, - args.components, - args.machines, - args.long, - args.xml, - files[args.driver], - ) - - -def get_compsets(files): - """ - Determine valid component values by checking the value attributes for COMPSETS_SPEC_FILE - """ - return files.get_components("COMPSETS_SPEC_FILE") - - -def get_components(files): - """ - Determine the valid component classes (e.g. atm) for the driver/cpl - These are then stored in comps_array - """ - infile = files.get_value("CONFIG_CPL_FILE") - config_drv = Component(infile, "CPL") - return config_drv.get_valid_model_components() - - -class ArgumentParser(argparse.ArgumentParser): - """ - we override the error message from ArgumentParser to have a more helpful - message in the case of missing arguments - """ - - def error(self, message): - self.print_usage(sys.stderr) - # missing argument - # TODO: assumes comp_interface='mct' - if "expected one argument" in message: - if "compset" in message: - components = get_compsets(Files(comp_interface="mct")) - self.exit( - 2, - "{}: error: {}\nValid input arguments are {}\n".format( - self.prog, message, components - ), - ) - elif "component" in message: - files = Files(comp_interface="mct") - components = get_components(files) - # Loop through the elements for each component class (in config_files.xml) - valid_components = [] - for comp in components: - string = "CONFIG_{}_FILE".format(comp) - - # determine all components in string - components = files.get_components(string) - for item in components: - valid_components.append(item) - self.exit( - 2, - "{}: error: {}\nValid input arguments are {}\n".format( - self.prog, message, valid_components - ), - ) - # for all other errors - self.exit(2, "{}: error: {}\n".format(self.prog, message)) - - -class Machines(CIME.XML.machines.Machines): - """ - we overide print_values from Machines to add current in machine description - """ - - def print_values(self, machine_name="all"): # pylint: disable=arguments-differ - # set flag to look for single machine - if "all" not in machine_name: - single_machine = True - if machine_name == "current": - machine_name = self.probe_machine_name(warn=False) + # Provide a special machine name indicating no need for a machine name + xml_machines = Machines(config_file, machine="Query") + if xml: + if machines == "all": + print("{}".format(machines.get_raw_record().decode("UTF-8"))) else: - single_machine = False - - # if we can't find the specified machine - if single_machine and machine_name is None: - files = Files() - config_file = files.get_value("MACHINES_SPEC_FILE") - print("Machine is not listed in config file: {}".format(config_file)) - else: # write out machines - if single_machine: - machine_names = [machine_name] - else: - machine_names = self.list_available_machines() - print("Machine(s)\n") - for name in machine_names: - self.set_machine(name) - desc = self.text(self.get_child("DESC")) - os_ = self.text(self.get_child("OS")) - compilers = self.text(self.get_child("COMPILERS")) - mpilibnodes = self.get_children("MPILIBS", root=self.machine_node) - mpilibs = [] - for node in mpilibnodes: - mpilibs.extend(self.text(node).split(",")) - # This does not include the possible depedancy of mpilib on compiler - # it simply provides a list of mpilibs available on the machine - mpilibs = list(set(mpilibs)) - max_tasks_per_node = self.text(self.get_child("MAX_TASKS_PER_NODE")) - mpitasks_node = self.get_optional_child( - "MAX_MPITASKS_PER_NODE", root=self.machine_node - ) - max_mpitasks_per_node = ( - self.text(mpitasks_node) if mpitasks_node else max_tasks_per_node - ) - max_gpus_node = self.get_optional_child( - "MAX_GPUS_PER_NODE", root=self.machine_node - ) - max_gpus_per_node = self.text(max_gpus_node) if max_gpus_node else 0 - - current_machine = self.probe_machine_name(warn=False) - name += ( - " (current)" if current_machine and current_machine in name else "" + xml_machines.set_machine(machines) + print( + "{}".format( + xml_machines.get_raw_record(root=machines.machine_node).decode( + "UTF-8" + ) ) - print(" {} : {} ".format(name, desc)) - print(" os ", os_) - print(" compilers ", compilers) - print(" mpilibs ", mpilibs) - if max_mpitasks_per_node is not None: - print(" pes/node ", max_mpitasks_per_node) - if max_tasks_per_node is not None: - print(" max_tasks/node ", max_tasks_per_node) - if max_gpus_per_node is not None: - print(" max_gpus/node ", max_gpus_per_node) - print("") - - -def _main_func(description=None): - """ - main function - """ - grids, compsets, components, machines, long_output, xml, files = parse_command_line( - sys.argv, description - ) - - if grids: - query_grids(files, long_output, xml=xml) + ) + else: + print_machine_values(xml_machines, machines) - if compsets is not None: - query_compsets(files, name=compsets, xml=xml) - if components is not None: - if re.search("^all$", components): # print all compsets - query_all_components(files, xml=xml) +def print_machine_values( + machine, machine_name="all" +): # pylint: disable=arguments-differ + # set flag to look for single machine + if "all" not in machine_name: + single_machine = True + if machine_name == "current": + machine_name = machine.probe_machine_name(warn=False) + else: + single_machine = False + + # if we can't find the specified machine + if single_machine and machine_name is None: + files = Files() + config_file = files.get_value("MACHINES_SPEC_FILE") + print("Machine is not listed in config file: {}".format(config_file)) + else: # write out machines + if single_machine: + machine_names = [machine_name] else: - query_component(components, files, xml=xml) - - if machines is not None: - query_machines(files, machine_name=machines, xml=xml) + machine_names = machine.list_available_machines() + print("Machine(s)\n") + for name in machine_names: + machine.set_machine(name) + desc = machine.text(machine.get_child("DESC")) + os_ = machine.text(machine.get_child("OS")) + compilers = machine.text(machine.get_child("COMPILERS")) + mpilibnodes = machine.get_children("MPILIBS", root=machine.machine_node) + mpilibs = [] + for node in mpilibnodes: + mpilibs.extend(machine.text(node).split(",")) + # This does not include the possible depedancy of mpilib on compiler + # it simply provides a list of mpilibs available on the machine + mpilibs = list(set(mpilibs)) + max_tasks_per_node = machine.text(machine.get_child("MAX_TASKS_PER_NODE")) + mpitasks_node = machine.get_optional_child( + "MAX_MPITASKS_PER_NODE", root=machine.machine_node + ) + max_mpitasks_per_node = ( + machine.text(mpitasks_node) if mpitasks_node else max_tasks_per_node + ) + max_gpus_node = machine.get_optional_child( + "MAX_GPUS_PER_NODE", root=machine.machine_node + ) + max_gpus_per_node = machine.text(max_gpus_node) if max_gpus_node else 0 + + current_machine = machine.probe_machine_name(warn=False) + name += " (current)" if current_machine and current_machine in name else "" + print(" {} : {} ".format(name, desc)) + print(" os ", os_) + print(" compilers ", compilers) + print(" mpilibs ", mpilibs) + if max_mpitasks_per_node is not None: + print(" pes/node ", max_mpitasks_per_node) + if max_tasks_per_node is not None: + print(" max_tasks/node ", max_tasks_per_node) + if max_gpus_per_node is not None: + print(" max_gpus/node ", max_gpus_per_node) + print("") -# main entry point if __name__ == "__main__": _main_func(__doc__) diff --git a/CIME/tests/test_unit_xml_grids.py b/CIME/tests/test_unit_xml_grids.py new file mode 100644 index 00000000000..77619e5f78c --- /dev/null +++ b/CIME/tests/test_unit_xml_grids.py @@ -0,0 +1,164 @@ +import os +import io +import unittest +import tempfile +from contextlib import contextmanager +from pathlib import Path +from unittest import mock + +from CIME.utils import CIMEError +from CIME.XML.grids import Grids + + +TEST_CONFIG = """ + + + null + null + null + null + rx1 + r05 + r05 + rx1 + r05 + r05 + null + null + null + null + null + + + + T62 + T62 + gx3v7 + rx1 + null + null + gx3v7 + + + 0.47x0.63 + 0.47x0.63 + gx1v6 + r05 + null + null + gx1v6 + + + 0.23x0.31 + 0.23x0.31 + gx1v6 + r05 + null + null + gx1v6 + + + T31 + T31 + gx3v7 + rx1 + null + null + gx3v7 + + + 0.9x1.25 + 0.9x1.25 + gx1v6 + r05 + null + null + gx1v6 + + + 1.9x2.5 + 1.9x2.5 + gx1v6 + r05 + null + null + + +""" + + +def write_config_grids(tempdir, config): + config_grids_path = os.path.join(tempdir, "config_grids.xml") + + with open(config_grids_path, "w") as fd: + fd.write(TEST_CONFIG) + + return config_grids_path + + +class TestXMLGrids(unittest.TestCase): + def test_read_config_grids(self): + with tempfile.TemporaryDirectory() as tempdir: + config_grids_path = write_config_grids(tempdir, TEST_CONFIG) + + grids = Grids(config_grids_path) + + lname = grids._read_config_grids("T62_g37", "DATM") + + assert lname == "a%T62_l%T62_oi%gx3v7_r%rx1_g%null_w%null_z%null_m%gx3v7" + + with self.assertRaisesRegex( + CIMEError, "ERROR: grid alias T62_g37 not valid for compset SCREAM" + ): + grids._read_config_grids("T62_g37", "SCREAM") + + lname = grids._read_config_grids("f02_g16", "DATM") + + assert ( + lname + == "a%0.23x0.31_l%0.23x0.31_oi%gx1v6_r%r05_g%null_w%null_z%null_m%gx1v6" + ) + + lname = grids._read_config_grids("f05_g16", "SCREAM") + + assert ( + lname + == "a%0.47x0.63_l%0.47x0.63_oi%gx1v6_r%r05_g%null_w%null_z%null_m%gx1v6" + ) + + with self.assertRaisesRegex( + CIMEError, "ERROR: grid alias f05_g16 not valid for compset DATM" + ): + grids._read_config_grids("f05_g16", "DATM") + + lname = grids._read_config_grids("T31_g37_rx1", "_DROF") + + assert lname == "a%T31_l%T31_oi%gx3v7_r%rx1_g%null_w%null_z%null_m%gx3v7" + + lname = grids._read_config_grids("f09_g16", "DATM3TEST") + + assert ( + lname + == "a%0.9x1.25_l%0.9x1.25_oi%gx1v6_r%r05_g%null_w%null_z%null_m%gx1v6" + ) + + with self.assertRaisesRegex( + CIMEError, "ERROR: grid alias f09_g16 not valid for compset DATM2TEST" + ): + grids._read_config_grids("f09_g16", "DATM2TEST") + + lname = grids._read_config_grids("f19_g16", "DATM") + + assert ( + lname + == "a%1.9x2.5_l%1.9x2.5_oi%gx1v6_r%r05_g%null_w%null_z%null_m%gx1v6" + ) + + lname = grids._read_config_grids( + "f19_g16", "DATM", atmnlev="2", lndnlev="4" + ) + + assert ( + lname + == "a%1.9x2.5z2_l%1.9x2.5z4_oi%gx1v6_r%r05_g%null_w%null_z%null_m%gx1v6" + )