Source code for pyorbb.orbitals.fragments

from scm import plams
import numpy as np
from typing import List, Dict


[docs] def get_fragments_data(reader: plams.KFReader) -> Dict: """ Read data about the fragments for the system. Args: reader: the plams.KFReader object that belongs to the system under study. Returns: :Dictionary containing information about the fragments: - **used_atomic_fragments (bool)** – whether atomic or molecular fragments were used. - **fragment_names (List[str])** - a list of the fragment names. - **fragment_molecules (Dict[str, plams.Molecule])** - a dictionary containing, for each fragment, the molecule containing the atoms that belong to the fragment. - **fmo_fragtype_to_fragname_map (Dict[int, str])** - a dictionary containing, for each FMO fragment type, the fragment name that belongs to it. - **complex_molecule (plams.Molecule)** - the molecule of the complex. """ def get_atoms() -> List[plams.Atom]: ''' Read in the atoms from a reader in internal order. ''' rkf_coordinates = np.atleast_1d(reader.read('Geometry', 'xyz')).reshape(-1, 3) * 0.529177 rkf_atomtypes = np.atleast_1d(reader.read('Geometry', 'atomtype').split()).astype(str) # we need the atomtype indices to figure out which atom type belongs to which atom index rkf_atomtype_indices = np.atleast_1d(reader.read('Geometry', 'fragment and atomtype index')).astype(int) rkf_atomtype_indices = rkf_atomtype_indices[len(rkf_atomtype_indices)//2:] # get the atom types atom_types = rkf_atomtypes[rkf_atomtype_indices-1] # and build the atoms atoms = [plams.Atom(symbol=atom_types[i], coords=rkf_coordinates[i]) for i in range(len(atom_types))] return atoms ret = {} # check if the calculations used atomic fragments # if it did it should have the same number of atoms as fragments ret['used_atomic_fragments'] = reader.read('Geometry', 'nr of fragments') == reader.read('Geometry', 'nr of atoms') # we need to treat systems that use atomic fragments differently from systems that use molecular fragments if ret['used_atomic_fragments']: rkf_atomtypes = np.atleast_1d(reader.read('Geometry', 'atomtype').split()).astype(str) rkf_atomtype_indices = np.atleast_1d(reader.read('Geometry', 'fragment and atomtype index')).astype(int) rkf_atomtype_indices = rkf_atomtype_indices[len(rkf_atomtype_indices)//2:] rkf_atomtype_order = np.atleast_1d(reader.read('Geometry', 'atom order index')).astype(int) rkf_atomtype_order = rkf_atomtype_order[len(rkf_atomtype_order)//2:] rkf_napp = np.atleast_1d(reader.read('Symmetry', 'napp')).astype(int) rkf_notyps = np.atleast_1d(reader.read('Symmetry', 'notyps')).astype(int) rkf_fmo_fragments = np.atleast_1d(reader.read('SFOs', 'fragment')).astype(int) notyp_atom_types = rkf_atomtypes[rkf_notyps-1] # go through each symmetry type and build a new fragment fragment_names = [] for i, atom_type in enumerate(notyp_atom_types): # get the number of fragments that have the same atom type is_unique = len([atom_type_ for atom_type_ in notyp_atom_types if atom_type == atom_type_]) == 1 if is_unique: fragment_names.append(str(atom_type)) continue # get the indices of the atoms that belong to this symm. type atom_internal_order_indices = np.where(rkf_napp == (i + 1))[0] # convert to input order atom_input_order_indices = rkf_atomtype_order[atom_internal_order_indices] # prepare atom indices to be added to the fragment name atom_index_label = ",".join(atom_input_order_indices.astype(str)) # generate the final fragment name fragment_names.append(f'{atom_type}:{atom_index_label}') atoms = get_atoms() fragment_molecules = {} for i, napp in enumerate(rkf_napp): fragment_name = fragment_names[napp-1] fragment_molecules.setdefault(fragment_name, plams.Molecule()) fragment_molecules[fragment_name].add_atom(atoms[i]) fmo_unique_fragments = np.unique(rkf_fmo_fragments) fmo_fragtype_to_name_map = {fragment_number: fragment_name for fragment_number, fragment_name in zip(fmo_unique_fragments, fragment_names)} ret['fragment_names'] = fragment_names ret['fragment_molecules'] = fragment_molecules ret['fmo_fragtype_to_fragname_map'] = fmo_fragtype_to_name_map else: rkf_fragtype_indices = np.atleast_1d(reader.read('Geometry', 'fragment and atomtype index')).astype(int) rkf_fragtype_indices = rkf_fragtype_indices[:len(rkf_fragtype_indices)//2] rkf_fragmenttypes = np.atleast_1d(reader.read('Geometry', 'fragmenttype').split()).astype(str) rkf_fmo_fragments = np.atleast_1d(reader.read('SFOs', 'fragment')).astype(int) atoms = get_atoms() fragment_molecules = {} for i, fragtype_index in enumerate(rkf_fragtype_indices): fragment_name = rkf_fragmenttypes[fragtype_index-1] fragment_molecules.setdefault(str(fragment_name), plams.Molecule()) fragment_molecules[str(fragment_name)].add_atom(atoms[i]) fmo_unique_fragments = np.unique(rkf_fmo_fragments) fmo_fragtype_to_name_map = {fragment_number: str(fragment_name) for fragment_number, fragment_name in zip(fmo_unique_fragments, rkf_fragmenttypes)} ret['fragment_names'] = rkf_fragmenttypes ret['fragment_molecules'] = fragment_molecules ret['fmo_fragtype_to_fragname_map'] = fmo_fragtype_to_name_map ret['complex_molecule'] = plams.Molecule() [ret['complex_molecule'].add_atom(atom) for atom in get_atoms()] return ret