Source code for qtdraw.multipie.tab_group

"""
Multipie group tab.

This module provides group tab in MultiPie dialog.
"""

import numpy as np
import sympy as sp
from PySide6.QtWidgets import QWidget
from PySide6.QtCore import Qt

from qtdraw.util.util import distance, to_latex
from qtdraw.widget.custom_widget import Label, Layout, Button, Combo, VSpacer, HSpacer, HBar, LineEdit, Check
from qtdraw.multipie.multipie_info_dialog import show_harmonics_decomp, show_harmonics_info, show_atomic_multipole, show_response
from qtdraw.multipie.multipie_plot import plot_cell_site, plot_cell_bond


# ==================================================
[docs] class TabGroup(QWidget): # ================================================== def __init__(self, parent): super().__init__(parent) self.parent = parent self.data = parent._data layout = Layout(self) layout.setContentsMargins(10, 10, 10, 10) layout.setHorizontalSpacing(30) layout.setVerticalSpacing(10) # irrep. decomposition. label_decomp = Label(parent, text="Irrep. Decomposition (PG)", bold=True) label_symmetric = Label(parent, text="symmetric") label_antisymmetric = Label(parent, text="anti-symmetric") self.combo_irrep1 = Combo(parent) self.combo_irrep2 = Combo(parent) self.label_symmetric_decomp = Label(parent) self.combo_irrep = Combo(parent) self.label_antisymmetric_decomp = Label(parent) panel1 = QWidget(parent) layout1 = Layout(panel1) layout1.addWidget(label_decomp, 0, 0, 1, 1) panel2 = QWidget(parent) layout2 = Layout(panel2) layout2.addWidget(label_symmetric, 0, 0, 1, 1, Qt.AlignRight) layout2.addWidget(self.combo_irrep1, 0, 1, 1, 1) layout2.addWidget(self.combo_irrep2, 0, 2, 1, 1) layout2.addWidget(self.label_symmetric_decomp, 0, 3, 1, 3) layout2.addWidget(label_antisymmetric, 1, 0, 1, 1, Qt.AlignRight) layout2.addWidget(self.combo_irrep, 1, 1, 1, 1) layout2.addWidget(self.label_antisymmetric_decomp, 1, 3, 1, 3) # harmonics decomposition. label_harmonics = Label(parent, text="Harmonics Decomposition (PG)", bold=True) label_harmonics_type = Label(parent, text="type") self.combo_harmonics_type = Combo(parent, ["Q", "G"]) label_harmonics_rank = Label(parent, text="rank") self.combo_harmonics_rank = Combo(parent, map(str, range(12))) label_harmonics_decomp = Label(parent, text="target PG") point_group_all_list = sum([i["PG"] for i in self.data._crystal_list.values()], []) self.combo_harmonics_decomp = Combo(parent, point_group_all_list) self.button_harmonics_decomp = Button(parent, text="decompose") panel3 = QWidget(parent) layout3 = Layout(panel3) layout3.addWidget(label_harmonics, 0, 0, 1, 1) layout3.addWidget(self.button_harmonics_decomp, 0, 2, 1, 1, Qt.AlignRight) panel4 = QWidget(parent) layout4 = Layout(panel4) layout4.addWidget(label_harmonics_type, 0, 0, 1, 1, Qt.AlignRight) layout4.addWidget(self.combo_harmonics_type, 0, 1, 1, 1) layout4.addWidget(label_harmonics_rank, 0, 2, 1, 1, Qt.AlignRight) layout4.addWidget(self.combo_harmonics_rank, 0, 3, 1, 1) layout4.addWidget(label_harmonics_decomp, 0, 4, 1, 1, Qt.AlignRight) layout4.addWidget(self.combo_harmonics_decomp, 0, 5, 1, 2) # harmonics. label_harmonics1 = Label(parent, text="Harmonics (PG)", bold=True) self.combo_harmonics1_type = Combo(parent, ["Q", "G"]) self.combo_harmonics1_rank = Combo(parent, map(str, range(12))) self.combo_harmonics1 = Combo(parent) self.combo_harmonics1.setMinimumWidth(150) label_harmonics_ex = Label(parent, text="expression") self.edit_harmonics1_ex = LineEdit(parent) self.check_harmonics1_latex = Check(parent, text="LaTeX") self.button_harmonics_info = Button(parent, text="info") panel5 = QWidget(parent) layout5 = Layout(panel5) layout5.addWidget(label_harmonics1, 0, 0, 1, 1) panel6 = QWidget(parent) layout6 = Layout(panel6) layout6.addWidget(self.combo_harmonics1_type, 0, 0, 1, 1) layout6.addWidget(self.combo_harmonics1_rank, 0, 1, 1, 1) layout6.addWidget(self.combo_harmonics1, 0, 2, 1, 1) layout6.addWidget(self.button_harmonics_info, 0, 3, 1, 1) layout6.addWidget(self.check_harmonics1_latex, 0, 4, 1, 1, Qt.AlignRight) layout6.addWidget(label_harmonics_ex, 1, 0, 1, 1) layout6.addWidget(self.edit_harmonics1_ex, 1, 1, 1, 4) # find Wyckoff. label_fwyckoff = Label(parent, text="Find Wyckoff Site(PG/SG/MPG/MSG)/Bond(PG/SG)", bold=True) label_fwyckoff_sb = Label(parent, text="site/bond") self.edit_find_wyckoff = LineEdit(parent, text="", validator=("site_bond", {"use_var": False})) label_wyckoff = Label(parent, text="Wyckoff") self.edit_find_wyckoff_position = Label(parent, text="") self.edit_find_wyckoff_position.set_background(True) self.edit_find_wyckoff_position.setMinimumWidth(200) label_symmetry = Label(parent, text="site symmetry") self.edit_find_wyckoff_symmetry = Label(parent, text="") self.edit_find_wyckoff_symmetry.set_background(True) self.edit_find_wyckoff_symmetry.setMinimumWidth(100) panel7 = QWidget(parent) layout7 = Layout(panel7) layout7.addWidget(label_fwyckoff, 0, 0, 1, 5, Qt.AlignLeft) panel8 = QWidget(parent) layout8 = Layout(panel8) layout8.addWidget(label_fwyckoff_sb, 0, 0, 1, 1) layout8.addWidget(self.edit_find_wyckoff, 0, 1, 1, 4) layout8.addWidget(label_wyckoff, 1, 1, 1, 1, Qt.AlignRight) layout8.addWidget(self.edit_find_wyckoff_position, 1, 2, 1, 1) layout8.addWidget(label_symmetry, 1, 3, 1, 1, Qt.AlignRight) layout8.addWidget(self.edit_find_wyckoff_symmetry, 1, 4, 1, 1) # Wyckoff site/bond. label_wyckoff_site = Label(parent, text="Draw Wyckoff Site(PG/SG/MPG/MSG)/Bond(PG/SG) (representative)", bold=True) label_ws_neighbor = Label(parent, text="neighbor") label_wyckoff_site_str = Label(parent, text="site") self.combo_wyckoff_site = Combo(parent) self.edit_ws_neighbor = LineEdit(parent, text="[1]", validator=("list_int", {"shape": (0,)})) self.button_wyckoff_bond = Button(parent, text="show bond") label_wyckoff_bond_str = Label(parent, text="bond") self.combo_wyckoff_bond = Combo(parent) panel9 = QWidget(parent) layout9 = Layout(panel9) layout9.addWidget(label_wyckoff_site, 0, 0, 1, 1) panel10 = QWidget(parent) layout10 = Layout(panel10) layout10.addWidget(label_wyckoff_site_str, 0, 0, 1, 1, Qt.AlignRight) layout10.addWidget(self.combo_wyckoff_site, 0, 1, 1, 1) layout10.addWidget(label_ws_neighbor, 0, 2, 1, 1, Qt.AlignRight) layout10.addWidget(self.edit_ws_neighbor, 0, 3, 1, 2) layout10.addWidget(label_wyckoff_bond_str, 1, 0, 1, 1, Qt.AlignRight) layout10.addWidget(self.combo_wyckoff_bond, 1, 1, 1, 1) layout10.addWidget(self.button_wyckoff_bond, 1, 4, 1, 1, Qt.AlignRight) # atomic multipole. label_atomic = Label(parent, text="Atomic Multipole (PG)", bold=True) self.button_atomic = Button(parent, text="show") label_atomic_type = Label(parent, text="type") self.combo_atomic_type = Combo(parent, ["", "Q", "G", "T", "M"]) self.combo_atomic_basis_type = Combo(parent, ["lg", "lgs", "jml"]) label_atomic_braket = Label(parent, text="bra(L)-ket(L)") self.combo_atomic_bra_basis = Combo(parent, ["s", "p", "d", "f"]) self.combo_atomic_ket_basis = Combo(parent, ["s", "p", "d", "f"]) self.check_tesseral = Check(parent, text="tesseral") panel11 = QWidget(parent) layout11 = Layout(panel11) layout11.addWidget(label_atomic, 0, 0, 1, 1) layout11.addWidget(self.button_atomic, 0, 2, 1, 1, Qt.AlignRight) panel12 = QWidget(parent) layout12 = Layout(panel12) layout12.addWidget(label_atomic_type, 0, 1, 1, 1, Qt.AlignRight) layout12.addWidget(self.combo_atomic_type, 0, 2, 1, 1) layout12.addWidget(self.combo_atomic_basis_type, 0, 3, 1, 1) layout12.addWidget(self.check_tesseral, 1, 0, 1, 1, Qt.AlignRight) layout12.addWidget(label_atomic_braket, 1, 1, 1, 1, Qt.AlignRight) layout12.addWidget(self.combo_atomic_bra_basis, 1, 2, 1, 1) layout12.addWidget(self.combo_atomic_ket_basis, 1, 3, 1, 1) # response tensor. label_response = Label(parent, text="Response Tensor (MPG)", bold=True) self.button_response = Button(parent, text="show") self.combo_response_type = Combo(parent, ["Q", "G", "T", "M"]) label_response_type = Label(parent, text="type") label_response_rank = Label(parent, text="rank") self.combo_response_rank = Combo(parent, map(str, range(5))) panel13 = QWidget(parent) layout13 = Layout(panel13) layout13.addWidget(label_response, 0, 0, 1, 1) layout13.addWidget(self.button_response, 0, 2, 1, 1, Qt.AlignRight) panel14 = QWidget(parent) layout14 = Layout(panel14) layout14.addWidget(label_response_type, 0, 0, 1, 1, Qt.AlignRight) layout14.addWidget(self.combo_response_type, 0, 1, 1, 1) layout14.addWidget(label_response_rank, 0, 2, 1, 1, Qt.AlignRight) layout14.addWidget(self.combo_response_rank, 0, 3, 1, 1) layout14.addItem(HSpacer(), 0, 4, 1, 1) # layout. layout.addWidget(panel1) layout.addWidget(panel2) layout.addWidget(HBar()) layout.addWidget(panel3) layout.addWidget(panel4) layout.addWidget(HBar()) layout.addWidget(panel5) layout.addWidget(panel6) layout.addWidget(HBar()) layout.addWidget(panel7) layout.addWidget(panel8) layout.addWidget(HBar()) layout.addWidget(panel9) layout.addWidget(panel10) layout.addWidget(HBar()) layout.addWidget(panel11) layout.addWidget(panel12) layout.addWidget(HBar()) layout.addWidget(panel13) layout.addWidget(panel14) layout.addItem(HSpacer(), 0, 10, 1, 1) layout.addItem(VSpacer()) # connections. self.combo_irrep1.currentTextChanged.connect(self.set_irrep_decomp) self.combo_irrep2.currentTextChanged.connect(self.set_irrep_decomp) self.combo_irrep.currentTextChanged.connect(self.set_irrep_decomp) self.button_harmonics_decomp.released.connect(self.show_harmonics_decomp) self.combo_harmonics1_type.currentTextChanged.connect(self.set_harm_list) self.combo_harmonics1_rank.currentTextChanged.connect(self.set_harm_list) self.combo_harmonics1.currentIndexChanged.connect(self.show_harmonics) self.check_harmonics1_latex.checkStateChanged.connect(self.show_harmonics) self.button_harmonics_info.released.connect(self.show_harmonics_info) self.edit_find_wyckoff.returnPressed.connect(self.find_wyckoff_set) self.edit_ws_neighbor.returnPressed.connect(self.show_wyckoff_site) self.button_wyckoff_bond.released.connect(self.show_wyckoff_bond) self.button_atomic.released.connect(self.show_atomic) self.button_response.released.connect(self.show_response) self.combo_atomic_basis_type.currentTextChanged.connect(self.set_atomic_bra_ket) # ==================================================
[docs] def set_irrep_list(self): group = self.data.p_group lst = list(group.character["table"].keys()) self.combo_irrep1.set_item(lst) self.combo_irrep2.set_item(lst) self.combo_irrep.set_item(lst) self.set_irrep_decomp()
# ==================================================
[docs] def set_harm_list(self): group = self.data.p_group rank = int(self.combo_harmonics1_rank.currentText()) head = self.combo_harmonics1_type.currentText() lst, self._harm_list = self.data._get_index_list(group.harmonics.select(l=rank, X=head).keys()) self.combo_harmonics1.set_item(lst) self.combo_harmonics1.currentIndexChanged.emit(0)
# ==================================================
[docs] def set_wyckoff_list(self): group = self.data.ps_group self.combo_wyckoff_site.set_item(group.wyckoff["site"].keys()) self.combo_wyckoff_bond.set_item(group.wyckoff["bond"].keys())
# ==================================================
[docs] def set_irrep_decomp(self, value=None): def _remove_latex(s): s = ( s.replace("_{", "") .replace("}", "") .replace("^{", "") .replace(r"\prime", "'") .replace("(", "") .replace(")", "") .replace("0", "-") ) return s pg = self.data.p_group irrep1 = self.combo_irrep1.currentText() irrep2 = self.combo_irrep2.currentText() irrep = self.combo_irrep.currentText() s = pg.character["symmetric_product"][(irrep1, irrep2)] a = pg.character["anti_symmetric_product"][irrep] s = _remove_latex(str(sum([n * sp.Symbol(v) for n, v in s]))) a = _remove_latex(str(sum([n * sp.Symbol(v) for n, v in a]))) self.label_symmetric_decomp.setText(" = " + s) self.label_antisymmetric_decomp.setText(" = " + a)
# ==================================================
[docs] def show_harmonics_decomp(self): group = self.data.p_group head = self.combo_harmonics_type.currentText() rank = int(self.combo_harmonics_rank.currentText()) basis = self.combo_harmonics_decomp.currentText() basis = basis.split(" ")[1] self._harmonics_decomp_dialog = show_harmonics_decomp(group, basis, rank, head, self)
# ==================================================
[docs] def show_harmonics(self): group = self.data.p_group harm, comp = self._harm_list[self.combo_harmonics1.currentIndex()] check = self.check_harmonics1_latex.is_checked() harm = group.harmonics[harm][0][comp] if check: harm = to_latex(harm) else: harm = str(harm) self.edit_harmonics1_ex.setText(harm)
# ==================================================
[docs] def show_harmonics_info(self): group = self.data.p_group head = self.combo_harmonics1_type.currentText() rank = int(self.combo_harmonics1_rank.currentText()) if self._harmonics_info_dialog is not None: self._harmonics_info_dialog.close() self._harmonics_info_dialog = show_harmonics_info(group, head, rank, self)
# ==================================================
[docs] def show_wyckoff_site(self): group = self.data.group wp = self.combo_wyckoff_site.currentText() # plot sites. sites = group.wyckoff["site"][wp]["reference"].astype(float) mp = group.wyckoff["site"][wp]["mapping"] if len(sites) != len(mp): mp = mp * (len(sites) // len(mp)) plot_cell_site(self.data, sites, wp=wp, label=mp) # plot bonds. neighbor = self.edit_ws_neighbor.text() neighbor = list(map(int, neighbor.strip("[]").split(","))) G = self.parent._pvw.G_matrix[0:3, 0:3] d = distance(sites, sites, G) dkey = list(d.keys()) for i in neighbor: if i < len(d): name = f"{wp}_N{i}" bonds = [] for idxs in d[dkey[i]]: t, h = sites[idxs[0]], sites[idxs[1]] c = (t + h) / 2 v = h - t bonds.append(np.concatenate([v, c]).tolist()) plot_cell_bond(self.data, bonds, name=name)
# ==================================================
[docs] def show_wyckoff_bond(self): group = self.data.ps_group wp = self.combo_wyckoff_bond.currentText() # plot bonds. bonds = group.wyckoff["bond"][wp]["reference"].astype(float) mp = group.wyckoff["bond"][wp]["mapping"] if len(bonds) != len(mp): mp = mp * (len(bonds) // len(mp)) plot_cell_bond(self.data, bonds, wp=wp, label=mp)
# ==================================================
[docs] def find_wyckoff_set(self): text = self.edit_find_wyckoff.raw_text() self.data.set_group_find_wyckoff(text) if text.count("[") == 2: group = self.data.ps_group wp, r = group.find_wyckoff_bond(text) # sym = group.wyckoff["bond"][wp]["symmetry"] sym = "" else: group = self.data.group wp, r = group.find_wyckoff_site(text) sym = group.wyckoff["site"][wp]["symmetry"] self.edit_find_wyckoff_position.setText(wp) self.edit_find_wyckoff_symmetry.setText(sym)
# ==================================================
[docs] def set_atomic_bra_ket(self): basis_type = self.combo_atomic_basis_type.currentText() if basis_type == "jml": self.combo_atomic_bra_basis.set_item( [ "s : 1/2", "p : 1/2, 3/2", "p : 1/2", "p : 3/2", "d : 3/2, 5/2", "d : 3/2", "d : 5/2", "f : 5/2, 7/2", "f : 5/2", "f : 7/2", ] ) self.combo_atomic_ket_basis.set_item( [ "s : 1/2", "p : 1/2, 3/2", "p : 1/2", "p : 3/2", "d : 3/2, 5/2", "d : 3/2", "d : 5/2", "f : 5/2, 7/2", "f : 5/2", "f : 7/2", ] ) else: self.combo_atomic_bra_basis.set_item(["s", "P", "d", "f"]) self.combo_atomic_ket_basis.set_item(["s", "P", "d", "f"])
# ==================================================
[docs] def show_atomic(self): group = self.data.p_group head = self.combo_atomic_type.currentText() basis_type = self.combo_atomic_basis_type.currentText() bra = self.combo_atomic_bra_basis.currentText() ket = self.combo_atomic_ket_basis.currentText() tesseral = self.check_tesseral.is_checked() self._atomic_dialog = show_atomic_multipole(group, bra, ket, head, basis_type, tesseral, self)
# ==================================================
[docs] def show_response(self): group = self.data.mp_group rank = int(self.combo_response_rank.currentText()) r_type = self.combo_response_type.currentText() self._response_dialog = show_response(group, rank, r_type, self)
# ==================================================
[docs] def closeEvent(self, event): self.clear_data() super().closeEvent(event)
# ==================================================
[docs] def set_data(self): find_wyckoff = self.data.status["group"]["find_wyckoff"] self.edit_find_wyckoff.setText(find_wyckoff) self._harmonics_decomp_dialog = None self._harmonics_info_dialog = None self._atomic_dialog = None self._response_dialog = None
# ==================================================
[docs] def clear_data(self): if self._harmonics_decomp_dialog is not None: self._harmonics_decomp_dialog.close() if self._harmonics_info_dialog is not None: self._harmonics_info_dialog.close() if self._atomic_dialog is not None: self._atomic_dialog.close() if self._response_dialog is not None: self._response_dialog.close() self._harmonics_decomp_dialog = None self._harmonics_info_dialog = None self._atomic_dialog = None self._response_dialog = None