Skip to content

Commit fb8b159

Browse files
committed
Multi-target support to batch engine
1 parent d66b0c3 commit fb8b159

File tree

9 files changed

+99
-83
lines changed

9 files changed

+99
-83
lines changed

examples/1D_bubblescreen/fort.1

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/common/m_compile_specific.f90

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ subroutine s_create_directory(dir_name)
2525

2626
end subroutine s_create_directory
2727

28+
subroutine s_delete_file(filepath)
29+
character(LEN=*), intent(IN) :: filepath
30+
31+
#ifdef _WIN32
32+
call system('del "'//filepath//'"')
33+
#else
34+
call system('rm "'//filepath//'"')
35+
#endif
36+
37+
end subroutine s_delete_file
38+
2839
subroutine s_delete_directory(dir_name)
2940
character(LEN=*), intent(IN) :: dir_name
3041

@@ -70,7 +81,8 @@ subroutine s_get_basename(dirpath, basename)
7081
open (100, FILE='basename', FORM='formatted', STATUS='old')
7182
read (100, '(A)') basename; close (100)
7283

73-
call s_delete_directory("dir_name")
84+
call s_delete_file("basename")
85+
7486
end subroutine s_get_basename
7587

7688
end module m_compile_specific

toolchain/mfc/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def build_target(mfc, name: str, history: typing.List[str] = None):
175175

176176
raise common.MFCException(f"Failed to configure the [bold magenta]{name}[/bold magenta] target.")
177177

178-
common.system(build, exception_text=f"Failed to build the [bold magenta]{name}[/bold magenta] target.")
178+
common.system(build, exception_text=f"Failed to build the [bold magenta]{name}[/bold magenta] target.")
179179
common.system(install, exception_text=f"Failed to install the [bold magenta]{name}[/bold magenta] target.")
180180
cons.print(no_indent=True)
181181

toolchain/mfc/run/engines.py

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def _init(self) -> None:
3737
def get_args(self) -> typing.List[str]:
3838
raise common.MFCException(f"MFCEngine::get_args: not implemented for {self.name}.")
3939

40-
def run(self, target_name: str) -> None:
40+
def run(self, names: typing.List[str]) -> None:
4141
raise common.MFCException(f"MFCEngine::run: not implemented for {self.name}.")
4242

4343
def validate_job_options(self) -> None:
@@ -78,7 +78,7 @@ def get_exec_cmd(self, target_name: str) -> typing.List[str]:
7878
return [self.mpibin.bin] + self.mpibin.gen_params(self.mfc.args) + flags + [binpath]
7979

8080

81-
def run(self, target_name: str) -> None:
81+
def run(self, names: typing.List[str]) -> None:
8282
if not self.bWorks:
8383
# Fix MFlowCode/MFC#21: Check whether attempting to run a job will hang
8484
# forever. This can happen when using the wrong queue system.
@@ -116,19 +116,19 @@ def run(self, target_name: str) -> None:
116116
+ f"[bold magenta]Reason[/bold magenta]: {'Time limit.' if p.is_alive() else 'Exit code.'}"
117117
)
118118

119+
for name in names:
120+
cons.print(f"[bold]Running [magenta]{name}[/magenta][/bold]:")
121+
cons.indent()
119122

120-
cons.print(f"Running [bold magenta]{target_name}[/bold magenta]:")
121-
cons.indent()
123+
if not self.mfc.args["dry_run"]:
124+
start_time = time.monotonic()
125+
common.system(self.get_exec_cmd(name), cwd=self.input.case_dirpath)
126+
end_time = time.monotonic()
127+
cons.print(no_indent=True)
122128

123-
if not self.mfc.args["dry_run"]:
124-
start_time = time.monotonic()
125-
common.system(self.get_exec_cmd(target_name), cwd=self.input.case_dirpath)
126-
end_time = time.monotonic()
127-
cons.print(no_indent=True)
129+
cons.print(f"[bold green]Done[/bold green] (in {datetime.timedelta(seconds=end_time - start_time)})")
128130

129-
cons.print(f"[bold green]Done[/bold green] (in {datetime.timedelta(seconds=end_time - start_time)})")
130-
131-
cons.unindent()
131+
cons.unindent()
132132

133133
def validate_job_options(self, mfc) -> None:
134134
if mfc.args["nodes"] != 1:
@@ -150,19 +150,17 @@ def get_args(self) -> str:
150150
Email (-@) {self.mfc.args["email"]}
151151
"""
152152

153-
def run(self, target_name: str) -> None:
154-
cons.print(f"Running [bold magenta]{target_name}[/bold magenta]:")
155-
cons.indent()
156-
153+
def run(self, names: typing.List[str]) -> None:
157154
system = queues.get_system()
158155
cons.print(f"Detected the [bold magenta]{system.name}[/bold magenta] queue system.")
159156

160-
self.__create_batch_file(system, target_name)
157+
cons.print(f"Running [bold magenta]{common.format_list_to_string(names)}[/bold magenta]:")
158+
cons.indent()
159+
160+
self.__create_batch_file(system, names)
161161

162162
if not self.mfc.args["dry_run"]:
163-
cons.print(no_indent=True)
164-
self.__execute_batch_file(system, target_name)
165-
cons.print(no_indent=True)
163+
self.__execute_batch_file(system, names)
166164

167165
cons.print("[bold yellow]INFO:[/bold yellow] Batch file submitted! Please check your queue system for the job status.")
168166
cons.print("[bold yellow]INFO:[/bold yellow] If an error occurs, please check the generated batch file and error logs for more information.")
@@ -173,16 +171,16 @@ def run(self, target_name: str) -> None:
173171
def __get_batch_dirpath(self) -> str:
174172
return copy.copy(self.input.case_dirpath)
175173

176-
def __get_batch_filename(self, target_name: str) -> str:
177-
return f"{target_name}.sh"
174+
def __get_batch_filename(self, names: typing.List[str]) -> str:
175+
return f"{self.mfc.args['name']}.sh"
178176

179-
def __get_batch_filepath(self, target_name: str):
177+
def __get_batch_filepath(self, names: typing.List[str]):
180178
return os.path.abspath(os.sep.join([
181179
self.__get_batch_dirpath(),
182-
self.__get_batch_filename(target_name)
180+
self.__get_batch_filename(names)
183181
]))
184182

185-
def __generate_prologue(self, system: queues.QueueSystem,) -> str:
183+
def __generate_prologue(self, system: queues.QueueSystem, names: typing.List[str]) -> str:
186184
modules = f""
187185

188186
if common.does_system_use_modules():
@@ -270,11 +268,11 @@ def __evaluate_expression(self, expr: str) -> str:
270268
except Exception as exc:
271269
raise common.MFCException(f"BatchEngine: {expr_original} (interpreted as {expr}) is not a valid expression in the template file. Please check your spelling.")
272270

273-
def __batch_evaluate(self, s: str, system: queues.QueueSystem, target_name: str):
271+
def __batch_evaluate(self, s: str, system: queues.QueueSystem, names: typing.List[str]):
274272
replace_list = [
275-
("{MFC::PROLOGUE}", self.__generate_prologue(system)),
273+
("{MFC::PROLOGUE}", self.__generate_prologue(system, names)),
276274
("{MFC::EPILOGUE}", self.__generate_epilogue()),
277-
("{MFC::BIN}", self.get_binpath(target_name))
275+
("{MFC::BINARIES}", ' '.join([f"'{self.get_binpath(x)}'" for x in names]))
278276
]
279277

280278
for (key, value) in replace_list:
@@ -298,27 +296,25 @@ def __batch_evaluate(self, s: str, system: queues.QueueSystem, target_name: str)
298296

299297
return s
300298

301-
def __create_batch_file(self, system: queues.QueueSystem, target_name: str):
302-
cons.print("> > Generating batch file...")
303-
filepath = self.__get_batch_filepath(target_name)
304-
cons.print("> > Evaluating template file...")
305-
content = self.__batch_evaluate(system.template, system, target_name)
299+
def __create_batch_file(self, system: queues.QueueSystem, names: typing.List[str]):
300+
cons.print("> Generating batch file...")
301+
filepath = self.__get_batch_filepath(names)
302+
cons.print("> Evaluating template file...")
303+
content = self.__batch_evaluate(system.template, system, names)
306304

307-
cons.print("> > Writing batch file...")
305+
cons.print("> Writing batch file...")
308306
common.file_write(filepath, content)
309307

310-
def __execute_batch_file(self, system: queues.QueueSystem, target_name: str):
308+
def __execute_batch_file(self, system: queues.QueueSystem, names: typing.List[str]):
311309
# We CD to the case directory before executing the batch file so that
312310
# any files the queue system generates (like .err and .out) are created
313311
# in the correct directory.
314-
cmd = system.gen_submit_cmd(self.__get_batch_filename(target_name))
312+
cmd = system.gen_submit_cmd(self.__get_batch_filename(names))
315313

316314
if common.system(cmd, cwd=self.__get_batch_dirpath()) != 0:
317315
raise common.MFCException(f"Submitting batch file for {system.name} failed. It can be found here: {self.__get_batch_filepath(target_name)}. Please check the file for errors.")
318316

319317
def validate_job_options(self, mfc) -> None:
320-
if len(mfc.args["targets"]) != 1:
321-
raise common.MFCException(f"The Batch engine requires a unique target (-t) to run.")
322318
pass
323319

324320

@@ -337,4 +333,3 @@ def get_engine(slug: str) -> Engine:
337333
raise common.MFCException(f"Unsupported engine {slug}.")
338334

339335
return engine
340-

toolchain/mfc/run/run.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ def validate_job_options(self) -> None:
3333
engines.get_engine(self.mfc.args["engine"]).validate_job_options(self.mfc)
3434

3535
def run(self) -> None:
36+
for name in self.mfc.args["targets"]:
37+
build.build_target(self.mfc, name)
38+
3639
cons.print("[bold]Run[/bold]")
3740
cons.indent()
3841

@@ -57,23 +60,9 @@ def run(self) -> None:
5760

5861
self.validate_job_options()
5962

60-
for target_name in self.mfc.args["targets"]:
61-
cons.print(no_indent=True)
62-
cons.print(f"[bold]Running [magenta]{target_name}[/magenta][/bold]:")
63-
cons.indent()
64-
65-
# We generate the /path/to/case/{target_name}.inp and
66-
# src/{target_name}/case.fpp input files before building so that, in
67-
# order for the target to be built with case optimization, if
68-
# args["case_optimization"] is true. Doing so later, would require
69-
# there be two builds of a target.
70-
input_file.generate(target_name)
71-
72-
if not self.mfc.args["no_build"]:
73-
build.build_target(self.mfc, target_name)
63+
cons.print("Generating input files...")
64+
for name in self.mfc.args["targets"]:
65+
input_file.generate(name)
7466

75-
engine.run(target_name)
67+
engine.run(self.mfc.args["targets"])
7668

77-
cons.unindent()
78-
79-
cons.unindent()

toolchain/mfc/util/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class MFCException(Exception):
3737

3838
def system(command: typing.List[str], no_exception: bool = False, exception_text=None, on_error=lambda: None, cwd=None, stdout=None, stderr=None) -> int:
3939
cmd = [ str(x) for x in command if not isspace(str(x)) ]
40-
40+
4141
cons.print(no_indent=True)
4242
cons.print(f"$ {' '.join(cmd)}")
4343
cons.print(no_indent=True)

toolchain/templates/lsf.sh

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,22 @@
6464
#> on your system - if at all. {MFC::BIN} refers to
6565
#> the path the MFC executable.
6666
#>
67-
jsrun {'--smpiargs="-gpu"' if gpus_per_node > 0 else ''} \
68-
--nrs {cpus_per_node*nodes} \
69-
--cpu_per_rs 1 \
70-
--gpu_per_rs {min(gpus_per_node, 1)} \
71-
--tasks_per_rs 1 \
72-
"{MFC::BIN}"
67+
68+
for binpath in {MFC::BINARIES}; do
69+
70+
echo -e ":) Running $binpath:"
71+
echo ""
72+
73+
jsrun {'--smpiargs="-gpu"' if gpus_per_node > 0 else ''} \
74+
--nrs {cpus_per_node*nodes} \
75+
--cpu_per_rs 1 \
76+
--gpu_per_rs {min(gpus_per_node, 1)} \
77+
--tasks_per_rs 1 \
78+
"$binpath"
79+
80+
echo ""
81+
82+
done
7383

7484
{MFC::EPILOGUE}
7585
#>

toolchain/templates/pbs.sh

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#!/usr/bin/env bash
22
#>
3-
#> - PBS Batch File Template -
3+
#> - PBS Batch File Template -
44
#>
55
#> This file is part of the ./mfc.sh run subsystem.
66
#> For more information, please consult the README.
7-
#>
7+
#>
88
#> - You are invited to modify this file to suit your
99
#> needs, in order to get MFC running properly on
1010
#> your system.
@@ -23,7 +23,7 @@
2323
#>
2424
#> - Statements of the form {MFC::expression} tell MFC
2525
#> where to place the common code, across all batch
26-
#> files that is required to run MFC. They are not
26+
#> files that is required to run MFC. They are not
2727
#> intended to be modified by users.
2828
#>
2929
#PBS -N {name}
@@ -41,11 +41,11 @@
4141

4242

4343

44-
#>
44+
#>
4545
#> Note: If your system requires you to load environment
4646
#> modules inside of your batch script, please load
4747
#> them bellow.
48-
#>
48+
#>
4949

5050

5151

@@ -60,10 +60,18 @@
6060
#> on your system - if at all. {MFC::BIN} refers to
6161
#> the path the MFC executable.
6262
#>
63-
srun \
64-
--nodes={nodes} \
65-
--ntasks-per-node {cpus_per_node} \
66-
"{MFC::BIN}"
63+
64+
for binpath in {MFC::BINARIES}; do
65+
66+
echo -e ":) Running $binpath:"
67+
68+
srun \
69+
--nodes={nodes} \
70+
--ntasks-per-node {cpus_per_node} \
71+
"$binpath"
72+
73+
done
74+
6775
#>
6876
#> srun --mpi=pmix \
6977
#> "{MFC::BIN}"
@@ -77,3 +85,4 @@ srun \
7785
#>
7886
#> Note: Lines after the MFC Epilogue will not be executed.
7987
#>
88+

toolchain/templates/slurm.sh

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,16 @@
7777
#> srun --mpi=pmix \
7878
#> "{MFC::BIN}"
7979
#>
80-
mpirun \
80+
81+
for binpath in {MFC::BINARIES}; do
82+
83+
echo -e ":) Running $binpath:"
84+
85+
mpirun \
8186
-np {cpus_per_node*nodes} \
82-
"{MFC::BIN}"
87+
"$binpath"
88+
89+
done
8390

8491
{MFC::EPILOGUE}
8592

0 commit comments

Comments
 (0)