Source code for synkit.Graph.Mech.electron_accounting

from __future__ import annotations

from typing import Any

import networkx as nx
from rdkit import Chem

from synkit.IO.graph_to_mol import GraphToMol


[docs] def bond_order_sum(graph: nx.Graph, node: Any) -> float: """Return the sigma-plus-pi bond-order sum around one node.""" total = 0.0 for _, _, data in graph.edges(node, data=True): total += float(data.get("sigma_order", 0.0)) + float(data.get("pi_order", 0.0)) return total
[docs] def recompute_charge(graph: nx.Graph, node: Any) -> int | float: """Recompute formal charge from stored electron-state fields.""" attrs = graph.nodes[node] charge = float(attrs["valence_electrons"]) - ( 2 * float(attrs.get("lone_pairs", 0)) + float(attrs.get("radical", 0)) + float(attrs.get("hcount", 0)) + bond_order_sum(graph, node) ) return int(charge) if charge.is_integer() else charge
[docs] def refresh_electron_fields(graph: nx.Graph, *, in_place: bool = False) -> nx.Graph: """Refresh derived electron bookkeeping on a molecular graph. The graph is expected to store scalar ``sigma_order`` and ``pi_order`` edge fields plus node-level electron state. Presentation-facing ``order`` is not rewritten here; RDKit reconstruction remains responsible for aromatic re-perception at the product boundary. """ target = graph if in_place else graph.copy() for _, _, data in target.edges(data=True): sigma = float(data.get("sigma_order", 0.0)) pi = float(data.get("pi_order", 0.0)) data["kekule_order"] = sigma + pi for node, attrs in target.nodes(data=True): attrs["bond_order_sum"] = bond_order_sum(target, node) if "valence_electrons" not in attrs: continue attrs["recomputed_charge"] = recompute_charge(target, node) represented_charge = float(attrs.get("charge", 0)) attrs["charge_mismatch"] = represented_charge != attrs["recomputed_charge"] return target
[docs] def graph_to_sanitized_kekule_mol(graph: nx.Graph) -> Chem.Mol: """Reconstruct a product from ``kekule_order`` and let RDKit sanitize it.""" refreshed = refresh_electron_fields(graph) return GraphToMol(edge_attributes={"order": "kekule_order"}).graph_to_mol( refreshed, sanitize=True, use_h_count=True, )