Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make everest config imutable #9842

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
9 changes: 4 additions & 5 deletions src/everest/bin/everexport_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@ def everexport_entry(args=None):

# Turn into .export once
# explicit None is disallowed
if config.export is None:
config.export = ExportConfig()
export_config = config.export or ExportConfig()

if options.batches is not None:
batch_list = [int(item) for item in options.batches]
config.export.batches = batch_list
export_config.batches = batch_list

err_msgs, export_ecl = check_for_errors(
config=config.export,
config=export_config,
optimization_output_path=config.optimization_output_dir,
storage_path=config.storage_dir,
data_file_path=config.model.data_file,
Expand All @@ -41,7 +40,7 @@ def everexport_entry(args=None):
logger.warning(msg)

export_to_csv(
data_frame=export_with_progress(config, export_ecl),
data_frame=export_with_progress(config, export_config, export_ecl),
export_path=config.export_path,
)

Expand Down
10 changes: 6 additions & 4 deletions src/everest/bin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from pandas import DataFrame

from ert.resources import all_shell_script_fm_steps
from everest.config import EverestConfig
from everest.config import EverestConfig, ExportConfig
from everest.detached import (
OPT_PROGRESS_ID,
SIM_PROGRESS_ID,
Expand All @@ -30,20 +30,22 @@
ProgressBar = None # type: ignore


def export_with_progress(config: EverestConfig, export_ecl=True):
def export_with_progress(
config: EverestConfig, export_config: ExportConfig, export_ecl=True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

):
logging.getLogger(EVEREST).info("Exporting results to csv ...")
if ProgressBar is not None:
widgets = [Percentage(), " ", Bar(), " ", Timer(), " ", AdaptiveETA()]
with ProgressBar(max_value=1, widgets=widgets) as bar:
return export_data(
export_config=config.export,
export_config=export_config,
output_dir=config.output_dir,
data_file=config.model.data_file if config.model else None,
export_ecl=export_ecl,
progress_callback=bar.update,
)
return export_data(
export_config=config.export,
export_config=export_config,
output_dir=config.output_dir,
data_file=config.model.data_file if config.model else None,
export_ecl=export_ecl,
Expand Down
2 changes: 1 addition & 1 deletion src/everest/config/everest_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@
description="Settings to control the exports of a optimization run by everest.",
)
config_path: Path = Field()
model_config = ConfigDict(extra="forbid")
model_config = ConfigDict(extra="forbid", frozen=True)

@model_validator(mode="after")
def validate_queue_system(self) -> Self: # pylint: disable=E0213
if self.server is None:

Check failure on line 236 in src/everest/config/everest_config.py

View workflow job for this annotation

GitHub Actions / type-checking (3.12)

Property "server" defined in "EverestConfig" is read-only
self.server = ServerConfig(queue_system=copy(self.simulator.queue_system))
elif self.server.queue_system is None:
self.server.queue_system = copy(self.simulator.queue_system)
Expand Down
6 changes: 3 additions & 3 deletions src/everest/detached/jobs/everserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from ert.ensemble_evaluator import EvaluatorServerConfig
from ert.run_models.everest_run_model import EverestExitCode, EverestRunModel
from everest import export_to_csv, export_with_progress
from everest.config import EverestConfig, ServerConfig
from everest.config import EverestConfig, ExportConfig, ServerConfig
from everest.detached import ServerStatus, get_opt_status, update_everserver_status
from everest.export import check_for_errors
from everest.plugins.everest_plugin_manager import EverestPluginManager
Expand Down Expand Up @@ -346,9 +346,9 @@ def main():
logging.getLogger(EVEREST).warning(msg)
else:
export_ecl = True

export_config = config.export or ExportConfig()
export_to_csv(
data_frame=export_with_progress(config, export_ecl),
data_frame=export_with_progress(config, export_config, export_ecl),
export_path=config.export_path,
)
except:
Expand Down
17 changes: 11 additions & 6 deletions tests/everest/entry_points/test_everest_entry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
import os
from functools import partial
from pathlib import Path
from unittest.mock import patch

import pytest
Expand Down Expand Up @@ -503,14 +502,20 @@ def test_complete_status_for_normal_run_monitor(
"everest.bin.everest_script.everserver_status",
return_value={"status": ServerStatus.never_run, "message": None},
)
@patch(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff for this file in commit "Avoid everest export config overwriting" seems to be only an autoformatting + deletion of unused import?

"everest.simulator.everest_to_ert._everest_to_ert_config_dict",
return_value={"SUMMARY": "*"},
)
def test_validate_ert_config_before_starting_everest_server(
server_is_running_mock, server_status_mock, copy_math_func_test_data_to_tmp
server_is_running_mock,
server_status_mock,
_ert_config_mock,
copy_math_func_test_data_to_tmp,
):
config_file = "config_minimal.yml"
everest_config = EverestConfig.with_defaults()
everest_config.model.realizations = []
everest_config.model.realizations = [0]
everest_config.dump(config_file)
everest_config.config_path = Path(config_file)

with pytest.raises(SystemExit):
everest_entry([str(everest_config.config_path)])
with pytest.raises(SystemExit, match="Config validation error:"):
everest_entry([config_file])
13 changes: 8 additions & 5 deletions tests/everest/entry_points/test_everexport.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from everest import MetaDataColumnNames as MDCN
from everest.bin.everexport_script import everexport_entry
from everest.bin.utils import ProgressBar
from everest.config import EverestConfig, ExportConfig
from everest.config import EverestConfig
from tests.everest.utils import (
satisfy,
satisfy_callable,
Expand Down Expand Up @@ -47,12 +47,12 @@
)


def export_mock(config, export_ecl=True, progress_callback=lambda _: None):
def export_mock(config, _, export_ecl=True, progress_callback=lambda _: None):
progress_callback(1.0)
return TEST_DATA


def empty_mock(config, export_ecl=True, progress_callback=lambda _: None):
def empty_mock(config, _, export_ecl=True, progress_callback=lambda _: None):
progress_callback(1.0)
return pd.DataFrame()

Expand Down Expand Up @@ -129,8 +129,11 @@ def test_everexport_entry_no_export(mocked_func, cached_example):
set to true"""

config_path, config_file, _ = cached_example("math_func/config_minimal.yml")
config = EverestConfig.load_file(Path(config_path) / config_file)
config.export = ExportConfig(skip_export=True)
config_path = Path(config_path) / config_file
with open(config_path, "a", encoding="utf-8") as f:
f.write("export:\n skip_export: True")
config = EverestConfig.load_file(config_path)

# Add export section to config file and set run_export flag to false
export_file_path = config.export_path
assert not os.path.isfile(export_file_path)
Expand Down
22 changes: 8 additions & 14 deletions tests/everest/test_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
ControlVariableConfig,
ControlVariableGuessListConfig,
)
from everest.config.input_constraint_config import InputConstraintConfig
from everest.config.well_config import WellConfig
from tests.everest.utils import relpath

Expand Down Expand Up @@ -144,30 +143,25 @@ def test_variable_name_index_validation(copy_test_data_to_tmp):
# specifying index

config.controls[0].variables[1].name = "w01"
input_constraints = [
InputConstraintConfig.model_validate(
{"upper_bound": 1, "lower_bound": 0, "weights": {"group.w00": 0.1}}
)
config_dict = config.model_dump(exclude_none=True)
config_dict["input_constraints"] = [
{"upper_bound": 1, "lower_bound": 0, "weights": {"group.w00": 0.1}}
]

config.input_constraints = input_constraints
with pytest.raises(
ValidationError,
match="does not match any instance of "
"control_name\\.variable_name-variable_index",
):
EverestConfig.model_validate(config.model_dump(exclude_none=True))
EverestConfig.model_validate(config_dict)

# Index and name unique and valid and input constraints are specifying
# index
input_constraints = [
InputConstraintConfig(
**{"upper_bound": 1, "lower_bound": 0, "weights": {"group.w00-0": 0.1}}
)
config_dict = config.model_dump(exclude_none=True)
config_dict["input_constraints"] = [
{"upper_bound": 1, "lower_bound": 0, "weights": {"group.w00-0": 0.1}}
]

config.input_constraints = input_constraints
EverestConfig.model_validate(config.model_dump(exclude_none=True))
EverestConfig.model_validate(config_dict)


@pytest.mark.integration_test
Expand Down
23 changes: 10 additions & 13 deletions tests/everest/test_cvar.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,27 @@

from ert.run_models.everest_run_model import EverestRunModel
from everest.config import (
CVaRConfig,
EverestConfig,
ModelConfig,
OptimizationConfig,
)


@pytest.mark.integration_test
def test_mathfunc_cvar(
copy_math_func_test_data_to_tmp, evaluator_server_config_generator
):
# Arrange
config = EverestConfig.load_file("config_minimal.yml")
config.optimization = OptimizationConfig(
backend="scipy",
algorithm="slsqp",
cvar=CVaRConfig(percentile=0.5),
max_batch_num=5,
)
config.model = ModelConfig(realizations=[0, 1])
config.forward_model = [
config_dict = config.model_dump(exclude_none=True)
config_dict["optimization"] = {
"backend": "scipy",
"algorithm": "slsqp",
"cvar": {"percentile": 0.5},
"max_batch_num": 5,
}
config_dict["model"] = {"realizations": [0, 1]}
config_dict["forward_model"] = [
"distance3 --point-file point.json --realization <GEO_ID> --target 0.5 0.5 0.5 --out distance"
]

config = EverestConfig.model_validate(config_dict)
# Act
run_model = EverestRunModel.create(config)
evaluator_server_config = evaluator_server_config_generator(run_model)
Expand Down
4 changes: 3 additions & 1 deletion tests/everest/test_detached.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@ async def server_running():
monkeypatch.chdir(tmp_path / "new_folder")
everest_config = EverestConfig.with_defaults()
everest_config.dump("minimal_config.yml")
everest_config.config_path = Path("minimal_config.yml").absolute()
config_dict = everest_config.model_dump(exclude_none=True)
config_dict["config_path"] = str(Path("minimal_config.yml").absolute())
everest_config = EverestConfig.model_validate(config_dict)
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("PATH", f".:{os.environ['PATH']}")
everserver_path = Path("everserver")
Expand Down
50 changes: 24 additions & 26 deletions tests/everest/test_discrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

from ert.run_models.everest_run_model import EverestRunModel
from everest.config import (
ControlConfig,
EverestConfig,
InputConstraintConfig,
InstallJobConfig,
OptimizationConfig,
)


Expand All @@ -16,30 +12,32 @@ def test_discrete_optimizer(
):
# Arrange
config = EverestConfig.load_file("config_minimal.yml")
config.controls = [
ControlConfig(
name="point",
type="generic_control",
min=0,
max=10,
control_type="integer",
initial_guess=0,
variables=[{"name": "x"}, {"name": "y"}],
)
config_dict = config.model_dump(exclude_none=True)
Copy link
Contributor

@yngve-sk yngve-sk Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick/style suggestion: Would do ** notation to declare the object in one go rather than assigning props to the dict, similar for other cases assigning stuff to the config dict

config_dict["optimization"] = {
"backend": "scipy",
"algorithm": "differential_evolution",
"max_function_evaluations": 4,
"parallel": False,
"backend_options": {"seed": 9},
}

config_dict["controls"] = [
{
"name": "point",
"type": "generic_control",
"min": 0,
"max": 10,
"control_type": "integer",
"initial_guess": 0,
"variables": [{"name": "x"}, {"name": "y"}],
}
]
config.input_constraints = [
InputConstraintConfig(weights={"point.x": 1.0, "point.y": 1.0}, upper_bound=10)
config_dict["input_constraints"] = [
{"weights": {"point.x": 1.0, "point.y": 1.0}, "upper_bound": 10}
]
config.optimization = OptimizationConfig(
backend="scipy",
algorithm="differential_evolution",
max_function_evaluations=4,
parallel=False,
backend_options={"seed": 9},
)
config.install_jobs = [InstallJobConfig(name="discrete", source="jobs/DISCRETE")]
config.forward_model = ["discrete --point-file point.json --out distance"]

config_dict["install_jobs"] = [{"name": "discrete", "source": "jobs/DISCRETE"}]
config_dict["forward_model"] = ["discrete --point-file point.json --out distance"]
config = EverestConfig.model_validate(config_dict)
# Act
run_model = EverestRunModel.create(config)
evaluator_server_config = evaluator_server_config_generator(run_model)
Expand Down
8 changes: 4 additions & 4 deletions tests/everest/test_egg_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from ert.ensemble_evaluator import EvaluatorServerConfig
from ert.run_models.everest_run_model import EverestRunModel
from everest.config import EverestConfig
from everest.config.export_config import ExportConfig
from everest.export import export_data
from everest.simulator.everest_to_ert import _everest_to_ert_config_dict
from tests.everest.utils import (
Expand Down Expand Up @@ -626,12 +625,13 @@ def test_opm_fail_explicit_summary_keys(copy_egg_test_data_to_tmp):
"GWPT:PRODUC",
"GWIR:INJECT",
]

with open(CONFIG_FILE, "a", encoding="utf-8") as f:
f.write("export:\n skip_export: False")
config = EverestConfig.load_file(CONFIG_FILE)
# The Everest config file will fail to load as an Eclipse data file
config.model.data_file = os.path.realpath(CONFIG_FILE)
if "export" not in config:
config.export = ExportConfig()

assert config.export is not None

config.export.keywords = extra_sum_keys
assert len(EverestConfig.lint_config_dict(config.to_dict())) == 0
Expand Down
8 changes: 5 additions & 3 deletions tests/everest/test_everest_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ def test_save_running_config(_, _1, _2, _3, _4, copy_math_func_test_data_to_tmp)
new_config = EverestConfig.load_file("saved_config.yml")

# Ignore different config names
config.config_path = None
new_config.config_path = None
old_config_dict = config.model_dump(exclude_none=True)
old_config_dict["config_path"] = None
new_config_dict = new_config.model_dump(exclude_none=True)
new_config_dict["config_path"] = None

assert config == new_config
assert old_config_dict == new_config_dict
8 changes: 4 additions & 4 deletions tests/everest/test_everserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from seba_sqlite.snapshot import SebaSnapshot

from ert.run_models.everest_run_model import EverestExitCode
from everest.config import EverestConfig, OptimizationConfig, ServerConfig
from everest.config import EverestConfig, ServerConfig
from everest.detached import ServerStatus, everserver_status
from everest.detached.jobs import everserver
from everest.simulator import JOB_FAILURE, JOB_SUCCESS
Expand Down Expand Up @@ -206,9 +206,9 @@ def test_everserver_status_max_batch_num(
_1, mock_server, copy_math_func_test_data_to_tmp
):
config = EverestConfig.load_file("config_minimal.yml")
config.optimization = OptimizationConfig(
algorithm="optpp_q_newton", max_batch_num=1
)
config_dict = config.model_dump(exclude_none=True)
config_dict["optimization"] = {"algorithm": "optpp_q_newton", "max_batch_num": 1}
config = EverestConfig.model_validate(config_dict)
config.dump("config_minimal.yml")

everserver.main()
Expand Down
Loading
Loading