Skip to content

Commit 0f11d30

Browse files
committed
Validate DESIGN_MATRIX when running DESIGN2PARAMS
This commit makes us run validation for DESIGN_MATRIX when parsing configs using DESIGN2PARAMS, and log a warning if it would have failed.
1 parent 45d11cc commit 0f11d30

File tree

3 files changed

+109
-3
lines changed

3 files changed

+109
-3
lines changed

src/ert/config/design_matrix.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __post_init__(self) -> None:
3333
self.active_realizations,
3434
self.design_matrix_df,
3535
self.parameter_configuration,
36-
) = self.read_design_matrix()
36+
) = self.read_and_validate_design_matrix()
3737
except (ValueError, AttributeError) as exc:
3838
raise ConfigValidationError.with_context(
3939
f"Error reading design matrix {self.xls_filename}: {exc}",
@@ -159,7 +159,7 @@ def merge_with_existing_parameters(
159159
new_param_config += [parameter_group]
160160
return new_param_config, design_parameter_group
161161

162-
def read_design_matrix(
162+
def read_and_validate_design_matrix(
163163
self,
164164
) -> tuple[list[bool], pd.DataFrame, GenKwConfig]:
165165
# Read the parameter names (first row) as strings to prevent pandas from modifying them.

src/ert/config/ert_config.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from pydantic import field_validator
2424
from pydantic.dataclasses import dataclass, rebuild_dataclass
2525

26+
from ert.config.design_matrix import DesignMatrix
2627
from ert.config.parsing.context_values import ContextBoolEncoder
2728
from ert.plugins import ErtPluginManager
2829
from ert.plugins.workflow_config import ErtScriptWorkflow
@@ -459,7 +460,7 @@ def create_list_of_forward_model_steps_to_run(
459460
env_pr_fm_step: dict[str, dict[str, Any]],
460461
) -> list[ForwardModelStep]:
461462
errors = []
462-
fm_steps = []
463+
fm_steps: list[ForwardModelStep] = []
463464

464465
env_vars = {}
465466
for key, val in config_dict.get("SETENV", []):
@@ -510,7 +511,17 @@ def create_list_of_forward_model_steps_to_run(
510511
if should_add_step:
511512
fm_steps.append(fm_step)
512513

514+
design_matrices: list[DesignMatrix] = []
513515
for fm_step in fm_steps:
516+
if fm_step.name == "DESIGN2PARAMS":
517+
xls_filename = fm_step.private_args.get("<xls_filename>")
518+
designsheet = fm_step.private_args.get("<designsheet>")
519+
defaultsheet = fm_step.private_args.get("<defaultssheet>")
520+
if design_matrix := validate_ert_design_matrix(
521+
xls_filename, designsheet, defaultsheet
522+
):
523+
design_matrices.append(design_matrix)
524+
514525
if fm_step.name in preinstalled_forward_model_steps:
515526
try:
516527
substituted_json = create_forward_model_json(
@@ -539,6 +550,15 @@ def create_list_of_forward_model_steps_to_run(
539550
f"Unexpected plugin forward model exception: {e!s}",
540551
context=fm_step.name,
541552
)
553+
try:
554+
main_design_matrix: DesignMatrix | None = None
555+
for design_matrix in design_matrices:
556+
if main_design_matrix is None:
557+
main_design_matrix = design_matrix
558+
else:
559+
main_design_matrix = main_design_matrix.merge_with_other(design_matrix)
560+
except Exception as exc:
561+
logger.warning(f"Design matrix merging would have failed due to: {exc}")
542562

543563
if errors:
544564
raise ConfigValidationError.from_collected(errors)
@@ -1253,5 +1273,16 @@ def _forward_model_step_from_config_file(
12531273
)
12541274

12551275

1276+
def validate_ert_design_matrix(
1277+
xlsfilename, designsheetname, defaultssheetname
1278+
) -> DesignMatrix | None:
1279+
try:
1280+
return DesignMatrix(xlsfilename, designsheetname, defaultssheetname)
1281+
except Exception as exc:
1282+
logger.warning(
1283+
f"DESIGN_MATRIX validation of DESIGN2PARAMS would have failed with: {exc!s}"
1284+
)
1285+
1286+
12561287
# Due to circular dependency in type annotations between ErtConfig -> WorkflowJob -> ErtScript -> ErtConfig
12571288
rebuild_dataclass(ErtConfig)

tests/ert/unit_tests/config/test_ert_config.py

+75
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
from textwrap import dedent
1111
from unittest.mock import MagicMock
1212

13+
import pandas as pd
1314
import pytest
1415
from hypothesis import HealthCheck, assume, given, settings
1516
from hypothesis import strategies as st
1617
from pydantic import RootModel, TypeAdapter
1718

1819
from ert.config import ConfigValidationError, ErtConfig, HookRuntime
1920
from ert.config.ert_config import _split_string_into_sections, create_forward_model_json
21+
from ert.config.forward_model_step import ForwardModelStep
2022
from ert.config.parsing import ConfigKeys, ConfigWarning
2123
from ert.config.parsing.context_values import (
2224
ContextBool,
@@ -29,6 +31,7 @@
2931
from ert.config.parsing.queue_system import QueueSystem
3032
from ert.plugins import ErtPluginManager
3133
from ert.shared import ert_share_path
34+
from tests.ert.ui_tests.cli.analysis.test_design_matrix import _create_design_matrix
3235

3336
from .config_dict_generator import config_generators
3437

@@ -1991,3 +1994,75 @@ def run(self, *args):
19911994

19921995
assert ert_config.substitutions["<FOO>"] == "ertconfig_foo"
19931996
assert ert_config.substitutions["<FOO2>"] == "ertconfig_foo2"
1997+
1998+
1999+
def test_design2params_also_validates_design_matrix(tmp_path, caplog, monkeypatch):
2000+
design_matrix_file = tmp_path / "my_design_matrix.xlsx"
2001+
_create_design_matrix(
2002+
design_matrix_file,
2003+
pd.DataFrame(
2004+
{
2005+
"REAL": ["not_a_valid_real"],
2006+
"a": [1],
2007+
"category": ["cat1"],
2008+
}
2009+
),
2010+
pd.DataFrame([["b", 1], ["c", 2]]),
2011+
)
2012+
mock_design2params = ForwardModelStep(
2013+
"DESIGN2PARAMS",
2014+
"/usr/bin/env",
2015+
arglist=["<IENS>", "<xls_filename>", "<designsheet>", "<defaultssheet>"],
2016+
)
2017+
monkeypatch.setattr(
2018+
ErtConfig,
2019+
"PREINSTALLED_FORWARD_MODEL_STEPS",
2020+
{"DESIGN2PARAMS": mock_design2params},
2021+
)
2022+
ErtConfig.from_file_contents(
2023+
f"NUM_REALIZATIONS 1\nFORWARD_MODEL DESIGN2PARAMS(<xls_filename>={design_matrix_file}, <designsheet>=DesignSheet01,<defaultssheet>=DefaultSheet)"
2024+
)
2025+
assert "DESIGN_MATRIX validation of DESIGN2PARAMS" in caplog.text
2026+
2027+
2028+
def test_multiple_design2params_also_validates_design_matrix_merging(
2029+
tmp_path, caplog, monkeypatch
2030+
):
2031+
design_matrix_file = tmp_path / "my_design_matrix.xlsx"
2032+
design_matrix_file2 = tmp_path / "my_design_matrix2.xlsx"
2033+
_create_design_matrix(
2034+
design_matrix_file,
2035+
pd.DataFrame(
2036+
{
2037+
"REAL": [0, 1],
2038+
"letters": ["x", "y"],
2039+
}
2040+
),
2041+
pd.DataFrame([["a", 1], ["c", 2]]),
2042+
)
2043+
_create_design_matrix(
2044+
design_matrix_file2,
2045+
pd.DataFrame({"REAL": [1, 2], "numbers": [99, 98]}),
2046+
pd.DataFrame(),
2047+
)
2048+
mock_design2params = ForwardModelStep(
2049+
"DESIGN2PARAMS",
2050+
"/usr/bin/env",
2051+
arglist=["<IENS>", "<xls_filename>", "<designsheet>", "<defaultssheet>"],
2052+
)
2053+
monkeypatch.setattr(
2054+
ErtConfig,
2055+
"PREINSTALLED_FORWARD_MODEL_STEPS",
2056+
{"DESIGN2PARAMS": mock_design2params},
2057+
)
2058+
ErtConfig.from_file_contents(
2059+
f"""\
2060+
NUM_REALIZATIONS 1
2061+
FORWARD_MODEL DESIGN2PARAMS(<xls_filename>={design_matrix_file}, <designsheet>=DesignSheet01,<defaultssheet>=DefaultSheet)
2062+
FORWARD_MODEL DESIGN2PARAMS(<xls_filename>={design_matrix_file2}, <designsheet>=DesignSheet01,<defaultssheet>=DefaultSheet)
2063+
"""
2064+
)
2065+
assert (
2066+
"Design matrix merging would have failed due to: Design Matrices don't have the same active realizations"
2067+
in caplog.text
2068+
)

0 commit comments

Comments
 (0)