Skip to content

Commit

Permalink
Reset GUI (#489)
Browse files Browse the repository at this point in the history
Reset the GUI when selecting to start a new workflow.
  • Loading branch information
superstar54 authored Oct 15, 2023
1 parent 967c3f8 commit fc36106
Show file tree
Hide file tree
Showing 14 changed files with 128 additions and 62 deletions.
9 changes: 5 additions & 4 deletions src/aiidalab_qe/app/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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()
Expand All @@ -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."""
Expand Down
30 changes: 22 additions & 8 deletions src/aiidalab_qe/app/configuration/advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -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
33 changes: 20 additions & 13 deletions src/aiidalab_qe/app/configuration/pseudos.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("<b>&nbsp;&nbsp;Override&nbsp;</b>")
self.override_protocol_pseudo_family = ipw.Checkbox(
description="",
Expand All @@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -212,7 +215,10 @@ class PseudoSetter(ipw.VBox):
ecutwfc = tl.Float()
ecutrho = tl.Float()

_pseudo_setter_helper_text = """<div style="line-height: 140%; padding-top: 0px; padding-bottom: 10px">
_default_pseudo_setter_helper_text = """<div style="line-height: 140%; padding-top: 0px; padding-bottom: 10px; color: red;">
Input structure is not set. Please set the structure first.
</div>"""
_update_pseudo_setter_helper_text = """<div style="line-height: 140%; padding-top: 0px; padding-bottom: 10px">
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
Expand All @@ -236,11 +242,7 @@ def __init__(
self.ecutwfc = 0
self.ecutrho = 0

self.pseudo_setter_helper = ipw.HTML(
"""<div style="line-height: 140%; padding-top: 0px; padding-bottom: 10px; color: red;">
Input structure is not set. Please set the structure first.
</div>"""
)
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)",
Expand Down Expand Up @@ -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()
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
12 changes: 10 additions & 2 deletions src/aiidalab_qe/app/configuration/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
)

Expand Down Expand Up @@ -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"]
7 changes: 2 additions & 5 deletions src/aiidalab_qe/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 15 additions & 16 deletions src/aiidalab_qe/app/parameters/qeapp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions src/aiidalab_qe/app/structure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""
3 changes: 3 additions & 0 deletions src/aiidalab_qe/app/submission/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
4 changes: 4 additions & 0 deletions src/aiidalab_qe/plugins/bands/setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
4 changes: 4 additions & 0 deletions src/aiidalab_qe/plugins/pdos/setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 35 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
@@ -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
13 changes: 0 additions & 13 deletions tests/test_app_reload.py

This file was deleted.

Loading

0 comments on commit fc36106

Please sign in to comment.