From 01105b2f1d129e01ffa0e7814d018ef1d39d724c Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 27 Feb 2023 10:45:03 -0500 Subject: [PATCH 01/47] refactor: Add _get_scheduler_values method to environments This moves much of the task and number of nodes logic to the environments where it is easier to manage the more complicated logic. --- flow/environment.py | 31 +++++++++++++++++++++++++++++++ flow/project.py | 5 +++++ 2 files changed, 36 insertions(+) diff --git a/flow/environment.py b/flow/environment.py index db73231f1..51369ea22 100644 --- a/flow/environment.py +++ b/flow/environment.py @@ -34,6 +34,7 @@ from .scheduling.pbs import PBSScheduler from .scheduling.simple_scheduler import SimpleScheduler from .scheduling.slurm import SlurmScheduler +from .util.template_filters import calc_num_nodes, calc_tasks logger = logging.getLogger(__name__) @@ -283,6 +284,36 @@ def _get_default_directives(cls): ) ) + @classmethod + def _get_scheduler_values(cls, context, operations): + """Return a dictionary of computed quantities regarding submission. + + Warning + ------- + Must be called after the rest of the template context has been gathered. + """ + threshold = 0.0 if context.get("force", False) else 0.9 + cpu_tasks_total = calc_tasks( + operations, + "np", + context.get("parallel", False), + context.get("force", False), + ) + gpu_tasks_total = calc_tasks( + operations, + "ngpu", + context.get("parallel", False), + context.get("force", False), + ) + num_nodes_cpu = calc_num_nodes(cpu_tasks_total, cls._cpus_per_node, threshold) + num_nodes_gpu = calc_num_nodes(gpu_tasks_total, cls._gpus_per_node, threshold) + num_nodes = max(num_nodes_cpu, num_nodes_gpu, 1) + return { + "ncpu_tasks": cpu_tasks_total, + "ngpu_tasks": gpu_tasks_total, + "num_nodes": num_nodes, + } + class StandardEnvironment(ComputeEnvironment): """Default environment which is always present.""" diff --git a/flow/project.py b/flow/project.py index b0e3ee4b1..57b0ba249 100644 --- a/flow/project.py +++ b/flow/project.py @@ -663,6 +663,7 @@ def __init__( self._project = project self.submit_options = submit_options self.run_options = run_options + # TODO: This is no longer true. # We register aggregators associated with operation functions in # `_register_groups` and we do not set the aggregator explicitly. # We delay setting the aggregator because we do not restrict the @@ -1534,6 +1535,7 @@ def _internal_call( # Append the name and function to the class registry self._parent_class._OPERATION_FUNCTIONS.append((name, func)) + # TODO: This is no longer true and can likely be changed. # We register aggregators associated with operation functions in # `_register_groups` and we do not set the aggregator explicitly. We # delay setting the aggregator because we do not restrict the decorator @@ -3948,6 +3950,9 @@ def _generate_submit_script( context["id"] = _id context["operations"] = list(operations) context.update(kwargs) + context["resources_"] = self._environment._get_scheduler_values( + context, operations + ) if show_template_help: self._show_template_help_and_exit(template_environment, context) return template.render(**context) From 550696edeb4588a1592c6fde7b174c7b4aae1b72 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 27 Feb 2023 10:47:03 -0500 Subject: [PATCH 02/47] refactor: Port some templates over as examples of new style. --- flow/templates/andes.sh | 23 +++++++---------------- flow/templates/lsf.sh | 2 +- flow/templates/pbs.sh | 6 ++---- flow/templates/slurm.sh | 2 +- flow/templates/umich-greatlakes.sh | 18 ++++++------------ 5 files changed, 17 insertions(+), 34 deletions(-) diff --git a/flow/templates/andes.sh b/flow/templates/andes.sh index 27a0172e4..39b65f38b 100644 --- a/flow/templates/andes.sh +++ b/flow/templates/andes.sh @@ -1,37 +1,28 @@ {# Templated in accordance with: https://docs.olcf.ornl.gov/systems/andes_user_guide.html #} {% extends "slurm.sh" %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} - {% if gpu_tasks %} + {% if resources.ngpu_tasks %} {% if not ('GPU' in partition or force) %} {% raise "GPU operations require a GPU partition!" %} {% endif %} - {# GPU nodes have 2 NVIDIA K80s #} - {% set nn_gpu = gpu_tasks|calc_num_nodes(2) %} - {% set nn = nn_gpu %} {% else %} {% if 'gpu' in partition and not force %} {% raise "Requesting gpu partition, but no GPUs requested!" %} {% endif %} - {% set nn = nn|default(cpu_tasks|calc_num_nodes(32), true) %} {% endif %} {% if 'gpu' in partition %} - {% set gpus_per_node = (gpu_tasks / nn)|round(0, 'ceil')|int %} - {% set cpus_per_node = (cpu_tasks / nn)|round(0, 'ceil')|int %} - {% if cpus_per_node > gpus_per_node * 14 and not force %} + {% if resources.ncpu_tasks > resources.ngpu_tasks * 14 and not force %} {% raise "Cannot request more than 14 CPUs per GPU." %} {% endif %} {% endif %} {% if partition == 'gpu' %} -#SBATCH -N {{ nn|check_utilization(gpu_tasks, 2, threshold, 'GPU') }} -#SBATCH --ntasks-per-node={{ cpus_per_node }} -#SBATCH --gpus={{ gpu_tasks }} +#SBATCH -N {{ resources.num_nodes }} +#SBATCH --ntasks={{ resourecs.ncpus_tasks }} +#SBATCH --gpus={{ resources.ngpu_tasks }} {% else %} {# This should cover batch #} -#SBATCH -N {{ nn|check_utilization(cpu_tasks, 32, threshold, 'CPU') }} -#SBATCH --ntasks-per-node={{ (32, cpu_tasks)|min }} +#SBATCH -N {{ resources.num_nodes }} +#SBATCH --ntasks={{ (32 * resources.num_nodes, resources.ncpu_tasks)|min }} {% endif %} {% endblock tasks %} {% block header %} diff --git a/flow/templates/lsf.sh b/flow/templates/lsf.sh index 1f47127f9..4c9672112 100644 --- a/flow/templates/lsf.sh +++ b/flow/templates/lsf.sh @@ -17,6 +17,6 @@ #BSUB -eo {{ job_output }} {% endif %} {% block tasks %} -#BSUB -n {{ operations|calc_tasks('np', parallel, force) }} +#BSUB -n {{ resources.ncpu_tasks }} {% endblock tasks %} {% endblock header %} diff --git a/flow/templates/pbs.sh b/flow/templates/pbs.sh index 24211d510..4c25b7aa5 100644 --- a/flow/templates/pbs.sh +++ b/flow/templates/pbs.sh @@ -20,12 +20,10 @@ {% endblock preamble %} {% block tasks %} {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} - {% set s_gpu = ':gpus=1' if gpu_tasks else '' %} + {% set s_gpu = ':gpus=1' if resources.ngpu_tasks else '' %} {% set ppn = ppn|default(operations|calc_tasks('omp_num_threads', parallel, force), true) %} {% if ppn %} -#PBS -l nodes={{ nn|default(cpu_tasks|calc_num_nodes(ppn, threshold, 'CPU'), true) }}:ppn={{ ppn }}{{ s_gpu }} +#PBS -l nodes={{ resources.num_nodes }}:ppn={{ ppn }}{{ s_gpu }} {% else %} #PBS -l procs={{ cpu_tasks }}{{ s_gpu }} {% endif %} diff --git a/flow/templates/slurm.sh b/flow/templates/slurm.sh index 6ab494032..88f4c2747 100644 --- a/flow/templates/slurm.sh +++ b/flow/templates/slurm.sh @@ -20,6 +20,6 @@ {% endif %} {% endblock preamble %} {% block tasks %} -#SBATCH --ntasks={{ operations|calc_tasks('np', parallel, force) }} +#SBATCH --ntasks={{ resources.ncpu_tasks }} {% endblock tasks %} {% endblock header %} diff --git a/flow/templates/umich-greatlakes.sh b/flow/templates/umich-greatlakes.sh index 673b90df0..ae52dd51e 100644 --- a/flow/templates/umich-greatlakes.sh +++ b/flow/templates/umich-greatlakes.sh @@ -1,22 +1,16 @@ {% extends "slurm.sh" %} {% set partition = partition|default('standard', true) %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} - {% if gpu_tasks and 'gpu' not in partition and not force %} + {% if resources.gpu_tasks and 'gpu' not in partition and not force %} {% raise "Requesting GPUs requires a gpu partition!" %} {% endif %} - {% set nn_cpu = cpu_tasks|calc_num_nodes(36) if 'gpu' not in partition else cpu_tasks|calc_num_nodes(40) %} - {% set nn_gpu = gpu_tasks|calc_num_nodes(2) if 'gpu' in partition else 0 %} - {% set nn = nn|default((nn_cpu, nn_gpu)|max, true) %} {% if partition == 'gpu' %} -#SBATCH --nodes={{ nn|default(1, true) }} -#SBATCH --ntasks-per-node={{ (gpu_tasks, cpu_tasks)|max }} -#SBATCH --gpus={{ gpu_tasks }} +#SBATCH --nodes={{ resources.num_nodes }} +#SBATCH --ntasks={{ (resources.ngpu_tasks, resources.ncpu_tasks)|max }} +#SBATCH --gpus={{ resources.ngpu_tasks }} {% else %}{# standard compute partition #} -#SBATCH --nodes={{ nn }} -#SBATCH --ntasks-per-node={{ (36, cpu_tasks)|min }} +#SBATCH --nodes={{ resources.num_nodes }} +#SBATCH --ntasks-per-node={{ resources.ncpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} From 75d873cda645e2b29bc753e1683f513444b299ab Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Tue, 7 Mar 2023 18:37:47 -0500 Subject: [PATCH 03/47] feat: Create way for environment classes to use partition config CPUS and GPUS per partition are in theory supported. --- flow/environment.py | 20 ++++++++++++++++++-- flow/environments/umich.py | 3 ++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/flow/environment.py b/flow/environment.py index 51369ea22..560d86947 100644 --- a/flow/environment.py +++ b/flow/environment.py @@ -293,6 +293,7 @@ def _get_scheduler_values(cls, context, operations): Must be called after the rest of the template context has been gathered. """ threshold = 0.0 if context.get("force", False) else 0.9 + partition = context.get("partition", "default") cpu_tasks_total = calc_tasks( operations, "np", @@ -305,8 +306,15 @@ def _get_scheduler_values(cls, context, operations): context.get("parallel", False), context.get("force", False), ) - num_nodes_cpu = calc_num_nodes(cpu_tasks_total, cls._cpus_per_node, threshold) - num_nodes_gpu = calc_num_nodes(gpu_tasks_total, cls._gpus_per_node, threshold) + num_nodes_cpu = calc_num_nodes( + cpu_tasks_total, cls._get_cpus_per_node(partition), threshold + ) + if gpu_tasks_total > 0: + num_nodes_gpu = calc_num_nodes( + gpu_tasks_total, cls._get_gpus_per_node(partition), threshold + ) + else: + num_nodes_gpu = 0 num_nodes = max(num_nodes_cpu, num_nodes_gpu, 1) return { "ncpu_tasks": cpu_tasks_total, @@ -314,6 +322,14 @@ def _get_scheduler_values(cls, context, operations): "num_nodes": num_nodes, } + @classmethod + def _get_cpus_per_node(cls, partition): + return cls._cpus_per_node.get(partition, cls._cpus_per_node["default"]) + + @classmethod + def _get_gpus_per_node(cls, partition): + return cls._gpus_per_node.get(partition, cls._gpus_per_node["default"]) + class StandardEnvironment(ComputeEnvironment): """Default environment which is always present.""" diff --git a/flow/environments/umich.py b/flow/environments/umich.py index d68b611a0..bfbbf0784 100644 --- a/flow/environments/umich.py +++ b/flow/environments/umich.py @@ -13,7 +13,8 @@ class GreatLakesEnvironment(DefaultSlurmEnvironment): hostname_pattern = r"gl(-login)?[0-9]+\.arc-ts\.umich\.edu" template = "umich-greatlakes.sh" - cores_per_node = 1 + _cpus_per_node = {"default": 36, "gpu": 40} + _gpus_per_node = {"default": 2} @classmethod def add_args(cls, parser): From f459a32d92f3af8caff3b4f538b54f12831cbaf6 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 22 Mar 2023 10:43:16 -0400 Subject: [PATCH 04/47] refactor: Update all environment which I have access to. --- flow/environment.py | 9 ++++-- flow/environments/incite.py | 11 ++++--- flow/environments/xsede.py | 50 +++++++++++++++++++++++++++--- flow/templates/andes.sh | 6 +--- flow/templates/bridges2.sh | 40 +++++------------------- flow/templates/crusher.sh | 6 +--- flow/templates/delta.sh | 46 +++++---------------------- flow/templates/expanse.sh | 42 +++++-------------------- flow/templates/pbs.sh | 2 +- flow/templates/stampede2.sh | 15 +++------ flow/templates/summit.sh | 4 +-- flow/templates/umich-greatlakes.sh | 5 ++- 12 files changed, 89 insertions(+), 147 deletions(-) diff --git a/flow/environment.py b/flow/environment.py index 560d86947..9bc39bf6b 100644 --- a/flow/environment.py +++ b/flow/environment.py @@ -306,15 +306,18 @@ def _get_scheduler_values(cls, context, operations): context.get("parallel", False), context.get("force", False), ) - num_nodes_cpu = calc_num_nodes( - cpu_tasks_total, cls._get_cpus_per_node(partition), threshold - ) if gpu_tasks_total > 0: num_nodes_gpu = calc_num_nodes( gpu_tasks_total, cls._get_gpus_per_node(partition), threshold ) + num_nodes_cpu = calc_num_nodes( + cpu_tasks_total, cls._get_cpus_per_node(partition), 0 + ) else: num_nodes_gpu = 0 + num_nodes_cpu = calc_num_nodes( + cpu_tasks_total, cls._get_cpus_per_node(partition), threshold + ) num_nodes = max(num_nodes_cpu, num_nodes_gpu, 1) return { "ncpu_tasks": cpu_tasks_total, diff --git a/flow/environments/incite.py b/flow/environments/incite.py index a911bbbed..caf08c804 100644 --- a/flow/environments/incite.py +++ b/flow/environments/incite.py @@ -36,8 +36,8 @@ def my_operation(job): hostname_pattern = r".*\.summit\.olcf\.ornl\.gov" template = "summit.sh" mpi_cmd = "jsrun" - cores_per_node = 42 - gpus_per_node = 6 + _cpus_per_node = {"default": 42} + _gpus_per_node = {"default": 6} @template_filter def calc_num_nodes(cls, resource_sets, parallel=False): @@ -187,7 +187,8 @@ class AndesEnvironment(DefaultSlurmEnvironment): hostname_pattern = r"andes-.*\.olcf\.ornl\.gov" template = "andes.sh" mpi_cmd = "srun" - cores_per_node = 32 + _cpus_per_node = {"default": 32, "gpu": 28} + _gpus_per_node = {"default": 0, "gpu": 2} @classmethod def add_args(cls, parser): @@ -216,8 +217,8 @@ class CrusherEnvironment(DefaultSlurmEnvironment): hostname_pattern = r".*\.crusher\.olcf\.ornl\.gov" template = "crusher.sh" - cores_per_node = 56 - gpus_per_node = 8 + _cpus_per_node = {"default": 56} + _gpus_per_node = {"default": 8} mpi_cmd = "srun" @template_filter diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index 316d77f52..5e5043b3c 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -6,6 +6,7 @@ import os from ..environment import DefaultSlurmEnvironment, template_filter +from ..util import template_filters logger = logging.getLogger(__name__) @@ -25,6 +26,14 @@ class Stampede2Environment(DefaultSlurmEnvironment): mpi_cmd = "ibrun" offset_counter = 0 base_offset = _STAMPEDE_OFFSET + _cpus_per_node = { + "default": 48, + "skx-dev": 68, + "skx-normal": 68, + "skx-large": 68, + "icx-normal": 80, + } + _gpus_per_node = {"default": 0.0} @template_filter def return_and_increment(cls, increment): @@ -129,6 +138,31 @@ def _get_mpi_prefix(cls, operation, parallel): prefix = "" return prefix + @classmethod + def _get_scheduler_values(cls, context, operations): + threshold = 0.0 if context.get("force", False) else 0.9 + partition = context.get("partition", "default") + cpu_tasks_total = template_filters.calc_tasks( + operations, + "np", + len(operations) > 1 or context.get("parallel", False), + context.get("force", False), + ) + gpu_tasks_total = template_filters.calc_tasks( + operations, + "ngpu", + context.get("parallel", False), + context.get("force", False), + ) + num_nodes = template_filters.calc_num_nodes( + cpu_tasks_total, cls._get_cpus_per_node(partition), threshold + ) + return { + "ncpu_tasks": cpu_tasks_total, + "ngpu_tasks": gpu_tasks_total, + "num_nodes": num_nodes, + } + class Bridges2Environment(DefaultSlurmEnvironment): """Environment profile for the Bridges-2 supercomputer. @@ -138,8 +172,9 @@ class Bridges2Environment(DefaultSlurmEnvironment): hostname_pattern = r".*\.bridges2\.psc\.edu$" template = "bridges2.sh" - cores_per_node = 128 mpi_cmd = "mpirun" + _cpus_per_node = {"default": 128, "EM": 96, "GPU": 40, "GPU-shared": 40} + _gpus_per_node = {"default": 8} @classmethod def add_args(cls, parser): @@ -175,8 +210,8 @@ class ExpanseEnvironment(DefaultSlurmEnvironment): hostname_pattern = r".*\.expanse\.sdsc\.edu$" template = "expanse.sh" - cores_per_node = 128 - gpus_per_node = 4 + _cpus_per_node = {"default": 128, "GPU": 40} + _gpus_per_node = {"default": 4} @classmethod def add_args(cls, parser): @@ -216,7 +251,14 @@ class DeltaEnvironment(DefaultSlurmEnvironment): # gpu host: gpua075.delta.internal.ncsa.edu hostname_pattern = r"(gpua|dt|cn)(-login)?[0-9]+\.delta\.internal\.ncsa\.edu" template = "delta.sh" - cores_per_node = 128 + _cpus_per_node = { + "default": 128, + "gpuA40x4": 64, + "gpuA100x4": 64, + "gpuA100x8": 128, + "gpuMI100x8": 128, + } + _gpus_per_node = {"default": 4, "gpuA100x8": 8, "gpuMI100x8": 8} @classmethod def add_args(cls, parser): diff --git a/flow/templates/andes.sh b/flow/templates/andes.sh index 39b65f38b..0938899ba 100644 --- a/flow/templates/andes.sh +++ b/flow/templates/andes.sh @@ -15,14 +15,10 @@ {% raise "Cannot request more than 14 CPUs per GPU." %} {% endif %} {% endif %} - {% if partition == 'gpu' %} #SBATCH -N {{ resources.num_nodes }} #SBATCH --ntasks={{ resourecs.ncpus_tasks }} + {% if partition == 'gpu' %} #SBATCH --gpus={{ resources.ngpu_tasks }} - {% else %} - {# This should cover batch #} -#SBATCH -N {{ resources.num_nodes }} -#SBATCH --ntasks={{ (32 * resources.num_nodes, resources.ncpu_tasks)|min }} {% endif %} {% endblock tasks %} {% block header %} diff --git a/flow/templates/bridges2.sh b/flow/templates/bridges2.sh index f04ede0be..c95047c8d 100644 --- a/flow/templates/bridges2.sh +++ b/flow/templates/bridges2.sh @@ -1,48 +1,22 @@ {# Templated in accordance with: https://www.psc.edu/resources/bridges-2/user-guide #} {% extends "slurm.sh" %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} - {% if gpu_tasks %} + {% if resources.gpu_tasks %} {% if not ('GPU' in partition or force) %} {% raise "GPU operations require a GPU partition!" %} {% endif %} - {#- GPU nodes have 8 NVIDIA V100-32GB SXM2 #} - {% set nn_gpu = gpu_tasks|calc_num_nodes(8) %} - {% set nn = nn_gpu %} {% else %} {% if 'GPU' in partition and not force %} {% raise "Requesting GPU partition, but no GPUs requested!" %} {% endif %} - {% set nn = nn|default(cpu_tasks|calc_num_nodes(128), true) %} {% endif %} - {% if 'GPU' in partition %} - {% set gpus_per_node = (gpu_tasks / nn)|round(0, 'ceil')|int %} - {% set cpus_per_node = (cpu_tasks / nn)|round(0, 'ceil')|int %} - {% if cpus_per_node > gpus_per_node * 5 and not force %} - {% raise "Cannot request more than 5 CPUs per GPU." %} - {% endif %} + {% if partition == 'RM-shared' and resources.cpu_tasks > 64 %} + {% raise "Cannot request RM-shared with more than 64 tasks or multiple nodes." %} {% endif %} - {% if partition == 'GPU' %} -#SBATCH -N {{ nn|check_utilization(gpu_tasks, 8, threshold, 'GPU') }} -#SBATCH --gpus={{ gpu_tasks }} - {% elif partition == 'GPU-shared' %} -#SBATCH -N {{ nn|check_utilization(gpu_tasks, 1, threshold, 'GPU') }} -#SBATCH --gpus={{ gpu_tasks }} - {% elif partition == 'EM' %} -#SBATCH -N {{ nn|check_utilization(cpu_tasks, 96, threshold, 'CPU') }} -#SBATCH --ntasks-per-node={{ (96, cpu_tasks)|min }} - {% elif partition == 'RM-shared' %} - {% if nn|default(1, true) > 1 or cpu_tasks > 64 %} - {% raise "Cannot request RM-shared with more than 64 tasks or multiple nodes." %} - {% endif %} -#SBATCH -N {{ nn|default(1, true) }} -#SBATCH --ntasks={{ cpu_tasks }} - {% else %} -{#- This should cover RM, RM-512, and possibly RM-small (not documented) #} -#SBATCH -N {{ nn|check_utilization(cpu_tasks, 128, threshold, 'CPU') }} -#SBATCH --ntasks-per-node={{ (128, cpu_tasks)|min }} +#SBATCH -N {{ resources.cpu_tasks }} +#SBATCH --ntasks={{ resources.cpu_tasks }} + {% if 'GPU' in partition %} +#SBATCH --gpus={{ resources.gpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} diff --git a/flow/templates/crusher.sh b/flow/templates/crusher.sh index 430be4c9d..842d10bb5 100644 --- a/flow/templates/crusher.sh +++ b/flow/templates/crusher.sh @@ -1,11 +1,7 @@ {# Templated in accordance with: https://docs.olcf.ornl.gov/systems/crusher_quick_start_guide.html #} {% extends "slurm.sh" %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} - {% set nn = gpu_tasks|calc_num_nodes(cpu_tasks, threshold) %} -#SBATCH --nodes={{ nn }} +#SBATCH --nodes={{ resources.num_nodes }} {% endblock tasks %} {% block header %} {{- super() -}} diff --git a/flow/templates/delta.sh b/flow/templates/delta.sh index 8e173ff20..0aef7554b 100644 --- a/flow/templates/delta.sh +++ b/flow/templates/delta.sh @@ -1,56 +1,24 @@ {# Templated in accordance with: https://https://wiki.ncsa.illinois.edu/display/DSC/Delta+User+Guide #} {% extends "slurm.sh" %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} {% if partition in ["gpuA100x8", "gpuMI100x8"] %} - {% raise "Cannot use given partition in default Delta template." %} + {% raise "This partition is not supported as it has few nodes, + increased charges and is expected to be suitable for a + minority of use cases." %} {% endif %} {% if gpu_tasks %} - {% if partition == "gpuA40x4" %} - {% set nn_gpu = gpu_tasks|calc_num_nodes(4) %} - {% elif partition == "gpuA100x4" %} - {% set nn_gpu = gpu_tasks|calc_num_nodes(4) %} - {# We do not allow submission to the partitions below as they have few #} - {# nodes, should be rarely needed, and have increased charges for use. #} - {% elif partition in ["gpuA100x8", "gpuMI100x8"] %} - {% raise "This partition is not supported as it has few nodes, - increased charges and is expected to be suitable for a - minority of use cases." %} - {% else %} + {% if not ("gpu" in partition or force) %} {% raise "GPU operations require a GPU partition!" %} {% endif %} - {% set nn = nn_gpu %} {% else %} {% if 'gpu' in partition and not force %} {% raise "Requesting GPU partition, but no GPUs requested!" %} {% endif %} - {% set nn = nn|default(cpu_tasks|calc_num_nodes(128), true) %} - {% endif %} - {% if 'gpu' in partition %} - {% set gpus_per_node = (gpu_tasks / nn)|round(0, 'ceil')|int %} - {% set cpus_per_node = (cpu_tasks / nn)|round(0, 'ceil')|int %} - {% if cpus_per_node > gpus_per_node * 16 and not force %} - {% raise "Cannot request more than 16 CPUs per GPU." %} - {% endif %} {% endif %} +#SBATCH -N {{ resources.num_nodes }} +#SBATCH --ntasks={{ resources.ncpus_tasks }} {% if "gpu" in partition %} - {% if nn ==1 %} -#SBATCH -N {{ nn }} - {% else %} -#SBATCH -N {{ nn|check_utilization(gpu_tasks, 4, threshold, 'GPU') }} - {% endif %} -#SBATCH --ntasks-per-node={{ cpus_per_node }} -#SBATCH --gpus-per-node={{ gpus_per_node }} - {% else %} - {% if nn == 1 %} -#SBATCH -N {{ nn }} -#SBATCH --ntasks-per-node={{ (128, cpu_tasks)|min }} - {% else %} -#SBATCH -N {{ nn|check_utilization(cpu_tasks, 128, threshold, 'CPU') }} -#SBATCH --ntasks-per-node={{ (128, cpu_tasks)|min }} - {% endif %} +#SBATCH --gpus-per-node={{ resources.gpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} diff --git a/flow/templates/expanse.sh b/flow/templates/expanse.sh index 9ec2778e9..192e0e546 100644 --- a/flow/templates/expanse.sh +++ b/flow/templates/expanse.sh @@ -1,50 +1,22 @@ {# Templated in accordance with: https://www.sdsc.edu/support/user_guides/expanse.html #} {% extends "slurm.sh" %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} - {% if gpu_tasks %} + {% if resources.gpu_tasks %} {% if not ('gpu' in partition or force) %} {% raise "GPU operations require a GPU partition!" %} {% endif %} - {# GPU nodes have 4 NVIDIA V100-32GB SMX2 #} - {% set nn_gpu = gpu_tasks|calc_num_nodes(4) %} - {% set nn = nn_gpu %} {% else %} {% if 'gpu' in partition and not force %} {% raise "Requesting GPU partition, but no GPUs requested!" %} {% endif %} - {% set nn = nn|default(cpu_tasks|calc_num_nodes(128), true) %} {% endif %} - {% if 'gpu' in partition %} - {% set gpus_per_node = (gpu_tasks / nn)|round(0, 'ceil')|int %} - {% set cpus_per_node = (cpu_tasks / nn)|round(0, 'ceil')|int %} - {% if cpus_per_node > gpus_per_node * 10 and not force %} - {% raise "Cannot request more than 10 CPUs per GPU." %} - {% endif %} + {% if "shared" in partition and resources.num_nodes > 1 %} + {% raise "Cannot request shared partition with resources spanning multiple nodes." %} {% endif %} - {% if partition == 'gpu' %} -#SBATCH -N {{ nn|check_utilization(gpu_tasks, 4, threshold, 'GPU') }} -#SBATCH --ntasks-per-node={{ cpus_per_node }} -#SBATCH --gpus={{ gpu_tasks }} - {% elif partition == 'gpu-shared' %} - {% if nn|default(1, true) > 1 %} - {% raise "Cannot request shared partition with resources spanning multiple nodes." %} - {% endif %} -#SBATCH -N {{ nn|check_utilization(gpu_tasks, 1, threshold, 'GPU') }} -#SBATCH --ntasks-per-node={{ cpus_per_node }} -#SBATCH --gpus={{ gpu_tasks }} - {% elif partition == 'shared' %} - {% if nn|default(1, true) > 1 %} - {% raise "Cannot request shared partition with resources spanning multiple nodes." %} - {% endif %} -#SBATCH -N 1 -#SBATCH --ntasks={{ cpu_tasks }} - {% else %} -{# This should cover compute and large-memory #} -#SBATCH -N {{ nn|check_utilization(cpu_tasks, 128, threshold, 'CPU') }} -#SBATCH --ntasks-per-node={{ (128, cpu_tasks)|min }} +#SBATCH -N {{ resources.num_nodes }} +#SBATCH --ntasks={{ resources.ncpus_tasks }} + {% if 'gpu' in partition %} +#SBATCH --gpus={{ resources.gpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} diff --git a/flow/templates/pbs.sh b/flow/templates/pbs.sh index 4c25b7aa5..08c28f9f1 100644 --- a/flow/templates/pbs.sh +++ b/flow/templates/pbs.sh @@ -25,7 +25,7 @@ {% if ppn %} #PBS -l nodes={{ resources.num_nodes }}:ppn={{ ppn }}{{ s_gpu }} {% else %} -#PBS -l procs={{ cpu_tasks }}{{ s_gpu }} +#PBS -l procs={{ resources.cpu_tasks }}{{ s_gpu }} {% endif %} {% endblock tasks %} {% endblock header %} diff --git a/flow/templates/stampede2.sh b/flow/templates/stampede2.sh index 80eb61b73..4ab554154 100644 --- a/flow/templates/stampede2.sh +++ b/flow/templates/stampede2.sh @@ -12,19 +12,12 @@ {% block tasks %} {% set threshold = 0 if force else 0.9 %} - {% if operations|calc_tasks('ngpu', false, true) and not force %} + {% if resources.ngpu_tasks and not force %} {% raise "GPUs were requested but are unsupported by Stampede2!" %} {% endif %} {% set cpn = 48 if 'skx' in partition else 68 %} - {% if ns.use_launcher %} - {% set cpu_tasks = operations|calc_tasks('np', true, force) %} -#SBATCH --nodes={{ nn|default(cpu_tasks|calc_num_nodes(cpn, threshold, 'CPU'), true) }} -#SBATCH --ntasks={{ nn|default(cpu_tasks|calc_num_nodes(cpn, threshold, 'CPU'), true) * cpn }} - {% else %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} -#SBATCH --nodes={{ nn|default(cpu_tasks|calc_num_nodes(cpn, threshold, 'CPU'), true) }} -#SBATCH --ntasks={{ (operations|calc_tasks('nranks', parallel, force), 1)|max }} - {% endif %} +#SBATCH --nodes={{ resources.num_nodes }} +#SBATCH --ntasks={{ resources.ncpu_tasks }} {% endblock tasks %} {% block header %} @@ -36,7 +29,7 @@ {% endblock %} {% block body %} - {% if ns.use_launcher %} + {% if use_launcher %} {% if parallel %} {{ ("Bundled submission without MPI on Stampede2 is using launcher; the --parallel option is therefore ignored.")|print_warning }} {% endif %} diff --git a/flow/templates/summit.sh b/flow/templates/summit.sh index e3ac08ba1..910383fdb 100644 --- a/flow/templates/summit.sh +++ b/flow/templates/summit.sh @@ -1,9 +1,7 @@ {# Templated in accordance with: https://www.olcf.ornl.gov/for-users/system-user-guides/summit/running-jobs/ #} {% extends "lsf.sh" %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set nn = operations|map('guess_resource_sets')|calc_num_nodes(parallel) %} -#BSUB -nnodes {{ nn }} +#BSUB -nnodes {{ resources.num_nodes }} {% endblock tasks %} {% block header %} {{- super() -}} diff --git a/flow/templates/umich-greatlakes.sh b/flow/templates/umich-greatlakes.sh index ae52dd51e..6432fb5d7 100644 --- a/flow/templates/umich-greatlakes.sh +++ b/flow/templates/umich-greatlakes.sh @@ -4,13 +4,12 @@ {% if resources.gpu_tasks and 'gpu' not in partition and not force %} {% raise "Requesting GPUs requires a gpu partition!" %} {% endif %} - {% if partition == 'gpu' %} #SBATCH --nodes={{ resources.num_nodes }} + {% if partition == 'gpu' %} #SBATCH --ntasks={{ (resources.ngpu_tasks, resources.ncpu_tasks)|max }} #SBATCH --gpus={{ resources.ngpu_tasks }} {% else %}{# standard compute partition #} -#SBATCH --nodes={{ resources.num_nodes }} -#SBATCH --ntasks-per-node={{ resources.ncpu_tasks }} +#SBATCH --ntasks={{ resources.ncpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} From 019cfb4acfbd97b55a90aeeb7d5f3fcde227390c Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 23 Mar 2023 20:18:46 -0400 Subject: [PATCH 05/47] fix: Provide default node behavior for _get_scheduler_values Use sentinal of -1 to denote no node structure and always return 1 node requested for either CPU or GPU tasks. --- flow/environment.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/flow/environment.py b/flow/environment.py index 9bc39bf6b..b14e26d0e 100644 --- a/flow/environment.py +++ b/flow/environment.py @@ -108,6 +108,8 @@ class ComputeEnvironment(metaclass=_ComputeEnvironmentType): submit_flags = None template = "base_script.sh" mpi_cmd = "mpiexec" + _cpus_per_node = {"default": -1} + _gpus_per_node = {"default": -1} @classmethod def is_present(cls): @@ -306,16 +308,17 @@ def _get_scheduler_values(cls, context, operations): context.get("parallel", False), context.get("force", False), ) + if gpu_tasks_total > 0: - num_nodes_gpu = calc_num_nodes( + num_nodes_gpu = cls._calc_num_nodes( gpu_tasks_total, cls._get_gpus_per_node(partition), threshold ) - num_nodes_cpu = calc_num_nodes( + num_nodes_cpu = cls._calc_num_nodes( cpu_tasks_total, cls._get_cpus_per_node(partition), 0 ) else: num_nodes_gpu = 0 - num_nodes_cpu = calc_num_nodes( + num_nodes_cpu = cls._calc_num_nodes( cpu_tasks_total, cls._get_cpus_per_node(partition), threshold ) num_nodes = max(num_nodes_cpu, num_nodes_gpu, 1) @@ -333,6 +336,13 @@ def _get_cpus_per_node(cls, partition): def _get_gpus_per_node(cls, partition): return cls._gpus_per_node.get(partition, cls._gpus_per_node["default"]) + @classmethod + def _calc_num_nodes(cls, tasks, processors, threshold): + """Call calc_num_nodes but handles the -1 sentinal value.""" + if processors == -1: + return 1 + return calc_num_nodes(tasks, processors, threshold) + class StandardEnvironment(ComputeEnvironment): """Default environment which is always present.""" From 33c624b252b1dc6976db2bf0705c6c9ce207dfa5 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 23 Mar 2023 20:19:54 -0400 Subject: [PATCH 06/47] fix: Fix typo in setting resources to Jinja2 context --- flow/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/project.py b/flow/project.py index 57b0ba249..630efad7a 100644 --- a/flow/project.py +++ b/flow/project.py @@ -3950,7 +3950,7 @@ def _generate_submit_script( context["id"] = _id context["operations"] = list(operations) context.update(kwargs) - context["resources_"] = self._environment._get_scheduler_values( + context["resources"] = self._environment._get_scheduler_values( context, operations ) if show_template_help: From 25263896f88c0a416529c9275c0ac14f20bdbb82 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 5 Apr 2023 11:32:36 -0400 Subject: [PATCH 07/47] fix: Delta template partition node submissions. --- flow/templates/delta.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flow/templates/delta.sh b/flow/templates/delta.sh index 0aef7554b..c2ff9a94a 100644 --- a/flow/templates/delta.sh +++ b/flow/templates/delta.sh @@ -15,8 +15,10 @@ {% raise "Requesting GPU partition, but no GPUs requested!" %} {% endif %} {% endif %} + {% if resources.num_nodes > 1 %} #SBATCH -N {{ resources.num_nodes }} -#SBATCH --ntasks={{ resources.ncpus_tasks }} + {% endif %} +#SBATCH --ntasks={{ resources.ncpu_tasks }} {% if "gpu" in partition %} #SBATCH --gpus-per-node={{ resources.gpu_tasks }} {% endif %} From 61ff9c687b8e3140cc8ef811594bd6d658f48ea2 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 5 Apr 2023 11:33:00 -0400 Subject: [PATCH 08/47] refactor: Provide infrastrucure to correctly warn on low-resources Add the ComputeEnvironment._shared_partitions attribute to check if less than single node submissions should be allowed in ComputeEnvironment._get_scheduler_values. --- flow/environment.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/flow/environment.py b/flow/environment.py index b14e26d0e..b79e3c89e 100644 --- a/flow/environment.py +++ b/flow/environment.py @@ -108,8 +108,10 @@ class ComputeEnvironment(metaclass=_ComputeEnvironmentType): submit_flags = None template = "base_script.sh" mpi_cmd = "mpiexec" + _cpus_per_node = {"default": -1} _gpus_per_node = {"default": -1} + _shared_partitions = {} @classmethod def is_present(cls): @@ -287,23 +289,27 @@ def _get_default_directives(cls): ) @classmethod - def _get_scheduler_values(cls, context, operations): + def _get_scheduler_values(cls, context): """Return a dictionary of computed quantities regarding submission. Warning ------- Must be called after the rest of the template context has been gathered. """ - threshold = 0.0 if context.get("force", False) else 0.9 - partition = context.get("partition", "default") + partition = context.get("partition", None) + force = context.get("force", False) + if force or partition in cls._shared_partitions: + threshold = 0.0 + else: + threshold = 0.9 cpu_tasks_total = calc_tasks( - operations, + context["operations"], "np", context.get("parallel", False), context.get("force", False), ) gpu_tasks_total = calc_tasks( - operations, + context["operations"], "ngpu", context.get("parallel", False), context.get("force", False), From 5c45b551bb2f9fd6db7130b25224802141233521 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 5 Apr 2023 11:34:50 -0400 Subject: [PATCH 09/47] refactor (WIP): Add _shared_partitions to Delta env for testing. --- flow/environments/xsede.py | 1 + 1 file changed, 1 insertion(+) diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index 5e5043b3c..c6ea51c65 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -259,6 +259,7 @@ class DeltaEnvironment(DefaultSlurmEnvironment): "gpuMI100x8": 128, } _gpus_per_node = {"default": 4, "gpuA100x8": 8, "gpuMI100x8": 8} + _shared_partitions = {"cpu", "gpuA100x4", "gpuA40x4", "gpuA100x8", "gpuMI100x8"} @classmethod def add_args(cls, parser): From 0226670805a667c195a31267e752789b9cb9a200 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 5 Apr 2023 12:00:30 -0400 Subject: [PATCH 10/47] fixup: removing operation from _get_scheduler_values --- flow/project.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flow/project.py b/flow/project.py index 3f8a68a3a..f8094d704 100644 --- a/flow/project.py +++ b/flow/project.py @@ -3948,9 +3948,7 @@ def _generate_submit_script( context["id"] = _id context["operations"] = list(operations) context.update(kwargs) - context["resources"] = self._environment._get_scheduler_values( - context, operations - ) + context["resources"] = self._environment._get_scheduler_values(context) if show_template_help: self._show_template_help_and_exit(template_environment, context) return template.render(**context) From 59908cf86e43c276f9d7101e69a56d93b5d77bee Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 6 Apr 2023 12:46:43 -0500 Subject: [PATCH 11/47] fix: Delta template The Delta template is now tested and works. --- flow/templates/delta.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flow/templates/delta.sh b/flow/templates/delta.sh index c2ff9a94a..4785da06a 100644 --- a/flow/templates/delta.sh +++ b/flow/templates/delta.sh @@ -6,7 +6,7 @@ increased charges and is expected to be suitable for a minority of use cases." %} {% endif %} - {% if gpu_tasks %} + {% if resources.ngpu_tasks %} {% if not ("gpu" in partition or force) %} {% raise "GPU operations require a GPU partition!" %} {% endif %} @@ -20,7 +20,7 @@ {% endif %} #SBATCH --ntasks={{ resources.ncpu_tasks }} {% if "gpu" in partition %} -#SBATCH --gpus-per-node={{ resources.gpu_tasks }} +#SBATCH --gpus={{ resources.ngpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} From 215c4d4762b1c6411ae846bed48b4f5b16325296 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Mon, 17 Apr 2023 16:37:00 -0400 Subject: [PATCH 12/47] fix: Bridges2 template and environment. The environment is now tested. --- flow/environments/xsede.py | 2 +- flow/templates/bridges2.sh | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index c6ea51c65..7e4c0f9e4 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -175,7 +175,7 @@ class Bridges2Environment(DefaultSlurmEnvironment): mpi_cmd = "mpirun" _cpus_per_node = {"default": 128, "EM": 96, "GPU": 40, "GPU-shared": 40} _gpus_per_node = {"default": 8} - + _shared_partitions = {"RM-shared", "GPU-shared"} @classmethod def add_args(cls, parser): """Add arguments to parser. diff --git a/flow/templates/bridges2.sh b/flow/templates/bridges2.sh index c95047c8d..6353ab6df 100644 --- a/flow/templates/bridges2.sh +++ b/flow/templates/bridges2.sh @@ -1,22 +1,27 @@ {# Templated in accordance with: https://www.psc.edu/resources/bridges-2/user-guide #} {% extends "slurm.sh" %} {% block tasks %} - {% if resources.gpu_tasks %} + {% if resources.ngpu_tasks %} {% if not ('GPU' in partition or force) %} {% raise "GPU operations require a GPU partition!" %} {% endif %} + {% if partition == "GPU-shared" and resources.ngpu_tasks > 4 %} + {% raise "Cannot request GPU-shared with more than 4 GPUs." %} + {% endif %} {% else %} {% if 'GPU' in partition and not force %} {% raise "Requesting GPU partition, but no GPUs requested!" %} {% endif %} {% endif %} - {% if partition == 'RM-shared' and resources.cpu_tasks > 64 %} + {% if partition == 'RM-shared' and resources.ncpu_tasks > 64 %} {% raise "Cannot request RM-shared with more than 64 tasks or multiple nodes." %} {% endif %} -#SBATCH -N {{ resources.cpu_tasks }} -#SBATCH --ntasks={{ resources.cpu_tasks }} + {% if resources.num_nodes > 1 or resources.ncpu_tasks >= 128 or resources.ngpu_tasks >= 8%} +#SBATCH -N {{ resources.num_nodes }} + {% endif %} +#SBATCH --ntasks={{ resources.ncpu_tasks }} {% if 'GPU' in partition %} -#SBATCH --gpus={{ resources.gpu_tasks }} +#SBATCH --gpus={{ resources.ngpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} From 2acaea5d2979f0ccfde1b87be5d6ceacf888f598 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 20:40:15 +0000 Subject: [PATCH 13/47] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- flow/environments/xsede.py | 1 + 1 file changed, 1 insertion(+) diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index 7e4c0f9e4..be264ba7e 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -176,6 +176,7 @@ class Bridges2Environment(DefaultSlurmEnvironment): _cpus_per_node = {"default": 128, "EM": 96, "GPU": 40, "GPU-shared": 40} _gpus_per_node = {"default": 8} _shared_partitions = {"RM-shared", "GPU-shared"} + @classmethod def add_args(cls, parser): """Add arguments to parser. From 0dcf6f7a99a26f510a863a3a1ea4485b5932448d Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 28 Apr 2023 13:26:10 -0400 Subject: [PATCH 14/47] refactor: Reset Stampede2 template I do not have access to the cluster, so to prevent regressions, I am reseting this. --- flow/templates/stampede2.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/flow/templates/stampede2.sh b/flow/templates/stampede2.sh index 4ab554154..80eb61b73 100644 --- a/flow/templates/stampede2.sh +++ b/flow/templates/stampede2.sh @@ -12,12 +12,19 @@ {% block tasks %} {% set threshold = 0 if force else 0.9 %} - {% if resources.ngpu_tasks and not force %} + {% if operations|calc_tasks('ngpu', false, true) and not force %} {% raise "GPUs were requested but are unsupported by Stampede2!" %} {% endif %} {% set cpn = 48 if 'skx' in partition else 68 %} -#SBATCH --nodes={{ resources.num_nodes }} -#SBATCH --ntasks={{ resources.ncpu_tasks }} + {% if ns.use_launcher %} + {% set cpu_tasks = operations|calc_tasks('np', true, force) %} +#SBATCH --nodes={{ nn|default(cpu_tasks|calc_num_nodes(cpn, threshold, 'CPU'), true) }} +#SBATCH --ntasks={{ nn|default(cpu_tasks|calc_num_nodes(cpn, threshold, 'CPU'), true) * cpn }} + {% else %} + {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} +#SBATCH --nodes={{ nn|default(cpu_tasks|calc_num_nodes(cpn, threshold, 'CPU'), true) }} +#SBATCH --ntasks={{ (operations|calc_tasks('nranks', parallel, force), 1)|max }} + {% endif %} {% endblock tasks %} {% block header %} @@ -29,7 +36,7 @@ {% endblock %} {% block body %} - {% if use_launcher %} + {% if ns.use_launcher %} {% if parallel %} {{ ("Bundled submission without MPI on Stampede2 is using launcher; the --parallel option is therefore ignored.")|print_warning }} {% endif %} From f5a7ecaef1aac7d308e4c801b6b3c592b629516f Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 28 Apr 2023 13:27:36 -0400 Subject: [PATCH 15/47] fix: Update shared partitions specifications in environments. --- flow/environments/incite.py | 6 ++++++ flow/environments/xsede.py | 1 + 2 files changed, 7 insertions(+) diff --git a/flow/environments/incite.py b/flow/environments/incite.py index caf08c804..e5d1fb09b 100644 --- a/flow/environments/incite.py +++ b/flow/environments/incite.py @@ -38,6 +38,7 @@ def my_operation(job): mpi_cmd = "jsrun" _cpus_per_node = {"default": 42} _gpus_per_node = {"default": 6} + _shared_partitions = set() @template_filter def calc_num_nodes(cls, resource_sets, parallel=False): @@ -189,6 +190,8 @@ class AndesEnvironment(DefaultSlurmEnvironment): mpi_cmd = "srun" _cpus_per_node = {"default": 32, "gpu": 28} _gpus_per_node = {"default": 0, "gpu": 2} + # No shared partitions requests must be in full nodes. + _shared_partitions = set() @classmethod def add_args(cls, parser): @@ -219,6 +222,9 @@ class CrusherEnvironment(DefaultSlurmEnvironment): template = "crusher.sh" _cpus_per_node = {"default": 56} _gpus_per_node = {"default": 8} + # Crusher has no shared partitions + _shared_partitions = set() + mpi_cmd = "srun" @template_filter diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index be264ba7e..5ecff08be 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -213,6 +213,7 @@ class ExpanseEnvironment(DefaultSlurmEnvironment): template = "expanse.sh" _cpus_per_node = {"default": 128, "GPU": 40} _gpus_per_node = {"default": 4} + _shared_partitions = {"shared", "gpu-shared"} @classmethod def add_args(cls, parser): From d981ee91b3b0578f88ce9a9863bce4e4a1011211 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 28 Apr 2023 13:28:04 -0400 Subject: [PATCH 16/47] fix: Only specify node request on non-shared partitions (Expanse). --- flow/templates/expanse.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flow/templates/expanse.sh b/flow/templates/expanse.sh index 192e0e546..a052726cc 100644 --- a/flow/templates/expanse.sh +++ b/flow/templates/expanse.sh @@ -13,7 +13,9 @@ {% if "shared" in partition and resources.num_nodes > 1 %} {% raise "Cannot request shared partition with resources spanning multiple nodes." %} {% endif %} + {% if "shared" not in partition %} #SBATCH -N {{ resources.num_nodes }} + {% endif %} #SBATCH --ntasks={{ resources.ncpus_tasks }} {% if 'gpu' in partition %} #SBATCH --gpus={{ resources.gpu_tasks }} From 058636b130d22cd7782501fa4f8d89c4e30e26ed Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:19:35 -0500 Subject: [PATCH 17/47] [pre-commit.ci] pre-commit autoupdate (#736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77c1655d9..b574d4e44 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: '23.1.0' + rev: '23.3.0' hooks: - id: black - repo: https://github.com/PyCQA/flake8 From c571532a482e3f7162d2fded5561468fe88d4e9a Mon Sep 17 00:00:00 2001 From: Corwin Kerr Date: Fri, 14 Apr 2023 16:24:36 -0400 Subject: [PATCH 18/47] Remove doc-filter after query switched to unified syntax (#738) * First pass at fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add filter test * Update changelog --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- changelog.txt | 13 +++++++++++++ flow/project.py | 24 ++++++------------------ tests/test_project.py | 12 ++++++++++++ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/changelog.txt b/changelog.txt index a002f4d5c..1657aefd3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,19 @@ The numbers in brackets denote the related GitHub issue and/or pull request. Version 0.25 ============ +[0.25.1] -- 2023-XX-XX +---------------------- + +Fixed ++++++ + +- Fix filter argument (#737, #738). + +Removed ++++++++ + +- ``--doc-filter`` argument (#738). + [0.25.0] -- 2023-03-30 ---------------------- diff --git a/flow/project.py b/flow/project.py index f8094d704..55f42f725 100644 --- a/flow/project.py +++ b/flow/project.py @@ -4226,12 +4226,6 @@ def _add_job_selection_args(cls, parser): nargs="+", help="Only select jobs that match the given state point filter.", ) - parser.add_argument( - "--doc-filter", - type=str, - nargs="+", - help="Only select jobs that match the given document filter.", - ) @classmethod def _add_operation_selection_arg_group(cls, parser): @@ -4760,7 +4754,6 @@ def _main_status(self, args): "debug", "job_id", "filter", - "doc_filter", ] } if args.pop("full"): @@ -4879,14 +4872,10 @@ def operation_function(job): operation_function(*aggregate) def _select_jobs_from_args(self, args): - """Select jobs with the given command line arguments ('-j/-f/--doc-filter/--job-id').""" - if ( - not args.func == self._main_exec - and args.job_id - and (args.filter or args.doc_filter) - ): + """Select jobs with the given command line arguments ('-j/-f/--job-id').""" + if not args.func == self._main_exec and args.job_id and (args.filter): raise ValueError( - "Cannot provide both -j/--job-id and -f/--filter or --doc-filter in combination." + "Cannot provide both -j/--job-id and -f/--filter in combination." ) if args.job_id: @@ -4903,12 +4892,11 @@ def _select_jobs_from_args(self, args): elif args.func == self._main_exec: # exec command does not support filters, so we must exit early. return _AggregateStoresCursor(self) - elif args.filter or args.doc_filter: - # filter or doc_filter provided. Filters can only be used to select + elif args.filter: + # filter, including doc_filter provided. Filters can only be used to select # single jobs and not aggregates of multiple jobs. filter_ = parse_filter_arg(args.filter) - doc_filter = parse_filter_arg(args.doc_filter) - return _JobAggregateCursor(self, filter_, doc_filter) + return _JobAggregateCursor(self, filter_) else: # Use all aggregates return _AggregateStoresCursor(self) diff --git a/tests/test_project.py b/tests/test_project.py index a8b99401d..7d0627329 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -1307,6 +1307,18 @@ def test_main_run(self): else: assert not job.isfile("world.txt") + def test_main_run_filter(self): + assert len(self.project) + for job in self.project: + assert not job.isfile("world.txt") + self.call_subcmd("run -o op1 -f b 2") + even_jobs = [job for job in self.project if job.sp.b == 2] + for job in self.project: + if job in even_jobs: + assert job.isfile("world.txt") + else: + assert not job.isfile("world.txt") + def test_main_run_invalid_op(self): assert len(self.project) run_output = self.call_subcmd( From d6a412daa924e63c969bc75c2b3e699bcf20575f Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 20 Apr 2023 14:40:42 -0400 Subject: [PATCH 19/47] fix: Delta hostname regex. (#740) Delta changed their compute node hostnames in their April 19th maintanance. This fixes the detection of the delta environment. --- flow/environments/xsede.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index 5ecff08be..d6f4d21c2 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -249,9 +249,11 @@ class DeltaEnvironment(DefaultSlurmEnvironment): # Example hostnames # login: dt-login02.delta.internal.ncsa.edu - # cpu host: cn001.delta.internal.ncsa.edu - # gpu host: gpua075.delta.internal.ncsa.edu - hostname_pattern = r"(gpua|dt|cn)(-login)?[0-9]+\.delta\.internal\.ncsa\.edu" + # cpu host: cn001.delta.ncsa.illinois.edu + # gpu host: gpua049.delta.ncsa.illinois.edu + # Avoid full specification of patterns as Delta has a habit of changing hostnames. This should + # be safer given the parts listed are less likely to change. + hostname_pattern = r"(gpua|dt|cn)(-login)?[0-9]+\.delta.*\.ncsa.*\.edu" template = "delta.sh" _cpus_per_node = { "default": 128, From a5ea111cbd9224fa900457aced738d5674b2de01 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Fri, 21 Apr 2023 16:31:42 -0400 Subject: [PATCH 20/47] Release/0.25.1 (#742) * doc: Update changelog. * Bump up to version 0.25.1. --- .bumpversion.cfg | 2 +- .zenodo.json | 2 +- CITATION.cff | 2 +- changelog.txt | 3 ++- doc/conf.py | 4 ++-- flow/version.py | 2 +- pyproject.toml | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 2b05d6292..2c5d512ce 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.25.0 +current_version = 0.25.1 commit = True tag = False message = Bump up to version {new_version}. diff --git a/.zenodo.json b/.zenodo.json index 21c1bac11..9673db661 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -194,5 +194,5 @@ }, "title": "signac-flow", "upload_type": "software", - "version": "0.25.0" + "version": "0.25.1" } diff --git a/CITATION.cff b/CITATION.cff index e451e5315..67c405c3c 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,7 +2,7 @@ --- cff-version: "1.0.3" title: signac-flow -version: 0.25.0 +version: 0.25.1 abstract: | The signac-flow tool provides the basic components to set up simple to complex workflows for projects as part of the signac framework. That includes the definition of data pipelines, execution of data space operations and the submission of operations to high-performance super computers. authors: diff --git a/changelog.txt b/changelog.txt index 1657aefd3..4544d1551 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,12 +8,13 @@ The numbers in brackets denote the related GitHub issue and/or pull request. Version 0.25 ============ -[0.25.1] -- 2023-XX-XX +[0.25.1] -- 2023-04-21 ---------------------- Fixed +++++ +- Delta hostname detection (#740). - Fix filter argument (#737, #738). Removed diff --git a/doc/conf.py b/doc/conf.py index aec38945c..2be98a766 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -78,9 +78,9 @@ def __getattr__(cls, name): # built documents. # # The short X.Y version. -version = "0.25.0" +version = "0.25.1" # The full version, including alpha/beta/rc tags. -release = "0.25.0" +release = "0.25.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/flow/version.py b/flow/version.py index e1a131ee4..47b288091 100644 --- a/flow/version.py +++ b/flow/version.py @@ -3,6 +3,6 @@ # This software is licensed under the BSD 3-Clause License. """Define the signac-flow version.""" -__version__ = "0.25.0" +__version__ = "0.25.1" __all__ = ["__version__"] diff --git a/pyproject.toml b/pyproject.toml index 02e5d258a..f6a6d5f33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ requires = ["setuptools>=64.0.0"] [project] name = "signac-flow" -version = "0.25.0" +version = "0.25.1" description = "Simple workflow management for signac projects." readme = "README.md" # Supported versions are determined according to NEP 29. From a5bba490dc73838d8c4d1103925696efce99c0b5 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 27 Apr 2023 10:44:17 -0400 Subject: [PATCH 21/47] Add Frontier Environment (#743) * feat: Add the Frontier supercomputer to environments. * test: Add Frontier to template testing. * test: Update environment test template generation to signac 2.0 * doc: Update changelog * doc: Add Frontier documentation. * doc: Update incode comment clarity Co-authored-by: Bradley Dice --------- Co-authored-by: Bradley Dice --- changelog.txt | 11 ++++ doc/supported_environments.rst | 1 + doc/supported_environments/frontier.rst | 12 +++++ flow/environments/incite.py | 58 ++++++++++++++++++++- flow/templates/frontier.sh | 19 +++++++ tests/generate_template_reference_data.py | 8 +++ tests/generate_template_review_document.py | 2 +- tests/template_reference_data.tar.gz | Bin 20602 -> 22174 bytes 8 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 doc/supported_environments/frontier.rst create mode 100644 flow/templates/frontier.sh diff --git a/changelog.txt b/changelog.txt index 4544d1551..dc0d4ecfb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,6 +5,17 @@ Changelog The **signac-flow** package follows `semantic versioning `_. The numbers in brackets denote the related GitHub issue and/or pull request. +Version 0.26 +============ + +[0.26.0] -- 2023-xx-xx +---------------------- + +Added ++++++ + +- Frontier environment and template (#743). + Version 0.25 ============ diff --git a/doc/supported_environments.rst b/doc/supported_environments.rst index 408c065c4..afb522b4d 100644 --- a/doc/supported_environments.rst +++ b/doc/supported_environments.rst @@ -29,6 +29,7 @@ In addition, the package provides specialized submission templates for the follo supported_environments/summit supported_environments/andes supported_environments/crusher + supported_environments/frontier supported_environments/bridges2 supported_environments/delta supported_environments/expanse diff --git a/doc/supported_environments/frontier.rst b/doc/supported_environments/frontier.rst new file mode 100644 index 000000000..bb03f818a --- /dev/null +++ b/doc/supported_environments/frontier.rst @@ -0,0 +1,12 @@ +.. _frontier: + +OLCF Frontier +============= + +`Link to official documentation `_ + +.. autoclass:: flow.environments.incite.FrontierEnvironment + +.. literalinclude:: ../../flow/templates/frontier.sh + :caption: frontier.sh + :language: jinja diff --git a/flow/environments/incite.py b/flow/environments/incite.py index e5d1fb09b..5f095c593 100644 --- a/flow/environments/incite.py +++ b/flow/environments/incite.py @@ -266,4 +266,60 @@ def _get_mpi_prefix(cls, operation, parallel): return base_str -__all__ = ["SummitEnvironment", "AndesEnvironment", "CrusherEnvironment"] +class FrontierEnvironment(DefaultSlurmEnvironment): + """Environment profile for the Frontier supercomputer. + + https://docs.olcf.ornl.gov/systems/frontier_user_guide.html + """ + + hostname_pattern = r".*\.frontier\.olcf\.ornl\.gov" + template = "frontier.sh" + cores_per_node = 64 + gpus_per_node = 8 + mpi_cmd = "srun" + + @template_filter + def calc_num_nodes(cls, ngpus, ncpus, threshold): + """Compute the number of nodes needed to meet the resource request. + + Also raise an error when the requested resource do not come close to saturating the asked + for nodes. + """ + nodes_gpu = max(1, int(ceil(ngpus / cls.gpus_per_node))) + nodes_cpu = max(1, int(ceil(ncpus / cls.cores_per_node))) + if nodes_gpu >= nodes_cpu: + check_utilization(nodes_gpu, ngpus, cls.gpus_per_node, threshold, "compute") + return nodes_gpu + check_utilization(nodes_cpu, ncpus, cls.cores_per_node, threshold, "compute") + return nodes_cpu + + @classmethod + def _get_mpi_prefix(cls, operation, parallel): + """Get the correct srun command for the job. + + We don't currently support CPU/GPU mapping and expect the program to do this in code. + """ + nranks = operation.directives.get("nranks", 0) + if nranks == 0: + return "" + ngpus = operation.directives["ngpu"] + np = operation.directives.get("np", 1) + omp_num_threads = max(operation.directives.get("omp_num_threads", 1), 1) + mpi_np_calc = nranks * omp_num_threads + if np > 1 and nranks > 1 and np != mpi_np_calc: + raise RuntimeWarning( + f"Using provided value for np={np}, which seems incompatible with MPI directives " + f"{mpi_np_calc}." + ) + base_str = f"{cls.mpi_cmd} --ntasks={nranks}" + threads = max(omp_num_threads, np) if nranks == 1 else max(1, omp_num_threads) + base_str += f" --cpus-per-task={threads} --gpus={ngpus}" + return base_str + + +__all__ = [ + "SummitEnvironment", + "AndesEnvironment", + "CrusherEnvironment", + "FrontierEnvironment", +] diff --git a/flow/templates/frontier.sh b/flow/templates/frontier.sh new file mode 100644 index 000000000..c92d65902 --- /dev/null +++ b/flow/templates/frontier.sh @@ -0,0 +1,19 @@ +{# Templated in accordance with: https://docs.olcf.ornl.gov/systems/crusher_quick_start_guide.html #} +{% extends "slurm.sh" %} +{% block tasks %} + {% set threshold = 0 if force else 0.9 %} + {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} + {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} + {% set nn = gpu_tasks|calc_num_nodes(cpu_tasks, threshold) %} + {% if gpu_tasks < 1 and not force %} + {% raise "Must request GPUs to use Frontier." %} + {% endif %} +#SBATCH --nodes={{ nn }} +{% endblock tasks %} +{% block header %} + {{- super() -}} + {% set account = account|default(project|get_account_name, true) %} + {% if account %} +#SBATCH --account={{ account }} + {% endif %} +{% endblock header %} diff --git a/tests/generate_template_reference_data.py b/tests/generate_template_reference_data.py index 19faf7b2e..cbf3e9534 100755 --- a/tests/generate_template_reference_data.py +++ b/tests/generate_template_reference_data.py @@ -154,6 +154,14 @@ def init(project): "bundle": [["mpi_op", "omp_op"]], }, ], + # Frontier cannot use partitions as logic requires gpu in the name of partitions that are gpu nodes. + "environments.incite.FrontierEnvironment": [ + {}, + { + "parallel": [False, True], + "bundle": [["mpi_op", "omp_op"]], + }, + ], } for environment, parameter_combinations in environments.items(): diff --git a/tests/generate_template_review_document.py b/tests/generate_template_review_document.py index ded389393..dd6caaa35 100755 --- a/tests/generate_template_review_document.py +++ b/tests/generate_template_review_document.py @@ -40,7 +40,7 @@ def process_job(document, job): if job.sp.parameters.get("bundle", False): h = "Bundled MPI and OpenMP jobs" - for fn in os.listdir(job.workspace()): + for fn in os.listdir(job.path): if fn == "signac_statepoint.json": continue document.add_heading(h, level=3) diff --git a/tests/template_reference_data.tar.gz b/tests/template_reference_data.tar.gz index 306d1ce73c98790f64f8010b58f00042da903033..0614d133bcd45c4aca45869e81dd7a3a5db619f1 100644 GIT binary patch literal 22174 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 Date: Wed, 17 May 2023 12:48:52 -0400 Subject: [PATCH 22/47] Feature/469 check status for specific operations (#725) Co-authored-by: Bradley Dice --- changelog.txt | 6 ++ contributors.yaml | 4 ++ flow/project.py | 33 ++++++++++- tests/generate_status_reference_data.py | 5 +- tests/generate_template_reference_data.py | 3 +- tests/status_reference_data.tar.gz | Bin 3761 -> 3755 bytes tests/test_project.py | 64 ++++++++++++++++++++++ 7 files changed, 110 insertions(+), 5 deletions(-) diff --git a/changelog.txt b/changelog.txt index dc0d4ecfb..e22549a7b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -15,6 +15,12 @@ Added +++++ - Frontier environment and template (#743). +- Added ``-o`` / ``--operation`` flag to report project status information for specific operations (#725). + +Changed ++++++++ + +- Removed ``-o`` flag as an alias for ``--output`` in `status` CLI subcommand (#725). Version 0.25 ============ diff --git a/contributors.yaml b/contributors.yaml index 10567c111..2d3a5304c 100644 --- a/contributors.yaml +++ b/contributors.yaml @@ -176,4 +176,8 @@ contributors: given-names: Brian orcid: "https://orcid.org/0000-0002-2461-6614" affiliation: "University of Michigan" + - + family-names: Asare + given-names: Raymond + affiliation: "University of Michigan" ... diff --git a/flow/project.py b/flow/project.py index 55f42f725..d396052ed 100644 --- a/flow/project.py +++ b/flow/project.py @@ -2604,6 +2604,7 @@ def _fetch_status( err, ignore_errors, status_parallelization="none", + names=None, ): """Fetch status for the provided aggregates / jobs. @@ -2618,6 +2619,10 @@ def _fetch_status( status_parallelization : str Parallelization mode for fetching the status. Allowed values are "thread", "process", or "none". (Default value = "none") + names : iterable of :class:`str` + Only show status for operations that match the provided set of names + (interpreted as regular expressions), or all if the argument is + None. (Default value = None) Returns ------- @@ -2636,6 +2641,14 @@ def _fetch_status( only has to occur one time. """ + if names is not None: + absent_ops = (set(self._groups.keys()) ^ set(names)) & set(names) + if absent_ops: + print( + f"Unrecognized flow operation(s): {', '.join(absent_ops)}", + file=sys.stderr, + ) + if status_parallelization not in ("thread", "process", "none"): raise RuntimeError( "Configuration value status_parallelization is invalid. " @@ -2651,7 +2664,6 @@ def _fetch_status( def compute_status(data): scheduler_id, scheduler_status, aggregate_id, aggregate, group = data - status = {} error_text = None try: @@ -2706,11 +2718,14 @@ def compute_status(data): ) return result + status_groups = set(self._gather_flow_groups(names)) + with self._buffered(): aggregate_groups = list( self._generate_selected_aggregate_groups_with_status( scheduler_info=scheduler_info, selected_aggregates=aggregates, + selected_groups=status_groups, ) ) status_results = [] @@ -2811,6 +2826,7 @@ def print_status( profile=False, eligible_jobs_max_lines=None, output_format="terminal", + operation=None, ): """Print the status of the project. @@ -2861,6 +2877,10 @@ def print_status( output_format : str Status output format, supports: 'terminal' (default), 'markdown' or 'html'. + operation : iterable of :class:`str` + Show status of operations that match the provided set of names + (interpreted as regular expressions), or all if the argument is + None. (Default value = None) """ if file is None: @@ -2905,6 +2925,7 @@ def print_status( err=err, ignore_errors=ignore_errors, status_parallelization=status_parallelization, + names=operation, ) prof._mergeFileTiming() @@ -2984,6 +3005,7 @@ def print_status( err=err, ignore_errors=ignore_errors, status_parallelization=status_parallelization, + names=operation, ) profiling_results = None @@ -4372,7 +4394,6 @@ def _add_print_status_args(cls, parser): help="Ignore errors that might occur when querying the scheduler.", ) view_group.add_argument( - "-o", "--output-format", type=str, default="terminal", @@ -4983,6 +5004,14 @@ class MyProject(FlowProject): "to show result for. Defaults to the main module. " "(requires pprofile)", ) + parser_status.add_argument( + "-o", + "--operation", + type=str, + nargs="+", + help="Select operation or groups that match the given " + "operation/group name(s). These are interpreted as regular expressions.", + ) parser_status.set_defaults(func=self._main_status) parser_next = subparsers.add_parser( diff --git a/tests/generate_status_reference_data.py b/tests/generate_status_reference_data.py index bf9efe1b1..44f5ed8a8 100755 --- a/tests/generate_status_reference_data.py +++ b/tests/generate_status_reference_data.py @@ -46,8 +46,9 @@ def init_status_options(project): {"compact": True}, # -1, --one-line {"pretty": True}, # --pretty {"eligible_jobs_max_lines": 2}, # --eligible-jobs-max-lines 2 - {"output_format": "markdown"}, # -o markdown, --output-format markdown - {"output_format": "html"}, # -o html, --output-format html + {"output_format": "markdown"}, # --output-format markdown + {"output_format": "html"}, # --output-format html + {"operation": ["op1", "op2"]}, # -o op1 op2, --operation op1 op2 ] for sp in options: diff --git a/tests/generate_template_reference_data.py b/tests/generate_template_reference_data.py index cbf3e9534..192a1a28c 100755 --- a/tests/generate_template_reference_data.py +++ b/tests/generate_template_reference_data.py @@ -154,7 +154,8 @@ def init(project): "bundle": [["mpi_op", "omp_op"]], }, ], - # Frontier cannot use partitions as logic requires gpu in the name of partitions that are gpu nodes. + # Frontier cannot use partitions as logic requires gpu + # in the name of partitions that are gpu nodes. "environments.incite.FrontierEnvironment": [ {}, { diff --git a/tests/status_reference_data.tar.gz b/tests/status_reference_data.tar.gz index f6e01016852d2c59a0f6b655b3b062db1f22e944..c5383094100d42f54c5f530acfd7a17ac3252da6 100644 GIT binary patch literal 3755 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 muJK!YT7 literal 3761 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 Date: Wed, 17 May 2023 11:49:27 -0500 Subject: [PATCH 23/47] Build(deps-dev): Bump pre-commit from 3.2.1 to 3.2.2 (#746) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index c6ca44fee..6a1102123 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,3 +1,3 @@ click>=7.0 ruamel.yaml>=0.16.12 -pre-commit==3.2.1 +pre-commit==3.2.2 From a4c64dc5be9b519b572d684bb3a4876ea1a813e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 11:49:48 -0500 Subject: [PATCH 24/47] Build(deps): Bump pytest from 7.2.2 to 7.3.1 (#744) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index 7a5ac1b67..6f4743c31 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,5 +1,5 @@ click==8.1.3 coverage==7.2.2 pytest-cov==4.0.0 -pytest==7.2.2 +pytest==7.3.1 ruamel.yaml==0.17.21 From f1e1ceacf235a0a82b08167f3d3e3a54bb4953c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 May 2023 17:31:12 +0000 Subject: [PATCH 25/47] Build(deps): Bump coverage from 7.2.2 to 7.2.5 (#745) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bradley Dice --- requirements/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index 6f4743c31..22a0d4563 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,5 +1,5 @@ click==8.1.3 -coverage==7.2.2 +coverage==7.2.5 pytest-cov==4.0.0 pytest==7.3.1 ruamel.yaml==0.17.21 From a90b9e22001abc348961f8528bb2e45be1ef5b5a Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 31 May 2023 16:30:31 -0400 Subject: [PATCH 26/47] Feat/template testing (#747) * feat (WIP): create flow CLI subcommand for testing templates * feat: Finish new CLI option. * test: flow test-workflow. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * doc: Add test-workflow to documentation. * doc: Add changes to changelog. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- changelog.txt | 1 + doc/cli.rst | 8 ++ flow/__main__.py | 7 ++ flow/scripts/template_test.py | 109 +++++++++++++++++ flow/templates/init_test_environment.pyt | 7 ++ flow/templates/project_test_environment.pyt | 127 ++++++++++++++++++++ tests/test_shell.py | 15 +++ 7 files changed, 274 insertions(+) create mode 100644 flow/scripts/template_test.py create mode 100644 flow/templates/init_test_environment.pyt create mode 100644 flow/templates/project_test_environment.pyt diff --git a/changelog.txt b/changelog.txt index e22549a7b..d11f2857f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -14,6 +14,7 @@ Version 0.26 Added +++++ +- ``test-workflow`` CLI option for testing template environments/submission scripts (#747). - Frontier environment and template (#743). - Added ``-o`` / ``--operation`` flag to report project status information for specific operations (#725). diff --git a/doc/cli.rst b/doc/cli.rst index a06a1270a..e9a083f09 100644 --- a/doc/cli.rst +++ b/doc/cli.rst @@ -16,6 +16,7 @@ List of commands: * `init`_ * `template`_ * `template create`_ + * `test-workflow`_ .. _flow-cli-init: @@ -37,3 +38,10 @@ template create --------------- .. command-output:: flow template create --help + +.. _flow-cli-test-workflow: + +test-workflow +------------- + +.. command-output:: flow test-workflow --help diff --git a/flow/__main__.py b/flow/__main__.py index a744b9e54..c03e1e556 100644 --- a/flow/__main__.py +++ b/flow/__main__.py @@ -19,6 +19,7 @@ from signac import get_project, init_project from . import __version__, environment, template +from .scripts.template_test import test_workflow_parser logger = logging.getLogger(__name__) @@ -155,6 +156,12 @@ def main(): print("signac-flow", __version__) sys.exit(0) + parser_test_environment = subparsers.add_parser( + "test-workflow", + help="Setup a test workspace with a project.py to test new environments.", + ) + test_workflow_parser(parser_test_environment) + args = parser.parse_args() if args.debug: logging.basicConfig(level=logging.DEBUG) diff --git a/flow/scripts/template_test.py b/flow/scripts/template_test.py new file mode 100644 index 000000000..2663cb62e --- /dev/null +++ b/flow/scripts/template_test.py @@ -0,0 +1,109 @@ +"""Implement the test-workflow flow CLI subcommand.""" +import errno +import logging +import os +import stat +import string +import subprocess + +import jinja2 + +logger = logging.getLogger(__name__) + + +def _make_executable(fn): + mode = stat.S_IMODE(os.stat(fn).st_mode) + os.chmod(fn, mode | stat.S_IXUSR) + + +def _cleanup(): + for fn in ("init.py", "project.py"): + if os.path.exists(fn): + os.remove(fn) + + +def _create_file(fn, content): + try: + with open(fn, "x") as fh: + fh.write(content) + except OSError as error: + if error.errno == errno.EEXIST: + logger.error( + f"Error while trying to create custom template. Delete '{fn}' " + f"first and rerun command." + ) + else: + logger.error(f"Error while trying to create testing project: '{error}'.") + _cleanup() + raise + _make_executable(fn) + + +def main_test_workflow(args): + """Create a workspace with a workflow to test submission to schedulers. + + This is designed for testing HPC clusters with templates where CI/automated testing that is + difficult. + """ + # Create project.py + prompt = "Number of {} per node: " + num_cpus = ( + args.num_cpus[0] + if args.num_cpus is not None + else int(input(prompt.format("CPU"))) + ) + num_gpus = ( + args.num_gpus[0] + if args.num_gpus is not None + else int(input(prompt.format("GPU"))) + ) + context = {"num_cpus": num_cpus, "num_gpus": num_gpus} + jinja_env = jinja2.Environment(loader=jinja2.PackageLoader("flow", "templates")) + project_template = jinja_env.get_template("project_test_environment.pyt") + project_content = project_template.render(**context).strip(string.whitespace) + _create_file("project.py", project_content) + + # create init.py + init_content = jinja_env.get_template("init_test_environment.pyt").render( + num_jobs=args.num_jobs[0] + ) + _create_file("init.py", init_content) + + # Run init.py + try: + subprocess.run(["python3", "init.py"]) + except subprocess.SubprocessError: + logger.warning( + "Error attempting to run init.py. Run manually to initialize workspace." + ) + + +def test_workflow_parser(subparser): + """Add parser arguments to the test-workflow subpaser.""" + subparser.add_argument( + "--num_gpus", + "-g", + nargs=1, + type=int, + default=None, + help="Specify the number of GPUs per node for the desired GPU partition." + "Set to 0 to specify no GPUs.", + ) + subparser.add_argument( + "--num_cpus", + "-c", + nargs=1, + type=int, + default=None, + help="Specify the number of CPUs per node for the desired CPU partition.", + ) + subparser.add_argument( + "--num_jobs", + "-n", + nargs=1, + type=int, + default=(10,), + help="Specify the number of jobs to initialize. Defaults to 10.", + ) + + subparser.set_defaults(func=main_test_workflow) diff --git a/flow/templates/init_test_environment.pyt b/flow/templates/init_test_environment.pyt new file mode 100644 index 000000000..cc8b9c0b6 --- /dev/null +++ b/flow/templates/init_test_environment.pyt @@ -0,0 +1,7 @@ +#! /usr/bin/env python3 +import signac + +if __name__ == "__main__": + pr = signac.init_project() + for a in range({{num_jobs}}): + pr.open_job({"a": a, "even": a % 2 == 0}).init() diff --git a/flow/templates/project_test_environment.pyt b/flow/templates/project_test_environment.pyt new file mode 100644 index 000000000..987189ddf --- /dev/null +++ b/flow/templates/project_test_environment.pyt @@ -0,0 +1,127 @@ +#! /usr/bin/env python3 + +import signac + +import flow + +CPUS_PER_NODE = {{num_cpus}} +GPUS_PER_NODE = {{num_gpus}} + + +class Project(flow.FlowProject): + pass + + +@Project.post.true("base_op") +@Project.operation(directives={"np": 1}) +def base_op(job): + """Operation with no parallelization.""" + job.doc["base_op"] = True + + +@Project.post.true("intra_node_np") +@Project.operation(directives={"np": CPUS_PER_NODE}) +def intra_node_np(job): + import multiprocessing + + job.doc["intra_node_np"] = multiprocessing.cpu_count() == max( + 8, CPUS_PER_NODE) + + +@Project.post.true("intra_node_mpi") +@Project.operation(directives={"nranks": CPUS_PER_NODE}) +def intra_node_mpi(job): + from mpi4py import MPI + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + if rank == 0: + job.doc["intra_node_mpi"] = size == CPUS_PER_NODE + + +@Project.post.true("inter_node_mpi") +@Project.operation(directives={"nranks": 2 * CPUS_PER_NODE}) +def inter_node_mpi(job): + from mpi4py import MPI + + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + if rank == 0: + job.doc["inter_node_mpi"] = size == 2 * CPUS_PER_NODE + + +{% if num_gpus > 0 %} +@Project.post.true("gpu_op") +@Project.operation(directives={"ngpu": 1}) +def gpu_op(job): + import cupy as cp + + # Will only return true if GPU can be accessed + mean_correct = cp.asnumpy(cp.mean(cp.arange(101)) == 50).item() + print(mean_correct, type(mean_correct), flush=True) + job.doc["gpu_op"] = mean_correct + + +@Project.post.true("intra_gpu_op") +@Project.operation(directives={"ngpu": GPUS_PER_NODE, "nranks": GPUS_PER_NODE}) +def node_gpu_op(job): + import cupy as cp + from mpi4py import MPI + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + assert comm.Get_size() == GPUS_PER_NODE + + try: + with cp.cuda.Device(rank): + assert cp.asnumpy(cp.mean(cp.arange(101)) == 50).item() + except Exception as err: + raised = True + error = err + else: + raised = False + error = None + raised = comm.gather(raised, 0) + errors = comm.gather(error, 0) + if rank == 0: + if any(raised): + print(list(filter(lambda x: x is not None, errors)), flush=True) + else: + job.doc["intra_gpu_op"] = True + + +@Project.post.true("inter_gpu_op") +@Project.operation( + directives={ + "nranks": 2 * GPUS_PER_NODE, "ngpu": 2 * GPUS_PER_NODE}) +def inter_node_gpu_op(job): + import cupy as cp + from mpi4py import MPI + + # TODO: check the functioning of each GPU. + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + assert comm.Get_size() == 2 * GPUS_PER_NODE + try: + with cp.cuda.Device(rank % GPUS_PER_NODE): + assert cp.asnumpy(cp.mean(cp.arange(101)) == 50).item() + except Exception as err: + raised = True + error = err + else: + raised = False + error = None + raised = comm.gather(raised, 0) + errors = comm.gather(error, 0) + if rank == 0: + print(raised, flush=True) + if any(raised): + print(list(filter(lambda x: x is not None, errors)), flush=True) + else: + job.doc["inter_gpu_op"] = True + + +{% endif %} +if __name__ == "__main__": + Project().main() diff --git a/tests/test_shell.py b/tests/test_shell.py index 9fca3b79d..3fd6c6a88 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -135,3 +135,18 @@ def test_template_create_custom_extends(self, extends_arg): with open("templates/script.sh") as fh: custom_script_lines = fh.read().splitlines() assert '{% extends "slurm.sh" %}' in custom_script_lines[0] + + def test_test_workflow(self): + self.call("python -m flow test-workflow -c 20 -g 8".split()) + assert os.path.exists("init.py") + assert os.path.exists("project.py") + assert os.path.exists("workspace") + assert len(os.listdir("workspace")) == 10 + with open("project.py") as fh: + lines = fh.read().splitlines() + assert lines[6].endswith("20") + assert lines[7].endswith("8") + + def test_test_workflow_num_jobs(self): + self.call("python -m flow test-workflow -c 20 -g 8 -n 5".split()) + assert len(os.listdir("workspace")) == 5 From 37e1b206fa919e260cc55e5f2084473b78f2b3cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 10:32:47 -0400 Subject: [PATCH 27/47] Build(deps): Bump ruamel-yaml from 0.17.21 to 0.17.31 (#752) Bumps [ruamel-yaml](https://sourceforge.net/p/ruamel-yaml/code/ci/default/tree) from 0.17.21 to 0.17.31. --- updated-dependencies: - dependency-name: ruamel-yaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index 22a0d4563..be8ece148 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -2,4 +2,4 @@ click==8.1.3 coverage==7.2.5 pytest-cov==4.0.0 pytest==7.3.1 -ruamel.yaml==0.17.21 +ruamel.yaml==0.17.31 From 19f8949faaa3ff9c3b91a5d25bdadf979166b8e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 14:57:22 +0000 Subject: [PATCH 28/47] Build(deps): Bump pytest-cov from 4.0.0 to 4.1.0 (#751) Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 4.0.0 to 4.1.0. - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v4.0.0...v4.1.0) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index be8ece148..e5fb16070 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,5 +1,5 @@ click==8.1.3 coverage==7.2.5 -pytest-cov==4.0.0 +pytest-cov==4.1.0 pytest==7.3.1 ruamel.yaml==0.17.31 From 34be1f60f597467ffb4b6fad3b56cbe9d1457315 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 15:45:25 +0000 Subject: [PATCH 29/47] Build(deps-dev): Bump pre-commit from 3.2.2 to 3.3.2 (#750) Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.2.2 to 3.3.2. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pre-commit/pre-commit/compare/v3.2.2...v3.3.2) --- updated-dependencies: - dependency-name: pre-commit dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index 6a1102123..57291c19d 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,3 +1,3 @@ click>=7.0 ruamel.yaml>=0.16.12 -pre-commit==3.2.2 +pre-commit==3.3.2 From d2ea9323f747e09434d6d42061e65b63dbe0e7f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:49:58 -0400 Subject: [PATCH 30/47] Build(deps): Bump coverage from 7.2.5 to 7.2.7 (#749) Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.2.5 to 7.2.7. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.2.5...7.2.7) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index e5fb16070..4b279be1c 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,5 +1,5 @@ click==8.1.3 -coverage==7.2.5 +coverage==7.2.7 pytest-cov==4.1.0 pytest==7.3.1 ruamel.yaml==0.17.31 From efa920c076286cf5e23f042b0aa657f8774164da Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 27 Jul 2023 12:41:08 -0400 Subject: [PATCH 31/47] fix: UMich Greatlakes environment configuration --- flow/environments/umich.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flow/environments/umich.py b/flow/environments/umich.py index bfbbf0784..fed0ecb95 100644 --- a/flow/environments/umich.py +++ b/flow/environments/umich.py @@ -15,6 +15,9 @@ class GreatLakesEnvironment(DefaultSlurmEnvironment): template = "umich-greatlakes.sh" _cpus_per_node = {"default": 36, "gpu": 40} _gpus_per_node = {"default": 2} + _shared_partitions = {"standard", "gpu"} + + mpi_cmd = "srun" @classmethod def add_args(cls, parser): From 3687c0c73a0edcead1a00a02344adc39be6416dc Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 27 Jul 2023 12:41:27 -0400 Subject: [PATCH 32/47] fix: Correctly GPU raise errors in Greatlakes template Error when GPU partition is requested without GPUs and when GPUs are requested without a GPU partition. --- flow/templates/umich-greatlakes.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flow/templates/umich-greatlakes.sh b/flow/templates/umich-greatlakes.sh index 6432fb5d7..393ca15ed 100644 --- a/flow/templates/umich-greatlakes.sh +++ b/flow/templates/umich-greatlakes.sh @@ -1,9 +1,12 @@ {% extends "slurm.sh" %} {% set partition = partition|default('standard', true) %} {% block tasks %} - {% if resources.gpu_tasks and 'gpu' not in partition and not force %} + {% if resources.ngpu_tasks and 'gpu' not in partition and not force %} {% raise "Requesting GPUs requires a gpu partition!" %} {% endif %} + {% if 'gpu' in partition and resources.ngpu_tasks == 0 and not force %} + {% raise "Requesting gpu partition without GPUs!" %} + {% endif %} #SBATCH --nodes={{ resources.num_nodes }} {% if partition == 'gpu' %} #SBATCH --ntasks={{ (resources.ngpu_tasks, resources.ncpu_tasks)|max }} From 614c5f6b0a2b793541524d387b4a46b5044a08b0 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 4 Oct 2023 10:54:23 -0400 Subject: [PATCH 33/47] refactor: Small changes to new template environment code --- flow/environment.py | 2 +- flow/templates/bridges2.sh | 2 +- flow/templates/umich-greatlakes.sh | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/flow/environment.py b/flow/environment.py index b79e3c89e..605bed25c 100644 --- a/flow/environment.py +++ b/flow/environment.py @@ -111,7 +111,7 @@ class ComputeEnvironment(metaclass=_ComputeEnvironmentType): _cpus_per_node = {"default": -1} _gpus_per_node = {"default": -1} - _shared_partitions = {} + _shared_partitions = set() @classmethod def is_present(cls): diff --git a/flow/templates/bridges2.sh b/flow/templates/bridges2.sh index 6353ab6df..6606021e1 100644 --- a/flow/templates/bridges2.sh +++ b/flow/templates/bridges2.sh @@ -16,7 +16,7 @@ {% if partition == 'RM-shared' and resources.ncpu_tasks > 64 %} {% raise "Cannot request RM-shared with more than 64 tasks or multiple nodes." %} {% endif %} - {% if resources.num_nodes > 1 or resources.ncpu_tasks >= 128 or resources.ngpu_tasks >= 8%} + {% if resources.num_nodes > 1 or resources.ncpu_tasks >= 128 or resources.ngpu_tasks >= 8 %} #SBATCH -N {{ resources.num_nodes }} {% endif %} #SBATCH --ntasks={{ resources.ncpu_tasks }} diff --git a/flow/templates/umich-greatlakes.sh b/flow/templates/umich-greatlakes.sh index 393ca15ed..abb640b75 100644 --- a/flow/templates/umich-greatlakes.sh +++ b/flow/templates/umich-greatlakes.sh @@ -8,11 +8,9 @@ {% raise "Requesting gpu partition without GPUs!" %} {% endif %} #SBATCH --nodes={{ resources.num_nodes }} +#SBATCH --ntasks={{ resources.ncpu_tasks }} {% if partition == 'gpu' %} -#SBATCH --ntasks={{ (resources.ngpu_tasks, resources.ncpu_tasks)|max }} #SBATCH --gpus={{ resources.ngpu_tasks }} - {% else %}{# standard compute partition #} -#SBATCH --ntasks={{ resources.ncpu_tasks }} {% endif %} {% endblock tasks %} {% block header %} From 1ed0b4d8604b017984d86efd25595e66405ca6d4 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 4 Oct 2023 10:54:54 -0400 Subject: [PATCH 34/47] refactor: Convert frontier template to new format --- flow/templates/frontier.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/flow/templates/frontier.sh b/flow/templates/frontier.sh index c92d65902..4eb5553ac 100644 --- a/flow/templates/frontier.sh +++ b/flow/templates/frontier.sh @@ -1,14 +1,10 @@ {# Templated in accordance with: https://docs.olcf.ornl.gov/systems/crusher_quick_start_guide.html #} {% extends "slurm.sh" %} {% block tasks %} - {% set threshold = 0 if force else 0.9 %} - {% set cpu_tasks = operations|calc_tasks('np', parallel, force) %} - {% set gpu_tasks = operations|calc_tasks('ngpu', parallel, force) %} - {% set nn = gpu_tasks|calc_num_nodes(cpu_tasks, threshold) %} - {% if gpu_tasks < 1 and not force %} + {% if not resources.ngpu_tasks and not force %} {% raise "Must request GPUs to use Frontier." %} {% endif %} -#SBATCH --nodes={{ nn }} +#SBATCH --nodes={{ resources.num_nodes }} {% endblock tasks %} {% block header %} {{- super() -}} From 3a35426ab218d893b2d1541244e15d0fa127391e Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 5 Oct 2023 09:57:16 -0400 Subject: [PATCH 35/47] refactor: Frontier remove FrontierEnvironment.calc_num_nodes --- flow/environments/incite.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/flow/environments/incite.py b/flow/environments/incite.py index 5f095c593..39fff3c98 100644 --- a/flow/environments/incite.py +++ b/flow/environments/incite.py @@ -274,25 +274,10 @@ class FrontierEnvironment(DefaultSlurmEnvironment): hostname_pattern = r".*\.frontier\.olcf\.ornl\.gov" template = "frontier.sh" - cores_per_node = 64 + cores_per_node = 58 gpus_per_node = 8 mpi_cmd = "srun" - @template_filter - def calc_num_nodes(cls, ngpus, ncpus, threshold): - """Compute the number of nodes needed to meet the resource request. - - Also raise an error when the requested resource do not come close to saturating the asked - for nodes. - """ - nodes_gpu = max(1, int(ceil(ngpus / cls.gpus_per_node))) - nodes_cpu = max(1, int(ceil(ncpus / cls.cores_per_node))) - if nodes_gpu >= nodes_cpu: - check_utilization(nodes_gpu, ngpus, cls.gpus_per_node, threshold, "compute") - return nodes_gpu - check_utilization(nodes_cpu, ncpus, cls.cores_per_node, threshold, "compute") - return nodes_cpu - @classmethod def _get_mpi_prefix(cls, operation, parallel): """Get the correct srun command for the job. From 787a2b6ddaddca940d884e77302ee8169c7dc004 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 5 Oct 2023 10:30:47 -0400 Subject: [PATCH 36/47] feat: Finish conversion of Frontier environment. --- flow/environments/incite.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flow/environments/incite.py b/flow/environments/incite.py index 39fff3c98..5f7c2e1a9 100644 --- a/flow/environments/incite.py +++ b/flow/environments/incite.py @@ -274,8 +274,8 @@ class FrontierEnvironment(DefaultSlurmEnvironment): hostname_pattern = r".*\.frontier\.olcf\.ornl\.gov" template = "frontier.sh" - cores_per_node = 58 - gpus_per_node = 8 + _cpus_per_node = {"default": 58} + _gpus_per_node = {"default": 8} mpi_cmd = "srun" @classmethod From 1e0bab9e167440ade60ce90dbcc64f75868c5463 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 5 Oct 2023 10:33:31 -0400 Subject: [PATCH 37/47] refactor: Remove unnecessary empty shared_partition sets --- flow/environments/incite.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/flow/environments/incite.py b/flow/environments/incite.py index 5f7c2e1a9..f04968c08 100644 --- a/flow/environments/incite.py +++ b/flow/environments/incite.py @@ -38,7 +38,6 @@ def my_operation(job): mpi_cmd = "jsrun" _cpus_per_node = {"default": 42} _gpus_per_node = {"default": 6} - _shared_partitions = set() @template_filter def calc_num_nodes(cls, resource_sets, parallel=False): @@ -190,8 +189,6 @@ class AndesEnvironment(DefaultSlurmEnvironment): mpi_cmd = "srun" _cpus_per_node = {"default": 32, "gpu": 28} _gpus_per_node = {"default": 0, "gpu": 2} - # No shared partitions requests must be in full nodes. - _shared_partitions = set() @classmethod def add_args(cls, parser): @@ -222,8 +219,6 @@ class CrusherEnvironment(DefaultSlurmEnvironment): template = "crusher.sh" _cpus_per_node = {"default": 56} _gpus_per_node = {"default": 8} - # Crusher has no shared partitions - _shared_partitions = set() mpi_cmd = "srun" From bed0b0ff993f8b95c6519ab74f6b72c68747f6c2 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 5 Oct 2023 15:37:49 -0400 Subject: [PATCH 38/47] fix: Frontier's allowable CPU use --- flow/environments/incite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/environments/incite.py b/flow/environments/incite.py index f04968c08..5b47e1482 100644 --- a/flow/environments/incite.py +++ b/flow/environments/incite.py @@ -269,7 +269,7 @@ class FrontierEnvironment(DefaultSlurmEnvironment): hostname_pattern = r".*\.frontier\.olcf\.ornl\.gov" template = "frontier.sh" - _cpus_per_node = {"default": 58} + _cpus_per_node = {"default": 56} _gpus_per_node = {"default": 8} mpi_cmd = "srun" From 27770c0bf16c4f95f03736d6cf88621af4c6c947 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 5 Oct 2023 16:59:42 -0400 Subject: [PATCH 39/47] test: Update submission scripts --- tests/template_reference_data.tar.gz | Bin 22174 -> 22396 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/template_reference_data.tar.gz b/tests/template_reference_data.tar.gz index 0614d133bcd45c4aca45869e81dd7a3a5db619f1..018441f404b317ab35a0122718988561fbd822f8 100644 GIT binary patch literal 22396 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 Date: Thu, 5 Oct 2023 17:00:09 -0400 Subject: [PATCH 40/47] fix: Stampede2Environment._get_scheduler_values --- flow/environments/xsede.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index d6f4d21c2..c15d4556f 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -139,17 +139,17 @@ def _get_mpi_prefix(cls, operation, parallel): return prefix @classmethod - def _get_scheduler_values(cls, context, operations): + def _get_scheduler_values(cls, context): threshold = 0.0 if context.get("force", False) else 0.9 partition = context.get("partition", "default") cpu_tasks_total = template_filters.calc_tasks( - operations, + context["operations"], "np", - len(operations) > 1 or context.get("parallel", False), + len(context["operations"]) > 1 or context.get("parallel", False), context.get("force", False), ) gpu_tasks_total = template_filters.calc_tasks( - operations, + context["operations"], "ngpu", context.get("parallel", False), context.get("force", False), From cdb226e7388a45e0cbee9da4f80820c6a0f6206a Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Thu, 5 Oct 2023 17:11:14 -0400 Subject: [PATCH 41/47] fix: remove Stampede2Environment._get_scheduler_values --- flow/environments/xsede.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/flow/environments/xsede.py b/flow/environments/xsede.py index c15d4556f..b8923bb22 100644 --- a/flow/environments/xsede.py +++ b/flow/environments/xsede.py @@ -6,7 +6,6 @@ import os from ..environment import DefaultSlurmEnvironment, template_filter -from ..util import template_filters logger = logging.getLogger(__name__) @@ -138,31 +137,6 @@ def _get_mpi_prefix(cls, operation, parallel): prefix = "" return prefix - @classmethod - def _get_scheduler_values(cls, context): - threshold = 0.0 if context.get("force", False) else 0.9 - partition = context.get("partition", "default") - cpu_tasks_total = template_filters.calc_tasks( - context["operations"], - "np", - len(context["operations"]) > 1 or context.get("parallel", False), - context.get("force", False), - ) - gpu_tasks_total = template_filters.calc_tasks( - context["operations"], - "ngpu", - context.get("parallel", False), - context.get("force", False), - ) - num_nodes = template_filters.calc_num_nodes( - cpu_tasks_total, cls._get_cpus_per_node(partition), threshold - ) - return { - "ncpu_tasks": cpu_tasks_total, - "ngpu_tasks": gpu_tasks_total, - "num_nodes": num_nodes, - } - class Bridges2Environment(DefaultSlurmEnvironment): """Environment profile for the Bridges-2 supercomputer. From b0a591b59b566918d311b30de6938754b8926f69 Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Tue, 10 Oct 2023 12:56:47 -0400 Subject: [PATCH 42/47] test: Update template tests. --- tests/template_reference_data.tar.gz | Bin 22396 -> 21522 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/template_reference_data.tar.gz b/tests/template_reference_data.tar.gz index 018441f404b317ab35a0122718988561fbd822f8..e83a60e0c9a5e18b5f4eb1c7c12ac41974fc223e 100644 GIT binary patch literal 21522 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 Y2s$^z!vDX|GjA?<-lgV`aovLXAMEQ!p#T5? literal 22396 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 Date: Tue, 10 Oct 2023 17:08:47 -0400 Subject: [PATCH 43/47] fix: typo in andes --- flow/templates/andes.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/templates/andes.sh b/flow/templates/andes.sh index 0938899ba..8308dda24 100644 --- a/flow/templates/andes.sh +++ b/flow/templates/andes.sh @@ -16,7 +16,7 @@ {% endif %} {% endif %} #SBATCH -N {{ resources.num_nodes }} -#SBATCH --ntasks={{ resourecs.ncpus_tasks }} +#SBATCH --ntasks={{ resources.ncpus_tasks }} {% if partition == 'gpu' %} #SBATCH --gpus={{ resources.ngpu_tasks }} {% endif %} From 7c6d4d66f2ed30e5f8332eef2a9a4c0a3f13cfae Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Tue, 10 Oct 2023 17:09:30 -0400 Subject: [PATCH 44/47] test: Update tests to new code. --- tests/template_reference_data.tar.gz | Bin 21522 -> 22155 bytes tests/test_project.py | 16 +++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/template_reference_data.tar.gz b/tests/template_reference_data.tar.gz index e83a60e0c9a5e18b5f4eb1c7c12ac41974fc223e..8445c83385c343e96a475f1cc7daeaac5d1af0ad 100644 GIT binary patch literal 22155 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 t-R-bDZ?(@EB`C^kupsjf`~IqCj8C0y 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 Y2s$^z!vDX|GjA?<-lgV`aovLXAMEQ!p#T5? diff --git a/tests/test_project.py b/tests/test_project.py index b5937cd0f..b69573250 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -1149,7 +1149,9 @@ def test_submit_operations(self): assert len(list(MockScheduler.jobs())) == 0 cluster_job_id = project._store_bundled(operations) with redirect_stderr(StringIO()): - project._submit_operations(_id=cluster_job_id, operations=operations) + project._submit_operations( + _id=cluster_job_id, operations=operations, force=True + ) assert len(list(MockScheduler.jobs())) == 1 def test_submit(self): @@ -1208,13 +1210,15 @@ def test_resubmit(self): def test_bundles(self): project = self.mock_project() assert len(list(MockScheduler.jobs())) == 0 + # Cannot use GPU operations since this will lead to missmatched resources + op_names = ["op1", "op2", "op4"] with redirect_stderr(StringIO()): - project.submit(bundle_size=2, num=2) + project.submit(bundle_size=2, num=2, names=op_names) assert len(list(MockScheduler.jobs())) == 1 - project.submit(bundle_size=2, num=4) + project.submit(bundle_size=2, num=4, names=op_names) assert len(list(MockScheduler.jobs())) == 3 MockScheduler.reset() - project.submit(bundle_size=0) + project.submit(bundle_size=0, names=op_names) assert len(list(MockScheduler.jobs())) == 1 def test_submit_status(self): @@ -1265,7 +1269,9 @@ def test_submit_operations_bad_directive(self): project = self.mock_project() operations = [] for job in project: - operations.extend(project._next_operations([(job,)])) + operations.extend( + project._next_operations([(job,)], operation_names=["op1"]) + ) assert len(list(MockScheduler.jobs())) == 0 cluster_job_id = project._store_bundled(operations) stderr = StringIO() From f2039707a4d1fc610c937add3ba5053ed659252b Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 11 Oct 2023 10:36:21 -0400 Subject: [PATCH 45/47] style: Remove incorrect comments. --- flow/project.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/flow/project.py b/flow/project.py index 7083ea950..e72d1e213 100644 --- a/flow/project.py +++ b/flow/project.py @@ -663,12 +663,6 @@ def __init__( self._project = project self.submit_options = submit_options self.run_options = run_options - # TODO: This is no longer true. - # We register aggregators associated with operation functions in - # `_register_groups` and we do not set the aggregator explicitly. - # We delay setting the aggregator because we do not restrict the - # decorator placement in terms of `@FlowGroupEntry`, `@aggregator`, or - # `@operation`. self.group_aggregator = group_aggregator def __call__(self, func=None, /, *, directives=None): @@ -1535,12 +1529,6 @@ def _internal_call( # Append the name and function to the class registry self._parent_class._OPERATION_FUNCTIONS.append((name, func)) - # TODO: This is no longer true and can likely be changed. - # We register aggregators associated with operation functions in - # `_register_groups` and we do not set the aggregator explicitly. We - # delay setting the aggregator because we do not restrict the decorator - # placement in terms of `@FlowGroupEntry`, `@aggregator`, or - # `@operation`. self._parent_class._GROUPS.append( FlowGroupEntry(name=name, project=self._parent_class) ) From 4edfa0c37168d6ca81f33e3920805214c2603e2a Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 18 Oct 2023 12:32:44 -0400 Subject: [PATCH 46/47] Fix: Andes template typo --- flow/templates/andes.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/templates/andes.sh b/flow/templates/andes.sh index 8308dda24..5fd55ff6e 100644 --- a/flow/templates/andes.sh +++ b/flow/templates/andes.sh @@ -16,7 +16,7 @@ {% endif %} {% endif %} #SBATCH -N {{ resources.num_nodes }} -#SBATCH --ntasks={{ resources.ncpus_tasks }} +#SBATCH --ntasks={{ resources.ncpu_tasks }} {% if partition == 'gpu' %} #SBATCH --gpus={{ resources.ngpu_tasks }} {% endif %} From 0cce0d6e67ffd659cfe18b4957b5a70f23b085bf Mon Sep 17 00:00:00 2001 From: Brandon Butler Date: Wed, 18 Oct 2023 12:34:09 -0400 Subject: [PATCH 47/47] test: Update Andes template --- tests/template_reference_data.tar.gz | Bin 22155 -> 22240 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/template_reference_data.tar.gz b/tests/template_reference_data.tar.gz index 8445c83385c343e96a475f1cc7daeaac5d1af0ad..96cdbcb958dd6cdf7874242fd995eb225b08b8e7 100644 GIT binary patch literal 22240 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 t-R-bDZ?(@EB`C^kupsjf`~IqCj8C0y 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