Source code for mofbuilder.visualization.viewer

import sys
import mpi4py.MPI as MPI
from veloxchem.outputstream import OutputStream
from veloxchem.veloxchemlib import mpi_master
from typing import Optional, Any, Dict
from ..io.xyz_writer import XyzWriter


[docs] class Viewer: """Visualization interface using py3Dmol for MOFbuilder. Attributes: comm (Any): MPI communicator (usually `MPI.COMM_WORLD`). rank (int): The MPI rank of this process. nodes (int): The total number of MPI ranks/nodes. ostream (OutputStream): VeloxChem-style output stream for logging/info. eG_dict (Optional[Dict[Any, Any]]): Dictionary mapping index to name (graph elements). merged_lines (Optional[Any]): Data lines to be visualized; formatted input for XYZWriter. eG_name_idx_dict (Optional[Dict[Any, int]]): Reverse-lookup of eG_dict, mapping names to indices. Methods: __init__: Initialize the Viewer with MPI context and output stream. _reverse_eG_dict: Reverse the element graph dictionary for label lookup. lines_show: Visualize merged_lines as a 3D molecular scene with labels. """
[docs] def __init__(self, comm: Optional[Any] = None, ostream: Optional[Any] = None, filepath: Optional[str] = None): """Initialize the Viewer for MOF visualization. Args: comm (Optional[Any]): MPI communicator. Defaults to `MPI.COMM_WORLD`. ostream (Optional[Any]): Output stream for logging. Defaults to VeloxChem's OutputStream. filepath (Optional[str]): Path to data file (currently unused). """ self.comm: Any = comm or MPI.COMM_WORLD self.rank: int = self.comm.Get_rank() self.nodes: int = self.comm.Get_size() if ostream is None: ostream = OutputStream(sys.stdout if self.rank == mpi_master() else None) self.ostream: Any = ostream self.eG_dict: Optional[Dict[Any, Any]] = None self.merged_lines: Optional[Any] = None self.eG_name_idx_dict: Optional[Dict[Any, int]] = None
[docs] def _reverse_eG_dict(self) -> None: """Reverse the internally stored eG_dict for fast name-to-index lookup. Note: eG_dict should be a dictionary where keys are index numbers (usually int as str), and values are unique names of the corresponding graph elements. After this call, eG_name_idx_dict maps element names to index numbers. """ # The eG dict is a dictionary: key = index, value = name. self.eG_name_idx_dict = {v: int(k) for k, v in self.eG_dict.items()}
[docs] def lines_show( self, w: int = 800, h: int = 600, res_indices: bool = True, res_name: bool = True, ) -> None: """Display `merged_lines` in a 3D viewer with optional residue names and indices. Args: w (int): Width of viewer in pixels. h (int): Height of viewer in pixels. res_indices (bool): If True, show residue indices as labels. res_name (bool): If True, show residue names as labels. Raises: ImportError: If py3Dmol is not installed. Note: - Requires `py3Dmol` for visualization. - Uses `self.merged_lines`, typically a list of atom-info lines output from the builder. - Residue labels skip any lines with resname "TNO". - If both `res_name` and `res_indices` are True, the label concatenates both. """ try: import py3Dmol merged_lines = self.merged_lines viewer = py3Dmol.view(width=w, height=h) xyz_writer = XyzWriter(comm=self.comm, ostream=self.ostream) xyz_lines = xyz_writer.get_xyzlines(lines=merged_lines) viewer.addModel("".join(xyz_lines), "xyz") viewer.setViewStyle({"style": "outline", "width": 0.05}) viewer.setStyle({"stick": {}, "sphere": {"scale": 0.20}}) if res_indices or res_name: self._reverse_eG_dict() old_resnumber = 0 for line in merged_lines: value_resname = line[3].split('_')[0][:3] if value_resname.strip() == "TNO": continue value_resnumber = self.eG_name_idx_dict[line[10]] if value_resnumber == old_resnumber: continue old_resnumber = value_resnumber value_x = float(line[5]) value_y = float(line[6]) value_z = float(line[7]) text = "" if res_name: text += str(value_resname) if res_indices: text += str(value_resnumber) viewer.addLabel( text, { "position": { "x": value_x, "y": value_y, "z": value_z, }, "alignment": "center", "fontColor": "white", "font": "Arial", "fontSize": 12, "backgroundColor": "black", "backgroundOpacity": 0.5, }, ) viewer.render() viewer.zoomTo() viewer.show() except ImportError: raise ImportError("Unable to import py3Dmol")