diff --git a/docs/reference/configuration/keywords.rst b/docs/reference/configuration/keywords.rst index 43f914d0e30..e29798fe0cf 100644 --- a/docs/reference/configuration/keywords.rst +++ b/docs/reference/configuration/keywords.rst @@ -477,8 +477,8 @@ Commonly used keywords will be replaced by the realization number and iteration number when ERT creates the folders. By default, RUNPATH is set to "simulations/realization-/iter-". - Deprecated syntax still allow use of two %d specifers. Use of less than two %d is prohibited. - The behaviour is identical to the default substitution. + Deprecated syntax still allow use of two `%d` specifers. Use of more than two `%d` specifiers, + using multiple `` or `` keywords or mixing styles is prohibited. *Example:* diff --git a/src/ert/config/model_config.py b/src/ert/config/model_config.py index bdde2ab1089..2f2357fba7d 100644 --- a/src/ert/config/model_config.py +++ b/src/ert/config/model_config.py @@ -54,11 +54,31 @@ def __init__( self.history_source = history_source self.jobname_format_string = _replace_runpath_format(jobname_format_string) self.eclbase_format_string = _replace_runpath_format(eclbase_format_string) - self.runpath_format_string = _replace_runpath_format(runpath_format_string) - if self.runpath_format_string is not None and not any( - x in self.runpath_format_string for x in ["", ""] + # do not combine styles + if "%d" in runpath_format_string and any( + x in runpath_format_string for x in ["", ""] ): + raise ConfigValidationError( + f"RUNPATH cannot combine deprecated and new style placeholders: `{runpath_format_string}`. Valid example `{DEFAULT_RUNPATH}`" + ) + + # do not allow multiple occurrences + for kw in ["", ""]: + if runpath_format_string.count(kw) > 1: + raise ConfigValidationError( + f"RUNPATH cannot contain multiple {kw} placeholders: `{runpath_format_string}`. Valid example `{DEFAULT_RUNPATH}`" + ) + + # do not allow too many placeholders + if runpath_format_string.count("%d") > 2: + raise ConfigValidationError( + f"RUNPATH cannot contain more than two value placeholders: `{runpath_format_string}`. Valid example `{DEFAULT_RUNPATH}`" + ) + + self.runpath_format_string = _replace_runpath_format(runpath_format_string) + + if not any(x in self.runpath_format_string for x in ["", ""]): logger.warning( "RUNPATH keyword contains no value placeholders: " f"`{runpath_format_string}`. Valid example: " diff --git a/tests/unit_tests/test_run_path_creation.py b/tests/unit_tests/test_run_path_creation.py index 4b716e1c4f5..3422934ac5f 100644 --- a/tests/unit_tests/test_run_path_creation.py +++ b/tests/unit_tests/test_run_path_creation.py @@ -5,7 +5,7 @@ import pytest -from ert.config import ErtConfig +from ert.config import ConfigValidationError, ErtConfig from ert.enkf_main import create_run_path, ensemble_context, sample_prior from ert.run_context import RunContext from ert.runpaths import Runpaths @@ -469,3 +469,53 @@ def test_num_cpu_subst(monkeypatch, tmp_path, append, numcpu, storage): with open("simulations/realization-0/iter-0/jobs.json", encoding="utf-8") as f: assert f'"argList": ["{numcpu}"]' in f.read() + + +@pytest.mark.parametrize( + "run_path, expected_raise, msg", + [ + ("simulations/realization-/iter-", False, ""), + ("simulations/realization-%d/iter-%d", False, ""), + ("simulations/realization-%d", False, ""), + ( + "simulations/realization-/iter-%d", + True, + "RUNPATH cannot combine deprecated ", + ), + ( + "simulations/realization-/iter-", + True, + "RUNPATH cannot contain multiple ", + ), + ( + "simulations/realization-/iter-", + True, + "RUNPATH cannot contain multiple ", + ), + ( + "simulations/realization-%d/iter-%d/more-%d", + True, + "RUNPATH cannot contain more than two", + ), + ], +) +@pytest.mark.usefixtures("use_tmpdir") +def test_that_runpaths_are_raised_when_invalid(run_path, expected_raise, msg): + """ + This checks that RUNPATH does not include too many or few substitution placeholders + """ + config_text = dedent( + """ + NUM_REALIZATIONS 2 + """ + ) + Path("config.ert").write_text(config_text + f"RUNPATH {run_path}", encoding="utf-8") + + if expected_raise: + with pytest.raises( + ConfigValidationError, + match=f"{msg}.*{run_path}`.", + ): + _ = ErtConfig.from_file("config.ert") + else: + _ = ErtConfig.from_file("config.ert")