We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add mpirun launcher
Needed at NSCC, which doesn't have aprun.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parsl/launchers/launchers.py`
Content:
```
1 from abc import ABCMeta, abstractmethod
2 
3 from parsl.utils import RepresentationMixin
4 
5 
6 class Launcher(RepresentationMixin, metaclass=ABCMeta):
7     """ Launcher base class to enforce launcher interface
8     """
9     @abstractmethod
10     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
11         """ Wraps the command with the Launcher calls.
12         *MUST* be implemented by the concrete child classes
13         """
14         pass
15 
16 
17 class SimpleLauncher(Launcher):
18     """ Does no wrapping. Just returns the command as-is
19     """
20 
21     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
22         """
23         Args:
24         - command (string): The command string to be launched
25         - task_block (string) : bash evaluated string.
26 
27         KWargs:
28         - walltime (int) : This is not used by this launcher.
29         """
30         return command
31 
32 
33 class SingleNodeLauncher(Launcher):
34     """ Worker launcher that wraps the user's command with the framework to
35     launch multiple command invocations in parallel. This wrapper sets the
36     bash env variable CORES to the number of cores on the machine. By setting
37     task_blocks to an integer or to a bash expression the number of invocations
38     of the command to be launched can be controlled.
39     """
40     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
41         """
42         Args:
43         - command (string): The command string to be launched
44         - task_block (string) : bash evaluated string.
45 
46         KWargs:
47         - walltime (int) : This is not used by this launcher.
48         """
49         task_blocks = tasks_per_node * nodes_per_block
50 
51         x = '''export CORES=$(getconf _NPROCESSORS_ONLN)
52 echo "Found cores : $CORES"
53 WORKERCOUNT={1}
54 
55 CMD ( ) {{
56 {0}
57 }}
58 for COUNT in $(seq 1 1 $WORKERCOUNT)
59 do
60     echo "Launching worker: $COUNT"
61     CMD &
62 done
63 wait
64 echo "All workers done"
65 '''.format(command, task_blocks)
66         return x
67 
68 
69 class GnuParallelLauncher(Launcher):
70     """ Worker launcher that wraps the user's command with the framework to
71     launch multiple command invocations via GNU parallel sshlogin.
72 
73     This wrapper sets the bash env variable CORES to the number of cores on the
74     machine.
75 
76     This launcher makes the following assumptions:
77     - GNU parallel is installed and can be located in $PATH
78     - Paswordless SSH login is configured between the controller node and the
79       target nodes.
80     - The provider makes available the $PBS_NODEFILE environment variable
81     """
82     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
83         """
84         Args:
85         - command (string): The command string to be launched
86         - task_block (string) : bash evaluated string.
87 
88         KWargs:
89         - walltime (int) : This is not used by this launcher.
90         """
91         task_blocks = tasks_per_node * nodes_per_block
92 
93         x = '''export CORES=$(getconf _NPROCESSORS_ONLN)
94 echo "Found cores : $CORES"
95 WORKERCOUNT={3}
96 
97 # Deduplicate the nodefile
98 SSHLOGINFILE="$JOBNAME.nodes"
99 if [ -z "$PBS_NODEFILE" ]; then
100     echo "localhost" > $SSHLOGINFILE
101 else
102     sort -u $PBS_NODEFILE > $SSHLOGINFILE
103 fi
104 
105 cat << PARALLEL_CMD_EOF > cmd_$JOBNAME.sh
106 {0}
107 PARALLEL_CMD_EOF
108 chmod u+x cmd_$JOBNAME.sh
109 
110 #file to contain the commands to parallel
111 PFILE=cmd_${{JOBNAME}}.sh.parallel
112 
113 # Truncate the file
114 cp /dev/null $PFILE
115 
116 for COUNT in $(seq 1 1 $WORKERCOUNT)
117 do
118     echo "sh cmd_$JOBNAME.sh" >> $PFILE
119 done
120 
121 parallel --env _ --joblog "$JOBNAME.sh.parallel.log" \
122     --sshloginfile $SSHLOGINFILE --jobs {1} < $PFILE
123 
124 echo "All workers done"
125 '''.format(command, tasks_per_node, nodes_per_block, task_blocks)
126         return x
127 
128 
129 class MpiExecLauncher(Launcher):
130     """ Worker launcher that wraps the user's command with the framework to
131     launch multiple command invocations via mpiexec.
132 
133     This wrapper sets the bash env variable CORES to the number of cores on the
134     machine.
135 
136     This launcher makes the following assumptions:
137     - mpiexec is installed and can be located in $PATH
138     - The provider makes available the $PBS_NODEFILE environment variable
139     """
140     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
141         """
142         Args:
143         - command (string): The command string to be launched
144         - task_block (string) : bash evaluated string.
145 
146         KWargs:
147         - walltime (int) : This is not used by this launcher.
148         """
149         task_blocks = tasks_per_node * nodes_per_block
150 
151         x = '''export CORES=$(getconf _NPROCESSORS_ONLN)
152 echo "Found cores : $CORES"
153 WORKERCOUNT={3}
154 
155 # Deduplicate the nodefile
156 HOSTFILE="$JOBNAME.nodes"
157 if [ -z "$PBS_NODEFILE" ]; then
158     echo "localhost" > $HOSTFILE
159 else
160     sort -u $PBS_NODEFILE > $HOSTFILE
161 fi
162 
163 cat << MPIEXEC_EOF > cmd_$JOBNAME.sh
164 {0}
165 MPIEXEC_EOF
166 chmod u+x cmd_$JOBNAME.sh
167 
168 mpiexec --bind-to none -n $WORKERCOUNT --hostfile $HOSTFILE /usr/bin/sh cmd_$JOBNAME.sh
169 
170 echo "All workers done"
171 '''.format(command, tasks_per_node, nodes_per_block, task_blocks)
172         return x
173 
174 
175 class SrunLauncher(Launcher):
176     """ Worker launcher that wraps the user's command with the SRUN launch framework
177     to launch multiple cmd invocations in parallel on a single job allocation.
178     """
179 
180     def __init__(self, overrides=''):
181         """
182         Parameters
183         ----------
184 
185         overrides: str
186              This string will be passed to the srun launcher. Default: ''
187         """
188         self.overrides = overrides
189 
190     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
191         """
192         Args:
193         - command (string): The command string to be launched
194         - task_block (string) : bash evaluated string.
195 
196         KWargs:
197         - walltime (int) : This is not used by this launcher.
198         """
199         task_blocks = tasks_per_node * nodes_per_block
200         x = '''export CORES=$SLURM_CPUS_ON_NODE
201 export NODES=$SLURM_JOB_NUM_NODES
202 
203 echo "Found cores : $CORES"
204 echo "Found nodes : $NODES"
205 WORKERCOUNT={1}
206 
207 cat << SLURM_EOF > cmd_$SLURM_JOB_NAME.sh
208 {0}
209 SLURM_EOF
210 chmod a+x cmd_$SLURM_JOB_NAME.sh
211 
212 TASKBLOCKS={1}
213 
214 srun --ntasks $TASKBLOCKS -l {overrides} bash cmd_$SLURM_JOB_NAME.sh
215 
216 echo "Done"
217 '''.format(command, task_blocks, overrides=self.overrides)
218         return x
219 
220 
221 class SrunMPILauncher(Launcher):
222     """Launches as many workers as MPI tasks to be executed concurrently within a block.
223 
224     Use this launcher instead of SrunLauncher if each block will execute multiple MPI applications
225     at the same time. Workers should be launched with independent Srun calls so as to setup the
226     environment for MPI application launch.
227     """
228     def __init__(self, overrides=''):
229         """
230         Parameters
231         ----------
232 
233         overrides: str
234              This string will be passed to the launcher. Default: ''
235         """
236         self.overrides = overrides
237 
238     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
239         """
240         Args:
241         - command (string): The command string to be launched
242         - task_block (string) : bash evaluated string.
243 
244         KWargs:
245         - walltime (int) : This is not used by this launcher.
246         """
247         task_blocks = tasks_per_node * nodes_per_block
248         x = '''export CORES=$SLURM_CPUS_ON_NODE
249 export NODES=$SLURM_JOB_NUM_NODES
250 
251 echo "Found cores : $CORES"
252 echo "Found nodes : $NODES"
253 WORKERCOUNT={1}
254 
255 cat << SLURM_EOF > cmd_$SLURM_JOB_NAME.sh
256 {0}
257 SLURM_EOF
258 chmod a+x cmd_$SLURM_JOB_NAME.sh
259 
260 TASKBLOCKS={1}
261 
262 # If there are more taskblocks to be launched than nodes use
263 if (( "$TASKBLOCKS" > "$NODES" ))
264 then
265     echo "TaskBlocks:$TASKBLOCKS > Nodes:$NODES"
266     CORES_PER_BLOCK=$(($NODES * $CORES / $TASKBLOCKS))
267     for blk in $(seq 1 1 $TASKBLOCKS):
268     do
269         srun --ntasks $CORES_PER_BLOCK -l {overrides} bash cmd_$SLURM_JOB_NAME.sh &
270     done
271     wait
272 else
273     # A Task block could be integer multiples of Nodes
274     echo "TaskBlocks:$TASKBLOCKS <= Nodes:$NODES"
275     NODES_PER_BLOCK=$(( $NODES / $TASKBLOCKS ))
276     for blk in $(seq 1 1 $TASKBLOCKS):
277     do
278         srun --exclusive --nodes $NODES_PER_BLOCK -l {overrides} bash cmd_$SLURM_JOB_NAME.sh &
279     done
280     wait
281 
282 fi
283 
284 
285 echo "Done"
286 '''.format(command, task_blocks, overrides=self.overrides)
287         return x
288 
289 
290 class AprunLauncher(Launcher):
291     """  Worker launcher that wraps the user's command with the Aprun launch framework
292     to launch multiple cmd invocations in parallel on a single job allocation
293 
294     """
295     def __init__(self, overrides=''):
296         """
297         Parameters
298         ----------
299 
300         overrides: str
301              This string will be passed to the aprun launcher. Default: ''
302         """
303         self.overrides = overrides
304 
305     def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
306         """
307         Args:
308         - command (string): The command string to be launched
309         - tasks_per_node (int) : Workers to launch per node
310         - nodes_per_block (int) : Number of nodes in a block
311 
312         KWargs:
313         - walltime (int) : This is not used by this launcher.
314         """
315 
316         tasks_per_block = tasks_per_node * nodes_per_block
317         x = '''
318 WORKERCOUNT={1}
319 
320 cat << APRUN_EOF > cmd_$JOBNAME.sh
321 {0}
322 APRUN_EOF
323 chmod a+x cmd_$JOBNAME.sh
324 
325 aprun -n {tasks_per_block} -N {tasks_per_node} {overrides} /bin/bash cmd_$JOBNAME.sh &
326 wait
327 
328 echo "Done"
329 '''.format(command, tasks_per_block,
330            tasks_per_block=tasks_per_block,
331            tasks_per_node=tasks_per_node,
332            overrides=self.overrides)
333         return x
334 
335 
336 if __name__ == '__main__':
337 
338     s = SingleNodeLauncher()
339     wrapped = s("hello", 1, 1)
340     print(wrapped)
341
```

Path: `parsl/launchers/__init__.py`
Content:
```
1 from parsl.launchers.launchers import SimpleLauncher, SingleNodeLauncher, \
2     SrunLauncher, AprunLauncher, SrunMPILauncher, AprunLauncher, \
3     GnuParallelLauncher, MpiExecLauncher
4 
5 __all__ = ['SimpleLauncher',
6            'SingleNodeLauncher',
7            'SrunLauncher',
8            'AprunLauncher',
9            'SrunMPILauncher',
10            'AprunLauncher',
11            'GnuParallelLauncher',
12            'MpiExecLauncher']
13
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/parsl/launchers/__init__.py b/parsl/launchers/__init__.py
--- a/parsl/launchers/__init__.py
+++ b/parsl/launchers/__init__.py
@@ -1,6 +1,6 @@
 from parsl.launchers.launchers import SimpleLauncher, SingleNodeLauncher, \
     SrunLauncher, AprunLauncher, SrunMPILauncher, AprunLauncher, \
-    GnuParallelLauncher, MpiExecLauncher
+    GnuParallelLauncher, MpiExecLauncher, MpiRunLauncher
 
 __all__ = ['SimpleLauncher',
            'SingleNodeLauncher',
@@ -9,4 +9,5 @@
            'SrunMPILauncher',
            'AprunLauncher',
            'GnuParallelLauncher',
-           'MpiExecLauncher']
+           'MpiExecLauncher',
+           'MpiRunLauncher']
diff --git a/parsl/launchers/launchers.py b/parsl/launchers/launchers.py
--- a/parsl/launchers/launchers.py
+++ b/parsl/launchers/launchers.py
@@ -172,6 +172,47 @@
         return x
 
 
+class MpiRunLauncher(Launcher):
+    """ Worker launcher that wraps the user's command with the framework to
+    launch multiple command invocations via mpirun.
+
+    This wrapper sets the bash env variable CORES to the number of cores on the
+    machine.
+
+    This launcher makes the following assumptions:
+    - mpirun is installed and can be located in $PATH
+    - The provider makes available the $PBS_NODEFILE environment variable
+    """
+    def __init__(self, bash_location='/bin/bash'):
+        self.bash_location = bash_location
+
+    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):
+        """
+        Args:
+        - command (string): The command string to be launched
+        - task_block (string) : bash evaluated string.
+
+        KWargs:
+        - walltime (int) : This is not used by this launcher.
+        """
+        task_blocks = tasks_per_node * nodes_per_block
+
+        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)
+echo "Found cores : $CORES"
+WORKERCOUNT={3}
+
+cat << MPIRUN_EOF > cmd_$JOBNAME.sh
+{0}
+MPIRUN_EOF
+chmod u+x cmd_$JOBNAME.sh
+
+mpirun -np $WORKERCOUNT {4} cmd_$JOBNAME.sh
+
+echo "All workers done"
+'''.format(command, tasks_per_node, nodes_per_block, task_blocks, self.bash_location)
+        return x
+
+
 class SrunLauncher(Launcher):
     """ Worker launcher that wraps the user's command with the SRUN launch framework
     to launch multiple cmd invocations in parallel on a single job allocation.

{"golden_diff": "diff --git a/parsl/launchers/__init__.py b/parsl/launchers/__init__.py\n--- a/parsl/launchers/__init__.py\n+++ b/parsl/launchers/__init__.py\n@@ -1,6 +1,6 @@\n from parsl.launchers.launchers import SimpleLauncher, SingleNodeLauncher, \\\n     SrunLauncher, AprunLauncher, SrunMPILauncher, AprunLauncher, \\\n-    GnuParallelLauncher, MpiExecLauncher\n+    GnuParallelLauncher, MpiExecLauncher, MpiRunLauncher\n \n __all__ = ['SimpleLauncher',\n            'SingleNodeLauncher',\n@@ -9,4 +9,5 @@\n            'SrunMPILauncher',\n            'AprunLauncher',\n            'GnuParallelLauncher',\n-           'MpiExecLauncher']\n+           'MpiExecLauncher',\n+           'MpiRunLauncher']\ndiff --git a/parsl/launchers/launchers.py b/parsl/launchers/launchers.py\n--- a/parsl/launchers/launchers.py\n+++ b/parsl/launchers/launchers.py\n@@ -172,6 +172,47 @@\n         return x\n \n \n+class MpiRunLauncher(Launcher):\n+    \"\"\" Worker launcher that wraps the user's command with the framework to\n+    launch multiple command invocations via mpirun.\n+\n+    This wrapper sets the bash env variable CORES to the number of cores on the\n+    machine.\n+\n+    This launcher makes the following assumptions:\n+    - mpirun is installed and can be located in $PATH\n+    - The provider makes available the $PBS_NODEFILE environment variable\n+    \"\"\"\n+    def __init__(self, bash_location='/bin/bash'):\n+        self.bash_location = bash_location\n+\n+    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n+        \"\"\"\n+        Args:\n+        - command (string): The command string to be launched\n+        - task_block (string) : bash evaluated string.\n+\n+        KWargs:\n+        - walltime (int) : This is not used by this launcher.\n+        \"\"\"\n+        task_blocks = tasks_per_node * nodes_per_block\n+\n+        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\n+echo \"Found cores : $CORES\"\n+WORKERCOUNT={3}\n+\n+cat << MPIRUN_EOF > cmd_$JOBNAME.sh\n+{0}\n+MPIRUN_EOF\n+chmod u+x cmd_$JOBNAME.sh\n+\n+mpirun -np $WORKERCOUNT {4} cmd_$JOBNAME.sh\n+\n+echo \"All workers done\"\n+'''.format(command, tasks_per_node, nodes_per_block, task_blocks, self.bash_location)\n+        return x\n+\n+\n class SrunLauncher(Launcher):\n     \"\"\" Worker launcher that wraps the user's command with the SRUN launch framework\n     to launch multiple cmd invocations in parallel on a single job allocation.\n", "issue": "Add mpirun launcher\nNeeded at NSCC, which doesn't have aprun.\n", "before_files": [{"content": "from abc import ABCMeta, abstractmethod\n\nfrom parsl.utils import RepresentationMixin\n\n\nclass Launcher(RepresentationMixin, metaclass=ABCMeta):\n    \"\"\" Launcher base class to enforce launcher interface\n    \"\"\"\n    @abstractmethod\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\" Wraps the command with the Launcher calls.\n        *MUST* be implemented by the concrete child classes\n        \"\"\"\n        pass\n\n\nclass SimpleLauncher(Launcher):\n    \"\"\" Does no wrapping. Just returns the command as-is\n    \"\"\"\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        return command\n\n\nclass SingleNodeLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the framework to\n    launch multiple command invocations in parallel. This wrapper sets the\n    bash env variable CORES to the number of cores on the machine. By setting\n    task_blocks to an integer or to a bash expression the number of invocations\n    of the command to be launched can be controlled.\n    \"\"\"\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n\n        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\necho \"Found cores : $CORES\"\nWORKERCOUNT={1}\n\nCMD ( ) {{\n{0}\n}}\nfor COUNT in $(seq 1 1 $WORKERCOUNT)\ndo\n    echo \"Launching worker: $COUNT\"\n    CMD &\ndone\nwait\necho \"All workers done\"\n'''.format(command, task_blocks)\n        return x\n\n\nclass GnuParallelLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the framework to\n    launch multiple command invocations via GNU parallel sshlogin.\n\n    This wrapper sets the bash env variable CORES to the number of cores on the\n    machine.\n\n    This launcher makes the following assumptions:\n    - GNU parallel is installed and can be located in $PATH\n    - Paswordless SSH login is configured between the controller node and the\n      target nodes.\n    - The provider makes available the $PBS_NODEFILE environment variable\n    \"\"\"\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n\n        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\necho \"Found cores : $CORES\"\nWORKERCOUNT={3}\n\n# Deduplicate the nodefile\nSSHLOGINFILE=\"$JOBNAME.nodes\"\nif [ -z \"$PBS_NODEFILE\" ]; then\n    echo \"localhost\" > $SSHLOGINFILE\nelse\n    sort -u $PBS_NODEFILE > $SSHLOGINFILE\nfi\n\ncat << PARALLEL_CMD_EOF > cmd_$JOBNAME.sh\n{0}\nPARALLEL_CMD_EOF\nchmod u+x cmd_$JOBNAME.sh\n\n#file to contain the commands to parallel\nPFILE=cmd_${{JOBNAME}}.sh.parallel\n\n# Truncate the file\ncp /dev/null $PFILE\n\nfor COUNT in $(seq 1 1 $WORKERCOUNT)\ndo\n    echo \"sh cmd_$JOBNAME.sh\" >> $PFILE\ndone\n\nparallel --env _ --joblog \"$JOBNAME.sh.parallel.log\" \\\n    --sshloginfile $SSHLOGINFILE --jobs {1} < $PFILE\n\necho \"All workers done\"\n'''.format(command, tasks_per_node, nodes_per_block, task_blocks)\n        return x\n\n\nclass MpiExecLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the framework to\n    launch multiple command invocations via mpiexec.\n\n    This wrapper sets the bash env variable CORES to the number of cores on the\n    machine.\n\n    This launcher makes the following assumptions:\n    - mpiexec is installed and can be located in $PATH\n    - The provider makes available the $PBS_NODEFILE environment variable\n    \"\"\"\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n\n        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\necho \"Found cores : $CORES\"\nWORKERCOUNT={3}\n\n# Deduplicate the nodefile\nHOSTFILE=\"$JOBNAME.nodes\"\nif [ -z \"$PBS_NODEFILE\" ]; then\n    echo \"localhost\" > $HOSTFILE\nelse\n    sort -u $PBS_NODEFILE > $HOSTFILE\nfi\n\ncat << MPIEXEC_EOF > cmd_$JOBNAME.sh\n{0}\nMPIEXEC_EOF\nchmod u+x cmd_$JOBNAME.sh\n\nmpiexec --bind-to none -n $WORKERCOUNT --hostfile $HOSTFILE /usr/bin/sh cmd_$JOBNAME.sh\n\necho \"All workers done\"\n'''.format(command, tasks_per_node, nodes_per_block, task_blocks)\n        return x\n\n\nclass SrunLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the SRUN launch framework\n    to launch multiple cmd invocations in parallel on a single job allocation.\n    \"\"\"\n\n    def __init__(self, overrides=''):\n        \"\"\"\n        Parameters\n        ----------\n\n        overrides: str\n             This string will be passed to the srun launcher. Default: ''\n        \"\"\"\n        self.overrides = overrides\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n        x = '''export CORES=$SLURM_CPUS_ON_NODE\nexport NODES=$SLURM_JOB_NUM_NODES\n\necho \"Found cores : $CORES\"\necho \"Found nodes : $NODES\"\nWORKERCOUNT={1}\n\ncat << SLURM_EOF > cmd_$SLURM_JOB_NAME.sh\n{0}\nSLURM_EOF\nchmod a+x cmd_$SLURM_JOB_NAME.sh\n\nTASKBLOCKS={1}\n\nsrun --ntasks $TASKBLOCKS -l {overrides} bash cmd_$SLURM_JOB_NAME.sh\n\necho \"Done\"\n'''.format(command, task_blocks, overrides=self.overrides)\n        return x\n\n\nclass SrunMPILauncher(Launcher):\n    \"\"\"Launches as many workers as MPI tasks to be executed concurrently within a block.\n\n    Use this launcher instead of SrunLauncher if each block will execute multiple MPI applications\n    at the same time. Workers should be launched with independent Srun calls so as to setup the\n    environment for MPI application launch.\n    \"\"\"\n    def __init__(self, overrides=''):\n        \"\"\"\n        Parameters\n        ----------\n\n        overrides: str\n             This string will be passed to the launcher. Default: ''\n        \"\"\"\n        self.overrides = overrides\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n        x = '''export CORES=$SLURM_CPUS_ON_NODE\nexport NODES=$SLURM_JOB_NUM_NODES\n\necho \"Found cores : $CORES\"\necho \"Found nodes : $NODES\"\nWORKERCOUNT={1}\n\ncat << SLURM_EOF > cmd_$SLURM_JOB_NAME.sh\n{0}\nSLURM_EOF\nchmod a+x cmd_$SLURM_JOB_NAME.sh\n\nTASKBLOCKS={1}\n\n# If there are more taskblocks to be launched than nodes use\nif (( \"$TASKBLOCKS\" > \"$NODES\" ))\nthen\n    echo \"TaskBlocks:$TASKBLOCKS > Nodes:$NODES\"\n    CORES_PER_BLOCK=$(($NODES * $CORES / $TASKBLOCKS))\n    for blk in $(seq 1 1 $TASKBLOCKS):\n    do\n        srun --ntasks $CORES_PER_BLOCK -l {overrides} bash cmd_$SLURM_JOB_NAME.sh &\n    done\n    wait\nelse\n    # A Task block could be integer multiples of Nodes\n    echo \"TaskBlocks:$TASKBLOCKS <= Nodes:$NODES\"\n    NODES_PER_BLOCK=$(( $NODES / $TASKBLOCKS ))\n    for blk in $(seq 1 1 $TASKBLOCKS):\n    do\n        srun --exclusive --nodes $NODES_PER_BLOCK -l {overrides} bash cmd_$SLURM_JOB_NAME.sh &\n    done\n    wait\n\nfi\n\n\necho \"Done\"\n'''.format(command, task_blocks, overrides=self.overrides)\n        return x\n\n\nclass AprunLauncher(Launcher):\n    \"\"\"  Worker launcher that wraps the user's command with the Aprun launch framework\n    to launch multiple cmd invocations in parallel on a single job allocation\n\n    \"\"\"\n    def __init__(self, overrides=''):\n        \"\"\"\n        Parameters\n        ----------\n\n        overrides: str\n             This string will be passed to the aprun launcher. Default: ''\n        \"\"\"\n        self.overrides = overrides\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - tasks_per_node (int) : Workers to launch per node\n        - nodes_per_block (int) : Number of nodes in a block\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n\n        tasks_per_block = tasks_per_node * nodes_per_block\n        x = '''\nWORKERCOUNT={1}\n\ncat << APRUN_EOF > cmd_$JOBNAME.sh\n{0}\nAPRUN_EOF\nchmod a+x cmd_$JOBNAME.sh\n\naprun -n {tasks_per_block} -N {tasks_per_node} {overrides} /bin/bash cmd_$JOBNAME.sh &\nwait\n\necho \"Done\"\n'''.format(command, tasks_per_block,\n           tasks_per_block=tasks_per_block,\n           tasks_per_node=tasks_per_node,\n           overrides=self.overrides)\n        return x\n\n\nif __name__ == '__main__':\n\n    s = SingleNodeLauncher()\n    wrapped = s(\"hello\", 1, 1)\n    print(wrapped)\n", "path": "parsl/launchers/launchers.py"}, {"content": "from parsl.launchers.launchers import SimpleLauncher, SingleNodeLauncher, \\\n    SrunLauncher, AprunLauncher, SrunMPILauncher, AprunLauncher, \\\n    GnuParallelLauncher, MpiExecLauncher\n\n__all__ = ['SimpleLauncher',\n           'SingleNodeLauncher',\n           'SrunLauncher',\n           'AprunLauncher',\n           'SrunMPILauncher',\n           'AprunLauncher',\n           'GnuParallelLauncher',\n           'MpiExecLauncher']\n", "path": "parsl/launchers/__init__.py"}], "after_files": [{"content": "from abc import ABCMeta, abstractmethod\n\nfrom parsl.utils import RepresentationMixin\n\n\nclass Launcher(RepresentationMixin, metaclass=ABCMeta):\n    \"\"\" Launcher base class to enforce launcher interface\n    \"\"\"\n    @abstractmethod\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\" Wraps the command with the Launcher calls.\n        *MUST* be implemented by the concrete child classes\n        \"\"\"\n        pass\n\n\nclass SimpleLauncher(Launcher):\n    \"\"\" Does no wrapping. Just returns the command as-is\n    \"\"\"\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        return command\n\n\nclass SingleNodeLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the framework to\n    launch multiple command invocations in parallel. This wrapper sets the\n    bash env variable CORES to the number of cores on the machine. By setting\n    task_blocks to an integer or to a bash expression the number of invocations\n    of the command to be launched can be controlled.\n    \"\"\"\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n\n        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\necho \"Found cores : $CORES\"\nWORKERCOUNT={1}\n\nCMD ( ) {{\n{0}\n}}\nfor COUNT in $(seq 1 1 $WORKERCOUNT)\ndo\n    echo \"Launching worker: $COUNT\"\n    CMD &\ndone\nwait\necho \"All workers done\"\n'''.format(command, task_blocks)\n        return x\n\n\nclass GnuParallelLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the framework to\n    launch multiple command invocations via GNU parallel sshlogin.\n\n    This wrapper sets the bash env variable CORES to the number of cores on the\n    machine.\n\n    This launcher makes the following assumptions:\n    - GNU parallel is installed and can be located in $PATH\n    - Paswordless SSH login is configured between the controller node and the\n      target nodes.\n    - The provider makes available the $PBS_NODEFILE environment variable\n    \"\"\"\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n\n        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\necho \"Found cores : $CORES\"\nWORKERCOUNT={3}\n\n# Deduplicate the nodefile\nSSHLOGINFILE=\"$JOBNAME.nodes\"\nif [ -z \"$PBS_NODEFILE\" ]; then\n    echo \"localhost\" > $SSHLOGINFILE\nelse\n    sort -u $PBS_NODEFILE > $SSHLOGINFILE\nfi\n\ncat << PARALLEL_CMD_EOF > cmd_$JOBNAME.sh\n{0}\nPARALLEL_CMD_EOF\nchmod u+x cmd_$JOBNAME.sh\n\n#file to contain the commands to parallel\nPFILE=cmd_${{JOBNAME}}.sh.parallel\n\n# Truncate the file\ncp /dev/null $PFILE\n\nfor COUNT in $(seq 1 1 $WORKERCOUNT)\ndo\n    echo \"sh cmd_$JOBNAME.sh\" >> $PFILE\ndone\n\nparallel --env _ --joblog \"$JOBNAME.sh.parallel.log\" \\\n    --sshloginfile $SSHLOGINFILE --jobs {1} < $PFILE\n\necho \"All workers done\"\n'''.format(command, tasks_per_node, nodes_per_block, task_blocks)\n        return x\n\n\nclass MpiExecLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the framework to\n    launch multiple command invocations via mpiexec.\n\n    This wrapper sets the bash env variable CORES to the number of cores on the\n    machine.\n\n    This launcher makes the following assumptions:\n    - mpiexec is installed and can be located in $PATH\n    - The provider makes available the $PBS_NODEFILE environment variable\n    \"\"\"\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n\n        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\necho \"Found cores : $CORES\"\nWORKERCOUNT={3}\n\n# Deduplicate the nodefile\nHOSTFILE=\"$JOBNAME.nodes\"\nif [ -z \"$PBS_NODEFILE\" ]; then\n    echo \"localhost\" > $HOSTFILE\nelse\n    sort -u $PBS_NODEFILE > $HOSTFILE\nfi\n\ncat << MPIEXEC_EOF > cmd_$JOBNAME.sh\n{0}\nMPIEXEC_EOF\nchmod u+x cmd_$JOBNAME.sh\n\nmpiexec --bind-to none -n $WORKERCOUNT --hostfile $HOSTFILE /usr/bin/sh cmd_$JOBNAME.sh\n\necho \"All workers done\"\n'''.format(command, tasks_per_node, nodes_per_block, task_blocks)\n        return x\n\n\nclass MpiRunLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the framework to\n    launch multiple command invocations via mpirun.\n\n    This wrapper sets the bash env variable CORES to the number of cores on the\n    machine.\n\n    This launcher makes the following assumptions:\n    - mpirun is installed and can be located in $PATH\n    - The provider makes available the $PBS_NODEFILE environment variable\n    \"\"\"\n    def __init__(self, bash_location='/bin/bash'):\n        self.bash_location = bash_location\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n\n        x = '''export CORES=$(getconf _NPROCESSORS_ONLN)\necho \"Found cores : $CORES\"\nWORKERCOUNT={3}\n\ncat << MPIRUN_EOF > cmd_$JOBNAME.sh\n{0}\nMPIRUN_EOF\nchmod u+x cmd_$JOBNAME.sh\n\nmpirun -np $WORKERCOUNT {4} cmd_$JOBNAME.sh\n\necho \"All workers done\"\n'''.format(command, tasks_per_node, nodes_per_block, task_blocks, self.bash_location)\n        return x\n\n\nclass SrunLauncher(Launcher):\n    \"\"\" Worker launcher that wraps the user's command with the SRUN launch framework\n    to launch multiple cmd invocations in parallel on a single job allocation.\n    \"\"\"\n\n    def __init__(self, overrides=''):\n        \"\"\"\n        Parameters\n        ----------\n\n        overrides: str\n             This string will be passed to the srun launcher. Default: ''\n        \"\"\"\n        self.overrides = overrides\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n        x = '''export CORES=$SLURM_CPUS_ON_NODE\nexport NODES=$SLURM_JOB_NUM_NODES\n\necho \"Found cores : $CORES\"\necho \"Found nodes : $NODES\"\nWORKERCOUNT={1}\n\ncat << SLURM_EOF > cmd_$SLURM_JOB_NAME.sh\n{0}\nSLURM_EOF\nchmod a+x cmd_$SLURM_JOB_NAME.sh\n\nTASKBLOCKS={1}\n\nsrun --ntasks $TASKBLOCKS -l {overrides} bash cmd_$SLURM_JOB_NAME.sh\n\necho \"Done\"\n'''.format(command, task_blocks, overrides=self.overrides)\n        return x\n\n\nclass SrunMPILauncher(Launcher):\n    \"\"\"Launches as many workers as MPI tasks to be executed concurrently within a block.\n\n    Use this launcher instead of SrunLauncher if each block will execute multiple MPI applications\n    at the same time. Workers should be launched with independent Srun calls so as to setup the\n    environment for MPI application launch.\n    \"\"\"\n    def __init__(self, overrides=''):\n        \"\"\"\n        Parameters\n        ----------\n\n        overrides: str\n             This string will be passed to the launcher. Default: ''\n        \"\"\"\n        self.overrides = overrides\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - task_block (string) : bash evaluated string.\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n        task_blocks = tasks_per_node * nodes_per_block\n        x = '''export CORES=$SLURM_CPUS_ON_NODE\nexport NODES=$SLURM_JOB_NUM_NODES\n\necho \"Found cores : $CORES\"\necho \"Found nodes : $NODES\"\nWORKERCOUNT={1}\n\ncat << SLURM_EOF > cmd_$SLURM_JOB_NAME.sh\n{0}\nSLURM_EOF\nchmod a+x cmd_$SLURM_JOB_NAME.sh\n\nTASKBLOCKS={1}\n\n# If there are more taskblocks to be launched than nodes use\nif (( \"$TASKBLOCKS\" > \"$NODES\" ))\nthen\n    echo \"TaskBlocks:$TASKBLOCKS > Nodes:$NODES\"\n    CORES_PER_BLOCK=$(($NODES * $CORES / $TASKBLOCKS))\n    for blk in $(seq 1 1 $TASKBLOCKS):\n    do\n        srun --ntasks $CORES_PER_BLOCK -l {overrides} bash cmd_$SLURM_JOB_NAME.sh &\n    done\n    wait\nelse\n    # A Task block could be integer multiples of Nodes\n    echo \"TaskBlocks:$TASKBLOCKS <= Nodes:$NODES\"\n    NODES_PER_BLOCK=$(( $NODES / $TASKBLOCKS ))\n    for blk in $(seq 1 1 $TASKBLOCKS):\n    do\n        srun --exclusive --nodes $NODES_PER_BLOCK -l {overrides} bash cmd_$SLURM_JOB_NAME.sh &\n    done\n    wait\n\nfi\n\n\necho \"Done\"\n'''.format(command, task_blocks, overrides=self.overrides)\n        return x\n\n\nclass AprunLauncher(Launcher):\n    \"\"\"  Worker launcher that wraps the user's command with the Aprun launch framework\n    to launch multiple cmd invocations in parallel on a single job allocation\n\n    \"\"\"\n    def __init__(self, overrides=''):\n        \"\"\"\n        Parameters\n        ----------\n\n        overrides: str\n             This string will be passed to the aprun launcher. Default: ''\n        \"\"\"\n        self.overrides = overrides\n\n    def __call__(self, command, tasks_per_node, nodes_per_block, walltime=None):\n        \"\"\"\n        Args:\n        - command (string): The command string to be launched\n        - tasks_per_node (int) : Workers to launch per node\n        - nodes_per_block (int) : Number of nodes in a block\n\n        KWargs:\n        - walltime (int) : This is not used by this launcher.\n        \"\"\"\n\n        tasks_per_block = tasks_per_node * nodes_per_block\n        x = '''\nWORKERCOUNT={1}\n\ncat << APRUN_EOF > cmd_$JOBNAME.sh\n{0}\nAPRUN_EOF\nchmod a+x cmd_$JOBNAME.sh\n\naprun -n {tasks_per_block} -N {tasks_per_node} {overrides} /bin/bash cmd_$JOBNAME.sh &\nwait\n\necho \"Done\"\n'''.format(command, tasks_per_block,\n           tasks_per_block=tasks_per_block,\n           tasks_per_node=tasks_per_node,\n           overrides=self.overrides)\n        return x\n\n\nif __name__ == '__main__':\n\n    s = SingleNodeLauncher()\n    wrapped = s(\"hello\", 1, 1)\n    print(wrapped)\n", "path": "parsl/launchers/launchers.py"}, {"content": "from parsl.launchers.launchers import SimpleLauncher, SingleNodeLauncher, \\\n    SrunLauncher, AprunLauncher, SrunMPILauncher, AprunLauncher, \\\n    GnuParallelLauncher, MpiExecLauncher, MpiRunLauncher\n\n__all__ = ['SimpleLauncher',\n           'SingleNodeLauncher',\n           'SrunLauncher',\n           'AprunLauncher',\n           'SrunMPILauncher',\n           'AprunLauncher',\n           'GnuParallelLauncher',\n           'MpiExecLauncher',\n           'MpiRunLauncher']\n", "path": "parsl/launchers/__init__.py"}]}