Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/ert/observation_converters/dispatcher.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from __future__ import annotations

from collections.abc import Callable
from enum import StrEnum
from typing import Any

from ert.cli.main import ErtCliError
from ert.namespace import Namespace
from ert.plugins import ErtRuntimePlugins

from .history_to_summary import convert_history_to_summary
from .summary_to_bulk import (
Expand All @@ -16,15 +18,15 @@ class SupportedFormat(StrEnum):
BULK = "bulk"


ConverterFunction = Callable[[str], None]
ConverterFunction = Callable[[str, ErtRuntimePlugins], None]

_SUPPORTED_CONVERSIONS: dict[SupportedFormat, ConverterFunction] = {
SupportedFormat.BULK: convert_summary_to_bulk,
SupportedFormat.SUMMARY: convert_history_to_summary,
}


def convert_observations(args: Namespace, _site_plugins: Any | None = None) -> None:
def convert_observations(args: Namespace, site_plugins: ErtRuntimePlugins) -> None:
converter_func = _SUPPORTED_CONVERSIONS.get(args.format)

if converter_func is None:
Expand All @@ -35,4 +37,4 @@ def convert_observations(args: Namespace, _site_plugins: Any | None = None) -> N
f"{supported_formats}"
)

converter_func(args.config)
converter_func(args.config, site_plugins)
Comment thread
ajaust marked this conversation as resolved.
17 changes: 15 additions & 2 deletions src/ert/observation_converters/summary_to_bulk.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations

import warnings
from collections import defaultdict
from dataclasses import fields
from pathlib import Path
Expand All @@ -7,13 +10,15 @@

from ert.cli.main import ErtCliError
from ert.config import (
ConfigValidationError,
ErtConfig,
Observation,
ShapeRegistry,
SummaryKeyData,
make_summary_key_data,
)
from ert.config._shapes import CircleShapeConfig
from ert.plugins import ErtRuntimePlugins

INDENT2 = " " * 2
INDENT4 = " " * 4
Expand Down Expand Up @@ -221,8 +226,16 @@ def print_bulk_config(
)


def convert_summary_to_bulk(config: str) -> None:
ert_config = ErtConfig.from_file(config)
def convert_summary_to_bulk(config: str, runtime_plugins: ErtRuntimePlugins) -> None:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
Comment thread
ajaust marked this conversation as resolved.
try:
ert_config = ErtConfig.with_plugins(runtime_plugins).from_file(config)
except ConfigValidationError as e:
raise ErtCliError(
f"Failed to internalize the ert config '{config}' with error:\n {e}"
) from e

if any(
obs.type == "summary_observation" for obs in ert_config.observation_declarations
):
Expand Down
96 changes: 81 additions & 15 deletions tests/ert/unit_tests/cli/test_summary_to_bulk.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
BulkConfigConverter,
_breakthrough_to_string,
)
from ert.plugins import ErtRuntimePlugins, get_site_plugins


@pytest.mark.usefixtures("snake_oil_case")
Expand All @@ -28,7 +29,7 @@ def test_that_happy_path_on_snake_oil_produces_csv_and_stdout(capsys):
stdout and moving the csv file into the observations folder.
"""
args = MagicMock(format="bulk", config="snake_oil.ert")
convert_observations(args)
convert_observations(args, ErtRuntimePlugins())

assert Path("summary_observations.csv").is_file()
csv_content = Path("summary_observations.csv").read_text(encoding="utf-8")
Expand Down Expand Up @@ -349,7 +350,7 @@ def test_that_combination_of_precisions_is_maintained_in_csv_conversion(
def test_that_invalid_format_raises_cli_error():
args = MagicMock(format="Foo")
with pytest.raises(ErtCliError):
convert_observations(args)
convert_observations(args, site_plugins=ErtRuntimePlugins())


def test_that_breakthrough_to_string_strips_hour_minute_second_from_date_precision():
Expand Down Expand Up @@ -380,19 +381,84 @@ def test_that_breakthrough_to_string_mainains_hour_minute_and_second_precision()
assert f"DATE={precision};" in res


@pytest.mark.usefixtures("snake_oil_case")
def test_that_no_summary_observations_raises_ert_cli_error():
Path("observations/observations.txt").write_text(
"""
GENERAL_OBSERVATION WPR_DIFF_1 {
DATA = SNAKE_OIL_WPR_DIFF;
INDEX_LIST = 400,800,1200,1800;
RESTART = 199;
OBS_FILE = wpr_diff_obs.txt;
};
""",
def test_that_no_summary_observations_raises_ert_cli_error(use_tmpdir):
obs_config = "foo"
brt_obs = (
"BREAKTHROUGH_OBSERVATION "
"{ KEY = FOPR; THRESHOLD = 10; ERROR = 5; DATE = 2000-01-01; };"
)
Path(obs_config).write_text(
brt_obs,
encoding="utf-8",
)
args = MagicMock(format="bulk", config="snake_oil.ert")

ert_config = "config.ert"
minimal_ert_config = f"""\
NUM_REALIZATIONS 10
ECLBASE foo
OBS_CONFIG {obs_config}
"""
Path(ert_config).write_text(minimal_ert_config, encoding="utf-8")

args = MagicMock(format="bulk", config=ert_config)
with pytest.raises(ErtCliError, match="No summary observations found"):
convert_observations(args)
convert_observations(args, ErtRuntimePlugins())


def test_that_errors_are_formatted_to_user_with_message(use_tmpdir):
obs_config = "foo"
summary_obs = "SUMMARY_OBSERVATION { This is not a valid observation };"
Path(obs_config).write_text(
summary_obs,
encoding="utf-8",
)

ert_config = "config.ert"
minimal_ert_config = f"""\
NUM_REALIZATIONS 10
ECLBASE foo
OBS_CONFIG {obs_config}
"""
Path(ert_config).write_text(minimal_ert_config, encoding="utf-8")
args = MagicMock(format="bulk", config=ert_config)
with pytest.raises(ErtCliError, match="Failed to internalize the ert config"):
convert_observations(args, ErtRuntimePlugins())


def test_that_convert_observations_does_not_fail_when_config_has_hooked_workflows(
use_tmpdir,
):
"""This reproduces the case where ErtConfig.from_file() is called without
plugins while hooked workflows reference plugin-provided jobs.
"""
site_plugins = get_site_plugins()

arbitrary_existing_job = next(iter(site_plugins.installed_workflow_jobs))

workflow_file = Path("my_hook_workflow")
workflow_file.write_text(f"{arbitrary_existing_job}\n", encoding="utf-8")

obs_config = "foo"
summary_obs = (
"SUMMARY_OBSERVATION { KEY = FOPR; VALUE = 10; ERROR = 5; DATE = 2000-01-01; };"
)
Path(obs_config).write_text(
summary_obs,
encoding="utf-8",
)

ert_config = "config.ert"
minimal_workflow_config = f"""\
NUM_REALIZATIONS 10
ECLBASE foo
OBS_CONFIG {obs_config}
LOAD_WORKFLOW {workflow_file} MY_HOOK
HOOK_WORKFLOW MY_HOOK PRE_SIMULATION
"""
Path(ert_config).write_text(
minimal_workflow_config,
encoding="utf-8",
)

args = MagicMock(format="bulk", config=ert_config)
convert_observations(args, site_plugins)
Loading