Skip to content

Commit

Permalink
Lazy loading refactoring (#105)
Browse files Browse the repository at this point in the history
Adapt plugin for lazy loading (Refactoring of the Plugin)
 Create model for Settings Panel
 Create model for the Code Panel
 Create widget/model logic for Results Panel
 Create widget/model logic for Phonons Results
 Create widget/model logic for IR/Raman Results
 Create widget/model logic for Dielectric Results
 Create widget/model logic for INS Results (This part was not completed)

We observed an issue with the INS Results, there is need of of refactoring the widget for visualisation
Co-author with @mikibonacci
  • Loading branch information
AndresOrtegaGuerrero authored Dec 6, 2024
1 parent e5be3ce commit 7249eca
Show file tree
Hide file tree
Showing 37 changed files with 3,361 additions and 2,225 deletions.
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ requires-python = ">=3.8"
dependencies = [
"aiida-vibroscopy>=1.0.2",
"aiida-phonopy>=1.1.3",
"phonopy",
#"euphonic==1.1.0",
"phonopy=2.25.0",
"pre-commit",
"euphonic",
"kaleido",
"weas-widget==0.1.15",
"weas-widget==0.1.19",
]

[tool.ruff.lint]
Expand Down
53 changes: 22 additions & 31 deletions src/aiidalab_qe_vibroscopy/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,34 @@
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.common.panel import PluginOutline

from aiidalab_qe.common.widgets import (
QEAppComputationalResourcesWidget,
PwCodeResourceSetupWidget,
from aiidalab_qe_vibroscopy.app.model import VibroConfigurationSettingsModel
from aiidalab_qe_vibroscopy.app.settings import VibroConfigurationSettingPanel
from aiidalab_qe_vibroscopy.app.code import (
VibroResourceSettingsModel,
VibroResourcesSettingsPanel,
)
from aiidalab_qe_vibroscopy.app.result.result import VibroResultsPanel
from aiidalab_qe_vibroscopy.app.result.model import VibroResultsModel

from aiidalab_qe_vibroscopy.app.workchain import workchain_and_builder

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)"

PhononWorkChainPwCode = PwCodeResourceSetupWidget(
description="pw.x for phonons", # code for the PhononWorkChain workflow",
default_calc_job_plugin="quantumespresso.pw",
)

# The finite electric field does not support npools (does not work with npools>1), so we just set it as QEAppComputationalResourcesWidget
DielectricWorkChainPwCode = QEAppComputationalResourcesWidget(
description="pw.x for dielectric", # code for the DielectricWorChain workflow",
default_calc_job_plugin="quantumespresso.pw",
)

PhonopyCalculationCode = QEAppComputationalResourcesWidget(
description="phonopy", # code for the PhonopyCalculation calcjob",
default_calc_job_plugin="phonopy.phonopy",
)

property = {
"outline": Outline,
"outline": VibroPluginOutline,
"configuration": {
"panel": VibroConfigurationSettingPanel,
"model": VibroConfigurationSettingsModel,
},
"code": {
"phonon": PhononWorkChainPwCode,
"dielectric": DielectricWorkChainPwCode,
"phonopy": PhonopyCalculationCode,
"panel": VibroResourcesSettingsPanel,
"model": VibroResourceSettingsModel,
},
"result": {
"panel": VibroResultsPanel,
"model": VibroResultsModel,
},
"setting": Setting,
"workchain": workchain_and_builder,
"result": Result,
}
29 changes: 29 additions & 0 deletions src/aiidalab_qe_vibroscopy/app/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from aiidalab_qe.common.code.model import CodeModel, PwCodeModel
from aiidalab_qe.common.panel import ResourceSettingsModel, ResourceSettingsPanel


class VibroResourceSettingsModel(ResourceSettingsModel):
"""Resource settings for the vibroscopy calculations."""

codes = {
"phonon": PwCodeModel(
description="pw.x for phonons",
default_calc_job_plugin="quantumespresso.pw",
),
"dielectric": PwCodeModel(
description="pw.x for dielectric",
default_calc_job_plugin="quantumespresso.pw",
),
"phonopy": CodeModel(
name="phonopy",
description="phonopy",
default_calc_job_plugin="phonopy.phonopy",
),
}


class VibroResourcesSettingsPanel(ResourceSettingsPanel[VibroResourceSettingsModel]):
"""Panel for the resource settings for the vibroscopy calculations."""

title = "Vibronic"
identifier = identifier = "vibronic"
221 changes: 221 additions & 0 deletions src/aiidalab_qe_vibroscopy/app/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import traitlets as tl

import numpy as np
from aiidalab_qe.common.mixins import HasInputStructure
from aiidalab_qe.common.panel import ConfigurationSettingsModel

from aiida_phonopy.data.preprocess import PreProcessData
from aiida.plugins import DataFactory
import sys
import os

HubbardStructureData = DataFactory("quantumespresso.hubbard_structure")
from aiida_vibroscopy.calculations.spectra_utils import get_supercells_for_hubbard
from aiida_vibroscopy.workflows.phonons.base import get_supercell_hubbard_structure

# spinner for waiting time (supercell estimations)
spinner_html = """
<style>
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
display: inline-block;
width: 15px;
height: 15px;
}
.spinner div {
width: 100%;
height: 100%;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
</style>
<div class="spinner">
<div></div>
</div>
"""


def disable_print(func):
def wrapper(*args, **kwargs):
# Save the current standard output
original_stdout = sys.stdout
# Redirect standard output to os.devnull
sys.stdout = open(os.devnull, "w")
try:
# Call the function
result = func(*args, **kwargs)
finally:
# Restore the original standard output
sys.stdout.close()
sys.stdout = original_stdout
return result

return wrapper


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)

# Control for disable the supercell widget

disable_x = tl.Bool(False)
disable_y = tl.Bool(False)
disable_z = tl.Bool(False)

supercell = tl.List(
trait=tl.Int(),
default_value=[2, 2, 2],
)
supercell_number_estimator = tl.Unicode(
"Click the button to estimate the supercell size."
)

def get_model_state(self):
return {
"simulation_type": self.simulation_type,
"symmetry_symprec": self.symmetry_symprec,
"supercell": self.supercell,
}

def set_model_state(self, parameters: dict):
self.simulation_type = parameters.get("simulation_type", 1)
self.symmetry_symprec = parameters.get("symmetry_symprec", 1e-5)
self.supercell = parameters.get("supercell", [2, 2, 2])
self.supercell_x, self.supercell_y, self.supercell_z = self.supercell

def reset(self):
with self.hold_trait_notifications():
self.simulation_type = 1
self.symmetry_symprec = self._get_default("symmetry_symprec")
self.supercell = [2, 2, 2]
self.supercell_x, self.supercell_y, self.supercell_z = self.supercell
self.supercell_number_estimator = self._get_default(
"supercell_number_estimator"
)

def _get_default(self, trait):
return self._defaults.get(trait, self.traits()[trait].default_value)

def on_input_structure_change(self, _=None):
if not self.input_structure:
self.reset()

else:
self.disable_x, self.disable_y, self.disable_z = True, True, True
pbc = self.input_structure.pbc

if pbc == (False, False, False):
# No periodicity; fully disable and reset supercell
self.supercell_x = self.supercell_y = self.supercell_z = 1
elif pbc == (True, False, False):
self.supercell_y = self.supercell_z = 1
self.disable_x = False
self.symmetry_symprec = 1e-3
elif pbc == (True, True, False):
self.supercell_z = 1
self.disable_x = self.disable_y = False
elif pbc == (True, True, True):
self.disable_x = self.disable_y = self.disable_z = False

self.supercell = [self.supercell_x, self.supercell_y, self.supercell_z]

def suggest_supercell(self, _=None):
"""
minimal supercell size for phonons, imposing a minimum lattice parameter of 15 A.
"""
if self.input_structure and self.input_structure.pbc != (False, False, False):
ase_structure = self.input_structure.get_ase()
suggested_3D = 15 // np.array(ase_structure.cell.cellpar()[:3]) + 1

# Update only dimensions that are not disabled
if not self.disable_x:
self.supercell_x = int(suggested_3D[0])
if not self.disable_y:
self.supercell_y = int(suggested_3D[1])
if not self.disable_z:
self.supercell_z = int(suggested_3D[2])

# Sync the updated values to the supercell list
self.supercell = [self.supercell_x, self.supercell_y, self.supercell_z]

else:
return

def supercell_reset(self, _=None):
if not self.disable_x:
self.supercell_x = self._get_default("supercell_x")
if not self.disable_y:
self.supercell_y = self._get_default("supercell_x")
if not self.disable_z:
self.supercell_z = self._get_default("supercell_x")
self.supercell = [self.supercell_x, self.supercell_y, self.supercell_z]

def reset_symprec(self, _=None):
self.symmetry_symprec = (
self._get_default("symmetry_symprec")
if self.input_structure.pbc != (True, False, False)
else 1e-3
)
self.supercell_number_estimator = self._get_default(
"supercell_number_estimator"
)

@disable_print
def _estimate_supercells(self, _=None):
if self.input_structure:
self.supercell_number_estimator = spinner_html

preprocess_data = PreProcessData(
structure=self.input_structure,
supercell_matrix=[
[self.supercell_x, 0, 0],
[0, self.supercell_y, 0],
[0, 0, self.supercell_z],
],
symprec=self.symmetry_symprec,
distinguish_kinds=False,
is_symmetry=True,
)

if isinstance(self.input_structure, HubbardStructureData):
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 = f"{len(supercells)}"

return
Loading

0 comments on commit 7249eca

Please sign in to comment.