Skip to content

Commit

Permalink
Add global option for MPAS driver (ufs-community#660)
Browse files Browse the repository at this point in the history
  • Loading branch information
christinaholtNOAA authored Nov 26, 2024
1 parent b00a124 commit b9f70c1
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 37 deletions.
32 changes: 16 additions & 16 deletions docs/sections/user_guide/cli/drivers/mpas/show-schema.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
"properties": {
"mpas": {
"additionalProperties": false,
"properties": {
"execution": {
"additionalProperties": false,
"properties": {
"batchargs": {
"additionalProperties": true,
"properties": {
"cores": {
"type": "integer"
},
"debug": {
"type": "boolean"
},
"exclusive": {
"type": "boolean"
},
"allOf": [
{
"if": {
"properties": {
"domain": {
"const": "regional"
}
}
},
"then": {
"required": [
"lateral_boundary_conditions"
]
}
}
],
11 changes: 8 additions & 3 deletions docs/sections/user_guide/yaml/components/mpas.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ An MPAS build provides prototype versions of certain required runtime files. Her
UW YAML for the ``mpas:`` Block
-------------------------------

domain:
^^^^^^^

Accepted values are ``global`` and ``regional``.

execution:
^^^^^^^^^^

See :ref:`this page <execution_yaml>` for details.

boundary_conditions:
^^^^^^^^^^^^^^^^^^^^
lateral_boundary_conditions:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Describes the boundary condition files needed for the forecast. These will be the output from the ``init_atmosphere`` executable, which may be run using the ``mpas_init`` UW driver. Please see its documentation :ref:`here <mpas_init_yaml>`.
Describes the boundary condition files needed for a regional forecast. This section is not used when ``domain`` is set to ``global``. These will be the output from the ``init_atmosphere`` executable, which may be run using the ``mpas_init`` UW driver. Please see its documentation :ref:`here <mpas_init_yaml>`.

**interval_hours:**

Expand Down
10 changes: 5 additions & 5 deletions docs/sections/user_guide/yaml/components/mpas_init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ An MPAS build provides prototype versions of certain required runtime files. Her
UW YAML for the ``mpas_init:`` Block
------------------------------------

execution:
^^^^^^^^^^

See :ref:`this page <execution_yaml>` for details.

boundary_conditions:
^^^^^^^^^^^^^^^^^^^^

Expand All @@ -43,6 +38,11 @@ Describes the boundary condition files needed for the forecast. These will most

An absolute path to the output of the ``ungrib`` tool that will be used to prepare MPAS-ready initial and lateral boundary conditions. The names of the files are specified in the ``streams.init_atmosphere`` XML file, and may be specified in the ``streams: values:`` block of the driver YAML.

execution:
^^^^^^^^^^

See :ref:`this page <execution_yaml>` for details.

files_to_copy:
^^^^^^^^^^^^^^

Expand Down
1 change: 1 addition & 0 deletions docs/shared/mpas.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mpas:
domain: regional
execution:
batchargs:
cores: 32
Expand Down
19 changes: 18 additions & 1 deletion src/uwtools/drivers/mpas.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import timedelta
from pathlib import Path

from iotaa import asset, task
from iotaa import asset, task, tasks

from uwtools.config.formats.nml import NMLConfig
from uwtools.drivers.mpas_base import MPASBase
Expand Down Expand Up @@ -66,6 +66,23 @@ def namelist_file(self):
schema=self.namelist_schema(),
)

@tasks
def provisioned_rundir(self):
"""
Run directory provisioned with all required content.
"""
yield self.taskname("provisioned run directory")
required = [
self.files_copied(),
self.files_linked(),
self.namelist_file(),
self.runscript(),
self.streams_file(),
]
if self.config["domain"] == "regional":
required.append(self.boundary_files())
yield required

# Public helper methods

@classmethod
Expand Down
10 changes: 1 addition & 9 deletions src/uwtools/drivers/mpas_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,11 @@ def namelist_file(self):
"""

@tasks
@abstractmethod
def provisioned_rundir(self):
"""
Run directory provisioned with all required content.
"""
yield self.taskname("provisioned run directory")
yield [
self.boundary_files(),
self.files_copied(),
self.files_linked(),
self.namelist_file(),
self.runscript(),
self.streams_file(),
]

@task
def streams_file(self):
Expand Down
15 changes: 15 additions & 0 deletions src/uwtools/drivers/mpas_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ def namelist_file(self):
schema=self.namelist_schema(),
)

@tasks
def provisioned_rundir(self):
"""
Run directory provisioned with all required content.
"""
yield self.taskname("provisioned run directory")
yield [
self.boundary_files(),
self.files_copied(),
self.files_linked(),
self.namelist_file(),
self.runscript(),
self.streams_file(),
]

# Public helper methods

@classmethod
Expand Down
24 changes: 24 additions & 0 deletions src/uwtools/resources/jsonschema/mpas.jsonschema
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,30 @@
"properties": {
"mpas": {
"additionalProperties": false,
"allOf": [
{
"if": {
"properties": {
"domain": {
"const": "regional"
}
}
},
"then": {
"required": [
"lateral_boundary_conditions"
]
}
}
],
"properties": {
"domain": {
"enum": [
"global",
"regional"
],
"type": "string"
},
"execution": {
"$ref": "urn:uwtools:execution-parallel"
},
Expand Down Expand Up @@ -73,6 +96,7 @@
}
},
"required": [
"domain",
"execution",
"namelist",
"rundir",
Expand Down
11 changes: 9 additions & 2 deletions src/uwtools/tests/drivers/test_mpas.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def streams_file(config, driverobj, drivername):
def config(tmp_path):
return {
"mpas": {
"domain": "regional",
"execution": {
"executable": "atmosphere_model",
"batchargs": {
Expand Down Expand Up @@ -233,7 +234,9 @@ def test_MPAS_output(driverobj):
assert str(e.value) == "The output() method is not yet implemented for this driver"


def test_MPAS_provisioned_rundir(driverobj):
@mark.parametrize("domain", ("global", "regional"))
def test_MPAS_provisioned_rundir(domain, driverobj):
driverobj._config["domain"] = domain
with patch.multiple(
driverobj,
boundary_files=D,
Expand All @@ -244,8 +247,12 @@ def test_MPAS_provisioned_rundir(driverobj):
streams_file=D,
) as mocks:
driverobj.provisioned_rundir()
excluded = ["boundary_files"] if domain == "global" else []
for m in mocks:
mocks[m].assert_called_once_with()
if m in excluded:
mocks[m].assert_not_called()
else:
mocks[m].assert_called_once_with()


def test_MPAS_streams_file(config, driverobj):
Expand Down
18 changes: 17 additions & 1 deletion src/uwtools/tests/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,9 @@ def test_schema_makedirs():

def test_schema_mpas(mpas_streams):
config = {
"domain": "regional",
"execution": {"executable": "atmosphere_model"},
"lateral_boundary_conditions": {"interval_hours": 3, "offset": 3, "path": "/path/to/lbcs"},
"namelist": {"base_file": "path/to/simple.nml", "validate": True},
"rundir": "path/to/rundir",
"streams": mpas_streams,
Expand All @@ -1263,10 +1265,18 @@ def test_schema_mpas(mpas_streams):
# Basic correctness:
assert not errors(config)
# All top-level keys are required:
for key in ("execution", "namelist", "rundir", "streams"):
for key in ("domain", "execution", "namelist", "rundir", "streams"):
assert f"'{key}' is a required property" in errors(with_del(config, key))
# Additional top-level keys are not allowed:
assert "Additional properties are not allowed" in errors({**config, "foo": "bar"})
# lateral_boundary_conditions are optional when domain is global:
assert not errors({**with_del(config, "lateral_boundary_conditions"), "domain": "global"})


def test_schema_mpas_domain(mpas_prop):
errors = mpas_prop("domain")
# There is a fixed set of domain values:
assert "'foo' is not one of ['global', 'regional']" in errors("foo")


def test_schema_mpas_lateral_boundary_conditions(mpas_prop):
Expand Down Expand Up @@ -1348,6 +1358,12 @@ def test_schema_mpas_rundir(mpas_prop):

def test_schema_mpas_init(mpas_streams):
config = {
"boundary_conditions": {
"interval_hours": 6,
"length": 12,
"offset": 0,
"path": "/path/to/bcs",
},
"execution": {"executable": "mpas_init"},
"namelist": {"base_file": "path/to/simple.nml", "validate": True},
"rundir": "path/to/rundir",
Expand Down

0 comments on commit b9f70c1

Please sign in to comment.