Skip to content

Commit

Permalink
First pass of tests
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-seaice committed Aug 30, 2024
1 parent 5facb60 commit 3871403
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 30 deletions.
4 changes: 2 additions & 2 deletions payu/models/cesm_cmeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def _setup_checks(self):
nthreads = int(self.runconfig.get("PELAYOUT_attributes", f"{realm}_nthreads"))
rootpe = int(self.runconfig.get("PELAYOUT_attributes", f"{realm}_rootpe"))
pestride = int(self.runconfig.get("PELAYOUT_attributes", f"{realm}_pestride"))
npes = (ntasks-1)*nthreads*pestride # count to the last process, starting at 0
npes = ntasks*nthreads*pestride # count to the last process, starting at 0
if (rootpe + npes) > cpucount:
raise ValueError(
f"Insufficient cpus for the {realm} pelayout in {NUOPC_CONFIG}"
Expand Down Expand Up @@ -224,7 +224,7 @@ def _setup_checks(self):
if self.runconfig.get(io_section, "pio_async_interface") == ".false.":
niotasks = int(self.runconfig.get(io_section, "pio_numiotasks"))
iostride = int(self.runconfig.get(io_section, "pio_stride"))
if (int(ioroot) + (niotasks-1)*iostride) > npes:
if (ioroot + niotasks*iostride) > npes:
raise ValueError(
f"The iolayout for {io_section} in {NUOPC_CONFIG} is "
"requesting out of range cpus"
Expand Down
251 changes: 223 additions & 28 deletions test/models/test_cesm_cmeps.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
import copy
import os
import shutil
from pathlib import Path
import pytest

import payu
from payu.models.cesm_cmeps import Runconfig

from test.common import cd, tmpdir, ctrldir, labdir, workdir, archive_dir, write_config, config_path
from test.common import config as config_orig
from test.common import make_inputs, make_exe
from unittest.mock import patch

verbose = True
NCPU=24

@pytest.fixture()
def runconfig_path():
return os.path.join('test', 'resources', 'nuopc.runconfig')

@pytest.fixture()
def runconfig(runconfig_path):
return Runconfig(runconfig_path)

# Runconfig tests:

@pytest.mark.parametrize(
"section, variable, expected",
[
Expand All @@ -17,27 +39,17 @@
("MED_attributes", "histaux_atm2med_file1_flds", "Faxa_swndr:Faxa_swvdr:Faxa_swndf:Faxa_swvdf"), # correctly read long colon separated value
]
)
def test_runconfig_get(section, variable, expected):
def test_runconfig_get(section, variable, expected, runconfig):
"""Test getting values from a nuopc.runconfig file"""
runconfig_path = os.path.join('test', 'resources', 'nuopc.runconfig')
runconfig = Runconfig(runconfig_path)

assert runconfig.get(section, variable) == expected

def test_runconfig_get_default():
def test_runconfig_get_default(runconfig):
"""Test getting default values from a nuopc.runconfig file"""
runconfig_path = os.path.join('test', 'resources', 'nuopc.runconfig')
runconfig = Runconfig(runconfig_path)

assert runconfig.get("DOES_NOT_EXIST", "DOES_NOT_EXIST", value="default") == "default"

def test_runconfig_get_component_list():
def test_runconfig_get_component_list(runconfig):
"""Test getting component_list from a nuopc.runconfig file"""
COMP_LIST = ['MED', 'ATM', 'ICE', 'OCN', 'ROF']

runconfig_path = os.path.join('test', 'resources', 'nuopc.runconfig')
runconfig = Runconfig(runconfig_path)

assert runconfig.get_component_list() == COMP_LIST

@pytest.mark.parametrize(
Expand All @@ -47,41 +59,224 @@ def test_runconfig_get_component_list():
("CLOCK_attributes", "restart_n", "2"),
]
)
def test_runconfig_set(section, variable, new_variable):
def test_runconfig_set(section, variable, new_variable, runconfig):
"""Test setting values in a nuopc.runconfig file"""
runconfig_path = os.path.join('test', 'resources', 'nuopc.runconfig')
runconfig = Runconfig(runconfig_path)

runconfig.set(section, variable, new_variable)

assert runconfig.get(section, variable) == new_variable

def test_runconfig_set_error():
def test_runconfig_set_error(runconfig):
"""Test error setting values in a nuopc.runconfig file that don't exist"""
runconfig_path = os.path.join('test', 'resources', 'nuopc.runconfig')
runconfig = Runconfig(runconfig_path)

with pytest.raises(
NotImplementedError,
match='Cannot set value of variable that does not already exist'
):
runconfig.set("DOES_NOT_EXIST", "OCN_model", "value")
runconfig.set("ALLCOMP_attributes", "DOES_NOT_EXIST", "value")

def test_runconfig_set_write_get():
def test_runconfig_set_write_get(runconfig):
"""Test updating the values in a nuopc.runconfig file"""
runconfig_path = os.path.join('test', 'resources', 'nuopc.runconfig')
runconfig = Runconfig(runconfig_path)

assert runconfig.get("CLOCK_attributes", "restart_n") == "1"

runconfig.set("CLOCK_attributes", "restart_n", "2")

runconfig_path_tmp = "nuopc.runconfig.tmp"
runconfig.write(runconfig_path_tmp)
runconfig_path_tmp = os.path.join(tmpdir,"nuopc.runconfig.tmp")
runconfig.write(file = runconfig_path_tmp)

runconfig_updated = Runconfig(runconfig_path_tmp)
assert runconfig.get("CLOCK_attributes", "restart_n") == "2"
assert runconfig_updated.get("CLOCK_attributes", "restart_n") == "2"

os.remove(runconfig_path_tmp)

# Tests of cesm_cmeps

def setup_module(module):
"""
Put any test-wide setup code in here, e.g. creating test files
"""
if verbose:
print("setup_module module:%s" % module.__name__)

# Should be taken care of by teardown, in case remnants lying around
try:
shutil.rmtree(tmpdir)
except FileNotFoundError:
pass

try:
tmpdir.mkdir()
labdir.mkdir()
ctrldir.mkdir()
workdir.mkdir()
# archive_dir.mkdir()
make_inputs()
make_exe()
except Exception as e:
print(e)


def teardown_module(module):
"""
Put any test-wide teardown code in here, e.g. removing test outputs
"""
if verbose:
print("teardown_module module:%s" % module.__name__)

try:
shutil.rmtree(tmpdir)
print('removing tmp')
except Exception as e:
print(e)

@pytest.fixture
def cmeps_config():
# Write a cmeps model config file with 1 year runtime

# Create a config.yaml file with the cice submodel and 1 year run length

config = copy.deepcopy(config_orig)
config['model'] = 'access-om3'
config['ncpus'] = NCPU

write_config(config)

Path.touch(os.path.join(ctrldir,'nuopc.runconfig'))

# shutil.copy(str(runconfig_path), ctrldir)

# Run test
yield

# Teardown
os.remove(config_path)

# Mock runconfig for some tests

#valid minimum nuopc.runconfig for _setup_checks
MOCK_IO_RUNCONF = {
"PELAYOUT_attributes": dict(
moc_ntasks = NCPU ,
moc_nthreads = 1 ,
moc_pestride = 1 ,
moc_rootpe = 0
),
"MOC_modelio": dict(
pio_numiotasks = 1 ,
pio_rearranger = 1 ,
pio_root = 0 ,
pio_stride = 1 ,
pio_typename = 'netcdf4p',
pio_async_interface = '.false.'
)
}


class MockRunConfig:

def __init__(self, config):
self.conf = config

def get_component_list(self):
return ['MOC']

def get(self, section, variable, value=None):
return self.conf[section][variable]

@pytest.mark.parametrize("PELAYOUT_patch", [
{"moc_ntasks":1},
{"moc_ntasks":NCPU},
{"moc_ntasks":2, "moc_nthreads":NCPU/2},
{"moc_ntasks":2, "moc_pestride":NCPU/2},
{"moc_ntasks":2, "moc_rootpe": NCPU-2},
{"moc_ntasks":NCPU/4, "moc_nthreads":2, "moc_pestride":2},
])
def test__setup_checks_npes(cmeps_config, PELAYOUT_patch):

test_runconf = copy.deepcopy(MOCK_IO_RUNCONF)
test_runconf["PELAYOUT_attributes"].update(PELAYOUT_patch)

with cd(ctrldir):
lab = payu.laboratory.Laboratory(lab_path=str(labdir))
expt = payu.experiment.Experiment(lab, reproduce=False)
model = expt.models[0]

model.realms = ["moc"]

model.runconfig=MockRunConfig(test_runconf)

model._setup_checks()

@pytest.mark.parametrize("PELAYOUT_patch", [
{"moc_ntasks":NCPU+1},
{"moc_ntasks":1, "moc_nthreads":NCPU+1},
{"moc_ntasks":1, "moc_pestride":NCPU+1},
{"moc_ntasks":1, "moc_rootpe": NCPU},
{"moc_ntasks":NCPU/4+1, "moc_nthreads":2, "moc_pestride":2},
])
def test__setup_checks_too_many_pes(cmeps_config, PELAYOUT_patch):

test_runconf = copy.deepcopy(MOCK_IO_RUNCONF)
test_runconf["PELAYOUT_attributes"].update(PELAYOUT_patch)

with cd(ctrldir):
lab = payu.laboratory.Laboratory(lab_path=str(labdir))
expt = payu.experiment.Experiment(lab, reproduce=False)
model = expt.models[0]

model.realms = ["moc"]

model.runconfig=MockRunConfig(test_runconf)

with pytest.raises(ValueError):
model._setup_checks()

@pytest.mark.parametrize("modelio_patch", [
{"pio_typename":"netcdf"},
{"pio_typename":"netcdf", "pio_root":NCPU-1},
{"pio_typename":"netcdf", "pio_stride":1000, "pio_numiotask":1000},
{"pio_numiotasks":NCPU},
{"pio_numiotasks":1,"pio_root":NCPU-1},
{"pio_numiotasks":1,"pio_stride":NCPU},
{"pio_numiotasks":1,"pio_root":NCPU/2,"pio_stride":NCPU/2}
])
def test__setup_checks_io(cmeps_config, modelio_patch):

test_runconf = copy.deepcopy(MOCK_IO_RUNCONF)
test_runconf["MOC_modelio"].update(modelio_patch)

with cd(ctrldir):
lab = payu.laboratory.Laboratory(lab_path=str(labdir))
expt = payu.experiment.Experiment(lab, reproduce=False)
model = expt.models[0]

model.realms = ["moc"]

model.runconfig=MockRunConfig(test_runconf)

model._setup_checks()

@pytest.mark.parametrize("modelio_patch", [
{"pio_typename":"netcdf4s"},
{"pio_typename":"netcdf", "pio_root":NCPU+1},
{"pio_numiotasks":NCPU+1},
{"pio_numiotasks":1,"pio_root":NCPU},
{"pio_numiotasks":2,"pio_stride":NCPU},
{"pio_numiotasks":1,"pio_stride":NCPU+1},
{"pio_numiotasks":1,"pio_root":NCPU/2,"pio_stride":NCPU/2+1}
])
def test__setup_checks_bad_io(cmeps_config, modelio_patch):

test_runconf = copy.deepcopy(MOCK_IO_RUNCONF)
test_runconf["MOC_modelio"].update(modelio_patch)

with cd(ctrldir):
lab = payu.laboratory.Laboratory(lab_path=str(labdir))
expt = payu.experiment.Experiment(lab, reproduce=False)
model = expt.models[0]

model.realms = ["moc"]

model.runconfig=MockRunConfig(test_runconf)

with pytest.raises(ValueError):
model._setup_checks()

0 comments on commit 3871403

Please sign in to comment.