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