Skip to content

Commit 63f2313

Browse files
committed
Internalize run_tepmlates into storage
All templates will be part of storage and thus restart could use only the storage templates. Additionally, this removes templates param from create_run_path, but it needs to be specified directly when calling create_experiment. It requires storage migration for run_templates.
1 parent 87d745b commit 63f2313

16 files changed

+82
-30
lines changed

src/ert/enkf_main.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ def create_run_path(
214214
env_pr_fm_step: dict[str, dict[str, Any]],
215215
forward_model_steps: list[ForwardModelStep],
216216
substitutions: Substitutions,
217-
templates: list[tuple[str, str]],
218217
parameters_file: str,
219218
runpaths: Runpaths,
220219
context_env: dict[str, str] | None = None,
@@ -226,7 +225,7 @@ def create_run_path(
226225
run_path = Path(run_arg.runpath)
227226
if run_arg.active:
228227
run_path.mkdir(parents=True, exist_ok=True)
229-
for source_file, target_file in templates:
228+
for source_file, target_file in ensemble.experiment.templates_configuration:
230229
target_file = substitutions.substitute_real_iter(
231230
target_file, run_arg.iens, ensemble.iteration
232231
)

src/ert/gui/tools/manage_experiments/storage_widget.py

+1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ def _addItem(self) -> None:
176176
responses=self._ert_config.ensemble_config.response_configuration,
177177
observations=self._ert_config.enkf_obs.datasets,
178178
name=create_experiment_dialog.experiment_name,
179+
templates=self._ert_config.ert_templates,
179180
).create_ensemble(
180181
name=create_experiment_dialog.ensemble_name,
181182
ensemble_size=self._ensemble_size,

src/ert/run_models/base_run_model.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,7 @@
7171
from ert.utils import log_duration
7272
from ert.workflow_runner import WorkflowRunner
7373

74-
from ..plugins.workflow_fixtures import (
75-
create_workflow_fixtures_from_hooked,
76-
)
74+
from ..plugins.workflow_fixtures import create_workflow_fixtures_from_hooked
7775
from ..run_arg import RunArg
7876
from .event import (
7977
AnalysisStatusEvent,
@@ -777,7 +775,6 @@ def _evaluate_and_postprocess(
777775
env_pr_fm_step=self._env_pr_fm_step,
778776
forward_model_steps=self._forward_model_steps,
779777
substitutions=self._substitutions,
780-
templates=self._templates,
781778
parameters_file=self._model_config.gen_kw_export_name,
782779
runpaths=self.run_paths,
783780
context_env=self._context_env,

src/ert/run_models/ensemble_experiment.py

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def run_experiment(
107107
),
108108
observations=self._observations,
109109
responses=self._response_configuration,
110+
templates=self._templates,
110111
)
111112
self.ensemble = self._storage.create_ensemble(
112113
self.experiment,

src/ert/run_models/ensemble_smoother.py

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def run_experiment(
104104
observations=self._observations,
105105
responses=self._response_configuration,
106106
name=self.experiment_name,
107+
templates=self._templates,
107108
)
108109

109110
self.set_env_key("_ERT_EXPERIMENT_ID", str(experiment.id))

src/ert/run_models/multiple_data_assimilation.py

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ def run_experiment(
154154
responses=self._response_configuration,
155155
simulation_arguments=sim_args,
156156
name=self.experiment_name,
157+
ert_templates=self._templates,
157158
)
158159

159160
prior = self._storage.create_ensemble(

src/ert/storage/local_experiment.py

+27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
import shutil
45
from collections.abc import Generator
56
from datetime import datetime
67
from functools import cached_property
@@ -47,6 +48,7 @@ class LocalExperiment(BaseMode):
4748
_parameter_file = Path("parameter.json")
4849
_responses_file = Path("responses.json")
4950
_metadata_file = Path("metadata.json")
51+
_templates_file = Path("templates.json")
5052

5153
def __init__(
5254
self,
@@ -86,6 +88,7 @@ def create(
8688
observations: dict[str, pl.DataFrame] | None = None,
8789
simulation_arguments: dict[Any, Any] | None = None,
8890
name: str | None = None,
91+
templates: list[tuple[str, str]] | None = None,
8992
) -> LocalExperiment:
9093
"""
9194
Create a new LocalExperiment and store its configuration data.
@@ -108,6 +111,8 @@ def create(
108111
Simulation arguments for the experiment.
109112
name : str, optional
110113
Experiment name. Defaults to current date if None.
114+
templates : list of tuple[str, str], optional
115+
Run templates for the experiment. Defaults to None.
111116
112117
Returns
113118
-------
@@ -130,6 +135,18 @@ def create(
130135
json.dumps(parameter_data, indent=2).encode("utf-8"),
131136
)
132137

138+
if templates:
139+
templates_abs: list[tuple[str, str]] = []
140+
for src, dst in templates:
141+
incoming_template_file_path = Path(src)
142+
template_file_path = Path(path / incoming_template_file_path.name)
143+
shutil.copyfile(incoming_template_file_path, template_file_path)
144+
templates_abs.append((str(template_file_path.resolve()), dst))
145+
storage._write_transaction(
146+
path / cls._templates_file,
147+
json.dumps(templates_abs).encode("utf-8"),
148+
)
149+
133150
response_data = {}
134151
for response in responses or []:
135152
response_data.update({response.response_type: response.to_dict()})
@@ -248,6 +265,16 @@ def parameter_info(self) -> dict[str, Any]:
248265
info = json.load(f)
249266
return info
250267

268+
@cached_property
269+
def templates_configuration(self) -> list[tuple[str, str]]:
270+
try:
271+
with open(self.mount_point / self._templates_file, encoding="utf-8") as f:
272+
return json.load(f)
273+
except (FileNotFoundError, json.JSONDecodeError):
274+
pass
275+
# If the file is missing or broken, we return an empty list
276+
return []
277+
251278
@property
252279
def response_info(self) -> dict[str, Any]:
253280
info: dict[str, Any]

src/ert/storage/local_storage.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ def create_experiment(
307307
observations: dict[str, pl.DataFrame] | None = None,
308308
simulation_arguments: dict[Any, Any] | None = None,
309309
name: str | None = None,
310+
templates: list[tuple[str, str]] | None = None,
310311
) -> LocalExperiment:
311312
"""
312313
Creates a new experiment in the storage.
@@ -323,6 +324,8 @@ def create_experiment(
323324
The simulation arguments for the experiment.
324325
name : str, optional
325326
The name of the experiment.
327+
templates : list of tuple[str, str], optional
328+
Run templates for the experiment. Defaults to None.
326329
327330
Returns
328331
-------
@@ -343,6 +346,7 @@ def create_experiment(
343346
observations=observations,
344347
simulation_arguments=simulation_arguments,
345348
name=name,
349+
templates=templates,
346350
)
347351

348352
self._experiments[exp.id] = exp
@@ -459,6 +463,7 @@ def _migrate(self, version: int) -> None:
459463
to7,
460464
to8,
461465
to9,
466+
to10,
462467
)
463468

464469
try:
@@ -478,7 +483,8 @@ def _migrate(self, version: int) -> None:
478483

479484
logger.info("Blockfs storage backed up")
480485
print(
481-
dedent(f"""
486+
dedent(
487+
f"""
482488
Detected outdated storage (blockfs), which is no longer supported
483489
by ERT. Its contents are copied to:
484490
@@ -497,13 +503,14 @@ def _migrate(self, version: int) -> None:
497503
This is not guaranteed to work. Other than setting the custom
498504
ENSPATH, the ERT config should ideally be the same as it was
499505
when the old blockfs storage was created.
500-
""")
506+
"""
507+
)
501508
)
502509
return None
503510

504511
elif version < _LOCAL_STORAGE_VERSION:
505512
migrations = list(
506-
enumerate([to2, to3, to4, to5, to6, to7, to8, to9], start=1)
513+
enumerate([to2, to3, to4, to5, to6, to7, to8, to9, to10], start=1)
507514
)
508515
for from_version, migration in migrations[version - 1 :]:
509516
print(f"* Updating storage to version: {from_version + 1}")

src/ert/storage/migration/to10.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import json
2+
import shutil
3+
from pathlib import Path
4+
5+
from ert.storage.local_storage import local_storage_get_ert_config
6+
7+
info = "Internalizes run templates in to the storage"
8+
9+
10+
def migrate(path: Path) -> None:
11+
ert_config = local_storage_get_ert_config()
12+
if ert_config.ert_templates:
13+
for experiment in path.glob("experiments/*"):
14+
templates_abs: list[tuple[str, str]] = []
15+
for src, dst in ert_config.ert_templates:
16+
incoming_template_file_path = Path(src)
17+
template_file_path = Path(path / incoming_template_file_path.name)
18+
shutil.copyfile(incoming_template_file_path, template_file_path)
19+
templates_abs.append((str(template_file_path.resolve()), dst))
20+
with open(experiment / "templates.json", "w", encoding="utf-8") as fout:
21+
fout.write(json.dumps(templates_abs))

tests/ert/unit_tests/config/test_gen_kw_config.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@
77
import pytest
88
from lark import Token
99

10-
from ert.config import (
11-
ConfigValidationError,
12-
ConfigWarning,
13-
ErtConfig,
14-
GenKwConfig,
15-
)
10+
from ert.config import ConfigValidationError, ConfigWarning, ErtConfig, GenKwConfig
1611
from ert.config.gen_kw_config import TransformFunctionDefinition
1712
from ert.config.parsing import ContextString
1813
from ert.config.parsing.file_context_token import FileContextToken
@@ -235,7 +230,6 @@ def test_gen_kw_is_log_or_not(
235230
env_vars=ert_config.env_vars,
236231
env_pr_fm_step=ert_config.env_pr_fm_step,
237232
substitutions=ert_config.substitutions,
238-
templates=ert_config.ert_templates,
239233
parameters_file="parameters",
240234
)
241235
assert re.match(

tests/ert/unit_tests/conftest.py

+13
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ def prior_ensemble(storage):
3333
return storage.create_ensemble(experiment_id, name="prior", ensemble_size=100)
3434

3535

36+
@pytest.fixture
37+
def prior_ensemble_args(storage):
38+
def _create_prior_ensemble(
39+
ensemble_name="prior", ensemble_size=100, **experiment_params
40+
):
41+
experiment_id = storage.create_experiment(**experiment_params)
42+
return storage.create_ensemble(
43+
experiment_id, name=ensemble_name, ensemble_size=ensemble_size
44+
)
45+
46+
return _create_prior_ensemble
47+
48+
3649
@pytest.fixture
3750
def run_paths():
3851
def func(ert_config: ErtConfig):

tests/ert/unit_tests/storage/create_runpath.py

-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ def create_runpath(
4848
env_pr_fm_step=ert_config.env_pr_fm_step,
4949
forward_model_steps=ert_config.forward_model_steps,
5050
substitutions=ert_config.substitutions,
51-
templates=ert_config.ert_templates,
5251
parameters_file="parameters",
5352
runpaths=runpaths,
5453
)

tests/ert/unit_tests/test_enkf_main.py

-2
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ def test_assert_symlink_deleted(snake_oil_field_example, storage, run_paths):
9090
env_vars=ert_config.env_vars,
9191
env_pr_fm_step=ert_config.env_pr_fm_step,
9292
substitutions=ert_config.substitutions,
93-
templates=ert_config.ert_templates,
9493
parameters_file="parameters",
9594
runpaths=runpaths,
9695
)
@@ -112,7 +111,6 @@ def test_assert_symlink_deleted(snake_oil_field_example, storage, run_paths):
112111
env_vars=ert_config.env_vars,
113112
env_pr_fm_step=ert_config.env_pr_fm_step,
114113
substitutions=ert_config.substitutions,
115-
templates=ert_config.ert_templates,
116114
parameters_file="parameters",
117115
runpaths=runpaths,
118116
)

tests/ert/unit_tests/test_load_forward_model.py

-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def func(config_text):
3535
env_pr_fm_step=ert_config.env_pr_fm_step,
3636
forward_model_steps=ert_config.forward_model_steps,
3737
substitutions=ert_config.substitutions,
38-
templates=ert_config.ert_templates,
3938
parameters_file="parameters",
4039
runpaths=run_paths(ert_config),
4140
)
@@ -148,7 +147,6 @@ def test_load_forward_model_summary(
148147
env_pr_fm_step=ert_config.env_pr_fm_step,
149148
forward_model_steps=ert_config.forward_model_steps,
150149
substitutions=ert_config.substitutions,
151-
templates=ert_config.ert_templates,
152150
parameters_file="parameters",
153151
runpaths=run_paths(ert_config),
154152
)
@@ -253,7 +251,6 @@ def test_loading_gen_data_without_restart(storage, run_paths, run_args):
253251
env_pr_fm_step=ert_config.env_pr_fm_step,
254252
forward_model_steps=ert_config.forward_model_steps,
255253
substitutions=ert_config.substitutions,
256-
templates=ert_config.ert_templates,
257254
parameters_file="parameters",
258255
runpaths=run_paths(ert_config),
259256
)
@@ -318,7 +315,6 @@ def test_loading_from_any_available_iter(storage, run_paths, run_args, itr):
318315
env_pr_fm_step=ert_config.env_pr_fm_step,
319316
forward_model_steps=ert_config.forward_model_steps,
320317
substitutions=ert_config.substitutions,
321-
templates=ert_config.ert_templates,
322318
parameters_file="parameters",
323319
runpaths=run_paths(ert_config),
324320
)

tests/ert/unit_tests/test_run_path_creation.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
def make_run_path(run_paths, run_args, storage):
3737
def func(ert_config):
3838
experiment_id = storage.create_experiment(
39-
parameters=ert_config.ensemble_config.parameter_configuration
39+
parameters=ert_config.ensemble_config.parameter_configuration,
40+
templates=ert_config.ert_templates,
4041
)
4142
prior_ensemble = storage.create_ensemble(
4243
experiment_id, name="prior", ensemble_size=1
@@ -52,7 +53,6 @@ def func(ert_config):
5253
env_vars=ert_config.env_vars,
5354
env_pr_fm_step=ert_config.env_pr_fm_step,
5455
substitutions=ert_config.substitutions,
55-
templates=ert_config.ert_templates,
5656
parameters_file="parameters",
5757
runpaths=runpaths,
5858
)
@@ -101,7 +101,7 @@ def test_jobs_json_is_backed_up(make_run_path):
101101

102102
@pytest.mark.usefixtures("use_tmpdir")
103103
def test_that_run_template_replace_symlink_does_not_write_to_source(
104-
prior_ensemble, run_args, run_paths
104+
prior_ensemble_args, run_args, run_paths
105105
):
106106
"""This test is meant to test that we can have a symlinked file in the
107107
run path before we do replacement on a target file with the same name,
@@ -121,6 +121,7 @@ def test_that_run_template_replace_symlink_does_not_write_to_source(
121121
"""
122122
)
123123
)
124+
prior_ensemble = prior_ensemble_args(templates=ert_config.ert_templates)
124125
run_arg = run_args(ert_config, prior_ensemble)
125126
run_path = Path(run_arg[0].runpath)
126127
os.makedirs(run_path)
@@ -138,7 +139,6 @@ def test_that_run_template_replace_symlink_does_not_write_to_source(
138139
env_pr_fm_step=ert_config.env_pr_fm_step,
139140
forward_model_steps=ert_config.forward_model_steps,
140141
substitutions=ert_config.substitutions,
141-
templates=ert_config.ert_templates,
142142
parameters_file="parameters",
143143
runpaths=run_paths(ert_config),
144144
)
@@ -468,7 +468,6 @@ def test_write_runpath_file(storage, itr, run_paths):
468468
env_pr_fm_step=ert_config.env_pr_fm_step,
469469
forward_model_steps=ert_config.forward_model_steps,
470470
substitutions=ert_config.substitutions,
471-
templates=ert_config.ert_templates,
472471
parameters_file="parameters",
473472
runpaths=run_path,
474473
)
@@ -735,7 +734,6 @@ def test_when_manifest_files_are_written_forward_model_ok_succeeds(storage, itr)
735734
env_pr_fm_step=config.env_pr_fm_step,
736735
forward_model_steps=config.forward_model_steps,
737736
substitutions=config.substitutions,
738-
templates=config.ert_templates,
739737
parameters_file="parameters",
740738
runpaths=run_paths,
741739
)

tests/ert/unit_tests/test_summary_response.py

-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ def test_load_summary_response_restart_not_zero(
4747
env_vars=ert_config.env_vars,
4848
env_pr_fm_step=ert_config.env_pr_fm_step,
4949
substitutions=ert_config.substitutions,
50-
templates=ert_config.ert_templates,
5150
parameters_file="parameters",
5251
runpaths=run_paths(ert_config),
5352
)

0 commit comments

Comments
 (0)