diff --git a/docs/regression_test_api.rst b/docs/regression_test_api.rst index bd893f1388..c8a6af109d 100644 --- a/docs/regression_test_api.rst +++ b/docs/regression_test_api.rst @@ -335,6 +335,7 @@ Mapping of Test Attributes to Job Scheduler Backends Test attribute Slurm option Torque option PBS option ============================ ============================= ========================================================================================== ================== :attr:`num_tasks` :obj:`--ntasks`:sup:`1` :obj:`-l nodes={num_tasks//num_tasks_per_node}:ppn={num_tasks_per_node*num_cpus_per_task}` :obj:`-l select={num_tasks//num_tasks_per_node}:mpiprocs={num_tasks_per_node}:ncpus={num_tasks_per_node*num_cpus_per_task}` + :attr:`num_nodes` :obj:`--nodes` n/a n/a :attr:`num_tasks_per_node` :obj:`--ntasks-per-node` see :attr:`num_tasks` see :attr:`num_tasks` :attr:`num_tasks_per_core` :obj:`--ntasks-per-core` n/a n/a :attr:`num_tasks_per_socket` :obj:`--ntasks-per-socket` n/a n/a diff --git a/reframe/core/launchers/mpi.py b/reframe/core/launchers/mpi.py index 1a936c995b..b88c5201f8 100644 --- a/reframe/core/launchers/mpi.py +++ b/reframe/core/launchers/mpi.py @@ -134,6 +134,9 @@ def command(self, job): if job.num_tasks: ret += ['--ntasks=%s' % str(job.num_tasks)] + + if job.num_nodes: + ret += ['--nodes=%s' % str(job.num_nodes)] if job.num_tasks_per_node: ret += ['--ntasks-per-node=%s' % str(job.num_tasks_per_node)] diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index e69f64c1bf..86a1b9dbde 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -527,8 +527,9 @@ def pipeline_hooks(cls): #: :attr:`num_tasks` to ``0`` is equivalent to setting it to #: :attr:`-num_tasks_per_node #: `. + #: If set to :attr:`None` it will be omitted from any job scripts created and when combined with :attr:`num_nodes` this will allow a more flexible number of tasks to be run. #: - #: :type: integral + #: :type: integral or :class:`None` #: :default: ``1`` #: #: .. note:: @@ -541,7 +542,16 @@ def pipeline_hooks(cls): #: #: .. |--flex-alloc-nodes| replace:: :attr:`--flex-alloc-nodes` #: .. _--flex-alloc-nodes: manpage.html#cmdoption-flex-alloc-nodes - num_tasks = variable(int, value=1, loggable=True) + num_tasks = variable(int,type(None), value=1, loggable=True) + + + #: Number of nodes for this job. + #: + #: :type: integral or :class:`None` + #: :default: ``None`` + #: + #: + num_nodes = variable(int, type(None), value=None,loggable=True) #: Number of tasks per node required by this test. #: @@ -1872,6 +1882,7 @@ def _get_cp_env(): ) self.job.num_tasks = self.num_tasks + self.job.num_nodes = self.num_nodes self.job.num_tasks_per_node = self.num_tasks_per_node self.job.num_tasks_per_core = self.num_tasks_per_core self.job.num_tasks_per_socket = self.num_tasks_per_socket diff --git a/reframe/core/schedulers/__init__.py b/reframe/core/schedulers/__init__.py index 7423a68736..17830c5817 100644 --- a/reframe/core/schedulers/__init__.py +++ b/reframe/core/schedulers/__init__.py @@ -138,7 +138,17 @@ class Job(jsonext.JSONSerializable, metaclass=JobMeta): #: based on the test information. #: #: .. versionadded:: 3.11.0 - num_tasks = variable(int, value=1) + num_tasks = variable(int, type(None), value=1) + + + + #: Number of nodes for this job. + #: + #: :type: integral or :class:`None` + #: :default: ``None`` + #: + #: + num_nodes = variable(int, type(None), value=None) #: Number of tasks per node for this job. #: @@ -461,7 +471,7 @@ def submit_time(self): def prepare(self, commands, environs=None, prepare_cmds=None, **gen_opts): environs = environs or [] - if self.num_tasks <= 0: + if self.num_tasks!=None and self.num_tasks <= 0: getlogger().debug(f'[F] Flexible node allocation requested') num_tasks_per_node = self.num_tasks_per_node or 1 min_num_tasks = (-self.num_tasks if self.num_tasks else diff --git a/reframe/core/schedulers/slurm.py b/reframe/core/schedulers/slurm.py index 4ecf2299b2..8a9ce762ae 100644 --- a/reframe/core/schedulers/slurm.py +++ b/reframe/core/schedulers/slurm.py @@ -169,6 +169,7 @@ def emit_preamble(self, job): self._format_option(job.num_tasks_per_socket, '--ntasks-per-socket={0}'), self._format_option(job.num_cpus_per_task, '--cpus-per-task={0}'), + self._format_option(job.num_nodes, '--nodes={0}') ] # Determine if job refers to a Slurm job array, by looking into the @@ -199,7 +200,7 @@ def emit_preamble(self, job): self._format_option(job.exclusive_access, '--exclusive') ) - if self._use_nodes_opt: + if self._use_nodes_opt and job.num_nodes==None: num_nodes = job.num_tasks // job.num_tasks_per_node preamble.append(self._format_option(num_nodes, '--nodes={0}'))