diff --git a/src/ert/config/analysis_module.py b/src/ert/config/analysis_module.py index 65da352e4c1..63f0b17aa5a 100644 --- a/src/ert/config/analysis_module.py +++ b/src/ert/config/analysis_module.py @@ -265,6 +265,9 @@ def get_steplength(self, iteration_nr: int) -> float: ) return step_length + def __str__(self) -> str: + return f"AnalysisModule: {self.name}" + def __repr__(self) -> str: return f"AnalysisModule(name = {self.name})" diff --git a/src/ert/gui/ertwidgets/analysismodulevariablespanel.py b/src/ert/gui/ertwidgets/analysismodulevariablespanel.py index 5236b2ca5a8..7c0f21406f2 100644 --- a/src/ert/gui/ertwidgets/analysismodulevariablespanel.py +++ b/src/ert/gui/ertwidgets/analysismodulevariablespanel.py @@ -1,18 +1,20 @@ from functools import partial +from qtpy.QtCore import Qt from qtpy.QtWidgets import ( QButtonGroup, QCheckBox, QDoubleSpinBox, QFormLayout, + QFrame, QHBoxLayout, QLabel, - QLineEdit, QRadioButton, QWidget, ) -from ert.config.analysis_module import AnalysisModule, correlation_threshold +from ert.config import AnalysisMode +from ert.config.analysis_module import AnalysisModule from ert.gui.ertwidgets.models.analysismodulevariablesmodel import ( AnalysisModuleVariablesModel, ) @@ -24,95 +26,102 @@ def __init__(self, analysis_module: AnalysisModule, ensemble_size: int): self.analysis_module = analysis_module layout = QFormLayout() + layout.setVerticalSpacing(5) + layout.setLabelAlignment(Qt.AlignLeft) + layout.setHorizontalSpacing(150) variable_names = analysis_module.get_variable_names() - if len(variable_names) == 0: - label = QLabel("No variables found to edit") - boxlayout = QHBoxLayout() - layout.addRow(label, boxlayout) - - else: - analysis_module_variables_model = AnalysisModuleVariablesModel - self.blockSignals(True) - - for variable_name in variable_names: - variable_type = analysis_module_variables_model.getVariableType( - variable_name + analysis_module_variables_model = AnalysisModuleVariablesModel + self.blockSignals(True) + + layout.addRow(QLabel(str(analysis_module))) + layout.addRow(self.create_horizontal_line()) + + if analysis_module.mode == AnalysisMode.ITERATED_ENSEMBLE_SMOOTHER: + for variable_name in ( + name for name in variable_names if "STEPLENGTH" in name + ): + layout.addRow( + analysis_module_variables_model.getVariableLabelName(variable_name), + self.createDoubleSpinBox( + variable_name, + analysis_module.get_variable_value(variable_name), + analysis_module_variables_model, + ), ) - variable_value = analysis_module.get_variable_value(variable_name) - - if variable_name == "LOCALIZATION_CORRELATION_THRESHOLD": - variable_value = correlation_threshold( - ensemble_size, variable_value - ) - label_name = analysis_module_variables_model.getVariableLabelName( - variable_name + for s in [ + "A good start is max steplength of 0.6, min steplength of 0.3, and decline of 2.5", + "A steplength of 1.0 and one iteration results in ES update", + ]: + lab = QLabel(s) + lab.setStyleSheet( + "font-style: italic; font-size: 10pt; font-weight: 300" ) - if variable_type == bool: - layout.addRow( - label_name, - self.createCheckBox( - variable_name, variable_value, variable_type - ), - ) - - elif variable_type == float: - layout.addRow( - label_name, - self.createDoubleSpinBox( - variable_name, - variable_value, - variable_type, - analysis_module_variables_model, - ), - ) - - if variable_name == "IES_INVERSION": - layout.addRow(QLabel("Inversion Algorithm")) - bg = QButtonGroup(self) - for button_id, s in enumerate( - [ - "Exact inversion with diagonal R=I", - "Subspace inversion with exact R", - "Subspace inversion using R=EE'", - "Subspace inversion using E", - ], - start=0, - ): - b = QRadioButton(s, self) - b.setObjectName(variable_name + "_" + str(button_id)) - bg.addButton(b, button_id) - layout.addRow(b) - - bg.buttons()[0].setChecked(True) # check the first option - bg.idClicked.connect(self.update_inversion_algorithm) - - if variable_name == "IES_DEC_STEPLENGTH": - for s in [ - "' - "A good start is max steplength of 0.6, min steplength of 0.3, and decline of 2.5", - "' - "A steplength of 1.0 and one iteration results in ES update", - ]: - layout.addRow(QLabel(s)) - - self.truncation_spinner = self.widget_from_layout(layout, "ENKF_TRUNCATION") + layout.addRow(lab) + + layout.addRow(self.create_horizontal_line()) + + layout.addRow(QLabel("Inversion Algorithm")) + bg = QButtonGroup(self) + for button_id, s in enumerate( + [ + "Exact inversion with diagonal R=I", + "Subspace inversion with exact R", + "Subspace inversion using R=EE'", + "Subspace inversion using E", + ], + start=0, + ): + b = QRadioButton(s, self) + b.setObjectName("IES_INVERSION_" + str(button_id)) + bg.addButton(b, button_id) + layout.addRow(b) + + self.truncation_spinner = self.createDoubleSpinBox( + "ENKF_TRUNCATION", + analysis_module.get_truncation(), + analysis_module_variables_model, + ) self.truncation_spinner.setEnabled(False) + layout.addRow("Singular value truncation", self.truncation_spinner) - localization_checkbox = self.widget_from_layout(layout, "LOCALIZATION") - localization_correlation_spinner = self.widget_from_layout( - layout, "LOCALIZATION_CORRELATION_THRESHOLD" - ) - localization_correlation_spinner.setEnabled(localization_checkbox.isChecked()) - localization_checkbox.stateChanged.connect( - lambda localization_is_on: localization_correlation_spinner.setEnabled( - localization_is_on + bg.idClicked.connect(self.update_inversion_algorithm) + bg.buttons()[analysis_module.inversion].click() # update the current value + + layout.addRow(self.create_horizontal_line()) + layout.addRow(QLabel("[EXPERIMENTAL]")) + + localization_frame = QFrame() + localization_frame.setLayout(QHBoxLayout()) + localization_frame.layout().setContentsMargins(0, 0, 0, 0) + + local_checkbox = QCheckBox("Adaptive localization correlation threshold") + local_checkbox.clicked.connect( + partial( + self.valueChanged, + "LOCALIZATION", + bool, + local_checkbox, ) ) + self.local_spinner = self.createDoubleSpinBox( + "LOCALIZATION_CORRELATION_THRESHOLD", + analysis_module.localization_correlation_threshold(ensemble_size), + analysis_module_variables_model, + ) + self.local_spinner.setEnabled(local_checkbox.isChecked()) + + localization_frame.layout().addWidget(local_checkbox) + localization_frame.layout().addWidget(self.local_spinner) + layout.addRow(localization_frame) + + local_checkbox.stateChanged.connect( + lambda localization_is_on: self.local_spinner.setEnabled(localization_is_on) + ) + local_checkbox.setChecked(analysis_module.localization()) + self.setLayout(layout) self.blockSignals(False) @@ -120,46 +129,35 @@ def update_inversion_algorithm(self, button_id): self.truncation_spinner.setEnabled(button_id != 0) # not for exact inversion self.analysis_module.inversion = button_id - def widget_from_layout(self, layout: QFormLayout, widget_name: str) -> QWidget: - for i in range(layout.count()): - widget = layout.itemAt(i).widget() - if widget.objectName() == widget_name: - return widget - - return None - - def createCheckBox(self, variable_name, variable_value, variable_type): - spinner = QCheckBox() - spinner.setChecked(variable_value) - spinner.setObjectName(variable_name) - spinner.clicked.connect( - partial(self.valueChanged, variable_name, variable_type, spinner) - ) - return spinner + def create_horizontal_line(self) -> QFrame: + hline = QFrame() + hline.setFrameShape(QFrame.HLine) + hline.setFrameShadow(QFrame.Sunken) + hline.setFixedHeight(20) + return hline def createDoubleSpinBox( self, variable_name, variable_value, - variable_type, analysis_module_variables_model, ): spinner = QDoubleSpinBox() spinner.setDecimals(6) - spinner.setMinimumWidth(75) - spinner.setMaximum( - analysis_module_variables_model.getVariableMaximumValue(variable_name) - ) - spinner.setMinimum( - analysis_module_variables_model.getVariableMinimumValue(variable_name) + spinner.setFixedWidth(100) + spinner.setObjectName(variable_name) + + spinner.setRange( + analysis_module_variables_model.getVariableMinimumValue(variable_name), + analysis_module_variables_model.getVariableMaximumValue(variable_name), ) + spinner.setSingleStep( analysis_module_variables_model.getVariableStepValue(variable_name) ) spinner.setValue(variable_value) - spinner.setObjectName(variable_name) spinner.valueChanged.connect( - partial(self.valueChanged, variable_name, variable_type, spinner) + partial(self.valueChanged, variable_name, float, spinner) ) return spinner @@ -171,12 +169,6 @@ def valueChanged(self, variable_name, variable_type, variable_control): elif variable_type == float: assert isinstance(variable_control, QDoubleSpinBox) value = variable_control.value() - elif variable_type == str: - assert isinstance(variable_control, QLineEdit) - value = variable_control.text() - value = str(value).strip() - if len(value) == 0: - value = None if value is not None: self.analysis_module.set_var(variable_name, value)