Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update documentation to use new operation.with_directives decorator #128

Merged
merged 2 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions docs/source/cluster_submission.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,10 @@ For example, to specify that a parallelized operation requires **4** processing

.. code-block:: python

from flow import FlowProject, directives
from flow import FlowProject
from multiprocessing import Pool

@FlowProject.operation
@directives(np=4)
@FlowProject.operation.with_directives({"np": 4})
def hello(job):
with Pool(4) as pool:
print("hello", job)
Expand All @@ -112,7 +111,7 @@ All directives are essentially conventions, the ``np`` directive in particular m

.. tip::

Note that all directives may be specified as callables, e.g. ``@directives(np = lambda job: job.doc.np)``.
Note that all directives may be specified as callables, e.g. ``FlowProject.operation.with_directives({"np": lambda job: job.doc.np})``.

Available directives
--------------------
Expand Down Expand Up @@ -147,27 +146,27 @@ Using these directives and their combinations allows us to realize the following
.. glossary::

serial:
``@flow.directives()``
``@FlowProject.operation.with_directives()``

This operation is a simple serial process, no directive needed.

parallelized:
``@flow.directives(np=4)``
``@FlowProject.operation.with_directives({"np": 4})``

This operation requires 4 processing units.

MPI parallelized:
``@flow.directives(nranks=4)``
``@FlowProject.operation.with_directives({"nranks": 4})``

This operation requires 4 MPI ranks.

MPI/OpenMP Hybrid:
``@flow.directives(nranks=4, omp_num_threads=2)``
``@FlowProject.operation.with_directives({"nranks": 4, "omp_num_threads": 2})``

This operation requires 4 MPI ranks with 2 OpenMP threads per rank.

GPU:
``@flow.directives(ngpu=1)``
``@FlowProject.operation.with_directives({"ngpu": 1})``

The operation requires one GPU for execution.

Expand Down
7 changes: 3 additions & 4 deletions docs/source/flow-group.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,15 @@ In the following example, :code:`op1` requests one GPU if run by itself or two G
.. code-block:: python

# project.py
from flow import FlowProject, directives
from flow import FlowProject

class Project(FlowProject):
pass

ex = Project.make_group(name='ex')

@ex.with_directives(directives=dict(ngpu=2))
@directives(ngpu=1)
@Project.operation
@ex.with_directives({"ngpu": 2})
@Project.operation.with_directives({"ngpu": 1})
def op1(job):
pass

Expand Down
13 changes: 6 additions & 7 deletions docs/source/recipes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -253,21 +253,21 @@ You could run this operation directly with: ``mpiexec -n 2 python project.py run
MPI-operations with ``flow.cmd``
--------------------------------

Alternatively, you can implement an MPI-parallelized operation with the ``flow.cmd`` decorator, optionally in combination with the ``flow.directives`` decorator.
Alternatively, you can implement an MPI-parallelized operation with the ``flow.cmd`` decorator, optionally in combination with the ``FlowProject.operation.with_directives`` decorator.
This strategy lets you define the number of ranks directly within the code and is also the only possible strategy when integrating external programs without a Python interface.

Assuming that we have an MPI-parallelized program named ``my_program``, which expects an input file as its first argument and which we want to run on two ranks, we could implement the operation like this:

.. code-block:: python

@FlowProject.operation
@FlowProject.operation.with_directives({"np": 2})
@flow.cmd
@flow.directives(np=2)
def hello_mpi(job):
return "mpiexec -n 2 mpi_program {job.ws}/input_file.txt"
Comment on lines 261 to 266
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this example still useful, or should we recommend the user to use the nranks directive and adapt the command to remove mpiexec?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section on MPI is out of date and actually would likely not work given the submit -> run -> exec model. However, I don't know if it is in scope to modify this in this PR.


The ``flow.cmd`` decorator instructs **signac-flow** to interpret the operation as a command rather than a Python function.
The ``flow.directives`` decorator provides additional instructions on how to execute this operation and is not strictly necessary for the example above to work.
The ``@FlowProject.operation.with_directives(...)`` decorator provides additional instructions on how to execute this operation.
The decorator ``@FlowProject.operation`` does not assign any directives to the operation.
bdice marked this conversation as resolved.
Show resolved Hide resolved
However, some script templates, including those designed for HPC cluster submissions, will use the value provided by the ``np`` key to compute the required compute ranks for a specific submission.

.. todo::
Expand All @@ -276,7 +276,7 @@ However, some script templates, including those designed for HPC cluster submiss

.. tip::

You do not have to *hard-code* the number of ranks, it may be a function of the job, *e.g.*: ``flow.directives(np=lambda job: job.sp.system_size // 1000)``.
You do not have to *hard-code* the number of ranks, it may be a function of the job, *e.g.*: ``FlowProject.operation.with_directives({"np": lambda job: job.sp.system_size // 1000})``.


MPI-operations with custom script templates
Expand Down Expand Up @@ -327,8 +327,7 @@ For example, assuming that we wanted to use a singularity container named ``soft

.. code-block:: jinja

@Project.operation
@flow.directives(executable='singularity exec software.simg python')
@Project.operation.with_directives({"executable": "singularity exec software.simg python"})
def containerized_operation(job):
pass

Expand Down