diff --git a/config/acme/machines/config_machines.xml b/config/acme/machines/config_machines.xml index e8289956938..c4db3bbbef2 100644 --- a/config/acme/machines/config_machines.xml +++ b/config/acme/machines/config_machines.xml @@ -1126,16 +1126,11 @@ /usr/bin/runjob - --label short - - --ranks-per-node $PES_PER_NODE - - --np $TOTALPES - --block $COBALT_PARTNAME $LOCARGS - --envs BG_THREADLAYOUT=1 - --envs XL_BG_SPREADLAYOUT=YES - --envs OMP_STACKSIZE=64M - --envs OMP_NUM_THREADS=$ENV{OMP_NUM_THREADS} + --label short + --ranks-per-node $PES_PER_NODE + --np $TOTALPES + --block $COBALT_PARTNAME $LOCARGS + $ENV{BGQ_SMP_VARS} @@ -1152,8 +1147,10 @@ 10000 - FALSE - 64M + + + + --envs BG_THREADLAYOUT=1 XL_BG_SPREADLAYOUT=YES OMP_DYNAMIC=FALSE OMP_STACKSIZE=64M OMP_NUM_THREADS=$ENV{OMP_NUM_THREADS} @@ -1289,16 +1286,11 @@ /usr/bin/runjob - --label short - - --ranks-per-node $PES_PER_NODE - - --np $TOTALPES - --block $COBALT_PARTNAME $LOCARGS - --envs BG_THREADLAYOUT=1 - --envs XL_BG_SPREADLAYOUT=YES - --envs OMP_STACKSIZE=64M - --envs OMP_NUM_THREADS=$ENV{OMP_NUM_THREADS} + --label short + --ranks-per-node $PES_PER_NODE + --np $TOTALPES + --block $COBALT_PARTNAME $LOCARGS + $ENV{BGQ_SMP_VARS} @@ -1315,8 +1307,10 @@ 10000 - FALSE - 64M + + + + --envs BG_THREADLAYOUT=1 XL_BG_SPREADLAYOUT=YES OMP_DYNAMIC=FALSE OMP_STACKSIZE=64M OMP_NUM_THREADS=$ENV{OMP_NUM_THREADS} diff --git a/config/config_tests.xml b/config/config_tests.xml index 6bef57899c7..edf0455d323 100644 --- a/config/config_tests.xml +++ b/config/config_tests.xml @@ -245,6 +245,7 @@ NODEFAIL Tests restart upon detected node failure. Generates fake failu ndays 11 FALSE + TRUE diff --git a/scripts/Tools/preview_run b/scripts/Tools/preview_run index eca424d052a..56d8c3fe726 100755 --- a/scripts/Tools/preview_run +++ b/scripts/Tools/preview_run @@ -58,6 +58,7 @@ def _main_func(description): with Case(caseroot, read_only=False) as case: print "BATCH SUBMIT:" + case.load_env() job = "case.test" if case.get_value("TEST") else "case.run" job_id_to_cmd = case.submit_jobs(dry_run=True, job=job) for job_id, cmd in job_id_to_cmd: diff --git a/scripts/fortran_unit_testing/run_tests.py b/scripts/fortran_unit_testing/run_tests.py index 9ce19b2d453..6d9c0d772dd 100755 --- a/scripts/fortran_unit_testing/run_tests.py +++ b/scripts/fortran_unit_testing/run_tests.py @@ -7,7 +7,7 @@ sys.path.append(os.path.join(_CIMEROOT, "scripts", "fortran_unit_testing", "python")) from standard_script_setup import * -from CIME.BuildTools.configure import configure +from CIME.BuildTools.configure import configure, FakeCase from CIME.utils import run_cmd_no_fail, stringify_bool, expect from CIME.XML.machines import Machines from CIME.XML.compilers import Compilers @@ -318,7 +318,8 @@ def _main(): unit_testing=True) machspecific = EnvMachSpecific(build_dir, unit_testing=True) - machspecific.load_env(compiler, debug, mpilib) + fake_case = FakeCase(compiler, mpilib, debug) + machspecific.load_env(fake_case) os.environ["OS"] = os_ os.environ["COMPILER"] = compiler os.environ["DEBUG"] = stringify_bool(debug) diff --git a/scripts/lib/CIME/BuildTools/configure.py b/scripts/lib/CIME/BuildTools/configure.py index 26281117df6..392468f7d8e 100644 --- a/scripts/lib/CIME/BuildTools/configure.py +++ b/scripts/lib/CIME/BuildTools/configure.py @@ -66,6 +66,14 @@ def _copy_depends_files(machine_name, machines_dir, output_dir, compiler): if os.path.isfile(dfile) and not os.path.isfile(outputdfile): shutil.copyfile(dfile, outputdfile) +class FakeCase(object): + + def __init__(self, compiler, mpilib, debug): + self._vals = {"COMPILER":compiler, "MPILIB":mpilib, "DEBUG":debug} + + def get_value(self, attrib): + expect(attrib in self._vals, "FakeCase does not support getting value of '%s'" % attrib) + return self._vals[attrib] def _generate_env_mach_specific(output_dir, machobj, compiler, mpilib, debug, sysos, unit_testing): @@ -79,8 +87,9 @@ def _generate_env_mach_specific(output_dir, machobj, compiler, mpilib, debug, ems_file = EnvMachSpecific(output_dir, unit_testing=unit_testing) ems_file.populate(machobj) ems_file.write() + fake_case = FakeCase(compiler, mpilib, debug) for shell in ('sh', 'csh'): - ems_file.make_env_mach_specific_file(compiler, debug, mpilib, shell) + ems_file.make_env_mach_specific_file(shell, fake_case) shell_path = os.path.join(output_dir, ".env_mach_specific." + shell) with open(shell_path, 'a') as shell_file: if shell == 'sh': diff --git a/scripts/lib/CIME/SystemTests/erp.py b/scripts/lib/CIME/SystemTests/erp.py index 5e0dc748b45..bebbba7e295 100644 --- a/scripts/lib/CIME/SystemTests/erp.py +++ b/scripts/lib/CIME/SystemTests/erp.py @@ -31,7 +31,6 @@ def build_phase(self, sharedlib_only=False, model_only=False): and tasks. This test will fail for components (e.g. pop) that do not reproduce exactly with different numbers of mpi tasks. """ - self._case.set_value("BUILD_THREADED",True) if sharedlib_only: return self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only) @@ -52,6 +51,8 @@ def build_phase(self, sharedlib_only=False, model_only=False): if is_locked(envbuild1): restore(envbuild1, newname="env_build.xml") + self._case.read_xml() + # Build two executables, one using the original tasks and threads (ERP1) and # one using the modified tasks and threads (ERP2) # The reason we currently need two executables that CESM-CICE has a compile time decomposition @@ -132,6 +133,8 @@ def run_phase(self): self._case.set_value("CONTINUE_RUN", True) self._case.set_value("REST_OPTION","never") suffix = "rest" + self.run_indv(suffix=suffix) + self._case.flush() self._component_compare_test("base", "rest") diff --git a/scripts/lib/CIME/XML/env_mach_specific.py b/scripts/lib/CIME/XML/env_mach_specific.py index 14083b7354d..a9cfb707a21 100644 --- a/scripts/lib/CIME/XML/env_mach_specific.py +++ b/scripts/lib/CIME/XML/env_mach_specific.py @@ -53,34 +53,34 @@ def populate(self, machobj): for node in nodes: self.add_child(node) - def _get_modules_for_case(self, compiler, debug, mpilib): + def _get_modules_for_case(self, case): module_nodes = self.get_nodes("modules") modules_to_load = None if module_nodes is not None: - modules_to_load = self._compute_module_actions(module_nodes, compiler, debug, mpilib) + modules_to_load = self._compute_module_actions(module_nodes, case) return modules_to_load - def _get_envs_for_case(self, compiler, debug, mpilib): + def _get_envs_for_case(self, case): env_nodes = self.get_nodes("environment_variables") envs_to_set = None if env_nodes is not None: - envs_to_set = self._compute_env_actions(env_nodes, compiler, debug, mpilib) + envs_to_set = self._compute_env_actions(env_nodes, case) return envs_to_set - def load_env(self, compiler, debug, mpilib): + def load_env(self, case): """ Should only be called by case.load_env """ # Do the modules so we can refer to env vars set by the modules # in the environment_variables block - modules_to_load = self._get_modules_for_case(compiler, debug, mpilib) + modules_to_load = self._get_modules_for_case(case) if (modules_to_load is not None): self.load_modules(modules_to_load) - envs_to_set = self._get_envs_for_case(compiler, debug, mpilib) + envs_to_set = self._get_envs_for_case(case) if (envs_to_set is not None): self.load_envs(envs_to_set) @@ -130,9 +130,9 @@ def save_all_env_info(self, filename): f.write(self.list_modules()) run_cmd_no_fail("echo -e '\n' && env", arg_stdout=filename) - def make_env_mach_specific_file(self, compiler, debug, mpilib, shell): - modules_to_load = self._get_modules_for_case(compiler, debug, mpilib) - envs_to_set = self._get_envs_for_case(compiler, debug, mpilib) + def make_env_mach_specific_file(self, shell, case): + modules_to_load = self._get_modules_for_case(case) + envs_to_set = self._get_envs_for_case(case) filename = ".env_mach_specific.%s" % shell lines = [] if modules_to_load is not None: @@ -152,24 +152,25 @@ def make_env_mach_specific_file(self, compiler, debug, mpilib, shell): def load_envs(self, envs_to_set): for env_name, env_value in envs_to_set: - os.environ[env_name] = env_value + os.environ[env_name] = "" if env_value is None else env_value # Private API - def _compute_module_actions(self, module_nodes, compiler, debug, mpilib): - return self._compute_actions(module_nodes, "command", compiler, debug, mpilib) + def _compute_module_actions(self, module_nodes, case): + return self._compute_actions(module_nodes, "command", case) - def _compute_env_actions(self, env_nodes, compiler, debug, mpilib): - return self._compute_actions(env_nodes, "env", compiler, debug, mpilib) + def _compute_env_actions(self, env_nodes, case): + return self._compute_actions(env_nodes, "env", case) - def _compute_actions(self, nodes, child_tag, compiler, debug, mpilib): + def _compute_actions(self, nodes, child_tag, case): result = [] # list of tuples ("name", "argument") + compiler, mpilib = case.get_value("COMPILER"), case.get_value("MPILIB") for node in nodes: - if (self._match_attribs(node.attrib, compiler, debug, mpilib)): + if (self._match_attribs(node.attrib, case)): for child in node: expect(child.tag == child_tag, "Expected %s element" % child_tag) - if (self._match_attribs(child.attrib, compiler, debug, mpilib)): + if (self._match_attribs(child.attrib, case)): val = child.text if val is not None: # We allow a couple special substitutions for these fields @@ -178,36 +179,39 @@ def _compute_actions(self, nodes, child_tag, compiler, debug, mpilib): val = self.get_resolved_value(val) expect("$" not in val, "Not safe to leave unresolved items in env var value: '%s'" % val) + # intentional unindent, result is appended even if val is None result.append( (child.get("name"), val) ) return result - def _match_attribs(self, attribs, compiler, debug, mpilib): - if ("compiler" in attribs and - not self._match(compiler, attribs["compiler"])): - return False - elif ("mpilib" in attribs and - not self._match(mpilib, attribs["mpilib"])): - return False - elif ("debug" in attribs and - not self._match("TRUE" if debug else "FALSE", attribs["debug"].upper())): - return False - elif ("unit_testing" in attribs and - not self._match("TRUE" if self._unit_testing else "FALSE", - attribs["unit_testing"].upper())): - return False + def _match_attribs(self, attribs, case): + # check for matches with case-vars + for attrib in attribs: + if attrib == "unit_testing": # special case + if not self._match(self._unit_testing, attribs["unit_testing"].upper()): + return False + elif attrib == "name": + pass + else: + val = case.get_value(attrib.upper()) + expect(val is not None, "Cannot match attrib '%s', case has no value for it" % attrib.upper()) + if not self._match(val, attribs[attrib]): + return False return True def _match(self, my_value, xml_value): - if (xml_value.startswith("!")): + if xml_value.startswith("!"): result = my_value != xml_value[1:] + elif isinstance(my_value, bool): + if my_value: result = xml_value == "TRUE" + else: result = xml_value == "FALSE" else: result = my_value == xml_value - logger.debug("(env_mach_specific) _match %s %s %s"%(my_value, xml_value, result)) - return result + logger.debug("(env_mach_specific) _match %s %s %s" % (my_value, xml_value, result)) + return result def _get_module_commands(self, modules_to_load, shell): # Note this is independent of module system type diff --git a/scripts/lib/CIME/XML/generic_xml.py b/scripts/lib/CIME/XML/generic_xml.py index 717e4b17bdd..43a31160425 100644 --- a/scripts/lib/CIME/XML/generic_xml.py +++ b/scripts/lib/CIME/XML/generic_xml.py @@ -4,7 +4,6 @@ """ from CIME.XML.standard_module_setup import * from distutils.spawn import find_executable -from xml.dom import minidom import getpass @@ -47,11 +46,12 @@ def read(self, infile, schema=None): Read and parse an xml file into the object """ logger.debug("read: " + infile) - if self.tree: - self.root.append(ET.parse(infile).getroot()) - else: - self.tree = ET.parse(infile) - self.root = self.tree.getroot() + with open(infile, 'r') as fd: + if self.tree: + self.root.append(ET.parse(fd).getroot()) + else: + self.tree = ET.parse(fd) + self.root = self.tree.getroot() if schema is not None and self.get_version() > 1.0: self.validate_xml_file(infile, schema) @@ -79,9 +79,8 @@ def write(self, outfile=None): if xmllint is not None: run_cmd_no_fail("%s --format --output %s -"%(xmllint,outfile), input_str=xmlstr) else: - doc = minidom.parseString(xmlstr) with open(outfile,'w') as xmlout: - doc.writexml(xmlout,addindent=' ') + xmlout.write(xmlstr) def get_node(self, nodename, attributes=None, root=None, xpath=None): """ diff --git a/scripts/lib/CIME/build.py b/scripts/lib/CIME/build.py index 111c629f742..9b7e5ff8932 100644 --- a/scripts/lib/CIME/build.py +++ b/scripts/lib/CIME/build.py @@ -267,7 +267,6 @@ def _build_libraries(case, exeroot, sharedpath, caseroot, cimeroot, libroot, lid if re.search("Current setting for", line): logger.warn(line) - # clm not a shared lib for ACME if get_model() != "acme": comp_lnd = case.get_value("COMP_LND") @@ -520,7 +519,6 @@ def _case_build_impl(caseroot, case, sharedlib_only, model_only): logs.extend(_build_model(build_threaded, exeroot, clm_config_opts, incroot, complist, lid, caseroot, cimeroot, compiler)) - if not sharedlib_only: # in case component build scripts updated the xml files, update the case object case.read_xml() post_build(case, logs) diff --git a/scripts/lib/CIME/case.py b/scripts/lib/CIME/case.py index 15ec0f83171..81ac7911a3e 100644 --- a/scripts/lib/CIME/case.py +++ b/scripts/lib/CIME/case.py @@ -149,6 +149,8 @@ def initialize_derived_attributes(self): "unit_testing" : False } + os.environ["OMP_NUM_THREADS"] = str(self.thread_count) + executable = env_mach_spec.get_mpirun(self, mpi_attribs, job="case.run", exe_only=True)[0] if executable is not None and "aprun" in executable: self.num_nodes = get_aprun_cmd_for_case(self, "acme.exe")[1] @@ -177,11 +179,8 @@ def schedule_rewrite(self, env_file): self._env_files_that_need_rewrite.add(env_file) def read_xml(self): - if(len(self._env_files_that_need_rewrite)>0): - files = "" - for env_file in self._env_files_that_need_rewrite: - files += " "+env_file.filename - expect(False,"Object(s) %s seem to have newer data than the corresponding case file"%files) + if self._env_files_that_need_rewrite: + expect(False, "Object(s) %s seem to have newer data than the corresponding case file" % " ".join([env_file.filename for env_file in self._env_files_that_need_rewrite])) self._env_entryid_files = [] self._env_entryid_files.append(EnvCase(self._caseroot, components=None)) @@ -1168,11 +1167,8 @@ def set_model_version(self, model): def load_env(self): if not self._is_env_loaded: - compiler = self.get_value("COMPILER") - debug=self.get_value("DEBUG") - mpilib=self.get_value("MPILIB") env_module = self.get_env("mach_specific") - env_module.load_env(compiler=compiler,debug=debug, mpilib=mpilib) + env_module.load_env(self) self._is_env_loaded = True def get_build_threaded(self): @@ -1180,7 +1176,8 @@ def get_build_threaded(self): Returns True if current settings require a threaded build/run. """ force_threaded = self.get_value("BUILD_THREADED") - return bool(force_threaded) or self.thread_count > 1 + smp_present = bool(force_threaded) or self.thread_count > 1 + return smp_present def _check_testlists(self, compset_alias, grid_name, files): """ @@ -1213,6 +1210,8 @@ def set_file(self, xmlfile): gfile = GenericXML(infile=xmlfile) ftype = gfile.get_id() + self.flush(flushall=True) + logger.warn("setting case file to %s"%xmlfile) new_env_file = None for env_file in self._env_entryid_files: diff --git a/scripts/lib/CIME/case_setup.py b/scripts/lib/CIME/case_setup.py index a4e158d7116..45157c58a9b 100644 --- a/scripts/lib/CIME/case_setup.py +++ b/scripts/lib/CIME/case_setup.py @@ -141,6 +141,8 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False): case.initialize_derived_attributes() + case.set_value("SMP_PRESENT", case.get_build_threaded()) + # Set TOTAL_CORES case.set_value("TOTAL_CORES", case.total_tasks * case.cores_per_task ) else: @@ -163,6 +165,8 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False): case.initialize_derived_attributes() + case.set_value("SMP_PRESENT", case.get_build_threaded()) + # create batch files logger.info("Creating batch script case.run") env_batch = case.get_env("batch") @@ -212,8 +216,8 @@ def _case_setup_impl(case, caseroot, clean=False, test_mode=False, reset=False): # Record env information env_module = case.get_env("mach_specific") - env_module.make_env_mach_specific_file(compiler, debug, mpilib, "sh") - env_module.make_env_mach_specific_file(compiler, debug, mpilib, "csh") + env_module.make_env_mach_specific_file("sh", case) + env_module.make_env_mach_specific_file("csh", case) env_module.save_all_env_info("software_environment.txt") ############################################################################### diff --git a/scripts/lib/CIME/utils.py b/scripts/lib/CIME/utils.py index 57a28addbe3..06cf1e14dc2 100644 --- a/scripts/lib/CIME/utils.py +++ b/scripts/lib/CIME/utils.py @@ -1243,7 +1243,7 @@ def _check_for_invalid_args(args): if arg.startswith("-") and len(arg) > 2: # Uncomment these lines when we want to enforce --mulitchararg syntax # if arg == "-value" or arg == "-noecho": - logger.warn("This argument is depricated, please use -%s"%arg) + logger.warn("This argument is depricated, please use -%s"%arg) # else: # expect(False, "Invalid argument %s\n Multi-character arguments should begin with \"--\" and single character with \"-\"\n Use --help for a complete list of available options"%arg) diff --git a/src/drivers/mct/cime_config/config_component.xml b/src/drivers/mct/cime_config/config_component.xml index b6a872f7d7c..68756483368 100644 --- a/src/drivers/mct/cime_config/config_component.xml +++ b/src/drivers/mct/cime_config/config_component.xml @@ -761,6 +761,15 @@ If TRUE, the component libraries are always built with OpenMP capability. + + logical + TRUE,FALSE + FALSE + build_def + env_build.xml + TRUE implies that at least one of the components is built threaded (DO NOT EDIT) + + logical TRUE,FALSE