Source code for openpnm.io._networkx

import logging
import numpy as np
logger = logging.getLogger(__name__)


[docs] def network_from_networkx(G): r""" Creates an OpenPNM Network from a undirected NetworkX graph object Parameters ---------- G : networkx.classes.graph.Graph Object The NetworkX graph. G should be undirected. The numbering of nodes should be numeric (int's), zero-based and should not contain any gaps, i.e. ``G.nodes() = [0,1,3,4,5]`` is not allowed and should be mapped to ``G.nodes() = [0,1,2,3,4]``. Returns ------- network : dict An OpenPNM network dictionary Notes ----- 1. Each node in a NetworkX object (i.e. ``G``) can be assigned properties using syntax like ``G.node[n]['diameter'] = 0.5`` where ``n`` is the node number. There is no need to precede the property name with any indication that it is pore data such as \'pore\_\'. OpenPNM will prepend \'pore.\' to each property name. 2. Since \'pore.coords\' is so central to OpenPNM it should be specified in the NetworkX object as \'coords\', and the [X, Y, Z] coordinates of each node should be a 1x3 list. 3. Edges in a NetworkX object are accessed using the index numbers of the two nodes it connects, such as ``G.adj[2][3]['length'] = 0.1`` indicating the edge that connects nodes 2 and 3. There is no need to precede the property name with any indication that it is throat data such as \'throat\_\'. OpenPNM will prepend \'throat.\' to each property name. 4. The \'throat.conns\' property is essential to OpenPNM, but this does NOT need to be specified explicitly as a property in NetworkX. The connectivity is embedded into the network representation and is extracted by OpenPNM. """ import networkx as nx from openpnm.network import Network net = {} # Ensure G is an undirected networkX graph with numerically numbered # nodes for which numbering starts at 0 and does not contain any gaps if not isinstance(G, nx.Graph): # pragma: no cover raise Exception('Provided object is not a NetworkX graph.') if nx.is_directed(G): # pragma: no cover raise Exception('Provided graph is directed. Convert to undirected graph.') if not all(isinstance(n, int) for n in G.nodes()): # pragma: no cover raise Exception('Node numbering is not numeric. Convert to int.') if min(G.nodes()) != 0: # pragma: no cover raise Exception('Node numbering does not start at zero.') if max(G.nodes()) + 1 != len(G.nodes()): # pragma: no cover raise Exception('Node numbering contains gaps. Map nodes to remove gaps.') # Parsing node data Np = len(G) net.update({'pore.all': np.ones((Np,), dtype=bool)}) for n, props in G.nodes(data=True): for item in props.keys(): val = props[item] dtype = type(val) # Remove prepended pore. and pore_ if present for b in ['pore.', 'pore_']: item = item.replace(b, '') # Create arrays for subsequent indexing, if not present already if 'pore.'+item not in net.keys(): if dtype == str: # handle strings of arbitrary length net['pore.'+item] = np.ndarray((Np,), dtype='object') elif dtype is list: dtype = type(val[0]) if dtype == str: dtype = 'object' cols = len(val) net['pore.'+item] = np.ndarray((Np, cols), dtype=dtype) else: net['pore.'+item] = np.ndarray((Np,), dtype=dtype) net['pore.'+item][n] = val # Parsing edge data # Deal with conns explicitly try: conns = list(G.edges) # NetworkX V2 except Exception: # pragma: no cover conns = G.edges() # NetworkX V1 conns.sort() # Add conns to Network Nt = len(conns) net.update({'throat.all': np.ones(Nt, dtype=bool)}) net.update({'throat.conns': np.array(conns)}) # Scan through each edge and extract all its properties i = 0 for t in conns: props = G[t[0]][t[1]] for item in props: val = props[item] dtype = type(val) # Remove prepended throat. and throat_ if present for b in ['throat.', 'throat_']: item = item.replace(b, '') # Create arrays for subsequent indexing, if not present already if 'throat.'+item not in net.keys(): if dtype == str: net['throat.'+item] = np.ndarray((Nt,), dtype='object') if dtype is list: dtype = type(val[0]) if dtype == str: dtype = 'object' cols = len(val) net['throat.'+item] = np.ndarray((Nt, cols), dtype=dtype) else: net['throat.'+item] = np.ndarray((Nt,), dtype=dtype) net['throat.'+item][i] = val i += 1 network = Network() network.update(net) return network
[docs] def network_to_networkx(network): r""" Write OpenPNM Network to a NetworkX object. Parameters ---------- network : dict The OpenPNM Network to be converted to a NetworkX object Returns ------- G : undirected graph object A NetworkX object with all pore/throat properties attached to it """ import networkx as nx from openpnm.network import Network # Ensure network is an Network. if not isinstance(network, Network): # pragma: no cover raise Exception('Provided network is not an OpenPNM Network.') G = nx.Graph() # Extracting node list and connectivity matrix from Network nodes = map(int, network.Ps) conns = network['throat.conns'] # Explicitly add nodes and connectivity matrix G.add_nodes_from(nodes) G.add_edges_from(conns) # Attach Network properties to G for prop in network.props() + network.labels(): if 'pore.' in prop: if len(network[prop].shape) > 1: val = {i: list(network[prop][i]) for i in network.Ps} else: val = {i: network[prop][i] for i in network.Ps} nx.set_node_attributes(G, name=prop[5:], values=val) if 'throat.' in prop: val = {tuple(conn): network[prop][i] for i, conn in enumerate(conns)} nx.set_edge_attributes(G, name=prop[7:], values=val) return G