Skip to content

Commit bb34cd3

Browse files
committed
Merge remote-tracking branch 'origin/main' into feature/cdeps_updated
2 parents 18db9e5 + 482f99a commit bb34cd3

File tree

13 files changed

+117
-39
lines changed

13 files changed

+117
-39
lines changed

docs/sections/user_guide/cli/drivers/esg_grid/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
.. include:: ../shared/idempotent.rst
55

6-
The ``uw`` mode for configuring and running the :ufs-utils:`regional_esg_grid<regional-esg-grid>` component.
6+
The ``uw`` mode for configuring and running the UFS Utils preprocessing component ``regional_esg_grid``. Documentation for this UFS Utils component is :ufs-utils:`here <regional-esg-grid>`.
77

88
.. include:: help.rst
99

src/uwtools/drivers/driver.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,11 +497,13 @@ def _runscript(
497497
498498
{execution}
499499
"""
500+
directives = scheduler.directives if scheduler else ""
501+
initcmds = scheduler.initcmds if scheduler else []
500502
rs = dedent(template).format(
501-
directives="\n".join(scheduler.directives if scheduler else ""),
503+
directives="\n".join(directives),
502504
envcmds="\n".join(envcmds or []),
503505
envvars="\n".join([f"export {k}={v}" for k, v in (envvars or {}).items()]),
504-
execution="\n".join(execution),
506+
execution="\n".join([*initcmds, *execution]),
505507
)
506508
return re.sub(r"\n\n\n+", "\n\n", rs.strip())
507509

src/uwtools/drivers/esg_grid.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,12 @@ def driver_name(cls) -> str:
5858
"""
5959
return STR.esggrid
6060

61+
@property
62+
def output(self) -> dict[str, Path]:
63+
"""
64+
Returns a description of the file(s) created when this component runs.
65+
"""
66+
return {"path": self.rundir / "regional_grid.nc"}
67+
6168

6269
set_driver_docstring(ESGGrid)

src/uwtools/drivers/global_equiv_resol.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ def driver_name(cls) -> str:
4747
"""
4848
return STR.globalequivresol
4949

50+
@property
51+
def output(self) -> dict[str, Path]:
52+
"""
53+
Returns a description of the file(s) created when this component runs.
54+
"""
55+
return {"path": Path(self.config["input_grid_file"])}
56+
5057
# Private helper methods
5158

5259
@property

src/uwtools/drivers/make_hgrid.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
A driver for make_hgrid.
33
"""
44

5+
from pathlib import Path
6+
57
from iotaa import tasks
68

79
from uwtools.drivers.driver import DriverTimeInvariant
@@ -33,6 +35,14 @@ def driver_name(cls) -> str:
3335
"""
3436
return STR.makehgrid
3537

38+
@property
39+
def output(self) -> dict[str, Path]:
40+
"""
41+
Returns a description of the file(s) created when this component runs.
42+
"""
43+
grid_name = self.config["config"].get("grid_name", "horizontal_grid")
44+
return {"path": (self.rundir / grid_name).with_suffix(".nc")}
45+
3646
# Private helper methods
3747

3848
@property

src/uwtools/drivers/make_solo_mosaic.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44

55
from __future__ import annotations
66

7+
from typing import TYPE_CHECKING
8+
79
from iotaa import tasks
810

911
from uwtools.drivers.driver import DriverTimeInvariant
1012
from uwtools.drivers.support import set_driver_docstring
1113
from uwtools.strings import STR
1214

15+
if TYPE_CHECKING:
16+
from pathlib import Path
17+
1318

1419
class MakeSoloMosaic(DriverTimeInvariant):
1520
"""
@@ -26,6 +31,14 @@ def provisioned_rundir(self):
2631
yield self.taskname("provisioned run directory")
2732
yield self.runscript()
2833

34+
@property
35+
def output(self) -> dict[str, Path]:
36+
"""
37+
Returns a description of the file(s) created when this component runs.
38+
"""
39+
mosaic_name = self.config["config"].get("mosaic_name", "mosaic")
40+
return {"path": (self.rundir / mosaic_name).with_suffix(".nc")}
41+
2942
# Public helper methods
3043

3144
def taskname(self, suffix: str | None = None) -> str:

src/uwtools/scheduler.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def directives(self) -> list[str]:
3636
"""
3737
The resource-request scheduler directives.
3838
"""
39-
pre, sep = self._prefix, self._directive_separator
39+
prefix, separator = self._prefix, self._directive_separator
40+
sep = lambda val: "%s%s" % (val, "" if val.endswith("=") else separator)
4041
ds = []
4142
for key, value in self._processed_props.items():
4243
if key in self._forbidden_directives:
@@ -45,11 +46,11 @@ def directives(self) -> list[str]:
4546
if key in self._managed_directives:
4647
switch = self._managed_directives[key]
4748
if callable(switch) and (x := switch(value)) is not None:
48-
ds.append("%s %s" % (pre, x))
49+
ds.append("%s %s" % (prefix, x))
4950
else:
50-
ds.append("%s %s%s%s" % (pre, switch, sep, value))
51+
ds.append("%s %s%s" % (prefix, sep(switch), value))
5152
else:
52-
ds.append("%s %s%s%s" % (pre, key, sep, value))
53+
ds.append("%s %s%s" % (prefix, sep(key), value))
5354
return sorted(ds)
5455

5556
@staticmethod
@@ -71,6 +72,13 @@ def get_scheduler(props: Mapping) -> JobScheduler:
7172
msg = f"No 'scheduler' defined in {props}"
7273
raise UWConfigError(msg)
7374

75+
@property
76+
def initcmds(self) -> list[str]:
77+
"""
78+
Additional initialization commands a batch job must run.
79+
"""
80+
return []
81+
7482
def submit_job(self, runscript: Path, submit_file: Path | None = None) -> bool:
7583
"""
7684
Submit a job to the scheduler.
@@ -210,6 +218,14 @@ class PBS(JobScheduler):
210218
Represents the PBS scheduler.
211219
"""
212220

221+
@property
222+
def initcmds(self) -> list[str]:
223+
"""
224+
Additional initialization commands a PBS batch job must run.
225+
"""
226+
rundir = self._props.get(_DirectivesOptional.RUNDIR)
227+
return [f"cd {rundir}"] if rundir else []
228+
213229
@property
214230
def _directive_separator(self) -> str:
215231
"""
@@ -222,7 +238,7 @@ def _forbidden_directives(self) -> list[str]:
222238
"""
223239
Directives that this scheduler does not support.
224240
"""
225-
return []
241+
return [_DirectivesOptional.RUNDIR]
226242

227243
@property
228244
def _managed_directives(self) -> dict[str, Any]:
@@ -273,11 +289,12 @@ def _processed_props(self) -> dict[str, Any]:
273289
"""
274290
Pre-processed runscript directives.
275291
"""
276-
props = self._props
292+
props = deepcopy(self._props)
277293
props.update(self._select(props))
278294
props.update(self._placement(props))
279295
props.pop(_DirectivesOptional.TASKS_PER_NODE, None)
280296
props.pop(_DirectivesOptional.NODES, None)
297+
props.pop(_DirectivesOptional.RUNDIR, None)
281298
props.pop(_DirectivesOptional.THREADS, None)
282299
props.pop(_DirectivesOptional.MEMORY, None)
283300
props.pop("exclusive", None)

src/uwtools/tests/drivers/test_driver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,10 +540,11 @@ def test_Driver__runscript(driverobj):
540540
export VAR1=1
541541
export VAR2=2
542542
543+
cd /path/to/rundir
543544
foo
544545
bar
545546
"""
546-
scheduler = Mock(directives=["#DIR --d1", "#DIR --d2"])
547+
scheduler = Mock(directives=["#DIR --d1", "#DIR --d2"], initcmds=["cd /path/to/rundir"])
547548
assert (
548549
driverobj._runscript(
549550
execution=["foo", "bar"],

src/uwtools/tests/drivers/test_esg_grid.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
from unittest.mock import patch
77

88
import f90nml # type: ignore[import-untyped]
9-
from pytest import fixture, mark, raises
9+
from pytest import fixture, mark
1010

1111
from uwtools.drivers.driver import Driver
1212
from uwtools.drivers.esg_grid import ESGGrid
13-
from uwtools.exceptions import UWNotImplementedError
1413

1514
# Fixtures
1615

@@ -71,7 +70,6 @@ def driverobj(config):
7170
"_scheduler",
7271
"_validate",
7372
"_write_runscript",
74-
"output",
7573
"run",
7674
"runscript",
7775
"taskname",
@@ -111,9 +109,7 @@ def test_ESGGrid_namelist_file_missing_base_file(driverobj, logged):
111109

112110

113111
def test_ESGGrid_output(driverobj):
114-
with raises(UWNotImplementedError) as e:
115-
assert driverobj.output
116-
assert str(e.value) == "The output() method is not yet implemented for this driver"
112+
assert driverobj.output["path"] == driverobj.rundir / "regional_grid.nc"
117113

118114

119115
def test_ESGGrid_provisioned_rundir(driverobj, ready_task):

src/uwtools/tests/drivers/test_global_equiv_resol.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
from pathlib import Path
66
from unittest.mock import patch
77

8-
from pytest import fixture, mark, raises
8+
from pytest import fixture, mark
99

1010
from uwtools.drivers.driver import Driver
1111
from uwtools.drivers.global_equiv_resol import GlobalEquivResol
12-
from uwtools.exceptions import UWNotImplementedError
1312

1413
# Fixtures
1514

@@ -55,7 +54,6 @@ def driverobj(config):
5554
"_scheduler",
5655
"_validate",
5756
"_write_runscript",
58-
"output",
5957
"run",
6058
"runscript",
6159
"taskname",
@@ -78,9 +76,7 @@ def test_GlobalEquivResol_input_file(driverobj):
7876

7977

8078
def test_GlobalEquivResol_output(driverobj):
81-
with raises(UWNotImplementedError) as e:
82-
assert driverobj.output
83-
assert str(e.value) == "The output() method is not yet implemented for this driver"
79+
assert driverobj.output["path"] == Path(driverobj.config["input_grid_file"])
8480

8581

8682
def test_GlobalEquivResol_provisioned_rundir(driverobj, ready_task):

0 commit comments

Comments
 (0)