Skip to content

Commit

Permalink
Enable load results manually from any available iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
DanSava committed Oct 10, 2024
1 parent 6500c1d commit 313caf9
Show file tree
Hide file tree
Showing 16 changed files with 346 additions and 132 deletions.
77 changes: 61 additions & 16 deletions src/ert/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
import logging
import time
from pathlib import Path
from typing import Iterable

from ert.config import ParameterConfig, ResponseConfig
from ert.run_arg import RunArg
from ert.storage import Ensemble
from ert.storage.realization_storage_state import RealizationStorageState

from .load_status import LoadResult, LoadStatus
Expand All @@ -16,24 +15,27 @@


async def _read_parameters(
run_arg: RunArg, parameter_configuration: Iterable[ParameterConfig]
run_path: str,
realization: int,
ensemble: Ensemble,
) -> LoadResult:
result = LoadResult(LoadStatus.LOAD_SUCCESSFUL, "")
error_msg = ""
parameter_configuration = ensemble.experiment.parameter_configuration.values()
for config in parameter_configuration:
if not config.forward_init:
continue
try:
start_time = time.perf_counter()
logger.debug(f"Starting to load parameter: {config.name}")
ds = config.read_from_runpath(Path(run_arg.runpath), run_arg.iens)
ds = config.read_from_runpath(Path(run_path), realization)
await asyncio.sleep(0)
logger.debug(
f"Loaded {config.name}",
extra={"Time": f"{(time.perf_counter() - start_time):.4f}s"},
)
start_time = time.perf_counter()
run_arg.ensemble_storage.save_parameters(config.name, run_arg.iens, ds)
ensemble.save_parameters(config.name, realization, ds)
await asyncio.sleep(0)
logger.debug(
f"Saved {config.name} to storage",
Expand All @@ -42,36 +44,37 @@ async def _read_parameters(
except Exception as err:
error_msg += str(err)
result = LoadResult(LoadStatus.LOAD_FAILURE, error_msg)
logger.warning(f"Failed to load: {run_arg.iens}", exc_info=err)
logger.warning(f"Failed to load: {realization}", exc_info=err)
return result


async def _write_responses_to_storage(
run_arg: RunArg, response_configs: Iterable[ResponseConfig]
run_path: str,
realization: int,
ensemble: Ensemble,
) -> LoadResult:
errors = []
response_configs = ensemble.experiment.response_configuration.values()
for config in response_configs:
try:
start_time = time.perf_counter()
logger.debug(f"Starting to load response: {config.response_type}")
ds = config.read_from_file(run_arg.runpath, run_arg.iens)
ds = config.read_from_file(run_path, realization)
await asyncio.sleep(0)
logger.debug(
f"Loaded {config.response_type}",
extra={"Time": f"{(time.perf_counter() - start_time):.4f}s"},
)
start_time = time.perf_counter()
run_arg.ensemble_storage.save_response(
config.response_type, ds, run_arg.iens
)
ensemble.save_response(config.response_type, ds, realization)
await asyncio.sleep(0)
logger.debug(
f"Saved {config.response_type} to storage",
extra={"Time": f"{(time.perf_counter() - start_time):.4f}s"},
)
except ValueError as err:
errors.append(str(err))
logger.warning(f"Failed to write: {run_arg.iens}", exc_info=err)
logger.warning(f"Failed to write: {realization}", exc_info=err)
if errors:
return LoadResult(LoadStatus.LOAD_FAILURE, "\n".join(errors))
return LoadResult(LoadStatus.LOAD_SUCCESSFUL, "")
Expand All @@ -87,14 +90,16 @@ async def forward_model_ok(
# handles parameters
if run_arg.itr == 0:
parameters_result = await _read_parameters(
run_arg,
run_arg.ensemble_storage.experiment.parameter_configuration.values(),
run_arg.runpath,
run_arg.iens,
run_arg.ensemble_storage,
)

if parameters_result.status == LoadStatus.LOAD_SUCCESSFUL:
response_result = await _write_responses_to_storage(
run_arg,
run_arg.ensemble_storage.experiment.response_configuration.values(),
run_arg.runpath,
run_arg.iens,
run_arg.ensemble_storage,
)

except Exception as err:
Expand All @@ -115,3 +120,43 @@ async def forward_model_ok(
run_arg.ensemble_storage.unset_failure(run_arg.iens)

return final_result


async def load_realization(
run_path: str,
realization: int,
ensemble: Ensemble,
) -> LoadResult:
response_result = LoadResult(LoadStatus.LOAD_SUCCESSFUL, "")
try:
parameters_result = await _read_parameters(
run_path,
realization,
ensemble,
)

if parameters_result.status == LoadStatus.LOAD_SUCCESSFUL:
response_result = await _write_responses_to_storage(
run_path,
realization,
ensemble,
)

except Exception as err:
logger.exception(f"Failed to load results for realization {realization}")
parameters_result = LoadResult(
LoadStatus.LOAD_FAILURE,
"Failed to load results for realization "
f"{realization}, failed with: {err}",
)

final_result = parameters_result
if response_result.status != LoadStatus.LOAD_SUCCESSFUL:
final_result = response_result
ensemble.set_failure(
realization, RealizationStorageState.LOAD_FAILURE, final_result.message
)
elif ensemble.has_failure(realization):
ensemble.unset_failure(realization)

return final_result
10 changes: 5 additions & 5 deletions src/ert/config/gen_data_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ def from_config_dict(cls, config_dict: ConfigDict) -> Optional[Self]:
)

def read_from_file(self, run_path: str, _: int) -> polars.DataFrame:
def _read_file(filename: Path, report_step: int) -> polars.DataFrame:
if not filename.exists():
raise ValueError(f"Missing output file: {filename}")
data = np.loadtxt(_run_path / filename, ndmin=1)
active_information_file = _run_path / (str(filename) + "_active")
def _read_file(file_path: Path, report_step: int) -> polars.DataFrame:
if not file_path.exists():
raise ValueError(f"Missing output file: {file_path}")
data = np.loadtxt(file_path, ndmin=1)
active_information_file = _run_path / (file_path.name + "_active")
if active_information_file.exists():
active_list = np.loadtxt(active_information_file)
data[active_list == 0] = np.nan
Expand Down
2 changes: 2 additions & 0 deletions src/ert/gui/ertwidgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def wrapper(*arg: Any) -> Any:
from .ensembleselector import EnsembleSelector
from .checklist import CheckList
from .stringbox import StringBox
from .textbox import TextBox
from .listeditbox import ListEditBox
from .customdialog import CustomDialog
from .pathchooser import PathChooser
Expand Down Expand Up @@ -57,6 +58,7 @@ def wrapper(*arg: Any) -> Any:
"SelectableListModel",
"StringBox",
"TargetEnsembleModel",
"TextBox",
"TextModel",
"ValueModel",
"showWaitCursorWhileWaiting",
Expand Down
110 changes: 110 additions & 0 deletions src/ert/gui/ertwidgets/textbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Optional

from qtpy.QtGui import QPalette
from qtpy.QtWidgets import QTextEdit

from .validationsupport import ValidationSupport

if TYPE_CHECKING:
from ert.validation import StringDefinition

from .models import TextModel


class TextBox(QTextEdit):
"""StringBox shows a string. The data structure expected and sent to the
getter and setter is a string."""

def __init__(
self,
model: TextModel,
default_string: str = "",
placeholder_text: str = "",
minimum_width: int = 250,
):
"""
:type model: ert.gui.ertwidgets.models.valuemodel.ValueModel
:type help_link: str
:type default_string: str
"""
QTextEdit.__init__(self)
self.setMinimumWidth(minimum_width)
self._validation = ValidationSupport(self)
self._validator: Optional[StringDefinition] = None
self._model = model
self._enable_validation = True

if placeholder_text:
self.setPlaceholderText(placeholder_text)

self.textChanged.connect(self.textBoxChanged)
self.textChanged.connect(self.validateString)

self._valid_color = self.palette().color(self.backgroundRole())
self.setText(default_string)

self._model.valueChanged.connect(self.modelChanged)
self.modelChanged()

def validateString(self) -> None:
if self._enable_validation:
string_to_validate = self.get_text
if self._validator is not None:
status = self._validator.validate(string_to_validate)

palette = QPalette()
if not status:
palette.setColor(
self.backgroundRole(), ValidationSupport.ERROR_COLOR
)
self.setPalette(palette)
self._validation.setValidationMessage(
str(status), ValidationSupport.EXCLAMATION
)
else:
palette.setColor(self.backgroundRole(), self._valid_color)
self.setPalette(palette)
self._validation.setValidationMessage("")

def emitChange(self, q_string: Any) -> None:
self.textChanged.emit(str(q_string))

def textBoxChanged(self) -> None:
"""Called whenever the contents of the textbox changes."""
text: Optional[str] = self.toPlainText()
if not text:
text = None

self._model.setValue(text)

def modelChanged(self) -> None:
"""Retrieves data from the model and inserts it into the textbox"""
text = self._model.getValue()
if text is None:
text = ""
# If model and view has same text, return
if text == self.toPlainText():
return
self.setText(str(text))

@property
def model(self) -> TextModel:
return self._model

def setValidator(self, validator: StringDefinition) -> None:
self._validator = validator

def getValidationSupport(self) -> ValidationSupport:
return self._validation

def isValid(self) -> bool:
return self._validation.isValid()

@property
def get_text(self) -> str:
return self.toPlainText() if self.toPlainText() else self.placeholderText()

def enable_validation(self, enabled: bool) -> None:
self._enable_validation = enabled
Loading

0 comments on commit 313caf9

Please sign in to comment.