Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reset GUI #489

Merged
merged 15 commits into from
Oct 15, 2023
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
29 changes: 16 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 @@ -212,7 +214,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 +241,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 @@ -278,7 +279,7 @@ def _on_cutoff_change(self, _=None):
self.ecutrho = self.ecutrho_setter.value

def _reset_cutoff_widgets(self):
self.ecutrho_setter.value = 0
self.ecutwfc_setter.value = 0
self.ecutrho_setter.value = 0

def _reset_traitlets(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should include docstring to these reset* functions , just to keep it consistent with the rest of your changes where you added docstring

Expand All @@ -293,6 +294,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 +401,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"]
3 changes: 0 additions & 3 deletions src/aiidalab_qe/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ 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():
Expand All @@ -144,7 +142,6 @@ def _observe_process_selection(self, change):
self.structure_manager_widget.input_structure = (
process.inputs.structure
)
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
2 changes: 2 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,6 @@ 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
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.

4 changes: 3 additions & 1 deletion tests/test_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down