Skip to content

Commit 45ff91a

Browse files
committed
Temporary commit (wip)
1 parent b5fe7fd commit 45ff91a

13 files changed

Lines changed: 438 additions & 120 deletions

.conda/benchcab-dev.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ dependencies:
1010
- pytest-cov
1111
- pyyaml
1212
- flatdict
13+
- gitpython

benchcab/benchcab.py

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,14 @@
1111
from benchcab import internal
1212
from benchcab.internal import get_met_forcing_file_names
1313
from benchcab.config import read_config
14-
from benchcab.workdir import setup_fluxsite_directory_tree, setup_src_dir
15-
from benchcab.repository import CableRepository
16-
from benchcab.fluxsite import (
17-
get_fluxsite_tasks,
18-
get_fluxsite_comparisons,
19-
run_tasks,
20-
run_tasks_in_parallel,
21-
Task,
14+
from benchcab.workdir import (
15+
setup_src_dir,
16+
setup_fluxsite_directory_tree,
17+
setup_spatial_directory_tree,
2218
)
19+
from benchcab.repository import CableRepository
20+
from benchcab import fluxsite
21+
from benchcab import spatial
2322
from benchcab.comparison import run_comparisons, run_comparisons_in_parallel
2423
from benchcab.cli import generate_parser
2524
from benchcab.environment_modules import EnvironmentModules, EnvironmentModulesInterface
@@ -48,7 +47,15 @@ def __init__(
4847
CableRepository(**config, repo_id=id)
4948
for id, config in enumerate(self.config["realisations"])
5049
]
51-
self.tasks: list[Task] = [] # initialise fluxsite tasks lazily
50+
self.science_configurations = self.config.get(
51+
"science_configurations", internal.DEFAULT_SCIENCE_CONFIGURATIONS
52+
)
53+
self.fluxsite_tasks: list[
54+
fluxsite.FluxsiteTask
55+
] = [] # initialise fluxsite tasks lazily
56+
self.spatial_tasks = spatial.get_spatial_tasks(
57+
repos=self.repos, science_configurations=self.science_configurations
58+
)
5259
self.benchcab_exe_path = benchcab_exe_path
5360

5461
if validate_env:
@@ -103,18 +110,16 @@ def _validate_environment(self, project: str, modules: list):
103110
)
104111
sys.exit(1)
105112

106-
def _initialise_tasks(self) -> list[Task]:
113+
def _initialise_tasks(self) -> list[fluxsite.FluxsiteTask]:
107114
"""A helper method that initialises and returns the `tasks` attribute."""
108-
self.tasks = get_fluxsite_tasks(
115+
self.fluxsite_tasks = fluxsite.get_fluxsite_tasks(
109116
repos=self.repos,
110-
science_configurations=self.config.get(
111-
"science_configurations", internal.DEFAULT_SCIENCE_CONFIGURATIONS
112-
),
117+
science_configurations=self.science_configurations,
113118
fluxsite_forcing_file_names=get_met_forcing_file_names(
114119
self.config["experiment"]
115120
),
116121
)
117-
return self.tasks
122+
return self.fluxsite_tasks
118123

119124
def fluxsite_submit_job(self) -> None:
120125
"""Submits the PBS job script step in the fluxsite test workflow."""
@@ -189,7 +194,7 @@ def checkout(self):
189194

190195
print("")
191196

192-
def build(self):
197+
def build(self, mpi=False):
193198
"""Endpoint for `benchcab build`."""
194199
for repo in self.repos:
195200
if repo.build_script:
@@ -201,30 +206,38 @@ def build(self):
201206
modules=self.config["modules"], verbose=self.args.verbose
202207
)
203208
else:
204-
build_mode = "with MPI" if internal.MPI else "serially"
209+
build_mode = "with MPI" if mpi else "serially"
205210
print(f"Compiling CABLE {build_mode} for realisation {repo.name}...")
206-
repo.pre_build(verbose=self.args.verbose)
211+
repo.pre_build(mpi=mpi, verbose=self.args.verbose)
207212
repo.run_build(
208-
modules=self.config["modules"], verbose=self.args.verbose
213+
modules=self.config["modules"], mpi=mpi, verbose=self.args.verbose
209214
)
210-
repo.post_build(verbose=self.args.verbose)
215+
repo.post_build(mpi=mpi, verbose=self.args.verbose)
211216
print(f"Successfully compiled CABLE for realisation {repo.name}")
212217
print("")
213218

214219
def fluxsite_setup_work_directory(self):
215220
"""Endpoint for `benchcab fluxsite-setup-work-dir`."""
216-
tasks = self.tasks if self.tasks else self._initialise_tasks()
221+
222+
if not self.fluxsite_tasks:
223+
self._initialise_tasks()
224+
217225
print("Setting up run directory tree for fluxsite tests...")
218-
setup_fluxsite_directory_tree(fluxsite_tasks=tasks, verbose=self.args.verbose)
226+
setup_fluxsite_directory_tree(
227+
fluxsite_tasks=self.fluxsite_tasks, verbose=self.args.verbose
228+
)
219229
print("Setting up tasks...")
220-
for task in tasks:
230+
for task in self.fluxsite_tasks:
221231
task.setup_task(verbose=self.args.verbose)
222232
print("Successfully setup fluxsite tasks")
223233
print("")
224234

225235
def fluxsite_run_tasks(self):
226236
"""Endpoint for `benchcab fluxsite-run-tasks`."""
227-
tasks = self.tasks if self.tasks else self._initialise_tasks()
237+
238+
if not self.fluxsite_tasks:
239+
self._initialise_tasks()
240+
228241
print("Running fluxsite tasks...")
229242
try:
230243
multiprocess = self.config["fluxsite"]["multiprocess"]
@@ -234,9 +247,11 @@ def fluxsite_run_tasks(self):
234247
ncpus = self.config.get("pbs", {}).get(
235248
"ncpus", internal.FLUXSITE_DEFAULT_PBS["ncpus"]
236249
)
237-
run_tasks_in_parallel(tasks, n_processes=ncpus, verbose=self.args.verbose)
250+
fluxsite.run_tasks_in_parallel(
251+
self.fluxsite_tasks, n_processes=ncpus, verbose=self.args.verbose
252+
)
238253
else:
239-
run_tasks(tasks, verbose=self.args.verbose)
254+
fluxsite.run_tasks(self.fluxsite_tasks, verbose=self.args.verbose)
240255
print("Successfully ran fluxsite tasks")
241256
print("")
242257

@@ -248,8 +263,10 @@ def fluxsite_bitwise_cmp(self):
248263
"nccmp/1.8.5.0"
249264
) # use `nccmp -df` for bitwise comparisons
250265

251-
tasks = self.tasks if self.tasks else self._initialise_tasks()
252-
comparisons = get_fluxsite_comparisons(tasks)
266+
if not self.fluxsite_tasks:
267+
self._initialise_tasks()
268+
269+
comparisons = fluxsite.get_fluxsite_comparisons(self.fluxsite_tasks)
253270

254271
print("Running comparison tasks...")
255272
try:
@@ -280,13 +297,38 @@ def fluxsite(self):
280297
else:
281298
self.fluxsite_submit_job()
282299

300+
def spatial_setup_work_directory(self):
301+
"""Endpoint for `benchcab spatial-setup-work-dir`."""
302+
print("Setting up run directory tree for spatial tests...")
303+
setup_spatial_directory_tree()
304+
print("Setting up tasks...")
305+
for task in self.spatial_tasks:
306+
task.setup_task(verbose=self.args.verbose)
307+
print("Successfully setup spatial tasks")
308+
print("")
309+
310+
def spatial_run_tasks(self):
311+
"""Endpoint for `benchcab spatial-run-tasks`."""
312+
print("Running spatial tasks...")
313+
spatial.run_tasks(tasks=self.spatial_tasks, verbose=self.args.verbose)
314+
print("")
315+
283316
def spatial(self):
284317
"""Endpoint for `benchcab spatial`."""
318+
self.checkout()
319+
self.build(mpi=True)
320+
self.spatial_setup_work_directory()
321+
self.spatial_run_tasks()
285322

286323
def run(self):
287324
"""Endpoint for `benchcab run`."""
288-
self.fluxsite()
289-
self.spatial()
325+
self.checkout()
326+
self.build()
327+
self.build(mpi=True)
328+
self.fluxsite_setup_work_directory()
329+
self.spatial_setup_work_directory()
330+
self.fluxsite_submit_job()
331+
self.spatial_run_tasks()
290332

291333
def main(self):
292334
"""Main function for `benchcab`."""
@@ -298,7 +340,7 @@ def main(self):
298340
self.checkout()
299341

300342
if self.args.subcommand == "build":
301-
self.build()
343+
self.build(mpi=self.args.mpi)
302344

303345
if self.args.subcommand == "fluxsite":
304346
self.fluxsite()

benchcab/cli.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ def generate_parser() -> argparse.ArgumentParser:
3232
action="store_true",
3333
)
3434

35-
# parent parser that contains arguments common to all run specific subcommands
36-
args_run_subcommand = argparse.ArgumentParser(add_help=False)
37-
args_run_subcommand.add_argument(
35+
# parent parser that contains the argument for --no-submit
36+
args_no_submit = argparse.ArgumentParser(add_help=False)
37+
args_no_submit.add_argument(
3838
"--no-submit",
3939
action="store_true",
4040
help="Force benchcab to execute tasks on the current compute node.",
@@ -74,7 +74,6 @@ def generate_parser() -> argparse.ArgumentParser:
7474
parents=[
7575
args_help,
7676
args_subcommand,
77-
args_run_subcommand,
7877
args_composite_subcommand,
7978
],
8079
help="Run all test suites for CABLE.",
@@ -89,7 +88,7 @@ def generate_parser() -> argparse.ArgumentParser:
8988
parents=[
9089
args_help,
9190
args_subcommand,
92-
args_run_subcommand,
91+
args_no_submit,
9392
args_composite_subcommand,
9493
],
9594
help="Run the fluxsite test suite for CABLE.",
@@ -110,14 +109,19 @@ def generate_parser() -> argparse.ArgumentParser:
110109
)
111110

112111
# subcommand: 'benchcab build'
113-
subparsers.add_parser(
112+
build_parser = subparsers.add_parser(
114113
"build",
115114
parents=[args_help, args_subcommand],
116115
help="Run the build step in the benchmarking workflow.",
117116
description="""Build the CABLE offline executable for each repository specified in the
118117
config file.""",
119118
add_help=False,
120119
)
120+
build_parser.add_argument(
121+
"--mpi",
122+
action="store_true",
123+
help="Enable MPI build.",
124+
)
121125

122126
# subcommand: 'benchcab fluxsite-setup-work-dir'
123127
subparsers.add_parser(
@@ -143,9 +147,9 @@ def generate_parser() -> argparse.ArgumentParser:
143147
"fluxsite-run-tasks",
144148
parents=[args_help, args_subcommand],
145149
help="Run the fluxsite tasks of the main fluxsite command.",
146-
description="""Runs the fluxsite tasks for the fluxsite test suite. Note, this command should
147-
ideally be run inside a PBS job. This command is invoked by the PBS job script generated by
148-
`benchcab run`.""",
150+
description="""Runs the fluxsite tasks for the fluxsite test suite.
151+
Note, this command should ideally be run inside a PBS job. This command
152+
is invoked by the PBS job script generated by `benchcab run`.""",
149153
add_help=False,
150154
)
151155

@@ -165,7 +169,7 @@ def generate_parser() -> argparse.ArgumentParser:
165169
# subcommand: 'benchcab spatial'
166170
subparsers.add_parser(
167171
"spatial",
168-
parents=[args_help, args_subcommand],
172+
parents=[args_help, args_subcommand, args_composite_subcommand],
169173
help="Run the spatial tests only.",
170174
description="""Runs the default spatial test suite for CABLE.""",
171175
add_help=False,

benchcab/fluxsite.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class CableError(Exception):
9797
"""Custom exception class for CABLE errors."""
9898

9999

100-
class Task:
100+
class FluxsiteTask:
101101
"""A class used to represent a single fluxsite task."""
102102

103103
root_dir: Path = internal.CWD
@@ -350,10 +350,10 @@ def get_fluxsite_tasks(
350350
repos: list[CableRepository],
351351
science_configurations: list[dict],
352352
fluxsite_forcing_file_names: list[str],
353-
) -> list[Task]:
353+
) -> list[FluxsiteTask]:
354354
"""Returns a list of fluxsite tasks to run."""
355355
tasks = [
356-
Task(
356+
FluxsiteTask(
357357
repo=repo,
358358
met_forcing_file=file_name,
359359
sci_conf_id=sci_conf_id,
@@ -366,14 +366,16 @@ def get_fluxsite_tasks(
366366
return tasks
367367

368368

369-
def run_tasks(tasks: list[Task], verbose=False):
369+
def run_tasks(tasks: list[FluxsiteTask], verbose=False):
370370
"""Runs tasks in `tasks` serially."""
371371
for task in tasks:
372372
task.run(verbose=verbose)
373373

374374

375375
def run_tasks_in_parallel(
376-
tasks: list[Task], n_processes=internal.FLUXSITE_DEFAULT_PBS["ncpus"], verbose=False
376+
tasks: list[FluxsiteTask],
377+
n_processes=internal.FLUXSITE_DEFAULT_PBS["ncpus"],
378+
verbose=False,
377379
):
378380
"""Runs tasks in `tasks` in parallel across multiple processes."""
379381

@@ -402,7 +404,7 @@ def worker_run(task_queue: multiprocessing.Queue, verbose=False):
402404

403405

404406
def get_fluxsite_comparisons(
405-
tasks: list[Task], root_dir=internal.CWD
407+
tasks: list[FluxsiteTask], root_dir=internal.CWD
406408
) -> list[ComparisonTask]:
407409
"""Returns a list of `ComparisonTask` objects to run comparisons with.
408410

benchcab/internal.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
"walltime": "6:00:00",
1818
"storage": [],
1919
}
20-
MPI = False
2120
FLUXSITE_DEFAULT_MULTIPROCESS = True
2221

2322
# DIRECTORY PATHS/STRUCTURE:
@@ -76,14 +75,36 @@
7675
# Relative path to directory that stores bitwise comparison results
7776
FLUXSITE_BITWISE_CMP_DIR = FLUXSITE_ANALYSIS_DIR / "bitwise-comparisons"
7877

79-
# Path to met files:
78+
# Relative path to root directory for CABLE spatial runs
79+
SPATIAL_RUN_DIR = RUN_DIR / "spatial"
80+
81+
# Relative path to tasks directory (contains payu control directories configured
82+
# for each spatial task)
83+
SPATIAL_TASKS_DIR = SPATIAL_RUN_DIR / "tasks"
84+
85+
# A custom payu laboratory directory for payu runs
86+
PAYU_LABORATORY_DIR = RUN_DIR / "payu-laboratory"
87+
88+
# URL to a payu experiment template for offline spatial runs:
89+
EXPERIMENT_TEMPLATE_SPATIAL = "https://github.com/CABLE-LSM/cable_example.git"
90+
91+
# Path to PLUMBER2 site forcing data directory (doi: 10.25914/5fdb0902607e1):
8092
MET_DIR = Path("/g/data/ks32/CLEX_Data/PLUMBER2/v1-0/Met/")
8193

94+
# Path to CRUJRA (ACCESS resolution) spatial forcing data:
95+
SPATIAL_MET_FORCING_CRUJRA = {
96+
"name": "CRUJRA-ACCESS",
97+
"path": "/g/data/tm70/ccc561/CABLE/CABLE-as-ACCESS_Yingping/cruncep10",
98+
}
99+
82100
# CABLE SVN root url:
83101
CABLE_SVN_ROOT = "https://trac.nci.org.au/svn/cable"
84102

85103
# CABLE executable file name:
86-
CABLE_EXE = "cable-mpi" if MPI else "cable"
104+
CABLE_EXE = "cable"
105+
106+
# CABLE MPI executable file name:
107+
CABLE_MPI_EXE = "cable-mpi"
87108

88109
# CABLE namelist file name:
89110
CABLE_NML = "cable.nml"

0 commit comments

Comments
 (0)