Skip to content

Commit ddbf707

Browse files
authored
Merge pull request #204 from henryleberre/master
Save build artifacts for all configurations (among other things)
2 parents d2062cf + ec68896 commit ddbf707

File tree

8 files changed

+224
-157
lines changed

8 files changed

+224
-157
lines changed

src/simulation/m_global_parameters.fpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,8 @@ contains
718718
if (fluid_pp(i)%Re(1) > 0) Re_size(1) = Re_size(1) + 1
719719
if (fluid_pp(i)%Re(2) > 0) Re_size(2) = Re_size(2) + 1
720720
end do
721+
722+
!$acc update device(Re_size)
721723

722724
! Bookkeeping the indexes of any viscous fluids and any pairs of
723725
! fluids whose interface will support effects of surface tension
@@ -770,8 +772,8 @@ contains
770772
! cell-boundary values or otherwise, the unaltered left and right,
771773
! WENO-reconstructed, cell-boundary values
772774
wa_flg = 0d0; IF(weno_avg) wa_flg = 1d0
775+
!$acc update device(wa_flg)
773776

774-
!$acc update device(Re_size)
775777
! Determining the number of cells that are needed in order to store
776778
! sufficient boundary conditions data as to iterate the solution in
777779
! the physical computational domain from one time-step iteration to

toolchain/mfc/args.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,16 @@ def add_common_arguments(p, mask = None):
7474

7575
# === TEST ===
7676
add_common_arguments(test, "t")
77-
test.add_argument("-l", "--list", action="store_true", help="List all available tests.")
78-
test.add_argument("-f", "--from", default=TEST_CASES[0].get_uuid(), type=str, help="First test UUID to run.")
79-
test.add_argument("-t", "--to", default=TEST_CASES[-1].get_uuid(), type=str, help="Last test UUID to run.")
80-
test.add_argument("-o", "--only", nargs="+", type=str, default=[], metavar="L", help="Only run tests with UUIDs or hashes L.")
81-
test.add_argument("-b", "--binary", choices=binaries, type=str, default=None, help="(Serial) Override MPI execution binary")
82-
test.add_argument("-r", "--relentless", action="store_true", default=False, help="Run all tests, even if multiple fail.")
83-
test.add_argument("-a", "--test-all", action="store_true", default=False, help="Run the Post Process Tests too.")
84-
test.add_argument("-g", "--gpus", type=str, default="0", help="(GPU) Comma separated list of GPU #s to use.")
85-
test.add_argument("-%", "--percent", type=int, default=100, help="Percentage of tests to run.")
77+
test.add_argument("-l", "--list", action="store_true", help="List all available tests.")
78+
test.add_argument("-f", "--from", default=TEST_CASES[0].get_uuid(), type=str, help="First test UUID to run.")
79+
test.add_argument("-t", "--to", default=TEST_CASES[-1].get_uuid(), type=str, help="Last test UUID to run.")
80+
test.add_argument("-o", "--only", nargs="+", type=str, default=[], metavar="L", help="Only run tests with UUIDs or hashes L.")
81+
test.add_argument("-b", "--binary", choices=binaries, type=str, default=None, help="(Serial) Override MPI execution binary")
82+
test.add_argument("-r", "--relentless", action="store_true", default=False, help="Run all tests, even if multiple fail.")
83+
test.add_argument("-a", "--test-all", action="store_true", default=False, help="Run the Post Process Tests too.")
84+
test.add_argument("-g", "--gpus", type=str, default="0", help="(GPU) Comma separated list of GPU #s to use.")
85+
test.add_argument("-%", "--percent", type=int, default=100, help="Percentage of tests to run.")
86+
test.add_argument("-m", "--max-attempts", type=int, default=3, help="Maximum number of attempts to run a test.")
8687

8788
test.add_argument("--case-optimization", action="store_true", default=False, help="(GPU Optimization) Compile MFC targets with some case parameters hard-coded.")
8889

toolchain/mfc/build.py

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os, typing, dataclasses
22

3-
from .state import ARG
3+
from .state import ARG, CFG
44
from .printer import cons
55
from . import common
66
from .run.input import MFCInputFile
@@ -20,25 +20,24 @@ def compute(self) -> typing.List[str]:
2020

2121
return r
2222

23-
name: str
24-
flags: typing.List[str]
25-
isDependency: bool
26-
isDefault: bool
27-
isRequired: bool
28-
requires: Dependencies
23+
name: str # Name of the target
24+
flags: typing.List[str] # Extra flags to pass to CMake
25+
isDependency: bool # Is it a dependency of an MFC target?
26+
isDefault: bool # Should it be built by default? (unspecified -t | --targets)
27+
isRequired: bool # Should it always be built? (no matter what -t | --targets is)
28+
requires: Dependencies # Build dependencies of the target
2929

3030

31-
TARGETS: typing.List[MFCTarget] = [
32-
MFCTarget('fftw', ['-DMFC_FFTW=ON'], True, False, False, MFCTarget.Dependencies([], [], [])),
33-
MFCTarget('hdf5', ['-DMFC_HDF5=ON'], True, False, False, MFCTarget.Dependencies([], [], [])),
34-
MFCTarget('silo', ['-DMFC_SILO=ON'], True, False, False, MFCTarget.Dependencies(["hdf5"], [], [])),
35-
MFCTarget('pre_process', ['-DMFC_PRE_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([], [], [])),
36-
MFCTarget('simulation', ['-DMFC_SIMULATION=ON'], False, True, False, MFCTarget.Dependencies([], ["fftw"], [])),
37-
MFCTarget('post_process', ['-DMFC_POST_PROCESS=ON'], False, True, False, MFCTarget.Dependencies(['fftw', 'silo'], [], [])),
38-
MFCTarget('syscheck', ['-DMFC_SYSCHECK=ON'], False, False, True, MFCTarget.Dependencies([], [], [])),
39-
MFCTarget('documentation', ['-DMFC_DOCUMENTATION=ON'], False, False, False, MFCTarget.Dependencies([], [], []))
40-
]
31+
FFTW = MFCTarget('fftw', ['-DMFC_FFTW=ON'], True, False, False, MFCTarget.Dependencies([], [], []))
32+
HDF5 = MFCTarget('hdf5', ['-DMFC_HDF5=ON'], True, False, False, MFCTarget.Dependencies([], [], []))
33+
SILO = MFCTarget('silo', ['-DMFC_SILO=ON'], True, False, False, MFCTarget.Dependencies(["hdf5"], [], []))
34+
PRE_PROCESS = MFCTarget('pre_process', ['-DMFC_PRE_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([], [], []))
35+
SIMULATION = MFCTarget('simulation', ['-DMFC_SIMULATION=ON'], False, True, False, MFCTarget.Dependencies([], ["fftw"], []))
36+
POST_PROCESS = MFCTarget('post_process', ['-DMFC_POST_PROCESS=ON'], False, True, False, MFCTarget.Dependencies(['fftw', 'silo'], [], []))
37+
SYSCHECK = MFCTarget('syscheck', ['-DMFC_SYSCHECK=ON'], False, False, True, MFCTarget.Dependencies([], [], []))
38+
DOCUMENTATION = MFCTarget('documentation', ['-DMFC_DOCUMENTATION=ON'], False, False, False, MFCTarget.Dependencies([], [], []))
4139

40+
TARGETS: typing.List[MFCTarget] = [ FFTW, HDF5, SILO, PRE_PROCESS, SIMULATION, POST_PROCESS, SYSCHECK, DOCUMENTATION ]
4241

4342
def get_mfc_target_names() -> typing.List[str]:
4443
return [ target.name for target in TARGETS if target.isDefault ]
@@ -66,23 +65,53 @@ def get_target(name: str) -> MFCTarget:
6665

6766
# Get path to directory that will store the build files
6867
def get_build_dirpath(target: MFCTarget) -> str:
69-
return os.sep.join([os.getcwd(), "build", target.name])
68+
return os.sep.join([
69+
os.getcwd(),
70+
"build",
71+
[CFG().make_slug(), 'dependencies'][int(target.isDependency)],
72+
target.name
73+
])
7074

7175

7276
# Get the directory that contains the target's CMakeLists.txt
7377
def get_cmake_dirpath(target: MFCTarget) -> str:
74-
subdir = ["", os.sep.join(["toolchain", "dependencies"])][int(target.isDependency)]
75-
76-
return os.sep.join([os.getcwd(), subdir])
77-
78+
# The CMakeLists.txt file is located:
79+
# * Regular: <root>/CMakelists.txt
80+
# * Dependency: <root>/toolchain/dependencies/CMakelists.txt
81+
return os.sep.join([
82+
os.getcwd(),
83+
os.sep.join(["toolchain", "dependencies"]) if target.isDependency else "",
84+
])
85+
86+
87+
def get_install_dirpath(target: MFCTarget) -> str:
88+
# The install directory is located:
89+
# Regular: <root>/build/install/<configuration_slug>
90+
# Dependency: <root>/build/install/dependencies (shared)
91+
return os.sep.join([
92+
os.getcwd(),
93+
"build",
94+
"install",
95+
'dependencies' if target.isDependency else CFG().make_slug()
96+
])
97+
98+
99+
def get_dependency_install_dirpath() -> str:
100+
# Since dependencies share the same install directory, we can just return
101+
# the install directory of the first dependency we find.
102+
for target in TARGETS:
103+
if target.isDependency:
104+
return get_install_dirpath(target)
78105

79-
def get_install_dirpath() -> str:
80-
return os.sep.join([os.getcwd(), "build", "install"])
106+
raise common.MFCException("No dependency target found.")
81107

82108

83109
def is_target_configured(target: MFCTarget) -> bool:
84-
cmake_cachepath = os.sep.join([get_build_dirpath(target), "CMakeCache.txt"])
85-
return os.path.isfile(cmake_cachepath)
110+
# We assume that if the CMakeCache.txt file exists, then the target is
111+
# configured. (this isn't perfect, but it's good enough for now)
112+
return os.path.isfile(
113+
os.sep.join([get_build_dirpath(target), "CMakeCache.txt"])
114+
)
86115

87116

88117
def clean_target(name: str):
@@ -146,7 +175,9 @@ def build_target(name: str, history: typing.List[str] = None):
146175

147176
build_dirpath = get_build_dirpath(target)
148177
cmake_dirpath = get_cmake_dirpath(target)
149-
install_dirpath = get_install_dirpath()
178+
install_dirpath = get_install_dirpath(target)
179+
180+
install_prefixes = ';'.join([install_dirpath, get_dependency_install_dirpath()])
150181

151182
flags: list = target.flags.copy() + [
152183
# Disable CMake warnings intended for developers (us).
@@ -163,10 +194,10 @@ def build_target(name: str, history: typing.List[str] = None):
163194
# second heighest level of priority, still letting users manually
164195
# specify <PackageName>_ROOT, which has precedence over CMAKE_PREFIX_PATH.
165196
# See: https://cmake.org/cmake/help/latest/command/find_package.html.
166-
f"-DCMAKE_PREFIX_PATH={install_dirpath}",
197+
f"-DCMAKE_PREFIX_PATH={install_prefixes}",
167198
# First directory that FIND_LIBRARY searches.
168199
# See: https://cmake.org/cmake/help/latest/command/find_library.html.
169-
f"-DCMAKE_FIND_ROOT_PATH={install_dirpath}",
200+
f"-DCMAKE_FIND_ROOT_PATH={install_prefixes}",
170201
# Location prefix to install bin/, lib/, include/, etc.
171202
# See: https://cmake.org/cmake/help/latest/command/install.html.
172203
f"-DCMAKE_INSTALL_PREFIX={install_dirpath}",

toolchain/mfc/lock.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os, dataclasses
22

3-
from . import build, state, common
3+
from . import state, common
44
from .state import MFCConfig
55
from .printer import cons
66

@@ -68,8 +68,3 @@ def switch(to: MFCConfig):
6868
data.config = to
6969
state.gCFG = to
7070
write()
71-
72-
for target_name in build.get_mfc_target_names() + build.get_required_target_names():
73-
dirpath = build.get_build_dirpath(build.get_target(target_name))
74-
cons.print(f"[bold red]Removing {os.path.relpath(dirpath)}[/bold red]")
75-
common.delete_directory(dirpath)

toolchain/mfc/packer/tol.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ def raise_err(msg: str):
5858
"""
5959

6060
if math.isnan(gVal):
61-
raise_err("is NaN in the golden file")
61+
return raise_err("is NaN in the golden file")
6262

6363
if math.isnan(cVal):
64-
raise_err("is NaN in the pack file")
64+
return raise_err("is NaN in the pack file")
6565

6666
if not is_close(error, tol):
67-
raise_err("is not within tolerance")
67+
return raise_err("is not within tolerance")
6868

6969
# Return the average relative error
7070
return avg_err.get(), None

toolchain/mfc/run/engines.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,13 @@ def run(self, names: typing.List[str]) -> None:
4444
raise common.MFCException(f"MFCEngine::run: not implemented for {self.name}.")
4545

4646
def get_binpath(self, target: str) -> str:
47-
return os.sep.join([build.get_install_dirpath(), "bin", target])
47+
# <root>/install/<slug>/bin/<target>
48+
prefix = build.get_install_dirpath(build.get_target(target))
49+
return os.sep.join([prefix, "bin", target])
4850

4951

50-
def _interactive_working_worker(cmd, q):
52+
def _interactive_working_worker(cmd: typing.List[str], q: multiprocessing.Queue):
53+
""" Runs a command and puts the result in a queue. """
5154
cmd = [ str(_) for _ in cmd ]
5255
cons.print(f"$ {' '.join(cmd)}")
5356
result = subprocess.run(
@@ -99,39 +102,44 @@ def run(self, names: typing.List[str]) -> None:
99102
p = multiprocessing.Process(
100103
target=_interactive_working_worker,
101104
args=(
102-
[self.mpibin.bin] + self.mpibin.gen_params() + [os.sep.join([build.get_install_dirpath(), "bin", "syscheck"])],
105+
[self.mpibin.bin] + self.mpibin.gen_params() + [os.sep.join([build.get_install_dirpath(build.SYSCHECK), "bin", "syscheck"])],
103106
q,
104107
))
105108

106109
p.start()
107110
p.join(work_timeout)
108111

109-
try:
110-
result = q.get(block=False)
111-
self.bKnowWorks = result.returncode == 0
112-
except queue.Empty as e:
113-
self.bKnowWorks = False
114-
115-
if p.is_alive() or not self.bKnowWorks:
116-
if p.is_alive():
117-
raise common.MFCException("""\
112+
if p.is_alive():
113+
raise common.MFCException("""\
118114
The [bold magenta]Interactive Engine[/bold magenta] appears to hang.
119115
This may indicate that the wrong MPI binary is being used to launch parallel jobs. You can specify the correct one for your system
120116
using the <-b,--binary> option. For example:
121-
* ./mfc.sh run <myfile.py> -b mpirun
122-
* ./mfc.sh run <myfile.py> -b srun
117+
* ./mfc.sh run <myfile.py> -b mpirun
118+
* ./mfc.sh run <myfile.py> -b srun
123119
""")
124-
else:
125-
raise common.MFCException(f"""\
120+
121+
result = q.get(block=False)
122+
self.bKnowWorks = result.returncode == 0
123+
124+
if not self.bKnowWorks:
125+
error_txt = f"""\
126126
MFC's [bold magenta]syscheck[/bold magenta] (system check) failed to run successfully.
127127
Please review the output bellow and ensure that your system is configured correctly:
128128
129+
"""
130+
131+
if result is not None:
132+
error_txt += f"""\
129133
STDOUT:
130134
{result.stdout}
131135
132136
STDERR:
133137
{result.stderr}
134-
""")
138+
"""
139+
else:
140+
error_txt += f"Evaluation timed out after {work_timeout}s."
141+
142+
raise common.MFCException(error_txt)
135143

136144
cons.print()
137145
cons.unindent()

toolchain/mfc/state.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,40 @@ class MFCConfig:
88
debug: bool = False
99

1010
def from_dict(d: dict):
11+
""" Create a MFCConfig object from a dictionary with the same keys
12+
as the fields of MFCConfig """
1113
r = MFCConfig()
1214

13-
for key in d:
14-
setattr(r, key, d[key])
15+
for field in dataclasses.fields(MFCConfig):
16+
setattr(r, field.name, d[field.name])
1517

1618
return r
1719

20+
def items(self) -> typing.List[typing.Tuple[str, bool]]:
21+
return dataclasses.asdict(self).items()
22+
23+
def make_options(self) -> typing.List[str]:
24+
""" Returns a list of options that could be passed to mfc.sh again.
25+
Example: --no-debug --mpi --no-gpu """
26+
return [ f"--{'no-' if not v else ''}{k}" for k, v in self.items() ]
27+
28+
def make_slug(self) -> str:
29+
""" Sort the items by key, then join them with underscores. This uniquely
30+
identifies the configuration. Example: no-debug_no-gpu_mpi """
31+
return '_'.join([ f"{'no-' if not v else ''}{k}" for k, v in sorted(self.items(), key=lambda x: x[0]) ])
32+
1833
def __eq__(self, other) -> bool:
34+
""" Check if two MFCConfig objects are equal, field by field. """
1935
for field in dataclasses.fields(self):
2036
if getattr(self, field.name) != getattr(other, field.name):
2137
return False
2238

2339
return True
2440

2541
def __str__(self) -> str:
26-
m = { False: "No", True: "Yes" }
27-
r = ' & '.join([ f"{field.name}={m[getattr(self, field.name)]}" for field in dataclasses.fields(self) ])
28-
29-
return r
42+
""" Returns a string like "mpi=No & gpu=No & debug=No" """
43+
44+
return ' & '.join([ f"{k}={'Yes' if v else 'No'}" for k, v in self.items() ])
3045

3146

3247
gCFG: MFCConfig = MFCConfig()

0 commit comments

Comments
 (0)