Skip to content

Commit 9c60ebf

Browse files
authored
MFCInputFile refactor & input file-defined tests (#410)
1 parent bbc7211 commit 9c60ebf

File tree

21 files changed

+463
-366
lines changed

21 files changed

+463
-366
lines changed

CMakeLists.txt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,22 +263,30 @@ macro(HANDLE_SOURCES target useCommon)
263263

264264
string(TOUPPER ${target} ${target}_UPPER)
265265

266-
# Gather src/[<target>,common]/*.f90
267-
file(GLOB ${target}_F90s CONFIGURE_DEPENDS "${${target}_DIR}/*.f90")
266+
# Gather:
267+
# * src/[<target>,(common)]/*.f90
268+
# * (if any) <build>/modules/<target>/*.f90
269+
file(GLOB ${target}_F90s CONFIGURE_DEPENDS "${${target}_DIR}/*.f90"
270+
"${CMAKE_BINARY_DIR}/modules/${target}/*.f90")
268271
set(${target}_SRCs ${${target}_F90s})
269272
if (${useCommon})
270273
file(GLOB common_F90s CONFIGURE_DEPENDS "${common_DIR}/*.f90")
271274
list(APPEND ${target}_SRCs ${common_F90s})
272275
endif()
273276

274-
# src/[<target>,common]/*.fpp -> src/<target>/fypp/*.f90
275-
file(GLOB ${target}_FPPs CONFIGURE_DEPENDS "${${target}_DIR}/*.fpp")
277+
# Gather:
278+
# * src/[<target>,(common)]/*.fpp]
279+
# * (if any) <build>/modules/<target>/*.fpp
280+
file(GLOB ${target}_FPPs CONFIGURE_DEPENDS "${${target}_DIR}/*.fpp"
281+
"${CMAKE_BINARY_DIR}/modules/${target}/*.fpp")
276282
if (${useCommon})
277283
file(GLOB common_FPPs CONFIGURE_DEPENDS "${common_DIR}/*.fpp")
278284
list(APPEND ${target}_FPPs ${common_FPPs})
279285
endif()
280286

281-
# Locate src/[<target>,common]/include/*.fpp
287+
# Gather:
288+
# * src/[<target>,common]/include/*.fpp
289+
# * (if any) <build>/include/<target>/*.fpp
282290
file(GLOB ${target}_incs CONFIGURE_DEPENDS "${${target}_DIR}/include/*.fpp"
283291
"${CMAKE_BINARY_DIR}/include/${target}/*.fpp")
284292

@@ -287,6 +295,7 @@ macro(HANDLE_SOURCES target useCommon)
287295
list(APPEND ${target}_incs ${common_incs})
288296
endif()
289297

298+
# /path/to/*.fpp (used by <target>) -> <build>/fypp/<target>/*.f90
290299
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/fypp/${target}")
291300
foreach(fpp ${${target}_FPPs})
292301
cmake_path(GET fpp FILENAME fpp_filename)

docs/documentation/testing.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ It is recommended that a range be specified when generating golden files for new
3030
**Note:** If you output new variables and want to update the golden files to include these without modifying the original data, use the `--add-new-variables` option instead.
3131

3232
Adding a new test case can be done by modifying [cases.py](https://github.com/MFlowCode/MFC/tree/master/toolchain/mfc/test/cases.py).
33-
The function `generate_cases` is responsible for generating the list of test cases.
33+
The function `list_cases` is responsible for generating the list of test cases.
3434
Loops and conditionals are used to vary parameters, whose defaults can be found in the `BASE_CFG` case object within [case.py](https://github.com/MFlowCode/MFC/tree/master/toolchain/mfc/test/case.py).
3535
The function operates on two variables:
3636

@@ -54,7 +54,7 @@ where:
5454
- `params` is the fully resolved case dictionary, as would appear in a Python case input file.
5555
- `ppn` is the number of processes per node to use when running the case.
5656

57-
To illustrate, consider the following excerpt from `generate_cases`:
57+
To illustrate, consider the following excerpt from `list_cases`:
5858

5959
```python
6060
for weno_order in [3, 5]:
@@ -67,22 +67,22 @@ for weno_order in [3, 5]:
6767
})
6868

6969
if not (mp_weno == 'T' and weno_order != 5):
70-
cases.append(create_case(stack, '', {}))
70+
cases.append(define_case_d(stack, '', {}))
7171

7272
stack.pop()
7373

7474
stack.pop()
7575
```
7676

77-
When pushing to the stack, or creating a new case with the `create_case` function, you must specify:
77+
When pushing to the stack, or creating a new case with the `define_case_d` function, you must specify:
7878
- `stack`: The current stack.
7979
- `trace`: A human-readable string describing what you are currently varying.
8080
- `variations`: A Python dictionary with case parameter variations.
8181
- (Optional) `ppn`: The number of processes per node to use (default is 1).
8282

8383
If a trace is empty (that is, the empty string `""`), it will not appear in the final trace, but any case parameter variations associated with it will still be applied.
8484

85-
Finally, the case is appended to the `cases` list, which will be returned by the `generate_cases` function.
85+
Finally, the case is appended to the `cases` list, which will be returned by the `list_cases` function.
8686

8787
### Testing Post Process
8888

src/simulation/m_derived_variables.f90

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ subroutine s_derive_acceleration_component(i, q_prim_vf0, q_prim_vf1, &
202202

203203
do r = -fd_number, fd_number
204204
if (n == 0) then ! 1D simulation
205-
print *, q_sf(j, k, l), q_prim_vf0(mom_idx%beg)%sf(j, k, l), fd_coeff_x(r, j), q_prim_vf0(mom_idx%beg)%sf(r + j, k, l)
206205
q_sf(j, k, l) = q_sf(j, k, l) &
207206
+ q_prim_vf0(mom_idx%beg)%sf(j, k, l)*fd_coeff_x(r, j)* &
208207
q_prim_vf0(mom_idx%beg)%sf(r + j, k, l)

toolchain/mfc/args.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .run.run import get_baked_templates
44
from .build import TARGETS, DEFAULT_TARGETS, DEPENDENCY_TARGETS
55
from .common import MFCException, format_list_to_string
6-
from .test.cases import generate_cases
6+
from .test.cases import list_cases
77

88
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
99
def parse(config):
@@ -77,7 +77,7 @@ def add_common_arguments(p, mask = None):
7777
add_common_arguments(clean, "jg")
7878

7979
# === TEST ===
80-
test_cases = generate_cases()
80+
test_cases = list_cases()
8181

8282
add_common_arguments(test, "t")
8383
test.add_argument("-l", "--list", action="store_true", help="List all available tests.")
@@ -124,6 +124,7 @@ def add_common_arguments(p, mask = None):
124124
run.add_argument("-f", "--flags", metavar="FLAGS", dest="--", nargs=argparse.REMAINDER, type=str, default=[], help="Arguments to forward to the MPI invocation.")
125125
run.add_argument("-c", "--computer", metavar="COMPUTER", type=str, default="default", help=f"(Batch) Path to a custom submission file template or one of {format_list_to_string(list(get_baked_templates().keys()))}.")
126126
run.add_argument("-o", "--output-summary", metavar="OUTPUT", type=str, default=None, help="Output file (YAML) for summary.")
127+
run.add_argument("--clean", action="store_true", default=False, help="Clean the case before running.")
127128

128129
# === BENCH ===
129130
add_common_arguments(bench)

toolchain/mfc/build.py

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,20 @@ def compute(self) -> typing.Set:
3131
def __hash__(self) -> int:
3232
return hash(self.name)
3333

34-
def get_slug(self) -> str:
34+
def get_slug(self, case: input.MFCInputFile) -> str:
3535
if self.isDependency:
3636
return self.name
3737

3838
m = hashlib.sha256()
3939
m.update(self.name.encode())
4040
m.update(CFG().make_slug().encode())
41-
m.update(input.load({}).get_fpp(self, False).encode())
41+
m.update(case.get_fpp(self, False).encode())
4242

4343
return m.hexdigest()[:10]
4444

4545
# Get path to directory that will store the build files
46-
def get_staging_dirpath(self) -> str:
47-
return os.sep.join([os.getcwd(), "build", "staging", self.get_slug() ])
46+
def get_staging_dirpath(self, case: input.MFCInputFile) -> str:
47+
return os.sep.join([os.getcwd(), "build", "staging", self.get_slug(case) ])
4848

4949
# Get the directory that contains the target's CMakeLists.txt
5050
def get_cmake_dirpath(self) -> str:
@@ -56,41 +56,39 @@ def get_cmake_dirpath(self) -> str:
5656
os.sep.join(["toolchain", "dependencies"]) if self.isDependency else "",
5757
])
5858

59-
def get_install_dirpath(self) -> str:
59+
def get_install_dirpath(self, case: input.MFCInputFile) -> str:
6060
# The install directory is located:
6161
# Regular: <root>/build/install/<slug>
6262
# Dependency: <root>/build/install/dependencies (shared)
6363
return os.sep.join([
6464
os.getcwd(),
6565
"build",
6666
"install",
67-
'dependencies' if self.isDependency else self.get_slug(),
67+
'dependencies' if self.isDependency else self.get_slug(case),
6868
])
6969

70-
def get_install_binpath(self) -> str:
70+
def get_install_binpath(self, case: input.MFCInputFile) -> str:
7171
# <root>/install/<slug>/bin/<target>
72-
return os.sep.join([self.get_install_dirpath(), "bin", self.name])
72+
return os.sep.join([self.get_install_dirpath(case), "bin", self.name])
7373

74-
def is_configured(self) -> bool:
74+
def is_configured(self, case: input.MFCInputFile) -> bool:
7575
# We assume that if the CMakeCache.txt file exists, then the target is
7676
# configured. (this isn't perfect, but it's good enough for now)
7777
return os.path.isfile(
78-
os.sep.join([self.get_staging_dirpath(), "CMakeCache.txt"])
78+
os.sep.join([self.get_staging_dirpath(case), "CMakeCache.txt"])
7979
)
8080

81-
def get_configuration_txt(self) -> typing.Optional[dict]:
82-
if not self.is_configured():
81+
def get_configuration_txt(self, case: input.MFCInputFile) -> typing.Optional[dict]:
82+
if not self.is_configured(case):
8383
return None
8484

85-
configpath = os.path.join(self.get_staging_dirpath(), "configuration.txt")
85+
configpath = os.path.join(self.get_staging_dirpath(case), "configuration.txt")
8686
if not os.path.exists(configpath):
8787
return None
8888

8989
with open(configpath) as f:
9090
return f.read()
9191

92-
return None
93-
9492
def is_buildable(self) -> bool:
9593
if ARG("no_build"):
9694
return False
@@ -100,12 +98,12 @@ def is_buildable(self) -> bool:
10098

10199
return True
102100

103-
def configure(self):
104-
build_dirpath = self.get_staging_dirpath()
101+
def configure(self, case: input.MFCInputFile):
102+
build_dirpath = self.get_staging_dirpath(case)
105103
cmake_dirpath = self.get_cmake_dirpath()
106-
install_dirpath = self.get_install_dirpath()
104+
install_dirpath = self.get_install_dirpath(case)
107105

108-
install_prefixes = ';'.join([install_dirpath, get_dependency_install_dirpath()])
106+
install_prefixes = ';'.join([install_dirpath, get_dependency_install_dirpath(case)])
109107

110108
mod_dirs = ';'.join(['build/install/dependencies/include/hipfort/amdgcn'])
111109

@@ -152,17 +150,17 @@ def configure(self):
152150
delete_directory(build_dirpath)
153151
create_directory(build_dirpath)
154152

155-
input.load({}).generate_fpp(self)
153+
case.generate_fpp(self)
156154

157155
if system(command).returncode != 0:
158156
raise MFCException(f"Failed to configure the [bold magenta]{self.name}[/bold magenta] target.")
159157

160158
cons.print(no_indent=True)
161159

162-
def build(self):
163-
input.load({}).generate_fpp(self)
160+
def build(self, case: input.MFCInputFile):
161+
case.generate_fpp(self)
164162

165-
command = ["cmake", "--build", self.get_staging_dirpath(),
163+
command = ["cmake", "--build", self.get_staging_dirpath(case),
166164
"--target", self.name,
167165
"--parallel", ARG("jobs"),
168166
"--config", 'Debug' if ARG('debug') else 'Release']
@@ -174,16 +172,16 @@ def build(self):
174172

175173
cons.print(no_indent=True)
176174

177-
def install(self):
178-
command = ["cmake", "--install", self.get_staging_dirpath()]
175+
def install(self, case: input.MFCInputFile):
176+
command = ["cmake", "--install", self.get_staging_dirpath(case)]
179177

180178
if system(command).returncode != 0:
181179
raise MFCException(f"Failed to install the [bold magenta]{self.name}[/bold magenta] target.")
182180

183181
cons.print(no_indent=True)
184182

185-
def clean(self):
186-
build_dirpath = self.get_staging_dirpath()
183+
def clean(self, case: input.MFCInputFile):
184+
build_dirpath = self.get_staging_dirpath(case)
187185

188186
if not os.path.isdir(build_dirpath):
189187
return
@@ -229,17 +227,17 @@ def get_targets(targets: typing.List[typing.Union[str, MFCTarget]]) -> typing.Li
229227
return [ get_target(t) for t in targets ]
230228

231229

232-
def get_dependency_install_dirpath() -> str:
230+
def get_dependency_install_dirpath(case: input.MFCInputFile) -> str:
233231
# Since dependencies share the same install directory, we can just return
234232
# the install directory of the first dependency we find.
235233
for target in TARGETS:
236234
if target.isDependency:
237-
return target.get_install_dirpath()
235+
return target.get_install_dirpath(case)
238236

239237
raise MFCException("No dependency target found.")
240238

241239

242-
def __build_target(target: typing.Union[MFCTarget, str], history: typing.Set[str] = None):
240+
def __build_target(target: typing.Union[MFCTarget, str], case: input.MFCInputFile, history: typing.Set[str] = None):
243241
if history is None:
244242
history = set()
245243

@@ -254,20 +252,20 @@ def __build_target(target: typing.Union[MFCTarget, str], history: typing.Set[str
254252
# If we have already built and installed this target,
255253
# do not do so again. This can be inferred by whether
256254
# the target requesting this dependency is already configured.
257-
if dep.isDependency and target.is_configured():
255+
if dep.isDependency and target.is_configured(case):
258256
continue
259257

260-
build([dep], history)
258+
build([dep], case, history)
261259

262-
if not target.is_configured():
263-
target.configure()
260+
if not target.is_configured(case):
261+
target.configure(case)
264262

265-
target.build()
266-
target.install()
263+
target.build(case)
264+
target.install(case)
267265

268266

269-
def get_configured_targets() -> typing.List[MFCTarget]:
270-
return [ target for target in TARGETS if target.is_configured() ]
267+
def get_configured_targets(case: input.MFCInputFile) -> typing.List[MFCTarget]:
268+
return [ target for target in TARGETS if target.is_configured(case) ]
271269

272270

273271
def __generate_header(step_name: str, targets: typing.List):
@@ -281,27 +279,29 @@ def __generate_header(step_name: str, targets: typing.List):
281279
return f"[bold]{step_name} | {target_list} | {caseopt_info}[/bold]"
282280

283281

284-
def build(targets = None, history: typing.Set[str] = None):
282+
def build(targets = None, case: input.MFCInputFile = None, history: typing.Set[str] = None):
285283
if history is None:
286284
history = set()
287285
if targets is None:
288286
targets = ARG("targets")
289287

290288
targets = get_targets(list(REQUIRED_TARGETS) + targets)
289+
case = case or input.load(ARG("input"), ARG("arguments"), {})
291290

292291
if len(history) == 0:
293292
cons.print(__generate_header("Build", targets))
294293
cons.print(no_indent=True)
295294

296295
for target in targets:
297-
__build_target(target, history)
296+
__build_target(target, case, history)
298297

299298
if len(history) == 0:
300299
cons.print(no_indent=True)
301300

302301

303-
def clean(targets = None):
302+
def clean(targets = None, case: input.MFCInputFile = None):
304303
targets = get_targets(list(REQUIRED_TARGETS) + (targets or ARG("targets")))
304+
case = case or input.load(ARG("input"), ARG("arguments"), {})
305305

306306
cons.print(__generate_header("Clean", targets))
307307
cons.print(no_indent=True)

0 commit comments

Comments
 (0)