Skip to content

Commit 88056b0

Browse files
committed
Review changes, black/ruff formats. Updated tests. Fixes #300
1 parent 0508cdb commit 88056b0

18 files changed

+285
-178
lines changed

.conda/meta.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ requirements:
2929
- cerberus >=1.3.5
3030
- gitpython
3131
- jinja2
32-
- hpcpy>=0.3.0
32+
- hpcpy>=0.5.0
3333
- meorg_client

docs/user_guide/config_options.md

+21-2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ fluxsite:
6868
walltime: 06:00:00
6969
storage: [scratch/a00, gdata/xy11]
7070
multiprocess: True
71+
meorg_model_output_id: XXXXXXXX
7172
```
7273
7374
### [experiment](#experiment)
@@ -154,7 +155,7 @@ fluxsite:
154155

155156
### [multiprocess](#multiprocess)
156157

157-
: **Default:** True, _optional key_. :octicons-dash-24: Enables or disables multiprocessing for executing embarrassingly parallel tasks.
158+
158159

159160
```yaml
160161
@@ -163,6 +164,14 @@ fluxsites:
163164
164165
```
165166

167+
### [meorg_model_output_id](#meorg_model_output_id)
168+
169+
: **Default:** False, _optional key_. :octicons-dash-24: The unique Model Output ID from modelevaluation.org to which output files will be automatically uploaded for analysis.
170+
171+
A separate upload job will be submitted at the successful completion of benchcab tasks if this key is present, however, the validity is not checked by benchcab at this stage.
172+
173+
Note: It is the user's responsbility to ensure the model output is configured on modelevaluation.org.
174+
166175
## spatial
167176

168177
Contains settings specific to spatial tests.
@@ -493,4 +502,14 @@ codecov:
493502
[f90nml-github]: https://github.com/marshallward/f90nml
494503
[environment-modules]: https://modules.sourceforge.net/
495504
[nci-pbs-directives]: https://opus.nci.org.au/display/Help/PBS+Directives+Explained
496-
[cable-github]: https://github.com/CABLE-LSM/CABLE
505+
[cable-github]: https://github.com/CABLE-LSM/CABLE
506+
507+
## meorg_bin
508+
509+
: **Default:** False, _optional key. :octicons-dash-24: Specifies the absolute system path to the ME.org client executable. In the absence of this key it will be inferred from the same directory as benchcab should `meorg_model_output_id` be set in `fluxsite` above.
510+
511+
``` yaml
512+
513+
meorg_bin: /path/to/meorg
514+
515+
```

src/benchcab/benchcab.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from subprocess import CalledProcessError
1212
from typing import Optional
1313

14+
import benchcab.utils.meorg as bm
1415
from benchcab import fluxsite, internal, spatial
1516
from benchcab.comparison import run_comparisons, run_comparisons_in_parallel
1617
from benchcab.config import read_config
@@ -24,7 +25,6 @@
2425
from benchcab.model import Model
2526
from benchcab.utils import is_verbose, task_summary
2627
from benchcab.utils.fs import mkdir, next_path
27-
import benchcab.utils.meorg as bm
2828
from benchcab.utils.pbs import render_job_script
2929
from benchcab.utils.repo import create_repo
3030
from benchcab.utils.subprocess import SubprocessWrapper, SubprocessWrapperInterface
@@ -245,15 +245,14 @@ def fluxsite_submit_job(self, config_path: str, skip: list[str]) -> None:
245245
logger.info(f"{internal.FLUXSITE_DIRS['TASKS']}/<task_name>/out.txt")
246246
logger.info("The NetCDF output for each task is written to:")
247247
logger.info(f"{internal.FLUXSITE_DIRS['OUTPUT']}/<task_name>_out.nc")
248-
248+
249249
# Upload to meorg by default
250250
bm.do_meorg(
251251
config,
252-
upload_dir=internal.FLUXSITE_DIRS['OUTPUT'],
252+
upload_dir=internal.FLUXSITE_DIRS["OUTPUT"],
253253
benchcab_bin=str(self.benchcab_exe_path),
254-
benchcab_job_id=job_id
254+
benchcab_job_id=job_id,
255255
)
256-
257256

258257
def gen_codecov(self, config_path: str):
259258
"""Endpoint for `benchcab codecov`."""
@@ -360,7 +359,7 @@ def fluxsite_run_tasks(self, config_path: str):
360359
else:
361360
fluxsite.run_tasks(tasks)
362361

363-
n_tasks, n_success, n_failed, all_complete = task_summary(tasks)
362+
_, n_success, n_failed, _ = task_summary(tasks)
364363
logger.info(f"{n_failed} failed, {n_success} passed")
365364

366365
def fluxsite_bitwise_cmp(self, config_path: str):
@@ -386,9 +385,9 @@ def fluxsite_bitwise_cmp(self, config_path: str):
386385
ncpus = config["fluxsite"]["pbs"]["ncpus"]
387386
run_comparisons_in_parallel(comparisons, n_processes=ncpus)
388387
else:
389-
run_comparisons(comparisons)
390-
391-
n_tasks, n_success, n_failed, all_complete = task_summary(comparisons)
388+
run_comparisons(comparisons)
389+
390+
_, n_success, n_failed, _ = task_summary(comparisons)
392391
logger.info(f"{n_failed} failed, {n_success} passed")
393392

394393
def fluxsite(self, config_path: str, no_submit: bool, skip: list[str]):

src/benchcab/config.py

+3
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ def read_optional_key(config: dict):
119119
config["fluxsite"]["pbs"] = internal.FLUXSITE_DEFAULT_PBS | config["fluxsite"].get(
120120
"pbs", {}
121121
)
122+
config["fluxsite"]["meorg_model_output_id"] = config["fluxsite"].get(
123+
"meorg_model_output_id", internal.FLUXSITE_DEFAULT_MEORG_MODEL_OUTPUT_ID
124+
)
122125

123126
config["codecov"] = config.get("codecov", False)
124127

src/benchcab/data/config-schema.yml

+12-2
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,11 @@ fluxsite:
108108
type: "string"
109109
required: false
110110
meorg_model_output_id:
111-
type: "string"
111+
type:
112+
- "boolean"
113+
- "string"
112114
required: false
115+
default: false
113116

114117
spatial:
115118
type: "dict"
@@ -137,4 +140,11 @@ spatial:
137140

138141
codecov:
139142
type: "boolean"
140-
required: false
143+
required: false
144+
145+
meorg_bin:
146+
type:
147+
- "boolean"
148+
- "string"
149+
required: False
150+
default: False

src/benchcab/data/test/config-optional.yml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ project: hh5
33

44
fluxsite:
55
experiment: AU-Tum
6+
meorg_model_output_id: False
67
multiprocess: False
78
pbs:
89
ncpus: 6

src/benchcab/data/test/integration_meorg.sh

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ fluxsite:
4646
storage:
4747
- scratch/$PROJECT
4848
- gdata/$PROJECT
49+
# This ID is currently configured on the me.org server.
4950
meorg_model_output_id: Sss7qupAHEZ8ovbCv
5051
EOL
5152

src/benchcab/internal.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
}
253253

254254
FLUXSITE_DEFAULT_EXPERIMENT = "forty-two-site-test"
255+
FLUXSITE_DEFAULT_MEORG_MODEL_OUTPUT_ID = False
255256

256257
OPTIONAL_COMMANDS = ["fluxsite-bitwise-cmp", "gen_codecov"]
257258

@@ -275,11 +276,12 @@ def get_met_forcing_file_names(experiment: str) -> list[str]:
275276

276277
return file_names
277278

279+
278280
# Configuration for the client upload
279281
MEORG_CLIENT = dict(
280-
num_threads=1, # Parallel uploads over 4 cores
281-
cache_delay=60*5, # 5mins between upload and analysis triggering
282+
num_threads=1, # Parallel uploads over 4 cores
283+
cache_delay=60 * 5, # 5mins between upload and analysis triggering
282284
mem="8G",
283285
walltime="01:00:00",
284-
storage=["gdata/ks32", "gdata/hh5", "gdata/wd9", "gdata/rp23"]
285-
)
286+
storage=["gdata/ks32", "gdata/hh5", "gdata/wd9", "gdata/rp23"],
287+
)

src/benchcab/utils/__init__.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import sys
1212
from importlib import resources
1313
from pathlib import Path
14-
from typing import Union, Iterable
14+
from typing import Iterable, Union
1515

1616
import yaml
1717
from jinja2 import BaseLoader, Environment
@@ -162,9 +162,10 @@ def task_summary(tasks: Iterable) -> tuple:
162162
-------
163163
tuple
164164
num_tasks, num_complete, num_failed, all_complete
165+
165166
"""
166167
num_tasks = len(tasks)
167168
num_complete = len([task for task in tasks if task.is_done()])
168169
num_failed = num_tasks - num_complete
169-
170-
return num_tasks, num_complete, num_failed, num_complete == num_tasks
170+
171+
return num_tasks, num_complete, num_failed, num_complete == num_tasks

src/benchcab/utils/meorg.py

+31-22
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
"""Utility methods for interacting with the ME.org client."""
2-
from benchcab.internal import MEORG_CLIENT
3-
from meorg_client.client import Client as MeorgClient
2+
3+
import os
4+
45
from hpcpy import get_client
6+
from meorg_client.client import Client as MeorgClient
7+
58
import benchcab.utils as bu
6-
import os
7-
from glob import glob
9+
from benchcab.internal import MEORG_CLIENT
10+
811

912
def do_meorg(config: dict, upload_dir: str, benchcab_bin: str, benchcab_job_id: str):
1013
"""Perform the upload of model outputs to modelevaluation.org
@@ -22,29 +25,29 @@ def do_meorg(config: dict, upload_dir: str, benchcab_bin: str, benchcab_job_id:
2225
-------
2326
bool
2427
True if successful, False otherwise
25-
"""
2628
29+
"""
2730
logger = bu.get_logger()
2831

29-
model_output_id = config.get("fluxsite").get("meorg_model_output_id", False)
32+
model_output_id = config["fluxsite"]["meorg_model_output_id"]
3033
num_threads = MEORG_CLIENT["num_threads"]
31-
34+
3235
# Check if a model output id has been assigned
3336
if model_output_id == False:
3437
logger.info("No model_output_id found in fluxsite configuration.")
3538
logger.info("NOT uploading to modelevaluation.org")
3639
return False
37-
40+
3841
# Allow the user to specify an absolute path to the meorg bin in config
3942
meorg_bin = config.get("meorg_bin", False)
40-
43+
4144
# Otherwise infer the path from the benchcab installation
4245
if meorg_bin == False:
4346
logger.debug(f"Inferring meorg bin from {benchcab_bin}")
4447
bin_segments = benchcab_bin.split("/")
4548
bin_segments[-1] = "meorg"
4649
meorg_bin = "/".join(bin_segments)
47-
50+
4851
logger.debug(f"meorg_bin = {meorg_bin}")
4952

5053
# Now, check if that actually exists
@@ -56,41 +59,47 @@ def do_meorg(config: dict, upload_dir: str, benchcab_bin: str, benchcab_job_id:
5659
# Also only run if the client is initialised
5760
if MeorgClient().is_initialised() == False:
5861

59-
logger.warn("A model_output_id has been supplied, but the meorg_client is not initialised.")
60-
logger.warn("To initialise, run `meorg initialise` in the installation environment.")
61-
logger.warn("Once initialised, the outputs from this run can be uploaded with the following command:")
62-
logger.warn(f"meorg file upload {upload_dir}/*.nc -n {num_threads} --attach_to {model_output_id}")
62+
logger.warn(
63+
"A model_output_id has been supplied, but the meorg_client is not initialised."
64+
)
65+
logger.warn(
66+
"To initialise, run `meorg initialise` in the installation environment."
67+
)
68+
logger.warn(
69+
"Once initialised, the outputs from this run can be uploaded with the following command:"
70+
)
71+
logger.warn(
72+
f"meorg file upload {upload_dir}/*.nc -n {num_threads} --attach_to {model_output_id}"
73+
)
6374
logger.warn("Then the analysis can be triggered with:")
6475
logger.warn(f"meorg analysis start {model_output_id}")
6576
return False
6677

6778
# Finally, attempt the upload!
6879
else:
69-
80+
7081
logger.info("Uploading outputs to modelevaluation.org")
7182

7283
# Submit the outputs
7384
client = get_client()
7485
meorg_jobid = client.submit(
75-
7686
bu.get_installed_root() / "data" / "meorg_jobscript.j2",
7787
render=True,
7888
dry_run=False,
7989
depends_on=benchcab_job_id,
80-
8190
# Interpolate into the job script
8291
model_output_id=model_output_id,
8392
data_dir=upload_dir,
8493
cache_delay=MEORG_CLIENT["cache_delay"],
8594
mem=MEORG_CLIENT["mem"],
8695
num_threads=MEORG_CLIENT["num_threads"],
8796
walltime=MEORG_CLIENT["walltime"],
88-
storage=MEORG_CLIENT['storage'],
89-
project=config['project'],
90-
modules=config['modules'],
97+
storage=MEORG_CLIENT["storage"],
98+
project=config["project"],
99+
modules=config["modules"],
91100
purge_outputs=True,
92-
meorg_bin=meorg_bin
101+
meorg_bin=meorg_bin,
93102
)
94103

95104
logger.info(f"Upload job submitted: {meorg_jobid}")
96-
return True
105+
return True

tests/test_comparison.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pathlib import Path
99

1010
import pytest
11+
1112
from benchcab import internal
1213
from benchcab.comparison import ComparisonTask
1314

tests/test_config.py

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def all_optional_default_config(no_optional_config) -> dict:
7575
"experiment": bi.FLUXSITE_DEFAULT_EXPERIMENT,
7676
"multiprocess": bi.FLUXSITE_DEFAULT_MULTIPROCESS,
7777
"pbs": bi.FLUXSITE_DEFAULT_PBS,
78+
"meorg_model_output_id": bi.FLUXSITE_DEFAULT_MEORG_MODEL_OUTPUT_ID
7879
},
7980
"science_configurations": bi.DEFAULT_SCIENCE_CONFIGURATIONS,
8081
"spatial": {
@@ -106,6 +107,7 @@ def all_optional_custom_config(no_optional_config) -> dict:
106107
"walltime": "10:00:00",
107108
"storage": ["scratch/$PROJECT"],
108109
},
110+
"meorg_model_output_id": False
109111
},
110112
"science_configurations": [
111113
{

tests/test_fluxsite.py

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import f90nml
1111
import netCDF4
1212
import pytest
13+
1314
from benchcab import __version__, internal
1415
from benchcab.fluxsite import (
1516
CableError,

tests/test_fs.py

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pathlib import Path
1111

1212
import pytest
13+
1314
from benchcab.utils.fs import chdir, mkdir, next_path, prepend_path
1415

1516

tests/test_spatial.py

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import f90nml
1111
import pytest
1212
import yaml
13+
1314
from benchcab import internal
1415
from benchcab.model import Model
1516
from benchcab.spatial import SpatialTask, get_spatial_tasks

tests/test_state.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import time
22
from pathlib import Path
3+
from tempfile import TemporaryDirectory
34

45
import pytest
6+
57
from benchcab.utils.state import State, StateAttributeError
6-
from tempfile import TemporaryDirectory
78

89

910
def test_state_is_set():
@@ -39,4 +40,4 @@ def test_state_get_raises_exception():
3940
with TemporaryDirectory() as tmp_dir:
4041
state = State(state_dir=Path(tmp_dir))
4142
with pytest.raises(StateAttributeError):
42-
state.get()
43+
state.get()

0 commit comments

Comments
 (0)