Source code for htpolynet.geometry.bondlist

"""Manages bidirectional interatomic bondlists.

Author: Cameron F. Abrams <cfa22@drexel.edu>
"""

import logging

import networkx as nx
import numpy as np
import pandas as pd
logger=logging.getLogger(__name__)

[docs] class Bondlist: """ The member "B" is a dictionary keyed on atom index whose values of lists of atom indices indicating bond partners of the key atom """ def __init__(self): self.B={}
[docs] @classmethod def fromDataFrame(cls,df:pd.DataFrame): inst=cls() inst.update(df) return inst
[docs] def update(self,df:pd.DataFrame): """Updates the bondlist using data in the parameter dataframe df. Args: df (pd.DataFrame): dataframe with minimally columns named 'ai' and 'aj' Raises: Exception: if no column 'ai' or 'aj' in df """ if not 'ai' in df.columns and not 'aj' in df.columns: raise Exception('Bondlist expects a dataframe with columns "ai" and "aj".') assert df['ai'].dtype==int assert df['aj'].dtype==int aiset=set(df.ai) ajset=set(df.aj) keyset=aiset.union(ajset) keys=sorted(list(keyset)) for k in keys: if k not in self.B: self.B[k] = [] assert all([type(x)==int for x in self.B.keys()]) for r in df.itertuples(): ai=r.ai aj=r.aj self.B[ai].append(aj) self.B[aj].append(ai) for k,v in self.B.items(): this_check=all([type(x)==int for x in v]) if not this_check: logger.error(f'{k} {v}') assert this_check
def __str__(self): retstr='' for k,v in self.B.items(): retstr+=f'{k}: '+' '.join(str(vv) for vv in v)+'\n' return retstr
[docs] def partners_of(self,idx): """Returns a copy of the value of self.B[idx]. Args: idx (int): atom index Returns: list: list of indices of atoms to which atom 'idx' is bound """ if idx in self.B: assert all([type(x)==int for x in self.B[idx]]) return self.B[idx][:] return []
[docs] def are_bonded(self,idx,jdx): """Returns True if atoms with indices idx and jdx are bonded neighbors. Args: idx (int): atom index jdx (int): another atom index Returns: bool: True if idx and jdx are bonded neighbors """ if idx in self.B and jdx in self.B: return jdx in self.B[idx] return False
[docs] def append(self,pair): """Appends the bonded pair in parameter 'pair' to the bondlist. Args: pair (list-like container): pair atom indices """ assert len(pair)==2 ai,aj=min(pair),max(pair) assert type(ai)==int assert type(aj)==int if not ai in self.B: self.B[ai]=[] self.B[ai].append(aj) if not aj in self.B: self.B[aj]=[] self.B[aj].append(ai)
[docs] def delete_atoms(self,idx): """Deletes all instances of atoms in the list idx from the bondlist. Args: idx (list): list of indices for atoms to delete """ ''' delete entries ''' for i in idx: if i in self.B: del self.B[i] ''' delete members in other entries ''' for k,v in self.B.items(): for i in idx: if i in v: self.B[k].remove(i)
[docs] def adjacency_matrix(self): """Generates and returns an adjacency matrix built from the bondlist. Returns: numpy.ndarray: adjacency matrix """ N=len(self.B) A=np.zeros((N,N)).astype(int) for i,n in self.B.items(): for j in n: A[i-1,j-1]=1 A[j-1,i-1]=1 return A
[docs] def as_list(self,root,depth): """Recursively builds a list of all atoms that form a bonded cluster by traversing maximally depth bonds. Args: root (list-like container of two ints): root bond depth (int): number of bonds to traverse to define bonded cluster Returns: list: list of atom indices in the bonded cluster """ if depth==0: return [root] a,b=root abranch=[] for an in self.partners_of(a): if an!=b: abranch.extend(self.as_list([a,an],depth-1)) bbranch=[] for bn in self.partners_of(b): if bn!=a: bbranch.extend(self.as_list([b,bn],depth-1)) result=[root] result.extend(abranch) result.extend(bbranch) # sort entries, remove duplicates for i in range(len(result)): if result[i][0]>result[i][1]: result[i]=result[i][::-1] return result
[docs] def half_as_list(self,root,depth): """Returns bonded cluster defined by atom b in root found by traversing depth bonds excluding the bond to atom a in root. Args: root (list-like container of two ints): root bond depth (int): number of bonds to traverse Returns: list: list of atom indices in bonded cluster "owned" by atom b not containing atom a """ a,b=root self.delete_atoms([a]) bbranch=[root] for bn in self.partners_of(b): if bn!=a: bbranch.extend(self.as_list([b,bn],depth-1)) for i in range(len(bbranch)): if bbranch[i][0]>bbranch[i][1]: bbranch[i]=bbranch[i][::-1] bbranch.sort() red=[] for b in bbranch: if not b in red: red.append(b) return red
[docs] def graph(self): """Generates a networkx Graph object from the bondlist. Returns: networkx.Graph: a networkx Graph object """ g=nx.Graph() for i,n in self.B.items(): for j in n: g.add_edge(i,j) return g