Skip to content

Commit

Permalink
Merge pull request #1494 from jedwards4b/pesfile_usage
Browse files Browse the repository at this point in the history
Pesfile usage

Previously the user was only allowed to specify a file in the config_pes.xml format in the --pesfile argument to create_newcase. This change allows the user to specify a file in either the config_pes.xml or the env_mach_pes.xml format. Also add a test for this feature

Test suite: scripts_regression_tests.py
Test baseline:
Test namelist changes:
Test status: bit for bit

Fixes

User interface changes?: --pesfile argument in create_newcase

Code review:
  • Loading branch information
jgfouca authored May 9, 2017
2 parents cdcb096 + f92477a commit 18112d9
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 123 deletions.
3 changes: 2 additions & 1 deletion scripts/create_newcase
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ OR
parser.add_argument("--pesfile",
help="Only used and required for --user-compset argument."
"Full pathname of the pes specification file"
"This argument is required if --user-compset is True")
"This argument is required if --user-compset is True"
"The file can follow either the config_pes.xml or the env_mach_pes.xml format")

parser.add_argument("--user-grid", action="store_true",
help="If set, then the -grid argument is treated as a user specified grid."
Expand Down
10 changes: 6 additions & 4 deletions scripts/lib/CIME/XML/env_mach_pes.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def get_max_thread_count(self, comp_classes):
''' Find the maximum number of openmp threads for any component in the case '''
max_threads = 1
for comp in comp_classes:
threads = self.get_value("NTHRDS_%s"%comp)
threads = self.get_value("NTHRDS",attribute={"component":comp})
expect(threads is not None, "Error no thread count found for component class %s"%comp)
if threads > max_threads:
max_threads = threads
Expand All @@ -44,6 +44,7 @@ def get_cost_pes(self, totaltasks, max_thread_count, machine=None):
"""
figure out the value of COST_PES which is the pe value used to estimate model cost
"""
expect(totaltasks > 0,"totaltasks > 0 expected totaltasks = %s"%totaltasks)
pespn = self.get_value("PES_PER_NODE")
num_nodes, spare_nodes = self.get_total_nodes(totaltasks, max_thread_count)
num_nodes += spare_nodes
Expand All @@ -57,14 +58,15 @@ def get_cost_pes(self, totaltasks, max_thread_count, machine=None):
def get_total_tasks(self, comp_classes):
total_tasks = 0
for comp in comp_classes:
ntasks = self.get_value("NTASKS_%s"%comp)
rootpe = self.get_value("ROOTPE_%s"%comp)
pstrid = self.get_value("PSTRID_%s"%comp)
ntasks = self.get_value("NTASKS", attribute={"component":comp})
rootpe = self.get_value("ROOTPE", attribute={"component":comp})
pstrid = self.get_value("PSTRID", attribute={"component":comp})
tt = rootpe + (ntasks - 1) * pstrid + 1
total_tasks = max(tt, total_tasks)
return total_tasks

def get_tasks_per_node(self, total_tasks, max_thread_count):
expect(total_tasks > 0,"totaltasks > 0 expected totaltasks = %s"%total_tasks)
tasks_per_node = min(self.get_value("MAX_TASKS_PER_NODE")/ max_thread_count,
self.get_value("PES_PER_NODE"), total_tasks)
return tasks_per_node
Expand Down
5 changes: 4 additions & 1 deletion scripts/lib/CIME/XML/generic_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,7 @@ def get_raw_record(self, root=None):
return xmlstr

def get_id(self):
return self.root.get("id")
xmlid = self.root.get("id")
if xmlid is not None:
return xmlid
return self.root.tag
230 changes: 133 additions & 97 deletions scripts/lib/CIME/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ def _set_compset_and_pesfile(self, compset_name, files, user_compset=False, pesf
components = files.get_components("COMPSETS_SPEC_FILE")
logger.debug(" Possible components for COMPSETS_SPEC_FILE are %s" % components)

if pesfile is not None:
self._pesfile = pesfile

# Loop through all of the files listed in COMPSETS_SPEC_FILE and find the file
# that has a match for either the alias or the longname in that order
for component in components:
Expand All @@ -426,9 +429,11 @@ def _set_compset_and_pesfile(self, compset_name, files, user_compset=False, pesf
if (os.path.isfile(compsets_filename)):
compsets = Compsets(compsets_filename)
match, compset_alias, science_support, self._user_mods = compsets.get_compset_match(name=compset_name)
pesfile = files.get_value("PES_SPEC_FILE" , {"component":component})
if match is not None:
self._pesfile = pesfile
if pesfile is None:
self._pesfile = files.get_value("PES_SPEC_FILE" , {"component":component})
self.set_lookup_value("PES_SPEC_FILE" ,
files.get_value("PES_SPEC_FILE" , {"component":component}, resolved=False))
self._compsetsfile = compsets_filename
self._compsetname = match
tests_filename = files.get_value("TESTS_SPEC_FILE" , {"component":component}, resolved=False)
Expand All @@ -439,8 +444,6 @@ def _set_compset_and_pesfile(self, compset_name, files, user_compset=False, pesf
self.set_lookup_value("TESTS_SPEC_FILE" , tests_filename)
self.set_lookup_value("TESTS_MODS_DIR" , tests_mods_dir)
self.set_lookup_value("USER_MODS_DIR" , user_mods_dir)
self.set_lookup_value("PES_SPEC_FILE" ,
files.get_value("PES_SPEC_FILE" , {"component":component}, resolved=False))
compset_info = "Compset longname is %s"%(match)
if self._user_mods is not None:
compset_info += " with user_mods directory %s"%(self._user_mods)
Expand All @@ -453,7 +456,7 @@ def _set_compset_and_pesfile(self, compset_name, files, user_compset=False, pesf
#Do not error out for user_compset
logger.warn("Could not find a compset match for either alias or longname in %s" %(compset_name))
self._compsetname = compset_name
self._pesfile = pesfile
logger.info("Pes specification file is %s" %(pesfile))
self.set_lookup_value("PES_SPEC_FILE", pesfile)
else:
expect(False,
Expand Down Expand Up @@ -552,95 +555,21 @@ def _get_component_config_data(self, files):
if result is not None:
del self.lookups[key]

def configure(self, compset_name, grid_name, machine_name=None,
project=None, pecount=None, compiler=None, mpilib=None,
user_compset=False, pesfile=None,
user_grid=False, gridfile=None, ninst=1, test=False,
walltime=None, queue=None, output_root=None, run_unsupported=False, answer=None,
input_dir=None):

#--------------------------------------------
# compset, pesfile, and compset components
#--------------------------------------------
files = Files()
compset_alias, science_support = self._set_compset_and_pesfile(compset_name, files, user_compset=user_compset, pesfile=pesfile)

self._components = self.get_compset_components()
#FIXME - if --user-compset is True then need to determine that
#all of the compset settings are valid

#--------------------------------------------
# grid
#--------------------------------------------
if user_grid is True and gridfile is not None:
self.set_value("GRIDS_SPEC_FILE", gridfile)
grids = Grids(gridfile)

gridinfo = grids.get_grid_info(name=grid_name, compset=self._compsetname)

self._gridname = gridinfo["GRID"]
for key,value in gridinfo.items():
logger.debug("Set grid %s %s"%(key,value))
self.set_lookup_value(key,value)

#--------------------------------------------
# component config data
#--------------------------------------------
self._get_component_config_data(files)

self.get_compset_var_settings()

#--------------------------------------------
# machine
#--------------------------------------------
# set machine values in env_xxx files
machobj = Machines(machine=machine_name)
probed_machine = machobj.probe_machine_name()
machine_name = machobj.get_machine_name()
self.set_value("MACH", machine_name)
if probed_machine != machine_name and probed_machine is not None:
logger.warning("WARNING: User-selected machine '%s' does not match probed machine '%s'" % (machine_name, probed_machine))
else:
logger.info("Machine is %s" % machine_name)

nodenames = machobj.get_node_names()
nodenames = [x for x in nodenames if
'_system' not in x and '_variables' not in x and 'mpirun' not in x and\
'COMPILER' not in x and 'MPILIB' not in x]

for nodename in nodenames:
value = machobj.get_value(nodename, resolved=False)
type_str = self.get_type_info(nodename)
if type_str is not None:
logger.debug("machine nodname %s value %s"%(nodename, value))
self.set_value(nodename, convert_to_type(value, type_str, nodename))

if compiler is None:
compiler = machobj.get_default_compiler()
else:
expect(machobj.is_valid_compiler(compiler),
"compiler %s is not supported on machine %s" %(compiler, machine_name))

self.set_value("COMPILER",compiler)

if mpilib is None:
mpilib = machobj.get_default_MPIlib({"compiler":compiler})
else:
expect(machobj.is_valid_MPIlib(mpilib, {"compiler":compiler}),
"MPIlib %s is not supported on machine %s" %(mpilib, machine_name))
self.set_value("MPILIB",mpilib)

machdir = machobj.get_machines_dir()
self.set_value("MACHDIR", machdir)

# Create env_mach_specific settings from machine info.
env_mach_specific_obj = self.get_env("mach_specific")
env_mach_specific_obj.populate(machobj)
self.schedule_rewrite(env_mach_specific_obj)

def _setup_mach_pes(self, pecount, ninst, machine_name, mpilib):
#--------------------------------------------
# pe layout
#--------------------------------------------
mach_pes_obj = None
# self._pesfile may already be env_mach_pes.xml if so we can just return
gfile = GenericXML(infile=self._pesfile)
ftype = gfile.get_id()
expect(ftype == "env_mach_pes.xml" or ftype == "config_pes", " Do not recognize %s as a valid CIME pes file %s"%(self._pesfile, ftype))
if ftype == "env_mach_pes.xml":
new_mach_pes_obj = EnvMachPes(infile=self._pesfile, components=self._components)
self.update_env(new_mach_pes_obj, "mach_pes")
return new_mach_pes_obj.get_value("TOTALPES")
pesobj = Pes(self._pesfile)

match1 = re.match('(.+)x([0-9]+)', "" if pecount is None else pecount)
match2 = re.match('([0-9]+)', "" if pecount is None else pecount)

Expand All @@ -649,8 +578,6 @@ def configure(self, compset_name, grid_name, machine_name=None,
pes_rootpe = {}
other = {}

pesobj = Pes(self._pesfile)

force_tasks = None
force_thrds = None

Expand Down Expand Up @@ -703,13 +630,13 @@ def configure(self, compset_name, grid_name, machine_name=None,
mach_pes_obj.set_value(nthrds_str, nthrds)
mach_pes_obj.set_value(rootpe_str, rootpe)

maxval = 1
pesize = 1
pes_per_node = self.get_value("PES_PER_NODE")
for val in totaltasks:
if val < 0:
val = -1*val*pes_per_node
if val > maxval:
maxval = val
if val > pesize:
pesize = val

# Make sure that every component has been accounted for
# set, nthrds and ntasks to 1 otherwise. Also set the ninst values here.
Expand All @@ -730,6 +657,97 @@ def configure(self, compset_name, grid_name, machine_name=None,
if compclass not in pes_nthrds.keys():
mach_pes_obj.set_value(compclass,1)

return pesize


def configure(self, compset_name, grid_name, machine_name=None,
project=None, pecount=None, compiler=None, mpilib=None,
user_compset=False, pesfile=None,
user_grid=False, gridfile=None, ninst=1, test=False,
walltime=None, queue=None, output_root=None, run_unsupported=False, answer=None,
input_dir=None):

#--------------------------------------------
# compset, pesfile, and compset components
#--------------------------------------------
files = Files()
compset_alias, science_support = self._set_compset_and_pesfile(compset_name, files, user_compset=user_compset, pesfile=pesfile)

self._components = self.get_compset_components()
#FIXME - if --user-compset is True then need to determine that
#all of the compset settings are valid

#--------------------------------------------
# grid
#--------------------------------------------
if user_grid is True and gridfile is not None:
self.set_value("GRIDS_SPEC_FILE", gridfile)
grids = Grids(gridfile)

gridinfo = grids.get_grid_info(name=grid_name, compset=self._compsetname)

self._gridname = gridinfo["GRID"]
for key,value in gridinfo.items():
logger.debug("Set grid %s %s"%(key,value))
self.set_lookup_value(key,value)

#--------------------------------------------
# component config data
#--------------------------------------------
self._get_component_config_data(files)

self.get_compset_var_settings()

#--------------------------------------------
# machine
#--------------------------------------------
# set machine values in env_xxx files
machobj = Machines(machine=machine_name)
probed_machine = machobj.probe_machine_name()
machine_name = machobj.get_machine_name()
self.set_value("MACH", machine_name)
if probed_machine != machine_name and probed_machine is not None:
logger.warning("WARNING: User-selected machine '%s' does not match probed machine '%s'" % (machine_name, probed_machine))
else:
logger.info("Machine is %s" % machine_name)

nodenames = machobj.get_node_names()
nodenames = [x for x in nodenames if
'_system' not in x and '_variables' not in x and 'mpirun' not in x and\
'COMPILER' not in x and 'MPILIB' not in x]

for nodename in nodenames:
value = machobj.get_value(nodename, resolved=False)
type_str = self.get_type_info(nodename)
if type_str is not None:
logger.debug("machine nodname %s value %s"%(nodename, value))
self.set_value(nodename, convert_to_type(value, type_str, nodename))

if compiler is None:
compiler = machobj.get_default_compiler()
else:
expect(machobj.is_valid_compiler(compiler),
"compiler %s is not supported on machine %s" %(compiler, machine_name))

self.set_value("COMPILER",compiler)

if mpilib is None:
mpilib = machobj.get_default_MPIlib({"compiler":compiler})
else:
expect(machobj.is_valid_MPIlib(mpilib, {"compiler":compiler}),
"MPIlib %s is not supported on machine %s" %(mpilib, machine_name))
self.set_value("MPILIB",mpilib)

machdir = machobj.get_machines_dir()
self.set_value("MACHDIR", machdir)

# Create env_mach_specific settings from machine info.
env_mach_specific_obj = self.get_env("mach_specific")
env_mach_specific_obj.populate(machobj)
self.schedule_rewrite(env_mach_specific_obj)

pesize = self._setup_mach_pes(pecount, ninst, machine_name, mpilib)

#--------------------------------------------
# batch system
#--------------------------------------------
Expand All @@ -741,7 +759,7 @@ def configure(self, compset_name, grid_name, machine_name=None,

env_batch.set_batch_system(batch, batch_system_type=batch_system_type)
env_batch.create_job_groups(bjobs)
env_batch.set_job_defaults(bjobs, pesize=maxval, walltime=walltime, force_queue=queue, allow_walltime_override=test)
env_batch.set_job_defaults(bjobs, pesize=pesize, walltime=walltime, force_queue=queue, allow_walltime_override=test)
self.schedule_rewrite(env_batch)

#--------------------------------------------
Expand Down Expand Up @@ -1246,3 +1264,21 @@ def set_file(self, xmlfile):
break

self._files = self._env_entryid_files + self._env_generic_files

def update_env(self, new_object, env_file):
"""
Replace a case env object file
"""
old_object = self.get_env(env_file)
new_object.filename = old_object.filename
if old_object in self._env_entryid_files:
self._env_entryid_files.remove(old_object)
self._env_entryid_files.append(new_object)
elif old_object in self._env_generic_files:
self._env_generic_files.remove(old_object)
self._env_generic_files.append(new_object)
if old_object in self._env_files_that_need_rewrite:
self._env_files_that_need_rewrite.remove(old_object)
self._files.remove(old_object)
self._files.append(new_object)
self.schedule_rewrite(new_object)
Loading

0 comments on commit 18112d9

Please sign in to comment.