From 8d171b0d7401745659065c49b0cc0ca41f206021 Mon Sep 17 00:00:00 2001 From: AndresOrtegaGuerrero Date: Thu, 28 Nov 2024 21:15:03 +0000 Subject: [PATCH] setting lazy loading --- src/aiidalab_qe_vibroscopy/app/__init__.py | 48 ++- src/aiidalab_qe_vibroscopy/app/model.py | 35 ++ src/aiidalab_qe_vibroscopy/app/settings.py | 460 +++++++++++---------- 3 files changed, 311 insertions(+), 232 deletions(-) create mode 100644 src/aiidalab_qe_vibroscopy/app/model.py diff --git a/src/aiidalab_qe_vibroscopy/app/__init__.py b/src/aiidalab_qe_vibroscopy/app/__init__.py index d4e2de5..6d9e76b 100644 --- a/src/aiidalab_qe_vibroscopy/app/__init__.py +++ b/src/aiidalab_qe_vibroscopy/app/__init__.py @@ -1,7 +1,13 @@ -from aiidalab_qe_vibroscopy.app.settings import Setting +from aiidalab_qe.common.panel import PluginOutline + +from aiidalab_qe_vibroscopy.app.model import VibroConfigurationSettingsModel +from aiidalab_qe_vibroscopy.app.settings import VibroConfigurationSettingPanel + + +# from aiidalab_qe_vibroscopy.app.settings import Setting from aiidalab_qe_vibroscopy.app.workchain import workchain_and_builder -from aiidalab_qe_vibroscopy.app.result import Result -from aiidalab_qe.common.panel import OutlinePanel +# from aiidalab_qe_vibroscopy.app.result import Result +# from aiidalab_qe.common.panel import OutlinePanel from aiidalab_qe.common.widgets import ( QEAppComputationalResourcesWidget, @@ -9,11 +15,19 @@ ) -class Outline(OutlinePanel): - title = "Vibrational properties" - # description = "IR and Raman spectra; you may also select phononic and dielectric properties" +class VibroPluginOutline(PluginOutline): + title = "Vibrational Spectroscopy (VIBRO)" +property = { + "outline": VibroPluginOutline, + "configuration": { + "panel": VibroConfigurationSettingPanel, + "model": VibroConfigurationSettingsModel, + }, + "workchain": workchain_and_builder, +} + PhononWorkChainPwCode = PwCodeResourceSetupWidget( description="pw.x for phonons", # code for the PhononWorkChain workflow", default_calc_job_plugin="quantumespresso.pw", @@ -30,14 +44,14 @@ class Outline(OutlinePanel): default_calc_job_plugin="phonopy.phonopy", ) -property = { - "outline": Outline, - "code": { - "phonon": PhononWorkChainPwCode, - "dielectric": DielectricWorkChainPwCode, - "phonopy": PhonopyCalculationCode, - }, - "setting": Setting, - "workchain": workchain_and_builder, - "result": Result, -} +# property = { +# "outline": Outline, +# "code": { +# "phonon": PhononWorkChainPwCode, +# "dielectric": DielectricWorkChainPwCode, +# "phonopy": PhonopyCalculationCode, +# }, +# "setting": Setting, +# "workchain": workchain_and_builder, +# "result": Result, +# } diff --git a/src/aiidalab_qe_vibroscopy/app/model.py b/src/aiidalab_qe_vibroscopy/app/model.py new file mode 100644 index 0000000..b1aa45e --- /dev/null +++ b/src/aiidalab_qe_vibroscopy/app/model.py @@ -0,0 +1,35 @@ +import traitlets as tl + + +from aiidalab_qe.common.mixins import HasInputStructure +from aiidalab_qe.common.panel import ConfigurationSettingsModel + + +class VibroConfigurationSettingsModel(ConfigurationSettingsModel, HasInputStructure): + dependencies = [ + "input_structure", + ] + + simulation_type_options = tl.List( + trait=tl.List(tl.Union([tl.Unicode(), tl.Int()])), + default_value=[ + ("IR/Raman, Phonon, Dielectric, INS properties", 1), + ("IR/Raman and Dielectric in Primitive Cell Approach", 2), + ("Phonons for non-polar materials and INS", 3), + ("Dielectric properties", 4), + ], + ) + simulation_type = tl.Int(1) + + symmetry_symprec = tl.Float(1e-5) + supercell_x = tl.Int(2) + supercell_y = tl.Int(2) + supercell_z = tl.Int(2) + + supercell = tl.List( + trait=tl.Int(), + default_value=[2, 2, 2], + ) + + def _get_default(self, trait): + return self._defaults.get(trait, self.traits()[trait].default_value) diff --git a/src/aiidalab_qe_vibroscopy/app/settings.py b/src/aiidalab_qe_vibroscopy/app/settings.py index f4992a5..ca61136 100644 --- a/src/aiidalab_qe_vibroscopy/app/settings.py +++ b/src/aiidalab_qe_vibroscopy/app/settings.py @@ -8,12 +8,9 @@ """ import ipywidgets as ipw -import traitlets as tl -import numpy as np - -from aiida import orm -from aiidalab_qe.common.panel import Panel +from aiidalab_qe.common.panel import ConfigurationSettingsPanel +from aiidalab_qe_vibroscopy.app.model import VibroConfigurationSettingsModel import sys import os @@ -69,19 +66,21 @@ def wrapper(*args, **kwargs): return wrapper -class Setting(Panel): +class VibroConfigurationSettingPanel( + ConfigurationSettingsPanel[VibroConfigurationSettingsModel], +): title = "Vibrational Settings" + identifier = "vibronic" + + # input_structure = tl.Instance(orm.StructureData, allow_none=True) - simulation_mode = [ - ("IR/Raman, Phonon, Dielectric, INS properties", 1), - ("IR/Raman and Dielectric in Primitive Cell Approach", 2), - ("Phonons for non-polar materials and INS", 3), - ("Dielectric properties", 4), - ] + def __init__(self, model: VibroConfigurationSettingsModel, **kwargs): + super().__init__(model, **kwargs) - input_structure = tl.Instance(orm.StructureData, allow_none=True) + def render(self): + if self.rendered: + return - def __init__(self, **kwargs): self.settings_title = ipw.HTML( """

Vibrational Settings

""" @@ -118,46 +117,86 @@ def __init__(self, **kwargs): """, ) - self.calc_options_description = ipw.HTML("Select calculation:") - self.calc_options = ipw.Dropdown( - options=self.simulation_mode, + self.simulation_type = ipw.Dropdown( layout=ipw.Layout(width="450px"), - value=self.simulation_mode[0][1], + ) + ipw.dlink( + (self._model, "simulation_type_options"), + (self.simulation_type, "options"), + ) + ipw.link( + (self._model, "simulation_type"), + (self.simulation_type, "value"), ) - self.calc_options.observe(self._display_supercell, names="value") - - # start Supercell + self.symmetry_symprec = ipw.FloatText( + max=1, + min=1e-7, + step=1e-4, + description="Symmetry tolerance (symprec):", + style={"description_width": "initial"}, + layout={"width": "300px"}, + ) + ipw.link( + (self._model, "symmetry_symprec"), + (self.symmetry_symprec, "value"), + ) - self.supercell = [2, 2, 2] + self.supercell_x = ipw.BoundedIntText( + min=1, + layout={"width": "40px"}, + ) + self.supercell_y = ipw.BoundedIntText( + min=1, + layout={"width": "40px"}, + ) + self.supercell_z = ipw.BoundedIntText( + min=1, + layout={"width": "40px"}, + ) + ipw.link( + (self._model, "supercell_x"), + (self.supercell_x, "value"), + ) + ipw.link( + (self._model, "supercell_y"), + (self.supercell_y, "value"), + ) + ipw.link( + (self._model, "supercell_z"), + (self.supercell_z, "value"), + ) - def change_supercell(_=None): - self.supercell = [ - self._sc_x.value, - self._sc_y.value, - self._sc_z.value, - ] - - if self.input_structure: - pbc = self.input_structure.pbc - else: - pbc = (True, True, True) - - for elem, periodic in zip(["x", "y", "z"], pbc): - # periodic allows support of hints also for 2D, 1D. - setattr( - self, - "_sc_" + elem, - ipw.BoundedIntText( - value=2 if periodic else 1, - min=1, - layout={"width": "40px"}, - disabled=False if periodic else True, - ), - ) - for elem in [self._sc_x, self._sc_y, self._sc_z]: - elem.observe(change_supercell, names="value") - elem.observe(self._activate_estimate_supercells, names="value") + # self.simulation_type.observe(self._display_supercell, names="value") + # self.supercell = [2, 2, 2] + + # def change_supercell(_=None): + # self.supercell = [ + # self._sc_x.value, + # self._sc_y.value, + # self._sc_z.value, + # ] + + # if self.input_structure: + # pbc = self.input_structure.pbc + # else: + # pbc = (True, True, True) + + # for elem, periodic in zip(["x", "y", "z"], pbc): + # # periodic allows support of hints also for 2D, 1D. + # setattr( + # self, + # "_sc_" + elem, + # ipw.BoundedIntText( + # value=2 if periodic else 1, + # min=1, + # layout={"width": "40px"}, + # disabled=False if periodic else True, + # ), + # ) + # for elem in [self._sc_x, self._sc_y, self._sc_z]: + # elem.observe(change_supercell, names="value") + # elem.observe(self._activate_estimate_supercells, names="value") self.supercell_selector = ipw.HBox( children=[ @@ -167,9 +206,9 @@ def change_supercell(_=None): ) ] + [ - self._sc_x, - self._sc_y, - self._sc_z, + self.supercell_x, + self.supercell_y, + self.supercell_z, ], ) @@ -183,7 +222,7 @@ def change_supercell(_=None): button_style="info", ) # supercell hint (15A lattice params) - self.supercell_hint_button.on_click(self._suggest_supercell) + # self.supercell_hint_button.on_click(self._suggest_supercell) # reset supercell self.supercell_reset_button = ipw.Button( @@ -193,7 +232,7 @@ def change_supercell(_=None): button_style="warning", ) # supercell reset reaction - self.supercell_reset_button.on_click(self._reset_supercell) + # self.supercell_reset_button.on_click(self._reset_supercell) # Estimate supercell button self.supercell_estimate_button = ipw.Button( @@ -204,7 +243,7 @@ def change_supercell(_=None): tooltip="Number of supercells for phonons calculations;\nwarning: for large systems, this may take some time.", ) # supercell reset reaction - self.supercell_estimate_button.on_click(self._estimate_supercells) + # self.supercell_estimate_button.on_click(self._estimate_supercells) # Estimate the number of supercells for frozen phonons. self.supercell_number_estimator = ipw.HTML( @@ -233,16 +272,7 @@ def change_supercell(_=None): self.supercell_widget.layout.display = "block" # end Supercell. - self.symmetry_symprec = ipw.FloatText( - value=1e-5, - max=1, - min=1e-7, # Ensure the value is always positive - step=1e-4, # Step value of 1e-4 - description="Symmetry tolerance (symprec):", - style={"description_width": "initial"}, - layout={"width": "300px"}, - ) - self.symmetry_symprec.observe(self._activate_estimate_supercells, "value") + # self.symmetry_symprec.observe(self._activate_estimate_supercells, "value") # reset supercell self.symmetry_symprec_reset_button = ipw.Button( @@ -252,7 +282,7 @@ def change_supercell(_=None): button_style="warning", ) # supercell reset reaction - self.symmetry_symprec_reset_button.on_click(self._reset_symprec) + # self.symmetry_symprec_reset_button.on_click(self._reset_symprec) self.children = [ ipw.VBox( @@ -273,8 +303,8 @@ def change_supercell(_=None): ), ipw.HBox( [ - self.calc_options_description, - self.calc_options, + ipw.HTML("Select calculation:"), + self.simulation_type, ], ), self.supercell_widget, @@ -286,157 +316,157 @@ def change_supercell(_=None): ), ] - super().__init__(**kwargs) + self.rendered = True # we define a block for the estimation of the supercell if we ask for hint, # so that we call the estimator only at the end of the supercell hint generator, # and now each time after the x, y, z generation (i.e., we don't lose time). # see the methods below. - self.block = False - - @tl.observe("input_structure") - def _update_input_structure(self, change): - if self.input_structure: - for direction, periodic in zip( - [self._sc_x, self._sc_y, self._sc_z], self.input_structure.pbc - ): - direction.value = 2 if periodic else 1 - direction.disabled = False if periodic else True - - self.supercell_number_estimator.layout.display = ( - "block" if len(self.input_structure.sites) <= 30 else "none" - ) - self.supercell_estimate_button.layout.display = ( - "block" if len(self.input_structure.sites) <= 30 else "none" - ) - else: - self.supercell_number_estimator.layout.display = "none" - self.supercell_estimate_button.layout.display = "none" - - def _display_supercell(self, change): - selected = change["new"] - if selected in [1, 3]: - self.supercell_widget.layout.display = "block" - else: - self.supercell_widget.layout.display = "none" - - def _suggest_supercell(self, _=None): - """ - minimal supercell size for phonons, imposing a minimum lattice parameter of 15 A. - """ - if self.input_structure: - s = self.input_structure.get_ase() - suggested_3D = 15 // np.array(s.cell.cellpar()[:3]) + 1 - - # if disabled, it means that it is a non-periodic direction. - # here we manually unobserve the `_activate_estimate_supercells`, so it is faster - # and only compute when all the three directions are updated - self.block = True - for direction, suggested, original in zip( - [self._sc_x, self._sc_y, self._sc_z], suggested_3D, s.cell.cellpar()[:3] - ): - direction.value = suggested if not direction.disabled else 1 - self.block = False - self._activate_estimate_supercells() - else: - return - - def _activate_estimate_supercells(self, _=None): - self.supercell_estimate_button.disabled = False - self.supercell_number_estimator.value = "?" + # self.block = False # @tl.observe("input_structure") - @disable_print - def _estimate_supercells(self, _=None): - """_summary_ - - Estimate the number of supercells to be computed for frozen phonon calculation. - """ - if self.block: - return - - symprec_value = self.symmetry_symprec.value - - self.symmetry_symprec.value = max(1e-5, min(symprec_value, 1)) - - self.supercell_number_estimator.value = spinner_html - - from aiida_phonopy.data.preprocess import PreProcessData - - if self.input_structure: - preprocess_data = PreProcessData( - structure=self.input_structure, - supercell_matrix=[ - [self._sc_x.value, 0, 0], - [0, self._sc_y.value, 0], - [0, 0, self._sc_z.value], - ], - symprec=self.symmetry_symprec.value, - distinguish_kinds=False, - is_symmetry=True, - ) - - supercells = preprocess_data.get_supercells_with_displacements() - - # for now, we comment the following part, as the HubbardSD is generated in the submission step. - """if isinstance(self.input_structure, HubbardStructureData): - from aiida_vibroscopy.calculations.spectra_utils import get_supercells_for_hubbard - from aiida_vibroscopy.workflows.phonons.base import get_supercell_hubbard_structure - supercell = get_supercell_hubbard_structure( - self.input_structure, - self.input_structure, - metadata={"store_provenance": False}, - ) - supercells = get_supercells_for_hubbard( - preprocess_data=preprocess_data, - ref_structure=supercell, - metadata={"store_provenance": False}, - ) - - else: - supercells = preprocess_data.get_supercells_with_displacements() - """ - self.supercell_number_estimator.value = f"{len(supercells)}" - self.supercell_estimate_button.disabled = True - - return - - def _reset_supercell(self, _=None): - if self.input_structure is not None: - reset_supercell = [] - self.block = True - for direction, periodic in zip( - [self._sc_x, self._sc_y, self._sc_z], self.input_structure.pbc - ): - reset_supercell.append(2 if periodic else 1) - (self._sc_x.value, self._sc_y.value, self._sc_z.value) = reset_supercell - self.block = False - self._activate_estimate_supercells() - return - - def _reset_symprec(self, _=None): - self.symmetry_symprec.value = 1e-5 - self._activate_estimate_supercells() - return - - def get_panel_value(self): - """Return a dictionary with the input parameters for the plugin.""" - return { - "simulation_mode": self.calc_options.value, - "supercell_selector": self.supercell, - "symmetry_symprec": self.symmetry_symprec.value, - } - - def set_panel_value(self, input_dict): - """Load a dictionary with the input parameters for the plugin.""" - self.calc_options.value = input_dict.get("simulation_mode", 1) - self.supercell = input_dict.get("supercell_selector", [2, 2, 2]) - self.symmetry_symprec.value = input_dict.get("symmetry_symprec", 1e-5) - self._sc_x.value, self._sc_y.value, self._sc_z.value = self.supercell - - def reset(self): - """Reset the panel""" - self.calc_options.value = 1 - self.supercell = [2, 2, 2] - self.symmetry_symprec.value = 1e-5 - self._sc_x.value, self._sc_y.value, self._sc_z.value = self.supercell + # def _update_input_structure(self, change): + # if self.input_structure: + # for direction, periodic in zip( + # [self._sc_x, self._sc_y, self._sc_z], self.input_structure.pbc + # ): + # direction.value = 2 if periodic else 1 + # direction.disabled = False if periodic else True + + # self.supercell_number_estimator.layout.display = ( + # "block" if len(self.input_structure.sites) <= 30 else "none" + # ) + # self.supercell_estimate_button.layout.display = ( + # "block" if len(self.input_structure.sites) <= 30 else "none" + # ) + # else: + # self.supercell_number_estimator.layout.display = "none" + # self.supercell_estimate_button.layout.display = "none" + + # def _display_supercell(self, change): + # selected = change["new"] + # if selected in [1, 3]: + # self.supercell_widget.layout.display = "block" + # else: + # self.supercell_widget.layout.display = "none" + + # def _suggest_supercell(self, _=None): + # """ + # minimal supercell size for phonons, imposing a minimum lattice parameter of 15 A. + # """ + # if self.input_structure: + # s = self.input_structure.get_ase() + # suggested_3D = 15 // np.array(s.cell.cellpar()[:3]) + 1 + + # # if disabled, it means that it is a non-periodic direction. + # # here we manually unobserve the `_activate_estimate_supercells`, so it is faster + # # and only compute when all the three directions are updated + # self.block = True + # for direction, suggested, original in zip( + # [self._sc_x, self._sc_y, self._sc_z], suggested_3D, s.cell.cellpar()[:3] + # ): + # direction.value = suggested if not direction.disabled else 1 + # self.block = False + # self._activate_estimate_supercells() + # else: + # return + + # def _activate_estimate_supercells(self, _=None): + # self.supercell_estimate_button.disabled = False + # self.supercell_number_estimator.value = "?" + + # # @tl.observe("input_structure") + # @disable_print + # def _estimate_supercells(self, _=None): + # """_summary_ + + # Estimate the number of supercells to be computed for frozen phonon calculation. + # """ + # if self.block: + # return + + # symprec_value = self.symmetry_symprec.value + + # self.symmetry_symprec.value = max(1e-5, min(symprec_value, 1)) + + # self.supercell_number_estimator.value = spinner_html + + # from aiida_phonopy.data.preprocess import PreProcessData + + # if self.input_structure: + # preprocess_data = PreProcessData( + # structure=self.input_structure, + # supercell_matrix=[ + # [self._sc_x.value, 0, 0], + # [0, self._sc_y.value, 0], + # [0, 0, self._sc_z.value], + # ], + # symprec=self.symmetry_symprec.value, + # distinguish_kinds=False, + # is_symmetry=True, + # ) + + # supercells = preprocess_data.get_supercells_with_displacements() + + # # for now, we comment the following part, as the HubbardSD is generated in the submission step. + # """if isinstance(self.input_structure, HubbardStructureData): + # from aiida_vibroscopy.calculations.spectra_utils import get_supercells_for_hubbard + # from aiida_vibroscopy.workflows.phonons.base import get_supercell_hubbard_structure + # supercell = get_supercell_hubbard_structure( + # self.input_structure, + # self.input_structure, + # metadata={"store_provenance": False}, + # ) + # supercells = get_supercells_for_hubbard( + # preprocess_data=preprocess_data, + # ref_structure=supercell, + # metadata={"store_provenance": False}, + # ) + + # else: + # supercells = preprocess_data.get_supercells_with_displacements() + # """ + # self.supercell_number_estimator.value = f"{len(supercells)}" + # self.supercell_estimate_button.disabled = True + + # return + + # def _reset_supercell(self, _=None): + # if self.input_structure is not None: + # reset_supercell = [] + # self.block = True + # for direction, periodic in zip( + # [self._sc_x, self._sc_y, self._sc_z], self.input_structure.pbc + # ): + # reset_supercell.append(2 if periodic else 1) + # (self._sc_x.value, self._sc_y.value, self._sc_z.value) = reset_supercell + # self.block = False + # self._activate_estimate_supercells() + # return + + # def _reset_symprec(self, _=None): + # self.symmetry_symprec.value = 1e-5 + # self._activate_estimate_supercells() + # return + + # def get_panel_value(self): + # """Return a dictionary with the input parameters for the plugin.""" + # return { + # "simulation_mode": self.calc_options.value, + # "supercell_selector": self.supercell, + # "symmetry_symprec": self.symmetry_symprec.value, + # } + + # def set_panel_value(self, input_dict): + # """Load a dictionary with the input parameters for the plugin.""" + # self.calc_options.value = input_dict.get("simulation_mode", 1) + # self.supercell = input_dict.get("supercell_selector", [2, 2, 2]) + # self.symmetry_symprec.value = input_dict.get("symmetry_symprec", 1e-5) + # self._sc_x.value, self._sc_y.value, self._sc_z.value = self.supercell + + # def reset(self): + # """Reset the panel""" + # self.calc_options.value = 1 + # self.supercell = [2, 2, 2] + # self.symmetry_symprec.value = 1e-5 + # self._sc_x.value, self._sc_y.value, self._sc_z.value = self.supercell