Source code for octadist.src.structure

# OctaDist  Copyright (C) 2019-2024  Rangsiman Ketkaew et al.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

import tkinter as tk
from tkinter import scrolledtext as tkscrolled

from scipy.spatial import distance

from octadist.src import linear, util


[docs] class DataComplex: """ Show info of input complex. Parameters ---------- master : object, optional If None, use tk.Tk(). If not None, use tk.Toplevel(master). icon : str, optional If None, use tkinter default icon. If not None, use user-defined icon. Examples -------- >>> file = "File_1" >>> atom = ['Fe', 'N', 'N', 'N', 'O', 'O', 'O'] >>> coord = [[2.298354000, 5.161785000, 7.971898000], [1.885657000, 4.804777000, 6.183726000], [1.747515000, 6.960963000, 7.932784000], [4.094380000, 5.807257000, 7.588689000], [0.539005000, 4.482809000, 8.460004000], [2.812425000, 3.266553000, 8.131637000], [2.886404000, 5.392925000, 9.848966000]] >>> my_app = DataComplex() >>> my_app.add_name(file) >>> my_app.add_coord(atom, coord) """ def __init__(self, master=None, icon=None): if master is None: self.wd = tk.Tk() else: self.wd = tk.Toplevel(master) self.icon = icon self.start_app()
[docs] def start_app(self): """ Start application. """ if self.icon is not None: self.wd.wm_iconbitmap(self.icon) self.wd.title("Complex info") self.wd.geometry("550x500") self.wd.option_add("*Font", "Arial 10") self.wd.resizable(0, 0) self.frame = tk.Frame(self.wd) self.frame.grid() self.box = tkscrolled.ScrolledText( self.frame, wrap="word", undo="True", width="75", height="30" ) self.box.grid(row=0, pady="5", padx="5")
[docs] def add_name(self, file_name): """ Add file name to box. Parameters ---------- file_name : array_like List containing the names of all input files. """ self.box.insert(tk.END, f"File: {file_name.split('/')[-1]}\n")
[docs] def add_coord(self, atom, coord): """ Add atomic symbols and coordinates to box. Parameters ---------- atom : array_like Atomic labels of full complex. coord : array_like Atomic coordinates of full complex. """ self.box.insert(tk.END, "===============================\n") self.box.insert(tk.END, f"{len(atom)}\n") atoms = list(set(atom)) self.box.insert(tk.END, f"List of atoms: {atoms}\n") for i in range(len(coord)): self.box.insert( tk.END, f"{atom[i]:>2}\t" f"{coord[i][0]:9.6f}\t\t" f"{coord[i][1]:9.6f}\t\t" f"{coord[i][2]:9.6f}", ) self.box.insert(tk.END, "\n") self.box.insert(tk.END, "\n\n")
[docs] class StructParam: """ Show structural parameters of structure. Parameters ---------- master : object, optional If None, use tk.Tk(). If not None, use tk.Toplevel(master). icon : str, optional If None, use tkinter default icon. If not None, use user-defined icon. Examples -------- >>> metal = 'Fe' >>> atom = ['Fe', 'N', 'N', 'N', 'O', 'O', 'O'] >>> coord = [[2.298354000, 5.161785000, 7.971898000], [1.885657000, 4.804777000, 6.183726000], [1.747515000, 6.960963000, 7.932784000], [4.094380000, 5.807257000, 7.588689000], [0.539005000, 4.482809000, 8.460004000], [2.812425000, 3.266553000, 8.131637000], [2.886404000, 5.392925000, 9.848966000]] >>> my_app = StructParam() >>> my_app.add_metal(metal) >>> my_app.add_coord(atom, coord) """ def __init__(self, master=None, icon=None): if master is None: self.wd = tk.Tk() else: self.wd = tk.Toplevel(master) self.icon = icon self.start_app()
[docs] def start_app(self): """ Start application. """ if self.icon is not None: self.wd.wm_iconbitmap(self.icon) self.wd.title("Results") self.wd.geometry("380x530") self.wd.option_add("*Font", "Arial 10") self.wd.resizable(0, 0) self.frame = tk.Frame(self.wd) self.frame.grid() self.lbl = tk.Label(self.frame, text="Structural parameters") self.lbl.grid(row=0, pady="5", padx="5", sticky=tk.W) self.box = tkscrolled.ScrolledText( self.frame, wrap="word", undo="True", width="50", height="30" ) self.box.grid(row=1, pady="5", padx="5")
[docs] def add_number(self, number): """ Add file number to box. Parameters ---------- number : int File number. """ self.box.insert(tk.INSERT, f"Number : {number}\n")
[docs] def add_metal(self, metal): """ Add metal atom to box: Parameters ---------- metal : str Metal atom. """ self.box.insert(tk.END, f"Metal: {metal}\n")
[docs] def add_coord(self, atom, coord): """ Add atomic symbols and coordinates to box. Parameters ---------- atom : array_like Atomic labels of full complex. coord : array_like Atomic coordinates of full complex. """ self.box.insert(tk.END, "Bond distance (Å)") # Bond distance for i in range(len(coord)): for j in range(i + 1, len(coord)): dist = distance.euclidean(coord[i], coord[j]) if i == 0: texts = f"{atom[i]}-{atom[j]}{j}\t\t{dist:10.6f}" else: texts = f"{atom[i]}{i}-{atom[j]}{j}\t\t{dist:10.6f}" self.box.insert(tk.END, "\n" + texts) self.box.insert(tk.END, "\n\nBond angle (°)") # Bond angle. for i in range(len(coord)): for j in range(i + 1, len(coord)): for k in range(j + 1, 7): vec1 = coord[i] - coord[j] vec2 = coord[k] - coord[j] angle = linear.angle_btw_vectors(vec1, vec2) if i == 0: texts = f"{atom[k]}{k}-{atom[i]}-{atom[j]}{j}\t\t{angle:10.6f}" else: texts = ( f"{atom[k]}{k}-{atom[i]}{i}-{atom[j]}{j}\t\t{angle:10.6f}" ) self.box.insert(tk.END, "\n" + texts) self.box.insert(tk.END, "\n\n=================================\n\n")
[docs] class SurfaceArea: """ Find the area of the faces of octahedral structure. Three ligand atoms are vertices of triangular face Parameters ---------- master : object, optional If None, use tk.Tk(). If not None, use tk.Toplevel(master). icon : str, optional If None, use tkinter default icon. If not None, use user-defined icon. Examples -------- >>> metal = 'Fe' >>> coord = [[2.298354000, 5.161785000, 7.971898000], [1.885657000, 4.804777000, 6.183726000], [1.747515000, 6.960963000, 7.932784000], [4.094380000, 5.807257000, 7.588689000], [0.539005000, 4.482809000, 8.460004000], [2.812425000, 3.266553000, 8.131637000], [2.886404000, 5.392925000, 9.848966000]] >>> my_app = SurfaceArea() >>> my_app.add_metal(metal) >>> my_app.add_octa(coord) """ def __init__(self, master=None, icon=None): if master is None: self.wd = tk.Tk() else: self.wd = tk.Toplevel(master) self.icon = icon self.start_app()
[docs] def start_app(self): """ Start application. """ if self.icon is not None: self.wd.wm_iconbitmap(self.icon) self.wd.title("The area of triangular face") self.wd.geometry("380x500") self.wd.option_add("*Font", "Arial 10") self.wd.resizable(0, 0) self.frame = tk.Frame(self.wd) self.frame.grid() self.box = tkscrolled.ScrolledText( self.frame, wrap="word", undo="True", width="50", height="30" ) self.box.grid(row=0, pady="5", padx="5")
[docs] def add_number(self, number): """ Add file number to box. Parameters ---------- number : int File number. """ self.box.insert(tk.INSERT, f"Number : {number}\n")
[docs] def add_metal(self, metal): """ Add metal atom to box: Parameters ---------- metal : str Metal atom. """ self.box.insert(tk.END, f"Metal: {metal}\n")
[docs] def add_octa(self, coord): """ Add atomic coordinates of octahedron and find triangle area of the faces. Parameters ---------- coord : array_like Atomic coordinates of octahedral structure. See Also -------- octadist.src.util.find_faces_octa : Find all faces of octahedron. """ a_ref, c_ref, a_oppo, c_oppo = util.find_faces_octa(coord) self.box.insert(tk.END, "\t\tAtoms*\t\tArea (ų)\n") total_area = 0 for i in range(8): area = linear.triangle_area(c_ref[i][0], c_ref[i][1], c_ref[i][2]) self.box.insert( tk.END, f"Face no. {i + 1}:\t\t{a_ref[i]}\t\t{area:10.6f}\n" ) total_area += area self.box.insert(tk.END, f"\nThe total surface area: {total_area:10.6f}\n") self.box.insert(tk.END, "\n==============================\n\n")