diff --git a/.github/workflows/test_ert_with_flow.yml b/.github/workflows/test_ert_with_flow.yml index 892424ae1cb..9da99297504 100644 --- a/.github/workflows/test_ert_with_flow.yml +++ b/.github/workflows/test_ert_with_flow.yml @@ -26,9 +26,10 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v4 - - name: Install ert - run: - uv pip install ".[dev]" + - name: Install ert and everest + run: | + uv pip install ".[everest,dev]" + uv pip install git+https://github.com/equinor/everest-models - name: Install flow run: | @@ -47,6 +48,14 @@ jobs: set -e pytest tests/ert/unit_tests/resources/test_run_flow_simulator.py - cd test-data/ert/flow_example + push test-data/ert/flow_example perl -p -i -e 's/NUM_REALIZATIONS\s*12/NUM_REALIZATIONS 2/g' flow.ert ert ensemble_experiment flow.ert --disable-monitor + popd + + - name: Run everest integration test with OPM flow + run: | + set -e + pushd test-data/everest/egg/everest/model + everest run config_flow.yml + popd diff --git a/src/ert/config/ert_config.py b/src/ert/config/ert_config.py index e7663b3d86f..ead115a2fca 100644 --- a/src/ert/config/ert_config.py +++ b/src/ert/config/ert_config.py @@ -797,6 +797,20 @@ def _create_list_of_forward_model_steps_to_run( ) continue fm_step.arglist = fm_step_description[1:] + + fm_step.environment = { + **cls.ENV_PR_FM_STEP.get(fm_step.name.upper(), {}), + **fm_step.environment, + } + + # Special casing for run_reservoirsimulator.py + if fm_step.name == "eclipse100": + fm_step.arglist = ("eclipse", *fm_step.arglist) + if fm_step.name == "eclipse300": + fm_step.arglist = ("eclipse300", *fm_step.arglist) + if fm_step.name == "flow": + fm_step.arglist = ("flow", *fm_step.arglist) + fm_steps.append(fm_step) for fm_step in fm_steps: diff --git a/src/ert/plugins/hook_implementations/forward_model_steps.py b/src/ert/plugins/hook_implementations/forward_model_steps.py index 02f2421d1c0..622e9395669 100644 --- a/src/ert/plugins/hook_implementations/forward_model_steps.py +++ b/src/ert/plugins/hook_implementations/forward_model_steps.py @@ -207,8 +207,9 @@ def __init__(self) -> None: ).resolve() ), "eclipse", - "", "", + "--version", + "", "-n", "", "", @@ -267,8 +268,9 @@ def __init__(self) -> None: ).resolve() ), "e300", - "", "", + "--version", + "", "-n", "", "", @@ -324,8 +326,9 @@ def __init__(self) -> None: ).resolve() ), "flow", - "", "", + "--version", + "", "-n", "", "", diff --git a/src/ert/resources/forward_models/run_reservoirsimulator.py b/src/ert/resources/forward_models/run_reservoirsimulator.py index e920da80a33..dda93dd5df9 100755 --- a/src/ert/resources/forward_models/run_reservoirsimulator.py +++ b/src/ert/resources/forward_models/run_reservoirsimulator.py @@ -195,7 +195,7 @@ def eclrun_command(self) -> list[str]: self.simulator, "--version", str(self.version), - self.data_file, + str(self.run_path / self.data_file), "--summary-conversion", "yes" if self.summary_conversion else "no", ] @@ -211,7 +211,7 @@ def flowrun_command(self) -> list[str]: self.runner_abspath, "--version", str(self.version), - self.data_file, + str(self.run_path / self.data_file), "--np", str(self.num_cpu), ] @@ -377,8 +377,8 @@ def tail_textfile(file_path: Path, num_chars: int) -> str: def run_reservoirsimulator(args: list[str]) -> None: parser = ArgumentParser() parser.add_argument("simulator", type=str, choices=["flow", "eclipse", "e300"]) - parser.add_argument("version", type=str) parser.add_argument("ecl_case", type=str) + parser.add_argument("--version", type=str, default="") parser.add_argument("-n", "--num-cpu", dest="num_cpu", type=int, default=1) parser.add_argument( "-i", "--ignore-errors", dest="ignore_errors", action="store_true" @@ -389,6 +389,10 @@ def run_reservoirsimulator(args: list[str]) -> None: options = parser.parse_args(args) + if not options.version: + raise RuntimeError( + f"The version to {options.simulator} must be explicitly passed" + ) if options.summary_conversion and options.simulator == "flow": raise RuntimeError("--summary-conversion is not available with simulator flow") diff --git a/test-data/everest/egg/everest/model/config_flow.yml b/test-data/everest/egg/everest/model/config_flow.yml index be1a3e27419..3d1110697ac 100644 --- a/test-data/everest/egg/everest/model/config_flow.yml +++ b/test-data/everest/egg/everest/model/config_flow.yml @@ -97,11 +97,6 @@ simulator: queue_system: local cores: 3 -install_jobs: - - - name: myflow - source: r{{ configpath }}/jobs/flow - install_data: - source: r{{ configpath }}/../../eclipse/include/realizations/realization-r{{ realization }}/eclipse @@ -116,5 +111,5 @@ forward_model: - well_constraints -i files/well_readydate.json -c files/wc_config.yml -rc well_rate.json -o wc_wells.json - add_templates -i wc_wells.json -c files/at_config.yml -o at_wells.json - schmerge -s eclipse/include/schedule/schedule.tmpl -i at_wells.json -o eclipse/include/schedule/schedule.sch - - myflow r{{ eclbase }} --enable-tuning + - flow r{{ eclbase }} --enable-tuning - rf -s r{{ eclbase }} -o rf diff --git a/test-data/everest/egg/everest/model/jobs/flow b/test-data/everest/egg/everest/model/jobs/flow deleted file mode 100644 index ae96ce70280..00000000000 --- a/test-data/everest/egg/everest/model/jobs/flow +++ /dev/null @@ -1,6 +0,0 @@ -EXECUTABLE ./flow.py -STDOUT flow.stdout -STDERR flow.stderr - -TARGET_FILE flow.ok -ARGLIST diff --git a/test-data/everest/egg/everest/model/jobs/flow.py b/test-data/everest/egg/everest/model/jobs/flow.py deleted file mode 100755 index 28a47a07a1d..00000000000 --- a/test-data/everest/egg/everest/model/jobs/flow.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -import argparse -import os -import subprocess - -arg_parser = argparse.ArgumentParser(description="Run a flow job") -arg_parser.add_argument("input", help="Input directory") -arg_parser.add_argument("--flow-path", help="Path to the flow binary", default="flow") -arg_parser.add_argument("--enable-tuning", help="Enable tuning", action="store_true") -options = arg_parser.parse_args() - -call_args = [options.flow_path, options.input] -if options.enable_tuning: - call_args.append("--enable-tuning=1") -flow_process = subprocess.call(call_args, cwd=os.getcwd()) - -with open("flow.ok", "w", encoding="utf-8"): - pass diff --git a/tests/ert/unit_tests/resources/test_run_eclipse_simulator.py b/tests/ert/unit_tests/resources/test_run_eclipse_simulator.py index a093ca1bb1d..6bd07c7e6e3 100644 --- a/tests/ert/unit_tests/resources/test_run_eclipse_simulator.py +++ b/tests/ert/unit_tests/resources/test_run_eclipse_simulator.py @@ -100,7 +100,9 @@ def test_runeclrun_argparse_api(source_root): source_root / "test-data/ert/eclipse/SPE1.DATA", "SPE1.DATA", ) - run_reservoirsimulator.run_reservoirsimulator(["eclipse", "2019.3", "SPE1.DATA"]) + run_reservoirsimulator.run_reservoirsimulator( + ["eclipse", "SPE1.DATA", "--version", "2019.3"] + ) assert Path("SPE1.OK").exists() @@ -116,7 +118,9 @@ def test_eclrun_when_unsmry_is_ambiguous(source_root): # Mock files from another existing run Path("PREVIOUS_SPE1.SMSPEC").touch() Path("PREVIOUS_SPE1.UNSMRY").touch() - run_reservoirsimulator.run_reservoirsimulator(["eclipse", "2019.3", "SPE1.DATA"]) + run_reservoirsimulator.run_reservoirsimulator( + ["eclipse", "SPE1.DATA", "--version", "2019.3"] + ) assert Path("SPE1.OK").exists() @@ -131,7 +135,7 @@ def test_eclrun_when_unsmry_is_ambiguous_with_mpi(source_root): Path("PREVIOUS_SPE1.SMSPEC").touch() Path("PREVIOUS_SPE1.UNSMRY").touch() run_reservoirsimulator.run_reservoirsimulator( - ["eclipse", "2019.3", "SPE1.DATA", "--num-cpu=2"] + ["eclipse", "SPE1.DATA", "--version", "2019.3", "--num-cpu=2"] ) assert Path("SPE1.OK").exists() @@ -144,7 +148,7 @@ def test_ecl_run_on_parallel_deck(source_root): deck = deck.replace("TITLE", "PARALLEL\n 2 /\n\nTITLE") Path("SPE1.DATA").write_text(deck, encoding="utf-8") run_reservoirsimulator.run_reservoirsimulator( - ["eclipse", "2019.3", "SPE1.DATA", "--num-cpu=2"] + ["eclipse", "SPE1.DATA", "--version", "2019.3", "--num-cpu=2"] ) assert Path("SPE1.OK").exists() @@ -156,7 +160,9 @@ def test_eclrun_on_nosim(source_root): deck = (source_root / "test-data/ert/eclipse/SPE1.DATA").read_text(encoding="utf-8") deck = deck.replace("TITLE", "NOSIM\n\nTITLE") Path("SPE1.DATA").write_text(deck, encoding="utf-8") - run_reservoirsimulator.run_reservoirsimulator(["eclipse", "2019.3", "SPE1.DATA"]) + run_reservoirsimulator.run_reservoirsimulator( + ["eclipse", "SPE1.DATA", "--version", "2019.3"] + ) assert Path("SPE1.OK").exists() assert not Path("SPE1.UNSMRY").exists() @@ -170,7 +176,9 @@ def test_eclrun_on_nosim_with_existing_unsmry_file(source_root): deck = deck.replace("TITLE", "NOSIM\n\nTITLE") Path("SPE1.UNSMRY").write_text("", encoding="utf-8") Path("SPE1.DATA").write_text(deck, encoding="utf-8") - run_reservoirsimulator.run_reservoirsimulator(["eclipse", "2019.3", "SPE1.DATA"]) + run_reservoirsimulator.run_reservoirsimulator( + ["eclipse", "SPE1.DATA", "--version", "2019.3"] + ) assert Path("SPE1.OK").exists() @@ -181,7 +189,7 @@ def test_await_completed_summary_file_does_not_time_out_on_nosim_with_mpi(source deck = deck.replace("TITLE", "NOSIM\n\nPARALLEL\n 2 /\n\nTITLE") Path("SPE1.DATA").write_text(deck, encoding="utf-8") run_reservoirsimulator.run_reservoirsimulator( - ["eclipse", "2019.3", "SPE1.DATA", "--num-cpu=2"] + ["eclipse", "SPE1.DATA", "--version", "2019.3", "--num-cpu=2"] ) assert Path("SPE1.OK").exists() assert not Path( @@ -207,7 +215,7 @@ def test_eclrun_on_nosim_with_mpi_and_existing_unsmry_file(source_root): Path("SPE1.UNSMRY").write_text("", encoding="utf-8") Path("SPE1.DATA").write_text(deck, encoding="utf-8") run_reservoirsimulator.run_reservoirsimulator( - ["eclipse", "2019.3", "SPE1.DATA", "--num-cpu=2"] + ["eclipse", "SPE1.DATA", "--version", "2019.3", "--num-cpu=2"] ) # There is no assert on runtime because we cannot predict how long the Eclipse license # checkout takes, otherwise we should assert that there is no await for unsmry completion. @@ -223,7 +231,7 @@ def test_eclrun_will_raise_on_deck_errors(source_root): "SPE1_ERROR.DATA", ) erun = run_reservoirsimulator.RunReservoirSimulator( - "eclipse", "2019.3", "SPE1_ERROR" + "eclipse", "SPE1_ERROR", "--version", "2019.3" ) with pytest.raises(Exception, match="ERROR"): erun.run_eclipseX00() @@ -236,7 +244,7 @@ def test_failed_run_gives_nonzero_returncode_and_exception(monkeypatch): deck = Path("MOCKED_DECK.DATA") deck.touch() erun = run_reservoirsimulator.RunReservoirSimulator( - "eclipse", "dummy_version", deck.name + "eclipse", deck.name, "--version", "dummy_version" ) return_value_with_code = mock.MagicMock() return_value_with_code.returncode = 1 @@ -260,7 +268,7 @@ def test_deck_errors_can_be_ignored(source_root): "SPE1_ERROR.DATA", ) run_reservoirsimulator.run_reservoirsimulator( - ["eclipse", "2019.3", "SPE1_ERROR.DATA", "--ignore-errors"] + ["eclipse", "SPE1_ERROR.DATA", "--version", "2019.3", "--ignore-errors"] ) @@ -273,7 +281,7 @@ def test_flag_needed_to_produce_hdf5_output_with_ecl100(source_root): "SPE1.DATA", ) run_reservoirsimulator.run_reservoirsimulator( - ["eclipse", "2019.3", "SPE1.DATA", "--summary-conversion"] + ["eclipse", "SPE1.DATA", "--version", "2019.3", "--summary-conversion"] ) assert Path("SPE1.h5").exists() @@ -290,7 +298,7 @@ def test_mpi_run_is_managed_by_system_tool(source_root): r"PARALLEL\s+2", Path("SPE1_PARALLEL.DATA").read_text(encoding="utf-8") ), "Test requires a deck needing 2 CPUs" run_reservoirsimulator.run_reservoirsimulator( - ["eclipse", "2019.3", "SPE1_PARALLEL.DATA"] + ["eclipse", "SPE1_PARALLEL.DATA", "--version", "2019.3"] ) assert Path("SPE1_PARALLEL.PRT").stat().st_size > 0, "Eclipse did not run at all"