diff --git a/payu/models/cesm_cmeps.py b/payu/models/cesm_cmeps.py index dd0a7dc4..e9b788fe 100644 --- a/payu/models/cesm_cmeps.py +++ b/payu/models/cesm_cmeps.py @@ -195,7 +195,16 @@ 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*nthreads*pestride # count to the last process, starting at 0 + + if (pestride cpucount: raise ValueError( f"Insufficient cpus for the {realm} pelayout in {NUOPC_CONFIG}" @@ -211,7 +220,7 @@ def _setup_checks(self): nc_type = self.runconfig.get(io_section, "pio_typename") ioroot = int(self.runconfig.get(io_section, "pio_root")) - if ioroot > npes: + if ioroot >= npes: raise ValueError( f"Insufficient cpus for the {io_section} ioroot pe in " f"{NUOPC_CONFIG}" @@ -224,7 +233,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 (ioroot + niotasks*iostride) > npes: + if (ioroot + (niotasks-1)*iostride) >= npes: raise ValueError( f"The iolayout for {io_section} in {NUOPC_CONFIG} is " "requesting out of range cpus" diff --git a/test/models/access-om3/test_access_om3.py b/test/models/access-om3/test_access_om3.py index b61de50c..c1e52f08 100644 --- a/test/models/access-om3/test_access_om3.py +++ b/test/models/access-om3/test_access_om3.py @@ -10,7 +10,6 @@ from test.common import config as config_orig from test.common import make_inputs, make_exe -NCPU = 24 MODEL = 'access-om3' # Tests of cesm_cmeps @@ -50,23 +49,21 @@ def teardown_module(module): print(e) -@pytest.fixture -def cmeps_config(): +def cmeps_config(ncpu): # Create a config.yaml and nuopc.runconfig file config = copy.deepcopy(config_orig) config['model'] = MODEL - config['ncpus'] = NCPU + config['ncpus'] = ncpu write_config(config) with open(os.path.join(ctrldir, 'nuopc.runconfig'), "w") as f: f.close() - # Run test - yield - # Teardown +def teardown_cmeps_config(): + # Teardown os.remove(config_path) @@ -74,7 +71,7 @@ def cmeps_config(): # valid minimum nuopc.runconfig for _setup_checks MOCK_IO_RUNCONF = { "PELAYOUT_attributes": dict( - moc_ntasks=NCPU, + moc_ntasks=1, moc_nthreads=1, moc_pestride=1, moc_rootpe=0 @@ -102,18 +99,30 @@ 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}, +@pytest.mark.parametrize("ncpu, moc_ntasks, moc_nthreads, moc_pestride, moc_rootpe", [ + (1,1,1,1,0), #min + (4,4,1,1,0), #min tasks + (4,2,2,2,0), #min tasks * threads + (4,2,1,1,2), #min tasks + rootpe + (4,1,2,2,2), #min threads + rootpe + (4,1,1,1,3), #max rootpe + (5,2,1,4,0), #max stride + (13,4,1,3,1), #odd ncpu + (13,2,3,3,1), #odd ncpu + (100000,50000,1,2,0), #max cpu + (100000,1,1,1,99999), #max cpu ]) -def test__setup_checks_npes(cmeps_config, PELAYOUT_patch): +def test__setup_checks_npes(ncpu, moc_ntasks, moc_nthreads, moc_pestride, moc_rootpe): + + cmeps_config(ncpu) test_runconf = copy.deepcopy(MOCK_IO_RUNCONF) - test_runconf["PELAYOUT_attributes"].update(PELAYOUT_patch) + test_runconf["PELAYOUT_attributes"].update({ + "moc_ntasks": moc_ntasks, + "moc_nthreads": moc_nthreads , + "moc_pestride": moc_pestride , + "moc_rootpe": moc_rootpe + }) with cd(ctrldir): lab = payu.laboratory.Laboratory(lab_path=str(labdir)) @@ -126,18 +135,32 @@ def test__setup_checks_npes(cmeps_config, PELAYOUT_patch): model._setup_checks() + teardown_cmeps_config() + -@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}, +@pytest.mark.parametrize("ncpu, moc_ntasks, moc_nthreads, moc_pestride, moc_rootpe", [ + (1,1,1,1,1), #min + (4,5,1,1,0), #min tasks + (4,2,2,2,1), #min tasks * threads + (2,1,2,1,1), #threads > strides + (4,1,3,1,2), #min threads + rootpe + (4,1,1,1,4), #max rootpe + (13,4,1,4,1), #odd ncpu + (13,2,7,7,0), #odd ncpu + (100000,50001,1,2,0), #max cpu + (100000,1,1,1,100000), #max cpu ]) -def test__setup_checks_too_many_pes(cmeps_config, PELAYOUT_patch): +def test__setup_checks_too_many_pes(ncpu, moc_ntasks, moc_nthreads, moc_pestride, moc_rootpe): + + cmeps_config(ncpu) test_runconf = copy.deepcopy(MOCK_IO_RUNCONF) - test_runconf["PELAYOUT_attributes"].update(PELAYOUT_patch) + test_runconf["PELAYOUT_attributes"].update({ + "moc_ntasks": moc_ntasks, + "moc_nthreads": moc_nthreads , + "moc_pestride": moc_pestride , + "moc_rootpe": moc_rootpe + }) with cd(ctrldir): lab = payu.laboratory.Laboratory(lab_path=str(labdir)) @@ -151,20 +174,31 @@ def test__setup_checks_too_many_pes(cmeps_config, PELAYOUT_patch): with pytest.raises(ValueError): model._setup_checks() + teardown_cmeps_config() -@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} + +@pytest.mark.parametrize("ncpu, pio_numiotasks, pio_stride, pio_root, pio_typename", [ + (1,1,1,0,"netcdf"), #min + (2,1,1,1,"netcdf"), #max root + (2,2,1,0,"netcdf4p"), #min tasks + rootpe + (2,1,1,1,"netcdf4p"), #max rootpe + (5,3,2,0,"netcdf4p"), + (100000,50001,1,2,"netcdf4p"), #odd ncpu ]) -def test__setup_checks_io(cmeps_config, modelio_patch): +def test__setup_checks_io(ncpu, pio_numiotasks, pio_stride, pio_root, pio_typename): + + cmeps_config(ncpu) test_runconf = copy.deepcopy(MOCK_IO_RUNCONF) - test_runconf["MOC_modelio"].update(modelio_patch) + test_runconf["PELAYOUT_attributes"].update({ + "moc_ntasks": ncpu + }) + test_runconf["MOC_modelio"].update(dict( + pio_numiotasks=pio_numiotasks, + pio_root=pio_root, + pio_stride=pio_stride, + pio_typename=pio_typename, + )) with cd(ctrldir): lab = payu.laboratory.Laboratory(lab_path=str(labdir)) @@ -177,20 +211,31 @@ def test__setup_checks_io(cmeps_config, modelio_patch): model._setup_checks() + teardown_cmeps_config() -@pytest.mark.parametrize("modelio_patch", [ - {"pio_typename": "netcdf4c"}, - {"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} + +@pytest.mark.parametrize("ncpu, pio_numiotasks, pio_stride, pio_root, pio_typename", [ + (1,1,1,0,"netcdf4c"), + (2,1,1,2,"netcdf"), #root too big + (2,3,1,0,"netcdf4p"), #too manu tasks + (2,2,2,0,"netcdf4p"), #stride too big + (5,2,2,3,"netcdf4p"), #stride too big + (100000,50000,2,2,"netcdf4p"), #odd ncpu ]) -def test__setup_checks_bad_io(cmeps_config, modelio_patch): +def test__setup_checks_bad_io(ncpu, pio_numiotasks, pio_stride, pio_root, pio_typename): + + cmeps_config(ncpu) test_runconf = copy.deepcopy(MOCK_IO_RUNCONF) - test_runconf["MOC_modelio"].update(modelio_patch) + test_runconf["PELAYOUT_attributes"].update({ + "moc_ntasks": ncpu + }) + test_runconf["MOC_modelio"].update(dict( + pio_numiotasks=pio_numiotasks, + pio_root=pio_root, + pio_stride=pio_stride, + pio_typename=pio_typename, + )) with cd(ctrldir): lab = payu.laboratory.Laboratory(lab_path=str(labdir)) @@ -203,3 +248,6 @@ def test__setup_checks_bad_io(cmeps_config, modelio_patch): with pytest.raises(ValueError): model._setup_checks() + + teardown_cmeps_config() +