diff --git a/src/aiidalab_qe/app/configuration/__init__.py b/src/aiidalab_qe/app/configuration/__init__.py index 06f2bd687..f0f09747b 100644 --- a/src/aiidalab_qe/app/configuration/__init__.py +++ b/src/aiidalab_qe/app/configuration/__init__.py @@ -10,7 +10,6 @@ from aiida import orm from aiidalab_widgets_base import WizardAppWidgetStep -from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS from aiidalab_qe.app.utils import get_entry_items from .advanced import AdvancedSettings @@ -27,7 +26,6 @@ class ConfigureQeAppWorkChainStep(ipw.VBox, WizardAppWidgetStep): def __init__(self, **kwargs): self.workchain_settings = WorkChainSettings() - self.workchain_settings.relax_type.observe(self._update_state, "value") self.advanced_settings = AdvancedSettings() ipw.dlink( @@ -141,7 +139,7 @@ def _update_state(self, _=None): else: self.confirm_button.disabled = True self.state = self.State.INIT - self.set_configuration_parameters(DEFAULT_PARAMETERS) + self.reset() def confirm(self, _=None): self.configuration_parameters = self.get_configuration_parameters() @@ -153,8 +151,11 @@ def _default_state(self): return self.State.INIT def reset(self): + """Reset the widgets in all settings to their initial states.""" with self.hold_trait_notifications(): - self.set_configuration_parameters(DEFAULT_PARAMETERS) + self.input_structure = None + for _, settings in self.settings.items(): + settings.reset() def _update_panel(self, _=None): """Dynamic add/remove the panel based on the selected properties.""" diff --git a/src/aiidalab_qe/app/configuration/advanced.py b/src/aiidalab_qe/app/configuration/advanced.py index 50c07995d..4a8ec6879 100644 --- a/src/aiidalab_qe/app/configuration/advanced.py +++ b/src/aiidalab_qe/app/configuration/advanced.py @@ -47,7 +47,9 @@ class AdvancedSettings(Panel): value = tl.Dict() def __init__(self, default_protocol=None, **kwargs): - self._default_protocol = default_protocol or DEFAULT_PARAMETERS["protocol"] + self._default_protocol = ( + default_protocol or DEFAULT_PARAMETERS["workchain"]["protocol"] + ) # clean-up workchain settings self.clean_workdir = ipw.Checkbox( @@ -291,11 +293,19 @@ def reset(self): with self.hold_trait_notifications(): # Reset protocol dependent settings self._update_settings_from_protocol(self.protocol) + self.pseudo_family_selector.set_from_pseudo_family( + DEFAULT_PARAMETERS["advanced"]["pseudo_family"] + ) # reset total charge - self.total_charge.value = DEFAULT_PARAMETERS["tot_charge"] - + self.total_charge.value = DEFAULT_PARAMETERS["advanced"]["tot_charge"] # reset the override checkbox self.override.value = False + self.smearing.reset() + # reset the pseudo setter + self.pseudo_setter.structure = None + self.pseudo_setter._reset() + # reset the magnetization + self.magnetization.reset() def _display_mesh(self, _=None): if self.input_structure is None: @@ -357,9 +367,11 @@ def _disabled_changed(self, _): def reset(self): self.disabled = True - if hasattr(self.kinds, "children") and self.kinds.children: - for i in range(len(self.kinds.children)): - self.kinds.children[i].value = 0.0 + self.kinds = None + self.description.value = "Define magnetization: Input structure not confirmed" + with self.kinds_widget_out: + clear_output() + display(self.kinds) def create_kinds_widget(self): if self.input_structure_labels: @@ -434,7 +446,9 @@ class SmearingSettings(ipw.VBox): disabled = tl.Bool() def __init__(self, default_protocol=None, **kwargs): - self._default_protocol = default_protocol or DEFAULT_PARAMETERS["protocol"] + self._default_protocol = ( + default_protocol or DEFAULT_PARAMETERS["workchain"]["protocol"] + ) self.smearing = ipw.Dropdown( options=["cold", "gaussian", "fermi-dirac", "methfessel-paxton"], @@ -512,4 +526,4 @@ def reset(self): with self.hold_trait_notifications(): self._update_settings_from_protocol(self.protocol) - self.disabled = False + self.disabled = True diff --git a/src/aiidalab_qe/app/configuration/pseudos.py b/src/aiidalab_qe/app/configuration/pseudos.py index 12d3cf397..2d53c5cef 100644 --- a/src/aiidalab_qe/app/configuration/pseudos.py +++ b/src/aiidalab_qe/app/configuration/pseudos.py @@ -64,12 +64,12 @@ class PseudoFamilySelector(ipw.VBox): disabled = tl.Bool() value = tl.Unicode( - default_value=DEFAULT_PARAMETERS["pseudo_family"], + default_value=DEFAULT_PARAMETERS["advanced"]["pseudo_family"], ) def __init__(self, **kwargs): # Enable manual setting of the pseudopotential family - self._default_protocol = DEFAULT_PARAMETERS["protocol"] + self._default_protocol = DEFAULT_PARAMETERS["workchain"]["protocol"] self.set_pseudo_family_prompt = ipw.HTML("  Override ") self.override_protocol_pseudo_family = ipw.Checkbox( description="", @@ -89,15 +89,17 @@ def __init__(self, **kwargs): # Choose the DFT functional self.dft_functional = ipw.Dropdown( options=["PBE", "PBEsol"], - value=DEFAULT_PARAMETERS["pseudo_family"].split("/")[2], + value=DEFAULT_PARAMETERS["advanced"]["pseudo_family"].split("/")[2], style={"description_width": "initial"}, ) self.dft_functional.observe(self.set_value_trait, "value") # - pseudo_family_type = DEFAULT_PARAMETERS["pseudo_family"].split("/")[0] + pseudo_family_type = DEFAULT_PARAMETERS["advanced"]["pseudo_family"].split("/")[ + 0 + ] if pseudo_family_type.upper() == "SSSP": pseudo_family_type += ( - " " + DEFAULT_PARAMETERS["pseudo_family"].split("/")[-1] + " " + DEFAULT_PARAMETERS["advanced"]["pseudo_family"].split("/")[-1] ) elif pseudo_family_type.upper() == "PSEUDODOJO": pseudo_family_type = "PseudoDojo " + pseudo_family_type.split("_")[-2] @@ -195,6 +197,7 @@ def _update_settings_from_protocol(self, protocol): self.set_from_pseudo_family(pseudo_family) def set_from_pseudo_family(self, pseudo_family): + """Set the widget values from the given pseudo family.""" family, _, functional, accuracy = pseudo_family.split("/") protocol_selection = f"{family} {accuracy}" self.protocol_selection.value = protocol_selection @@ -212,7 +215,10 @@ class PseudoSetter(ipw.VBox): ecutwfc = tl.Float() ecutrho = tl.Float() - _pseudo_setter_helper_text = """
+ _default_pseudo_setter_helper_text = """
+ Input structure is not set. Please set the structure first. +
""" + _update_pseudo_setter_helper_text = """
The pseudopotential for each kind of atom in the structure can be set customly. The default pseudopotential and cutoffs are get from the pseudo family. The cutoffs used for the calculation are the maximum of the default from all pseudopotentials @@ -236,11 +242,7 @@ def __init__( self.ecutwfc = 0 self.ecutrho = 0 - self.pseudo_setter_helper = ipw.HTML( - """
- Input structure is not set. Please set the structure first. -
""" - ) + self.pseudo_setter_helper = ipw.HTML(self._default_pseudo_setter_helper_text) self.cutoff_setter_helper = ipw.HTML(self._cutoff_setter_helper_text) self.ecutwfc_setter = ipw.FloatText( description="Wavefunction cutoff (Ry)", @@ -274,14 +276,17 @@ def __init__( self.pseudo_family = pseudo_family def _on_cutoff_change(self, _=None): + """Update the cutoffs according to the cutoff widgets""" self.ecutwfc = self.ecutwfc_setter.value self.ecutrho = self.ecutrho_setter.value def _reset_cutoff_widgets(self): - self.ecutrho_setter.value = 0 + """Reset the cutoff widgets to 0""" + self.ecutwfc_setter.value = 0 self.ecutrho_setter.value = 0 def _reset_traitlets(self): + """Reset the traitlets to the initial state""" self.ecutwfc = 0 self.ecutrho = 0 self.pseudos = dict() @@ -293,6 +298,8 @@ def _reset(self): if self.structure is None: self._reset_cutoff_widgets() self._reset_traitlets() + self.pseudo_setting_widgets.children = () + self.pseudo_setter_helper.value = self._default_pseudo_setter_helper_text return if self.pseudo_family is None: @@ -398,7 +405,7 @@ def _update_pseudos(self, _=None): if w.pseudo is not None: self.pseudos[w.kind] = w.pseudo.uuid - self.pseudo_setter_helper.value = self._pseudo_setter_helper_text + self.pseudo_setter_helper.value = self._update_pseudo_setter_helper_text with self.hold_trait_notifications(): self.ecutwfc_setter.value = max(self.ecutwfc, w.ecutwfc) diff --git a/src/aiidalab_qe/app/configuration/workflow.py b/src/aiidalab_qe/app/configuration/workflow.py index 9c6915672..444213630 100644 --- a/src/aiidalab_qe/app/configuration/workflow.py +++ b/src/aiidalab_qe/app/configuration/workflow.py @@ -71,14 +71,14 @@ def __init__(self, **kwargs): # SpinType: magnetic properties of material self.spin_type = ipw.ToggleButtons( options=[("Off", "none"), ("On", "collinear")], - value=DEFAULT_PARAMETERS["spin_type"], + value=DEFAULT_PARAMETERS["workchain"]["spin_type"], style={"description_width": "initial"}, ) # ElectronicType: electronic properties of material self.electronic_type = ipw.ToggleButtons( options=[("Metal", "metal"), ("Insulator", "insulator")], - value=DEFAULT_PARAMETERS["electronic_type"], + value=DEFAULT_PARAMETERS["workchain"]["electronic_type"], style={"description_width": "initial"}, ) @@ -178,3 +178,11 @@ def set_panel_value(self, parameters): self.properties[name].run.value = True else: self.properties[name].run.value = False + + def reset(self): + """Reset the panel to the default value.""" + for key in ["relax_type", "spin_type", "electronic_type"]: + getattr(self, key).value = DEFAULT_PARAMETERS["workchain"][key] + self.workchain_protocol.value = DEFAULT_PARAMETERS["workchain"]["protocol"] + for key, p in self.properties.items(): + p.run.value = key in DEFAULT_PARAMETERS["workchain"]["properties"] diff --git a/src/aiidalab_qe/app/main.py b/src/aiidalab_qe/app/main.py index 5a4e57eef..415301fac 100644 --- a/src/aiidalab_qe/app/main.py +++ b/src/aiidalab_qe/app/main.py @@ -134,17 +134,14 @@ def _observe_process_selection(self, change): if pk is None: self._wizard_app_widget.reset() self._wizard_app_widget.selected_index = 0 - self.configure_step.reset() - self.submit_step.reset() else: process = load_node(pk) with self.structure_manager_widget.hold_sync(): with self.structure_step.hold_sync(): self._wizard_app_widget.selected_index = 3 - self.structure_manager_widget.input_structure = ( - process.inputs.structure + self.structure_manager_widget.viewer.structure = ( + process.inputs.structure.get_ase() ) - self.structure_step.structure = process.inputs.structure self.structure_step.confirmed_structure = process.inputs.structure self.configure_step.state = WizardAppWidgetStep.State.SUCCESS self.submit_step.process = process diff --git a/src/aiidalab_qe/app/parameters/qeapp.yaml b/src/aiidalab_qe/app/parameters/qeapp.yaml index 2914ba706..3901183a7 100644 --- a/src/aiidalab_qe/app/parameters/qeapp.yaml +++ b/src/aiidalab_qe/app/parameters/qeapp.yaml @@ -2,25 +2,24 @@ # Default parameters for the QeAppWorkChain widgets initialization # The builder is not directly effected by this file. -## Properties -relax_type: positions_cell -run_bands: false -run_pdos: false +workchain: + relax_type: positions_cell + ## Properties + properties: [] + ## Material settings + spin_type: none + electronic_type: metal + + ## Calculation settings + protocol: moderate + +## Advanced pw settings +advanced: + pseudo_family: SSSP/1.2/PBEsol/efficiency + tot_charge: 0 ## Codes codes: dos: dos-7.2@localhost projwfc: projwfc-7.2@localhost pw: pw-7.2@localhost - -## Material settings -spin_type: none -electronic_type: metal - -## Calculation settings -protocol: moderate -kpoints_distance_override: -pseudo_family: SSSP/1.2/PBEsol/efficiency - -## Advanced pw settings -tot_charge: 0 diff --git a/src/aiidalab_qe/app/structure/__init__.py b/src/aiidalab_qe/app/structure/__init__.py index 824957b42..79d441611 100644 --- a/src/aiidalab_qe/app/structure/__init__.py +++ b/src/aiidalab_qe/app/structure/__init__.py @@ -124,4 +124,8 @@ def can_reset(self): return self.confirmed_structure is not None def reset(self): # unconfirm + """Reset the widget to its initial state.""" self.confirmed_structure = None + self.manager.structure = None + self.manager.viewer.structure = None + self.manager.output.value = "" diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index 9d3f09322..abd359a82 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -483,6 +483,9 @@ def get_submission_parameters(self): } def reset(self): + """Reset the widget to its initial state.""" with self.hold_trait_notifications(): self.process = None self.input_structure = None + self.set_selected_codes(DEFAULT_PARAMETERS["codes"]) + self.set_resource_defaults() diff --git a/src/aiidalab_qe/plugins/bands/setting.py b/src/aiidalab_qe/plugins/bands/setting.py index e0cee3330..dd08875a4 100644 --- a/src/aiidalab_qe/plugins/bands/setting.py +++ b/src/aiidalab_qe/plugins/bands/setting.py @@ -50,3 +50,7 @@ def get_panel_value(self): def set_panel_value(self, input_dict): """Load a dictionary with the input parameters for the plugin.""" self.kpath_2d.value = input_dict.get("kpath_2d", "hexagonal") + + def reset(self): + """Reset the panel to its default values.""" + self.kpath_2d.value = "hexagonal" diff --git a/src/aiidalab_qe/plugins/pdos/setting.py b/src/aiidalab_qe/plugins/pdos/setting.py index 39466ab2a..37c6c3f46 100644 --- a/src/aiidalab_qe/plugins/pdos/setting.py +++ b/src/aiidalab_qe/plugins/pdos/setting.py @@ -74,3 +74,7 @@ def get_panel_value(self): def set_panel_value(self, input_dict): """Load a dictionary with the input parameters for the plugin.""" self.nscf_kpoints_distance.value = input_dict.get("nscf_kpoints_distance", 0.1) + + def reset(self): + """Reset the panel to its default values.""" + self.nscf_kpoints_distance.value = 0.1 diff --git a/tests/conftest.py b/tests/conftest.py index 1c03dd7ee..8c0cbcb8f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -363,6 +363,7 @@ def _submit_app_generator( # submit_step = app.submit_step submit_step.input_structure = generate_structure_data() + submit_step.resources_config.num_cpus.value = 2 return app diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 000000000..38cd75e9a --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,35 @@ +def test_reload_and_reset(submit_app_generator, generate_qeapp_workchain): + """Test if the GUI paramters can be reload and reset properly""" + wkchain = generate_qeapp_workchain( + relax_type="positions", run_bands=True, run_pdos=False, spin_type="collinear" + ) + app = submit_app_generator() + # select the pk + app.work_chain_selector.value = wkchain.node.pk + # check if the value are reload correctly + assert app.configure_step.workchain_settings.relax_type.value == "positions" + assert app.configure_step.workchain_settings.spin_type.value == "collinear" + assert app.configure_step.workchain_settings.properties["bands"].run.value is True + assert app.configure_step.workchain_settings.properties["pdos"].run.value is False + assert ( + len( + app.configure_step.advanced_settings.pseudo_setter.pseudo_setting_widgets.children + ) + > 0 + ) + # new workflow, this will reset the GUI + app.work_chain_selector.value = None + # check if the value are reload correctly + assert app.structure_step.manager.structure is None + assert app.configure_step.workchain_settings.relax_type.value == "positions_cell" + assert app.configure_step.workchain_settings.spin_type.value == "none" + assert app.configure_step.workchain_settings.properties["bands"].run.value is False + assert app.configure_step.workchain_settings.properties["pdos"].run.value is False + assert app.configure_step.advanced_settings.pseudo_setter.ecutwfc_setter.value == 0 + assert ( + len( + app.configure_step.advanced_settings.pseudo_setter.pseudo_setting_widgets.children + ) + == 0 + ) + assert app.submit_step.resources_config.num_cpus.value == 1 diff --git a/tests/test_app_reload.py b/tests/test_app_reload.py deleted file mode 100644 index 250621448..000000000 --- a/tests/test_app_reload.py +++ /dev/null @@ -1,13 +0,0 @@ -def test_reload(submit_app_generator, generate_qeapp_workchain): - """Test if the GUI paramters can be reload properly""" - wkchain = generate_qeapp_workchain( - relax_type="positions", run_bands=True, run_pdos=False, spin_type="collinear" - ) - app = submit_app_generator() - # select the pk - app.work_chain_selector.value = wkchain.node.pk - # check if the value are reload correctly - assert app.configure_step.workchain_settings.relax_type.value == "positions" - assert app.configure_step.workchain_settings.spin_type.value == "collinear" - assert app.configure_step.workchain_settings.properties["bands"].run.value is True - assert app.configure_step.workchain_settings.properties["pdos"].run.value is False diff --git a/tests/test_configure.py b/tests/test_configure.py index 4e677afa1..ae0db140b 100644 --- a/tests/test_configure.py +++ b/tests/test_configure.py @@ -12,6 +12,7 @@ def test_protocol(): def test_get_configuration_parameters(): + """Test the get_configuration_parameters method.""" from aiidalab_qe.app.configuration import ConfigureQeAppWorkChainStep wg = ConfigureQeAppWorkChainStep() @@ -23,7 +24,8 @@ def test_get_configuration_parameters(): assert parameters == parameters_ref -def test_set_configuration_parameters(submit_app_generator): +def test_set_configuration_parameters(): + """Test the set_configuration_parameters method.""" from aiidalab_qe.app.configuration import ConfigureQeAppWorkChainStep wg = ConfigureQeAppWorkChainStep()