Source code for gmso.external.convert_networkx

"""Convert to/from NetworkX graphs and GMSO topologies."""

import logging

import networkx as nx

from gmso.abc.abstract_connection import Connection
from gmso.abc.abstract_site import Site
from gmso.core.bond import Bond
from gmso.core.topology import Topology

logger = logging.getLogger(__name__)


[docs] def from_networkx(graph: nx.Graph) -> Topology: """Convert a ``networkx.Graph`` to a :class:`~gmso.Topology`. Each graph node must be a :class:`~gmso.abc.abstract_site.Site` and each edge becomes a :class:`~gmso.Bond`. Parameters ---------- graph : networkx.Graph Graph whose nodes are :class:`~gmso.abc.abstract_site.Site` instances. Edge data may optionally contain a ``'connection'`` key holding a :class:`~gmso.abc.abstract_connection.Connection` object. Returns ------- gmso.Topology Topology containing the sites and bonds from *graph*. Notes ----- Much information is lost when converting a topology to a graph (mixing rules, metadata, etc.). The edge ``'connection'`` attribute stores the original :class:`~gmso.Bond` and is used to reconstruct it during the reverse conversion. Angle and dihedral information stored as node attributes is detected but not converted; a warning is logged if such data is found. Raises ------ TypeError If *graph* is not a ``networkx.Graph`` or if any node is not a :class:`~gmso.abc.abstract_site.Site`. """ if not isinstance(graph, nx.Graph): raise TypeError( "Type mismatch, graph object is expected to be " "an instance of networkx.Graph, was provided: {}".format(type(graph)) ) top = Topology() for node in graph.nodes: if not isinstance(node, Site): raise TypeError("Nodes must be instances of gmso.abc.Site") else: top.add_site(node) for edge in graph.edges: try: conn = graph.get_edge_data(*edge)["connection"] if isinstance(conn, Connection) and set(edge).issubset( set(conn.connection_members) ): top.add_connection(conn) except KeyError: conn = Bond(connection_members=edge) top.add_connection(conn) for node in graph.nodes: try: graph.nodes[node]["angles"] or graph.nodes[node]["dihedrals"] logger.info("Angle and Dihedral information is not converted.") except KeyError: pass return top
[docs] def to_networkx( top: Topology, parse_angles: bool = True, parse_dihedrals: bool = True, ) -> nx.Graph: """Convert a :class:`~gmso.Topology` to a ``networkx.Graph``. Each site becomes a graph node and each bond becomes an edge. Parameters ---------- top : gmso.Topology The topology to convert. parse_angles : bool, optional, default=True When ``True``, populate an ``'angles'`` attribute on every node with the list of angles that include that site. parse_dihedrals : bool, optional, default=True When ``True``, populate a ``'dihedrals'`` attribute on every node with the list of dihedrals that include that site. Returns ------- networkx.Graph Graph where nodes are :class:`~gmso.Atom` objects and edges carry a ``'connection'`` attribute holding the corresponding :class:`~gmso.Bond`. Notes ----- Converting to a graph loses metadata, mixing rules, and all potential type information. The graph is primarily useful for connectivity analysis. """ graph = nx.Graph() for n in top.sites: graph.add_node(n) for b in top.bonds: graph.add_edge(b.connection_members[0], b.connection_members[1], connection=b) if parse_angles: for node in graph.nodes: graph.nodes[node]["angles"] = top._get_angles_for(node) if parse_dihedrals: for node in graph.nodes: graph.nodes[node]["dihedrals"] = top._get_dihedrals_for(node) return graph