Source code for rdkit2ase.networkx2x
import ase
import networkx as nx
import numpy as np
from rdkit import Chem
from rdkit2ase.utils import bond_type_from_order
[docs]
def networkx2ase(graph: nx.Graph) -> ase.Atoms:
"""Convert a NetworkX graph to an ASE Atoms object.
Parameters
----------
graph : networkx.Graph
The molecular graph to convert. Node attributes must include:
* position (numpy.ndarray): Cartesian coordinates
* atomic_number (int): Element atomic number
* charge (float, optional): Formal charge
Edge attributes must include:
* bond_order (float): Bond order
Optional graph attributes:
* pbc (bool): Periodic boundary conditions
* cell (numpy.ndarray): Unit cell vectors
Returns
-------
ase.Atoms
The resulting Atoms object with:
* Atomic positions and numbers
* Initial charges if present in graph
* Connectivity information stored in atoms.info
* PBC and cell if present in graph
Examples
--------
>>> import networkx as nx
>>> from rdkit2ase import networkx2ase
>>> graph = nx.Graph()
>>> graph.add_node(0, position=[0,0,0], atomic_number=1, charge=0)
>>> graph.add_node(1, position=[1,0,0], atomic_number=1, charge=0)
>>> graph.add_edge(0, 1, bond_order=1.0)
>>> atoms = networkx2ase(graph)
>>> len(atoms)
2
"""
positions = np.array([graph.nodes[n]["position"] for n in graph.nodes])
numbers = np.array([graph.nodes[n]["atomic_number"] for n in graph.nodes])
charges = np.array([graph.nodes[n]["charge"] for n in graph.nodes])
atoms = ase.Atoms(
positions=positions,
numbers=numbers,
charges=charges,
pbc=graph.graph.get("pbc", False),
cell=graph.graph.get("cell", None),
)
connectivity = []
for u, v, data in graph.edges(data=True):
bond_order = data["bond_order"]
connectivity.append((u, v, bond_order))
atoms.info["connectivity"] = connectivity
return atoms
[docs]
def networkx2rdkit(graph: nx.Graph) -> Chem.Mol:
"""Convert a NetworkX graph to an RDKit molecule.
Parameters
----------
graph : networkx.Graph
The molecular graph to convert
Node attributes:
* atomic_number (int): Element atomic number
* charge (float, optional): Formal charge
Edge attributes:
* bond_order (float): Bond order
Returns
-------
rdkit.Chem.Mol
The resulting RDKit molecule with:
* Atoms and bonds from the graph
* Formal charges if specified
* Sanitized molecular structure
Raises
------
ValueError:
If nodes are missing atomic_number attribute,
or if edges are missing bond_order attribute
Examples
--------
>>> import networkx as nx
>>> from rdkit2ase import networkx2rdkit
>>> graph = nx.Graph()
>>> graph.add_node(0, atomic_number=6, charge=0)
>>> graph.add_node(1, atomic_number=8, charge=0)
>>> graph.add_edge(0, 1, bond_order=2.0)
>>> mol = networkx2rdkit(graph)
>>> mol.GetNumAtoms()
2
"""
mol = Chem.RWMol()
nx_to_rdkit_atom_map = {}
for node_id, attributes in graph.nodes(data=True):
atomic_number = attributes.get("atomic_number")
charge = attributes.get("charge", 0)
if atomic_number is None:
raise ValueError(f"Node {node_id} is missing 'atomic_number' attribute.")
atom = Chem.Atom(int(atomic_number))
atom.SetFormalCharge(int(charge))
idx = mol.AddAtom(atom)
nx_to_rdkit_atom_map[node_id] = idx
for u, v, data in graph.edges(data=True):
bond_order = data.get("bond_order")
if bond_order is None:
raise ValueError(f"Edge ({u}, {v}) is missing 'bond_order' attribute.")
mol.AddBond(
nx_to_rdkit_atom_map[u],
nx_to_rdkit_atom_map[v],
bond_type_from_order(bond_order),
)
Chem.SanitizeMol(mol)
return mol.GetMol()