diff --git a/src/aiidalab_qe_vibroscopy/app/model.py b/src/aiidalab_qe_vibroscopy/app/model.py
index 0df0759..c6cf08c 100644
--- a/src/aiidalab_qe_vibroscopy/app/model.py
+++ b/src/aiidalab_qe_vibroscopy/app/model.py
@@ -91,7 +91,9 @@ class VibroConfigurationSettingsModel(ConfigurationSettingsModel, HasInputStruct
trait=tl.Int(),
default_value=[2, 2, 2],
)
- supercell_number_estimator = tl.Unicode("?")
+ supercell_number_estimator = tl.Unicode(
+ "Click the button to estimate the supercell size."
+ )
def get_model_state(self):
return {
diff --git a/src/aiidalab_qe_vibroscopy/app/result.py b/src/aiidalab_qe_vibroscopy/app/result.py
index 4b1d19e..d62b276 100644
--- a/src/aiidalab_qe_vibroscopy/app/result.py
+++ b/src/aiidalab_qe_vibroscopy/app/result.py
@@ -9,7 +9,6 @@
import numpy as np
from ..utils.raman.result import export_iramanworkchain_data
-from ..utils.dielectric.result import export_dielectric_data, DielectricResults
from ..utils.phonons.result import export_phononworkchain_data
from ..utils.euphonic import (
@@ -86,7 +85,6 @@ def _update_view(self):
spectra_data = export_iramanworkchain_data(self.node)
phonon_data = export_phononworkchain_data(self.node)
ins_data = export_euphonic_data(self.node)
- dielectric_data = export_dielectric_data(self.node)
if phonon_data:
phonon_children = ()
@@ -208,11 +206,6 @@ def _update_view(self):
)
tab_titles.append("Raman/IR spectra")
- if dielectric_data:
- dielectric_results = DielectricResults(dielectric_data)
- children_result_widget += (dielectric_results,)
- tab_titles.append("Dielectric properties")
-
# euphonic
if ins_data:
intensity_maps = EuphonicSuperWidget(
diff --git a/src/aiidalab_qe_vibroscopy/app/result/result.py b/src/aiidalab_qe_vibroscopy/app/result/result.py
index 849bb56..d445519 100644
--- a/src/aiidalab_qe_vibroscopy/app/result/result.py
+++ b/src/aiidalab_qe_vibroscopy/app/result/result.py
@@ -3,6 +3,8 @@
from aiidalab_qe_vibroscopy.app.result.model import VibroResultsModel
from aiidalab_qe.common.panel import ResultsPanel
+from aiidalab_qe_vibroscopy.app.widgets.dielectricwidget import DielectricWidget
+from aiidalab_qe_vibroscopy.app.widgets.dielectricmodel import DielectricModel
import ipywidgets as ipw
@@ -19,6 +21,10 @@ def render(self):
layout=ipw.Layout(min_height="250px"),
selected_index=None,
)
+ self.tabs.observe(
+ self._on_tab_change,
+ "selected_index",
+ )
tab_data = []
# vibro_node = self._model.get_vibro_node()
@@ -29,8 +35,14 @@ def render(self):
if self._model.needs_raman_tab():
tab_data.append(("Raman", ipw.HTML("raman_data")))
- if self._model.needs_dielectric_tab():
- tab_data.append(("Dielectric", ipw.HTML("dielectric_data")))
+ dielectric_data = self._model.needs_dielectric_tab()
+
+ if dielectric_data:
+ dielectric_model = DielectricModel()
+ dielectric_widget = DielectricWidget(
+ model=dielectric_model, dielectric_data=dielectric_data
+ )
+ tab_data.append(("Dielectric Properties", dielectric_widget))
if self._model.needs_euphonic_tab():
tab_data.append(("Euphonic", ipw.HTML("euphonic_data")))
@@ -43,3 +55,8 @@ def render(self):
self.children = [self.tabs]
self.rendered = True
+
+ def _on_tab_change(self, change):
+ if (tab_index := change["new"]) is None:
+ return
+ self.tabs.children[tab_index].render() # type: ignore
diff --git a/src/aiidalab_qe_vibroscopy/app/widgets/dielectricmodel.py b/src/aiidalab_qe_vibroscopy/app/widgets/dielectricmodel.py
index 6b76f76..3fbd2d1 100644
--- a/src/aiidalab_qe_vibroscopy/app/widgets/dielectricmodel.py
+++ b/src/aiidalab_qe_vibroscopy/app/widgets/dielectricmodel.py
@@ -1,9 +1,156 @@
from aiidalab_qe.common.mvc import Model
-from aiida.common.extendeddicts import AttributeDict
import traitlets as tl
+from aiidalab_qe_vibroscopy.utils.dielectric.result import NumpyEncoder
+import numpy as np
+import base64
+import json
+from IPython.display import display
-class DielectricModel(Model):
- vibro = tl.Instance(AttributeDict, allow_none=True)
+class DielectricModel(Model):
dielectric_data = {}
+
+ site_selector_options = tl.List(
+ trait=tl.Tuple((tl.Unicode(), tl.Int())),
+ )
+
+ dielectric_tensor_table = tl.Unicode("")
+ born_charges_table = tl.Unicode("")
+ raman_tensors_table = tl.Unicode("")
+ site = tl.Int()
+
+ def set_initial_values(self):
+ """Set the initial values for the model."""
+
+ self.dielectric_tensor_table = self._create_dielectric_tensor_table()
+ self.born_charges_table = self._create_born_charges_table(0)
+ self.raman_tensors_table = self._create_raman_tensors_table(0)
+ self.site_selector_options = self._get_site_selector_options()
+
+ def _get_site_selector_options(self):
+ """Get the site selector options."""
+ if not self.dielectric_data:
+ return []
+
+ unit_cell_sites = self.dielectric_data["unit_cell"]
+ decimal_places = 5
+ # Create the options with rounded positions
+ site_selector_options = [
+ (
+ f"{site.kind_name} @ ({', '.join(f'{coord:.{decimal_places}f}' for coord in site.position)})",
+ index,
+ )
+ for index, site in enumerate(unit_cell_sites)
+ ]
+ return site_selector_options
+
+ def _create_dielectric_tensor_table(self):
+ """Create the HTML table for the dielectric tensor."""
+ if not self.dielectric_data:
+ return ""
+
+ dielectric_tensor = self.dielectric_data["dielectric_tensor"]
+ table_data = self._generate_table(dielectric_tensor)
+ return table_data
+
+ def _create_born_charges_table(self, site_index):
+ """Create the HTML table for the Born charges."""
+ if not self.dielectric_data:
+ return ""
+
+ born_charges = self.dielectric_data["born_charges"]
+ round_data = born_charges[site_index].round(6)
+ table_data = self._generate_table(round_data)
+ return table_data
+
+ def _create_raman_tensors_table(self, site_index):
+ """Create the HTML table for the Raman tensors."""
+ if not self.dielectric_data:
+ return ""
+
+ raman_tensors = self.dielectric_data["raman_tensors"]
+ round_data = raman_tensors[site_index].round(6)
+ table_data = self._generate_table(round_data, cell_width="200px")
+ return table_data
+
+ def download_data(self, _=None):
+ """Function to download the data."""
+ if self.dielectric_data:
+ data_to_print = {
+ key: value
+ for key, value in self.dielectric_data.items()
+ if key != "unit_cell"
+ }
+ file_name = "dielectric_data.json"
+ json_str = json.dumps(data_to_print, cls=NumpyEncoder)
+ b64_str = base64.b64encode(json_str.encode()).decode()
+ self._download(payload=b64_str, filename=file_name)
+
+ @staticmethod
+ def _download(payload, filename):
+ """Download payload as a file named as filename."""
+ from IPython.display import Javascript
+
+ javas = Javascript(
+ f"""
+ var link = document.createElement('a');
+ link.href = 'data:text/json;charset=utf-8;base64,{payload}'
+ link.download = "{filename}"
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ """
+ )
+ display(javas)
+
+ def on_site_selection_change(self, site):
+ self.site = site
+ self.born_charges_table = self._create_born_charges_table(site)
+ self.raman_tensors_table = self._create_raman_tensors_table(site)
+
+ def _generate_table(self, data, cell_width="50px"):
+ rows = []
+ for row in data:
+ cells = []
+ for value in row:
+ # Check if value is a numpy array
+ if isinstance(value, np.ndarray):
+ # Format the numpy array as a string, e.g., "[0, 0, 1]"
+ value_str = np.array2string(
+ value, separator=", ", formatter={"all": lambda x: f"{x:.6g}"}
+ )
+ cell = f"
{value_str} | "
+ elif isinstance(value, str) and value == "special":
+ # Handle the "special" keyword
+ cell = f'{value} | '
+ else:
+ # Handle other types (numbers, strings, etc.)
+ cell = f"{value} | "
+ cells.append(cell)
+ rows.append(f"{''.join(cells)}
")
+
+ # Define the HTML with styles, using the dynamic cell width
+ table_html = f"""
+
+
+ """
+ return table_html
diff --git a/src/aiidalab_qe_vibroscopy/app/widgets/dielectricwidget.py b/src/aiidalab_qe_vibroscopy/app/widgets/dielectricwidget.py
index 4a57b75..d729e25 100644
--- a/src/aiidalab_qe_vibroscopy/app/widgets/dielectricwidget.py
+++ b/src/aiidalab_qe_vibroscopy/app/widgets/dielectricwidget.py
@@ -8,13 +8,16 @@ class DielectricWidget(ipw.VBox):
Widget for displaying dielectric properties results
"""
- def __init__(self, model: DielectricModel, dielectric_node: None, **kwargs):
+ def __init__(self, model: DielectricModel, dielectric_data: None, **kwargs):
super().__init__(
children=[LoadingWidget("Loading widgets")],
**kwargs,
)
self._model = model
+ self.rendered = False
+ self._model.dielectric_data = dielectric_data
+
def render(self):
if self.rendered:
return
@@ -31,8 +34,76 @@ def render(self):
"""
)
+ self.site_selector = ipw.Dropdown(
+ layout=ipw.Layout(width="450px"),
+ description="Select atom site:",
+ style={"description_width": "initial"},
+ )
+ ipw.dlink(
+ (self._model, "site_selector_options"),
+ (self.site_selector, "options"),
+ )
+ self.site_selector.observe(self._on_site_change, names="value")
+
+ self.download_button = ipw.Button(
+ description="Download Data", icon="download", button_style="primary"
+ )
+
+ self.download_button.on_click(self._model.download_data)
+
+ # HTML table with the dielectric tensor
+ self.dielectric_tensor_table = ipw.HTML()
+ ipw.link(
+ (self._model, "dielectric_tensor_table"),
+ (self.dielectric_tensor_table, "value"),
+ )
+
+ # HTML table with the Born charges @ site
+ self.born_charges_table = ipw.HTML()
+ ipw.link(
+ (self._model, "born_charges_table"),
+ (self.born_charges_table, "value"),
+ )
+
+ # HTML table with the Raman tensors @ site
+ self.raman_tensors_table = ipw.HTML()
+ ipw.link(
+ (self._model, "raman_tensors_table"),
+ (self.raman_tensors_table, "value"),
+ )
+
self.children = [
self.dielectric_results_help,
+ ipw.HTML("Dielectric tensor
"),
+ self.dielectric_tensor_table,
+ self.site_selector,
+ ipw.HBox(
+ [
+ ipw.VBox(
+ [
+ ipw.HTML("Born effective charges
"),
+ self.born_charges_table,
+ ]
+ ),
+ ipw.VBox(
+ [
+ ipw.HTML("Raman Tensor
"),
+ self.raman_tensors_table,
+ ]
+ ),
+ ]
+ ),
+ self.download_button,
]
self.rendered = True
+ self._initial_view()
+
+ def _initial_view(self):
+ self._model.set_initial_values()
+ self.dielectric_tensor_table.layout = ipw.Layout(width="300px", height="auto")
+ self.born_charges_table.layout = ipw.Layout(width="300px", height="auto")
+ # self.raman_tensors_table.layout = ipw.Layout(width="auto", height="auto")
+
+ def _on_site_change(self, change):
+ self._model.on_site_selection_change(change["new"])
diff --git a/src/aiidalab_qe_vibroscopy/utils/dielectric/result.py b/src/aiidalab_qe_vibroscopy/utils/dielectric/result.py
index 2bec7dd..2806e9c 100644
--- a/src/aiidalab_qe_vibroscopy/utils/dielectric/result.py
+++ b/src/aiidalab_qe_vibroscopy/utils/dielectric/result.py
@@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
-import ipywidgets as ipw
-from IPython.display import HTML, display
-import base64
import json
import numpy as np
@@ -113,172 +110,3 @@ def export_dielectric_data(node):
"nlo_susceptibility": nlo_susceptibility,
"unit_cell": unit_cell,
}
-
-
-class DielectricResults(ipw.VBox):
- def __init__(self, dielectric_data):
- # Helper
- self.dielectric_results_help = ipw.HTML(
- """
- The DielectricWorkchain computes different properties:
- -High Freq. Dielectric Tensor
- -Born Charges
- -Raman Tensors
- -The non-linear optical susceptibility tensor
- All information can be downloaded as a JSON file.
-
-
"""
- )
-
- self.unit_cell_sites = dielectric_data.pop("unit_cell")
- self.dielectric_data = dielectric_data
- self.dielectric_tensor = dielectric_data["dielectric_tensor"]
- self.born_charges = dielectric_data["born_charges"]
- self.volume = dielectric_data["volume"]
- self.raman_tensors = dielectric_data["raman_tensors"]
- self.nlo_susceptibility = dielectric_data["nlo_susceptibility"]
-
- # HTML table with the dielectric tensor
- self.dielectric_tensor_table = ipw.Output()
-
- # HTML table with the Born charges @ site
- self.born_charges_table = ipw.Output()
-
- # HTML table with the Raman tensors @ site
- self.raman_tensors_table = ipw.Output()
-
- decimal_places = 6
- # Create the options with rounded positions
- site_selector_options = [
- (
- f"{site.kind_name} @ ({', '.join(f'{coord:.{decimal_places}f}' for coord in site.position)})",
- index,
- )
- for index, site in enumerate(self.unit_cell_sites)
- ]
-
- self.site_selector = ipw.Dropdown(
- options=site_selector_options,
- value=site_selector_options[0][1],
- layout=ipw.Layout(width="450px"),
- description="Select atom site:",
- style={"description_width": "initial"},
- )
- # Download button
- self.download_button = ipw.Button(
- description="Download Data", icon="download", button_style="primary"
- )
- self.download_button.on_click(self.download_data)
-
- # Initialize the HTML table
- self._create_dielectric_tensor_table()
- # Initialize Born Charges Table
- self._create_born_charges_table(self.site_selector.value)
- # Initialize Raman Tensors Table
- self._create_raman_tensors_table(self.site_selector.value)
-
- self.site_selector.observe(self._on_site_selection_change, names="value")
- super().__init__(
- children=(
- self.dielectric_results_help,
- ipw.HTML("Dielectric tensor
"),
- self.dielectric_tensor_table,
- self.site_selector,
- ipw.HBox(
- [
- ipw.VBox(
- [
- ipw.HTML("Born effective charges
"),
- self.born_charges_table,
- ]
- ),
- ipw.VBox(
- [
- ipw.HTML("Raman Tensor
"),
- self.raman_tensors_table,
- ]
- ),
- ]
- ),
- self.download_button,
- )
- )
-
- def download_data(self, _=None):
- """Function to download the data."""
- file_name = "dielectric_data.json"
-
- json_str = json.dumps(self.dielectric_data, cls=NumpyEncoder)
- b64_str = base64.b64encode(json_str.encode()).decode()
- self._download(payload=b64_str, filename=file_name)
-
- @staticmethod
- def _download(payload, filename):
- """Download payload as a file named as filename."""
- from IPython.display import Javascript
-
- javas = Javascript(
- f"""
- var link = document.createElement('a');
- link.href = 'data:text/json;charset=utf-8;base64,{payload}'
- link.download = "{filename}"
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- """
- )
- display(javas)
-
- def _create_html_table(self, matrix):
- """
- Create an HTML table representation of a 3x3 matrix.
-
- :param matrix: List of lists representing a 3x3 matrix
- :return: HTML table string
- """
- html = ''
- for row in matrix:
- html += ""
- for cell in row:
- html += f'{cell} | '
- html += "
"
- html += "
"
- return html
-
- def _create_dielectric_tensor_table(self):
- table_data = self._create_html_table(self.dielectric_tensor)
- self.dielectric_tensor_table.layout = {
- "overflow": "auto",
- "height": "100px",
- "width": "300px",
- }
- with self.dielectric_tensor_table:
- display(HTML(table_data))
-
- def _create_born_charges_table(self, site_index):
- round_data = self.born_charges[site_index].round(6)
- table_data = self._create_html_table(round_data)
- self.born_charges_table.layout = {
- "overflow": "auto",
- "height": "150px",
- "width": "300px",
- }
- with self.born_charges_table:
- display(HTML(table_data))
-
- def _create_raman_tensors_table(self, site_index):
- round_data = self.raman_tensors[site_index].round(6)
- table_data = self._create_html_table(round_data)
- self.raman_tensors_table.layout = {
- "overflow": "auto",
- "height": "150px",
- "width": "500px",
- }
- with self.raman_tensors_table:
- display(HTML(table_data))
-
- def _on_site_selection_change(self, change):
- self.born_charges_table.clear_output()
- self.raman_tensors_table.clear_output()
- self._create_born_charges_table(change["new"])
- self._create_raman_tensors_table(change["new"])