def load(cls, filename, project=None, settings={}): r""" """ # Parse the link1 file filename = cls._parse_filename(filename=filename, ext='dict') with open(filename, mode='rb') as f: net = pk.load(f) network = GenericNetwork(project=project) network = cls._update_network(network=network, net=net) # The following list of props comes from porespy but are not good for item in [ 'pore.diameter', 'pore.area', 'throat.conduit_lengths.pore1', 'throat.conduit_lengths.pore2', 'throat.conduit_lengths.throat', 'throat.endpoints.head', 'throat.endpoints.tail', 'throat.diameter', 'throat.length', 'throat.volume' ]: network.pop(item, None) # Deal with a few other PoreSpy specific issues network['pore.region_volume'] = network.pop('pore.volume') Imported(network=network, settings=settings) return network.project
def import_data(cls, filename, project=None, settings=None): r""" Load a network extracted using the PoreSpy package Parameters ---------- filename : str or dict Can either be a filename point to a pickled dictionary, or an actual dictionary. The second option lets users avoid the step of saving the dictionary to a file project : Project If given, the loaded network and geometry will be added to this project, otherwise a new one will be created. """ # Parse the filename if isinstance(filename, dict): net = filename else: filename = cls._parse_filename(filename=filename, ext='dict') with open(filename, mode='rb') as f: net = pk.load(f) network = GenericNetwork(project=project) network = cls._update_network(network=network, net=net) Imported(network=network, settings=settings) return network.project
def import_data(cls, filename, project=None, settings={}): r""" Load a network extracted using the PoreSpy package Parameters ---------- filename : str or dict Can either be a filename point to a pickled dictionary, or an actual dictionary. The second option lets users avoid the step of saving the dictionary to a file project : OpenPNM Project object If given, the loaded network and geometry will be added to this project, otherwise a new one will be created. """ # Parse the filename if isinstance(filename, dict): net = filename else: filename = cls._parse_filename(filename=filename, ext='dict') with open(filename, mode='rb') as f: net = pk.load(f) network = GenericNetwork(project=project) network = cls._update_network(network=network, net=net) # The following list of props comes from porespy but are not good for item in [ 'pore.diameter', 'pore.area', 'throat.conduit_lengths.pore1', 'throat.conduit_lengths.pore2', 'throat.conduit_lengths.throat', 'throat.endpoints.head', 'throat.endpoints.tail', 'throat.diameter', 'throat.length', 'throat.volume' ]: network.pop(item, None) # Deal with a few other PoreSpy specific issues network['pore.region_volume'] = network.pop('pore.volume') Imported(network=network, settings=settings) return network.project
def to_openpnm(net, filename): r""" Save the result of the `snow` network extraction function in a format suitable for opening in OpenPNM. Parameters ---------- net : dict The dictionary object produced by the network extraction functions filename : string or path object The name and location to save the file, which will have `.net` file extension. """ from openpnm.network import GenericNetwork # Convert net dict to an openpnm Network pn = GenericNetwork() pn.update(net) pn.project.save_project(filename) ws = pn.project.workspace ws.close_project(pn.project)
def load(cls, path, node_file="throats_cellsThroatsGraph_Nodes.txt", graph_file="throats_cellsThroatsGraph.txt", network=None, voxel_size=None, return_geometry=False): r""" Loads network data from an iMorph processed image stack Parameters ---------- path : string The path of the folder where the subfiles are held node_file : string The file that describes the pores and throats, the default iMorph name is: throats_cellsThroatsGraph_Nodes.txt graph_file : string The file that describes the connectivity of the network, the default iMorph name is: throats_cellsThroatsGraph.txt network : OpenPNM Network Object The OpenPNM Network onto which the data should be loaded. If no network is supplied then an empty import network is created and returned. voxel_size : float Allows the user to define a voxel size different than what is contained in the node_file. The value must be in meters. return_geometry : Boolean If True, then all geometrical related properties are removed from the Network object and added to a GenericGeometry object. In this case the method returns a tuple containing (network, geometry). If False (default) then the returned Network will contain all properties that were in the original file. In this case, the user can call the ```split_geometry``` method explicitly to perform the separation. Returns ------- If no Network object is supplied then one will be created and returned. If return_geometry is True, then a tuple is returned containing both the network and a geometry object. """ # path = Path(path) node_file = os.path.join(path.resolve(), node_file) graph_file = os.path.join(path.resolve(), graph_file) # parsing the nodes file with open(node_file, 'r') as file: Np = sp.fromstring(file.readline().rsplit('=')[1], sep='\t', dtype=int)[0] vox_size = sp.fromstring( file.readline().rsplit(')')[1], sep='\t', )[0] # network always recreated to prevent errors network = GenericNetwork(Np=Np, Nt=0) # Define expected properies network['pore.volume'] = sp.nan scrap_lines = [file.readline() for line in range(4)] while True: vals = file.readline().split('\t') if len(vals) == 1: break network['pore.volume'][int(vals[0])] = float(vals[3]) if 'pore.' + vals[2] not in network.labels(): network['pore.' + vals[2]] = False network['pore.' + vals[2]][int(vals[0])] = True if voxel_size is None: voxel_size = vox_size * 1.0E-6 # file stores value in microns if voxel_size < 0: raise (Exception('Error - Voxel size must be specfied in ' + 'the Nodes file or as a keyword argument.')) # parsing the graph file with open(graph_file, 'r') as file: # Define expected properties network['pore.coords'] = sp.zeros((Np, 3)) * sp.nan network['pore.types'] = sp.nan network['pore.color'] = sp.nan network['pore.radius'] = sp.nan network['pore.dmax'] = sp.nan network['pore.node_number'] = sp.nan # Scan file to get pore coordinate data scrap_lines = [file.readline() for line in range(3)] line = file.readline() xmax = 0.0 ymax = 0.0 zmax = 0.0 node_num = 0 while line != 'connectivity table\n': vals = sp.fromstring(line, sep='\t') xmax = vals[1] if vals[1] > xmax else xmax ymax = vals[2] if vals[2] > ymax else ymax zmax = vals[3] if vals[3] > zmax else zmax network['pore.coords'][int(vals[0]), :] = vals[1:4] network['pore.types'][int(vals[0])] = vals[4] network['pore.color'][int(vals[0])] = vals[5] network['pore.radius'][int(vals[0])] = vals[6] network['pore.dmax'][int(vals[0])] = vals[7] network['pore.node_number'][int(vals[0])] = node_num node_num += 1 line = file.readline() # Scan file to get to connectivity data scrap_lines.append(file.readline()) # Skip line # Create sparse lil array incrementally build adjacency matrix lil = sp.sparse.lil_matrix((Np, Np), dtype=int) while True: vals = sp.fromstring(file.readline(), sep='\t', dtype=int) if len(vals) <= 1: break lil.rows[vals[0]] = vals[2:] lil.data[vals[0]] = sp.ones(vals[1]) # fixing any negative volumes or distances so they are 1 voxel/micron network['pore.volume'][sp.where(network['pore.volume'] < 0)[0]] = 1.0 network['pore.radius'][sp.where(network['pore.radius'] < 0)[0]] = 1.0 network['pore.dmax'][sp.where(network['pore.dmax'] < 0)[0]] = 1.0 # Add adjacency matrix to OpenPNM network conns = sp.sparse.triu(lil, k=1, format='coo') network.update({'throat.all': sp.ones(len(conns.col), dtype=bool)}) network['throat.conns'] = sp.vstack([conns.row, conns.col]).T network['pore.to_trim'] = False network['pore.to_trim'][network.pores('*throat')] = True Ts = network.pores('to_trim') new_conns = network.find_neighbor_pores(pores=Ts, flatten=False) extend(network=network, throat_conns=new_conns, labels='new_conns') for item in network.props('pore'): item = item.split('.')[1] arr = sp.ones_like(network['pore.' + item])[0] arr = sp.tile(A=arr, reps=[network.Nt, 1]) * sp.nan network['throat.' + item] = sp.squeeze(arr) network['throat.'+item][network.throats('new_conns')] = \ network['pore.'+item][Ts] trim(network=network, pores=Ts) # setting up boundary pores x_coord, y_coord, z_coord = sp.hsplit(network['pore.coords'], 3) network['pore.front_boundary'] = sp.ravel(x_coord == 0) network['pore.back_boundary'] = sp.ravel(x_coord == xmax) network['pore.left_boundary'] = sp.ravel(y_coord == 0) network['pore.right_boundary'] = sp.ravel(y_coord == ymax) network['pore.bottom_boundary'] = sp.ravel(z_coord == 0) network['pore.top_boundary'] = sp.ravel(z_coord == zmax) # removing any pores that got classified as a boundary pore but # weren't labled a border_cell_face ps = sp.where(~sp.in1d(network.pores('*_boundary'), network.pores('border_cell_face')))[0] ps = network.pores('*_boundary')[ps] for side in ['front', 'back', 'left', 'right', 'top', 'bottom']: network['pore.' + side + '_boundary'][ps] = False # setting internal label network['pore.internal'] = False network['pore.internal'][network.pores('*_boundary', mode='not')] = True # adding props to border cell face throats and from pores Ts = sp.where( network['throat.conns'][:, 1] > network.pores('border_cell_face')[0] - 1)[0] faces = network['throat.conns'][Ts, 1] for item in network.props('pore'): item = item.split('.')[1] network['throat.' + item][Ts] = network['pore.' + item][faces] network['pore.volume'][faces] = 0.0 # applying unit conversions # TODO: Determine if radius and dmax are indeed microns and not voxels network['pore.coords'] = network['pore.coords'] * 1e-6 network['pore.radius'] = network['pore.radius'] * 1e-6 network['pore.dmax'] = network['pore.dmax'] * 1e-6 network['pore.volume'] = network['pore.volume'] * voxel_size**3 network['throat.coords'] = network['throat.coords'] * 1e-6 network['throat.radius'] = network['throat.radius'] * 1e-6 network['throat.dmax'] = network['throat.dmax'] * 1e-6 network['throat.volume'] = network['throat.volume'] * voxel_size**3 return network.project
def load(cls, path, prefix, network=None): r""" Load data from the \'dat\' files located in specified folder. Parameters ---------- path : string The full path to the folder containing the set of \'dat\' files. prefix : string The file name prefix on each file. The data files are stored as \<prefix\>_node1.dat. network : OpenPNM Network Object If given then the data will be loaded on it and returned. If not given, a Network will be created and returned. Returns ------- An OpenPNM Project containing a GenericNetwork holding all the data """ from pandas import read_table, DataFrame net = {} # Parse the link1 file path = Path(path) filename = Path(path.resolve(), prefix + '_link1.dat') with open(filename, mode='r') as f: link1 = read_table(filepath_or_buffer=f, header=None, skiprows=1, sep=' ', skipinitialspace=True, index_col=0) link1.columns = [ 'throat.pore1', 'throat.pore2', 'throat.radius', 'throat.shape_factor', 'throat.total_length' ] # Add link1 props to net net['throat.conns'] = np.vstack( (link1['throat.pore1'] - 1, link1['throat.pore2'] - 1)).T net['throat.conns'] = np.sort(net['throat.conns'], axis=1) net['throat.radius'] = np.array(link1['throat.radius']) net['throat.shape_factor'] = np.array(link1['throat.shape_factor']) net['throat.total_length'] = np.array(link1['throat.total_length']) filename = Path(path.resolve(), prefix + '_link2.dat') with open(filename, mode='r') as f: link2 = read_table(filepath_or_buffer=f, header=None, sep=' ', skipinitialspace=True, index_col=0) link2.columns = [ 'throat.pore1', 'throat.pore2', 'throat.pore1_length', 'throat.pore2_length', 'throat.length', 'throat.volume', 'throat.clay_volume' ] # Add link2 props to net cl_t = np.array(link2['throat.length']) net['throat.length'] = cl_t net['throat.conduit_lengths.throat'] = cl_t net['throat.volume'] = np.array(link2['throat.volume']) cl_p1 = np.array(link2['throat.pore1_length']) net['throat.conduit_lengths.pore1'] = cl_p1 cl_p2 = np.array(link2['throat.pore2_length']) net['throat.conduit_lengths.pore2'] = cl_p2 net['throat.clay_volume'] = np.array(link2['throat.clay_volume']) # --------------------------------------------------------------------- # Parse the node1 file filename = Path(path.resolve(), prefix + '_node1.dat') with open(filename, mode='r') as f: row_0 = f.readline().split() num_lines = int(row_0[0]) array = sp.ndarray([num_lines, 6]) for i in range(num_lines): row = f.readline()\ .replace('\t', ' ').replace('\n', ' ').split() array[i, :] = row[0:6] node1 = DataFrame(array[:, [1, 2, 3, 4]]) node1.columns = [ 'pore.x_coord', 'pore.y_coord', 'pore.z_coord', 'pore.coordination_number' ] # Add node1 props to net net['pore.coords'] = np.vstack( (node1['pore.x_coord'], node1['pore.y_coord'], node1['pore.z_coord'])).T # --------------------------------------------------------------------- # Parse the node1 file filename = Path(path.resolve(), prefix + '_node2.dat') with open(filename, mode='r') as f: node2 = read_table(filepath_or_buffer=f, header=None, sep=' ', skipinitialspace=True, index_col=0) node2.columns = [ 'pore.volume', 'pore.radius', 'pore.shape_factor', 'pore.clay_volume' ] # Add node2 props to net net['pore.volume'] = np.array(node2['pore.volume']) net['pore.radius'] = np.array(node2['pore.radius']) net['pore.shape_factor'] = np.array(node2['pore.shape_factor']) net['pore.clay_volume'] = np.array(node2['pore.clay_volume']) net['throat.area'] = ((net['throat.radius']**2) / (4.0 * net['throat.shape_factor'])) net['pore.area'] = ((net['pore.radius']**2) / (4.0 * net['pore.shape_factor'])) if network is None: network = GenericNetwork() network = cls._update_network(network=network, net=net) # Use OpenPNM Tools to clean up network # Trim throats connected to 'inlet' or 'outlet' reservoirs trim1 = np.where(np.any(net['throat.conns'] == -1, axis=1))[0] # Apply 'outlet' label to these pores outlets = network['throat.conns'][trim1, 1] network['pore.outlets'] = False network['pore.outlets'][outlets] = True trim2 = np.where(np.any(net['throat.conns'] == -2, axis=1))[0] # Apply 'inlet' label to these pores inlets = network['throat.conns'][trim2, 1] network['pore.inlets'] = False network['pore.inlets'][inlets] = True # Now trim the throats to_trim = np.hstack([trim1, trim2]) trim(network=network, throats=to_trim) return network.project
def from_networkx(cls, G, project=None): r""" Add data to 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]``. project : OpenPNM Project object A GenericNetwork is created and added to the specified Project. If no Project is supplied then one will be created and returned. Returns ------- An OpenPNM Project containing a GenericNetwork with all the data from the NetworkX object. """ import networkx as nx 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): raise ('Provided object is not a NetworkX graph.') if nx.is_directed(G): raise ('Provided graph is directed. Convert to undirected graph.') if not all(isinstance(n, int) for n in G.nodes()): raise ('Node numbering is not numeric. Convert to int.') if min(G.nodes()) != 0: raise ('Node numbering does not start at zero.') if max(G.nodes()) + 1 != len(G.nodes()): raise ('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] = sp.ndarray((Np,), dtype='object') elif dtype is list: dtype = type(val[0]) if dtype == str: dtype = 'object' cols = len(val) net['pore.'+item] = sp.ndarray((Np, cols), dtype=dtype) else: net['pore.'+item] = sp.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: 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] = sp.ndarray((Nt,), dtype='object') if dtype is list: dtype = type(val[0]) if dtype == str: dtype = 'object' cols = len(val) net['throat.'+item] = sp.ndarray((Nt, cols), dtype=dtype) else: net['throat.'+item] = sp.ndarray((Nt,), dtype=dtype) net['throat.'+item][i] = val i += 1 network = GenericNetwork(project=project) network = cls._update_network(network=network, net=net) return network.project
def import_data(cls, path, node_file="throats_cellsThroatsGraph_Nodes.txt", graph_file="throats_cellsThroatsGraph.txt", voxel_size=None): r""" Loads network data from an iMorph processed image stack Parameters ---------- path : string The path of the folder where the subfiles are held node_file : string The file that describes the pores and throats, the default iMorph name is: throats_cellsThroatsGraph_Nodes.txt graph_file : string The file that describes the connectivity of the network, the default iMorph name is: throats_cellsThroatsGraph.txt voxel_size : float Allows the user to define a voxel size different than what is contained in the node_file. The value must be in meters. Returns ------- project : list An OpenPNM project object containing a network and a geometry object. The geometry-related data are automatically placed on the geometry object using the ``Imported`` geometry class. """ path = Path(path) node_file = os.path.join(path.resolve(), node_file) graph_file = os.path.join(path.resolve(), graph_file) # Parsing the nodes file with open(node_file, "r") as file: Np = np.fromstring(file.readline().rsplit("=")[1], sep="\t", dtype=int)[0] vox_size = np.fromstring( file.readline().rsplit(")")[1], sep="\t", )[0] # Network always recreated to prevent errors network = GenericNetwork(Np=Np, Nt=0) # Define expected properies network["pore.volume"] = np.nan scrap_lines = [file.readline() for line in range(4)] while True: vals = file.readline().split("\t") if len(vals) == 1: break network["pore.volume"][int(vals[0])] = float(vals[3]) if "pore." + vals[2] not in network.labels(): network["pore." + vals[2]] = False network["pore." + vals[2]][int(vals[0])] = True if voxel_size is None: voxel_size = vox_size * 1.0e-6 # File stores value in microns if voxel_size < 0: raise Exception("Error - Voxel size must be specfied in " + "the Nodes file or as a keyword argument.") # Parsing the graph file with open(graph_file, "r") as file: # Define expected properties network["pore.coords"] = np.zeros((Np, 3)) * np.nan network["pore.types"] = np.nan network["pore.color"] = np.nan network["pore.radius"] = np.nan network["pore.dmax"] = np.nan network["pore.node_number"] = np.nan # Scan file to get pore coordinate data scrap_lines = [file.readline() for line in range(3)] line = file.readline() xmax = 0.0 ymax = 0.0 zmax = 0.0 node_num = 0 while line != "connectivity table\n": vals = np.fromstring(line, sep="\t") xmax = vals[1] if vals[1] > xmax else xmax ymax = vals[2] if vals[2] > ymax else ymax zmax = vals[3] if vals[3] > zmax else zmax network["pore.coords"][int(vals[0]), :] = vals[1:4] network["pore.types"][int(vals[0])] = vals[4] network["pore.color"][int(vals[0])] = vals[5] network["pore.radius"][int(vals[0])] = vals[6] network["pore.dmax"][int(vals[0])] = vals[7] network["pore.node_number"][int(vals[0])] = node_num node_num += 1 line = file.readline() # Scan file to get to connectivity data scrap_lines.append(file.readline()) # Skip line # Create sparse lil array incrementally build adjacency matrix lil = sp.sparse.lil_matrix((Np, Np), dtype=int) while True: vals = np.fromstring(file.readline(), sep="\t", dtype=int) if len(vals) <= 1: break lil.rows[vals[0]] = vals[2:].tolist() lil.data[vals[0]] = np.ones(vals[1]).tolist() # Fixing any negative volumes or distances so they are 1 voxel/micron network["pore.volume"][np.where(network["pore.volume"] < 0)[0]] = 1.0 network["pore.radius"][np.where(network["pore.radius"] < 0)[0]] = 1.0 network["pore.dmax"][np.where(network["pore.dmax"] < 0)[0]] = 1.0 # Add adjacency matrix to OpenPNM network conns = sp.sparse.triu(lil, k=1, format="coo") network.update({"throat.all": np.ones(len(conns.col), dtype=bool)}) network["throat.conns"] = np.vstack([conns.row, conns.col]).T network["pore.to_trim"] = False network["pore.to_trim"][network.pores("*throat")] = True Ts = network.pores("to_trim") new_conns = network.find_neighbor_pores(pores=Ts, flatten=False) extend(network=network, throat_conns=new_conns, labels="new_conns") for item in network.props("pore"): item = item.split(".")[1] arr = np.ones_like(network["pore." + item])[0] arr = np.tile(A=arr, reps=[network.Nt, 1]) * np.nan network["throat." + item] = np.squeeze(arr) network["throat." + item][network.throats("new_conns")] = network["pore." + item][Ts] trim(network=network, pores=Ts) # Setting up boundary pores x_coord, y_coord, z_coord = np.hsplit(network["pore.coords"], 3) network["pore.front_boundary"] = np.ravel(x_coord == 0) network["pore.back_boundary"] = np.ravel(x_coord == xmax) network["pore.left_boundary"] = np.ravel(y_coord == 0) network["pore.right_boundary"] = np.ravel(y_coord == ymax) network["pore.bottom_boundary"] = np.ravel(z_coord == 0) network["pore.top_boundary"] = np.ravel(z_coord == zmax) # Removing any pores that got classified as a boundary pore but # Weren't labled a border_cell_face ps = np.where(~np.in1d(network.pores("*_boundary"), network.pores("border_cell_face")))[0] ps = network.pores("*_boundary")[ps] for side in ["front", "back", "left", "right", "top", "bottom"]: network["pore." + side + "_boundary"][ps] = False # Setting internal label network["pore.internal"] = False network["pore.internal"][network.pores("*_boundary", mode="not")] = True # Adding props to border cell face throats and from pores Ts = np.where( network["throat.conns"][:, 1] > network.pores("border_cell_face")[0] - 1)[0] faces = network["throat.conns"][Ts, 1] for item in network.props("pore"): item = item.split(".")[1] network["throat." + item][Ts] = network["pore." + item][faces] network["pore.volume"][faces] = 0.0 # Applying unit conversions # TODO: Determine if radius and dmax are indeed microns and not voxels network["pore.coords"] = network["pore.coords"] * 1e-6 network["pore.radius"] = network["pore.radius"] * 1e-6 network["pore.dmax"] = network["pore.dmax"] * 1e-6 network["pore.volume"] = network["pore.volume"] * voxel_size**3 network["throat.coords"] = network["throat.coords"] * 1e-6 network["throat.radius"] = network["throat.radius"] * 1e-6 network["throat.dmax"] = network["throat.dmax"] * 1e-6 network["throat.volume"] = network["throat.volume"] * voxel_size**3 return network.project
def load(cls, path, node_file="throats_cellsThroatsGraph_Nodes.txt", graph_file="throats_cellsThroatsGraph.txt", network=None, voxel_size=None, return_geometry=False): r""" Loads network data from an iMorph processed image stack Parameters ---------- path : string The path of the folder where the subfiles are held node_file : string The file that describes the pores and throats, the default iMorph name is: throats_cellsThroatsGraph_Nodes.txt graph_file : string The file that describes the connectivity of the network, the default iMorph name is: throats_cellsThroatsGraph.txt network : OpenPNM Network Object The OpenPNM Network onto which the data should be loaded. If no network is supplied then an empty import network is created and returned. voxel_size : float Allows the user to define a voxel size different than what is contained in the node_file. The value must be in meters. return_geometry : Boolean If True, then all geometrical related properties are removed from the Network object and added to a GenericGeometry object. In this case the method returns a tuple containing (network, geometry). If False (default) then the returned Network will contain all properties that were in the original file. In this case, the user can call the ```split_geometry``` method explicitly to perform the separation. Returns ------- If no Network object is supplied then one will be created and returned. If return_geometry is True, then a tuple is returned containing both the network and a geometry object. """ # path = Path(path) node_file = os.path.join(path.resolve(), node_file) graph_file = os.path.join(path.resolve(), graph_file) # parsing the nodes file with open(node_file, 'r') as file: Np = sp.fromstring(file.readline().rsplit('=')[1], sep='\t', dtype=int)[0] vox_size = sp.fromstring(file.readline().rsplit(')')[1], sep='\t',)[0] # network always recreated to prevent errors network = GenericNetwork(Np=Np, Nt=0) # Define expected properies network['pore.volume'] = sp.nan scrap_lines = [file.readline() for line in range(4)] while True: vals = file.readline().split('\t') if len(vals) == 1: break network['pore.volume'][int(vals[0])] = float(vals[3]) if 'pore.'+vals[2] not in network.labels(): network['pore.'+vals[2]] = False network['pore.'+vals[2]][int(vals[0])] = True if voxel_size is None: voxel_size = vox_size * 1.0E-6 # file stores value in microns if voxel_size < 0: raise(Exception('Error - Voxel size must be specfied in ' + 'the Nodes file or as a keyword argument.')) # parsing the graph file with open(graph_file, 'r') as file: # Define expected properties network['pore.coords'] = sp.zeros((Np, 3))*sp.nan network['pore.types'] = sp.nan network['pore.color'] = sp.nan network['pore.radius'] = sp.nan network['pore.dmax'] = sp.nan network['pore.node_number'] = sp.nan # Scan file to get pore coordinate data scrap_lines = [file.readline() for line in range(3)] line = file.readline() xmax = 0.0 ymax = 0.0 zmax = 0.0 node_num = 0 while line != 'connectivity table\n': vals = sp.fromstring(line, sep='\t') xmax = vals[1] if vals[1] > xmax else xmax ymax = vals[2] if vals[2] > ymax else ymax zmax = vals[3] if vals[3] > zmax else zmax network['pore.coords'][int(vals[0]), :] = vals[1:4] network['pore.types'][int(vals[0])] = vals[4] network['pore.color'][int(vals[0])] = vals[5] network['pore.radius'][int(vals[0])] = vals[6] network['pore.dmax'][int(vals[0])] = vals[7] network['pore.node_number'][int(vals[0])] = node_num node_num += 1 line = file.readline() # Scan file to get to connectivity data scrap_lines.append(file.readline()) # Skip line # Create sparse lil array incrementally build adjacency matrix lil = sp.sparse.lil_matrix((Np, Np), dtype=int) while True: vals = sp.fromstring(file.readline(), sep='\t', dtype=int) if len(vals) <= 1: break lil.rows[vals[0]] = vals[2:] lil.data[vals[0]] = sp.ones(vals[1]) # fixing any negative volumes or distances so they are 1 voxel/micron network['pore.volume'][sp.where(network['pore.volume'] < 0)[0]] = 1.0 network['pore.radius'][sp.where(network['pore.radius'] < 0)[0]] = 1.0 network['pore.dmax'][sp.where(network['pore.dmax'] < 0)[0]] = 1.0 # Add adjacency matrix to OpenPNM network conns = sp.sparse.triu(lil, k=1, format='coo') network.update({'throat.all': sp.ones(len(conns.col), dtype=bool)}) network['throat.conns'] = sp.vstack([conns.row, conns.col]).T network['pore.to_trim'] = False network['pore.to_trim'][network.pores('*throat')] = True Ts = network.pores('to_trim') new_conns = network.find_neighbor_pores(pores=Ts, flatten=False) extend(network=network, throat_conns=new_conns, labels='new_conns') for item in network.props('pore'): item = item.split('.')[1] arr = sp.ones_like(network['pore.'+item])[0] arr = sp.tile(A=arr, reps=[network.Nt, 1])*sp.nan network['throat.'+item] = sp.squeeze(arr) network['throat.'+item][network.throats('new_conns')] = \ network['pore.'+item][Ts] trim(network=network, pores=Ts) # setting up boundary pores x_coord, y_coord, z_coord = sp.hsplit(network['pore.coords'], 3) network['pore.front_boundary'] = sp.ravel(x_coord == 0) network['pore.back_boundary'] = sp.ravel(x_coord == xmax) network['pore.left_boundary'] = sp.ravel(y_coord == 0) network['pore.right_boundary'] = sp.ravel(y_coord == ymax) network['pore.bottom_boundary'] = sp.ravel(z_coord == 0) network['pore.top_boundary'] = sp.ravel(z_coord == zmax) # removing any pores that got classified as a boundary pore but # weren't labled a border_cell_face ps = sp.where(~sp.in1d(network.pores('*_boundary'), network.pores('border_cell_face')))[0] ps = network.pores('*_boundary')[ps] for side in ['front', 'back', 'left', 'right', 'top', 'bottom']: network['pore.'+side+'_boundary'][ps] = False # setting internal label network['pore.internal'] = False network['pore.internal'][network.pores('*_boundary', mode='not')] = True # adding props to border cell face throats and from pores Ts = sp.where(network['throat.conns'][:, 1] > network.pores('border_cell_face')[0] - 1)[0] faces = network['throat.conns'][Ts, 1] for item in network.props('pore'): item = item.split('.')[1] network['throat.'+item][Ts] = network['pore.'+item][faces] network['pore.volume'][faces] = 0.0 # applying unit conversions # TODO: Determine if radius and dmax are indeed microns and not voxels network['pore.coords'] = network['pore.coords'] * 1e-6 network['pore.radius'] = network['pore.radius'] * 1e-6 network['pore.dmax'] = network['pore.dmax'] * 1e-6 network['pore.volume'] = network['pore.volume'] * voxel_size**3 network['throat.coords'] = network['throat.coords'] * 1e-6 network['throat.radius'] = network['throat.radius'] * 1e-6 network['throat.dmax'] = network['throat.dmax'] * 1e-6 network['throat.volume'] = network['throat.volume'] * voxel_size**3 return network.project
def load(cls, filename, network=None): r""" """ net = {} # --------------------------------------------------------------------- # Parse the link1 file filename = cls._parse_filename(filename=filename, ext='am') with open(filename, mode='r') as f: Np = None Nt = None while (Np is None) or (Nt is None): s = f.readline()[:-1].split(' ') if s[0] == 'define': if s[1] == 'VERTEX': Np = int(s[2]) if s[1] == 'EDGE': Nt = int(s[2]) net = {} propmap = {} typemap = {} shapemap = {} while True: s = f.readline()[:-1].split(' ') if s[0] == 'VERTEX': dshape = [Np] if s[2].endswith(']'): ncols = int(s[2].split('[', 1)[1].split(']')[0]) dshape.append(ncols) dtype = s[2].split('[')[0] temp = np.zeros(dshape, dtype=dtype) net['pore.' + s[3]] = temp key = int(s[-1].replace('@', '')) propmap[key] = 'pore.' + s[3] typemap[key] = dtype shapemap[key] = dshape elif s[0] == 'EDGE': dshape = [Nt] if s[2].endswith(']'): ncols = int(s[2].split('[', 1)[1].split(']')[0]) dshape.append(ncols) dtype = s[2].split('[')[0] temp = np.zeros(dshape, dtype=dtype) net['throat.' + s[3]] = temp key = int(s[-1].replace('@', '')) propmap[key] = 'throat.' + s[3] typemap[key] = dtype shapemap[key] = dshape elif s[0] == '#': break s = f.read().split('@') for key in propmap.keys(): if key in s: data = s[key].split('\n')[1:] data = ' '.join(data) arr = np.fromstring(data, dtype=typemap[key], sep=' ') arr = np.reshape(arr, newshape=shapemap[key]) net[propmap[key]] = arr # End file parsing net['pore.coords'] = net['pore.VertexCoordinates'] net['throat.conns'] = np.sort(net['throat.EdgeConnectivity'], axis=1) if network is None: network = GenericNetwork() network = cls._update_network(network=network, net=net) return network.project
def load(self, filename, project=None): r""" Loads the JGF file onto the given project. Parameters ---------- filename : string The name of the file containing the data to import. The formatting of this file is outlined below. project : OpenPNM Project object A GenericNetwork is created and added to the specified Project. If no Project object is supplied then one will be created and returned. Returns ------- If no project object is supplied then one will be created and returned. """ # Ensure input file is valid filename = self._parse_filename(filename=filename, ext='json') # Load and validate input JSON with open(filename, 'r') as file: json_file = json.load(file) if not self.__validate_json__(json_file): raise Exception('FIle is not in the JSON Graph Format') # Extract graph metadata from JSON number_of_nodes = json_file['graph']['metadata']['number_of_nodes'] number_of_links = json_file['graph']['metadata']['number_of_links'] # Extract node properties from JSON nodes = sorted(json_file['graph']['nodes'], key=lambda node: int(node['id'])) x = np.array( [node['metadata']['node_coordinates']['x'] for node in nodes]) y = np.array( [node['metadata']['node_coordinates']['y'] for node in nodes]) z = np.array( [node['metadata']['node_coordinates']['z'] for node in nodes]) # Extract link properties from JSON edges = sorted(json_file['graph']['edges'], key=lambda edge: int(edge['id'])) source = np.array([int(edge['source']) for edge in edges]) target = np.array([int(edge['target']) for edge in edges]) link_length = np.array( [edge['metadata']['link_length'] for edge in edges]) link_squared_radius = np.array( [edge['metadata']['link_squared_radius'] for edge in edges]) # Generate network object network = GenericNetwork(Np=number_of_nodes, Nt=number_of_links) # Define primitive throat properties network['throat.length'] = link_length network['throat.conns'] = np.column_stack([source, target]) network['throat.diameter'] = 2.0 * np.sqrt(link_squared_radius) # Define derived throat properties network['throat.area'] = gmods.throat_area.cylinder(network) network['throat.volume'] = gmods.throat_volume.cylinder(network) network['throat.perimeter'] = gmods.throat_perimeter.cylinder(network) network['throat.surface_area'] = gmods.throat_surface_area.cylinder( network) # Define primitive pore properties network['pore.index'] = np.arange(number_of_nodes) network['pore.coords'] = np.column_stack([x, y, z]) network['pore.diameter'] = np.zeros(number_of_nodes) # Define derived pore properties network['pore.area'] = gmods.pore_area.sphere(network) network['pore.volume'] = gmods.pore_volume.sphere(network) return network.project
def load(cls, path, voxel_size=1, project=None): r""" Load data from a 3DMA-Rock extracted network. This format consists of two files: 'rockname.np2th' and 'rockname.th2pn'. They should be stored together in a folder which is referred to by the path argument. These files are binary and therefore not human readable. Parameters ---------- path : string The location of the 'np2th' and 'th2np' files. This can be an absolute path or relative to the current working directory. network : OpenPNM Network Object If an Network object is recieved, this method will add new data to it but NOT overwrite anything that already exists. This can be used to append data from different sources. voxel_size : scalar The resolution of the image on which 3DMA-Rock was run, in terms of the linear length of eac voxel. The default is 1. This is used to scale the voxel counts to actual dimension. It is recommended that this value be in SI units [m] to work well with OpenPNM. project : OpenPNM Project object A GenericNetwork is created and added to the specified Project. If no Project is supplied then one will be created and returned. """ net = {} path = Path(path) path = path.resolve() for file in os.listdir(path): if file.endswith(".np2th"): np2th_file = os.path.join(path, file) elif file.endswith(".th2np"): th2np_file = os.path.join(path, file) with open(np2th_file, mode='rb') as f: [Np, Nt] = np.fromfile(file=f, count=2, dtype='u4') net['pore.boundary_type'] = sp.ndarray([ Np, ], int) net['throat.conns'] = np.ones([Nt, 2], int) * (-1) net['pore.coordination'] = sp.ndarray([ Np, ], int) net['pore.ID_number'] = sp.ndarray([ Np, ], int) for i in range(0, Np): ID = np.fromfile(file=f, count=1, dtype='u4') net['pore.ID_number'][i] = ID net['pore.boundary_type'][i] = np.fromfile(file=f, count=1, dtype='u1') z = np.fromfile(file=f, count=1, dtype='u4')[0] net['pore.coordination'][i] = z att_pores = np.fromfile(file=f, count=z, dtype='u4') att_throats = np.fromfile(file=f, count=z, dtype='u4') for j in range(0, len(att_throats)): t = att_throats[j] - 1 p = att_pores[j] - 1 net['throat.conns'][t] = [i, p] net['throat.conns'] = np.sort(net['throat.conns'], axis=1) net['pore.volume'] = np.fromfile(file=f, count=Np, dtype='u4') nx = np.fromfile(file=f, count=1, dtype='u4') nxy = np.fromfile(file=f, count=1, dtype='u4') pos = np.fromfile(file=f, count=Np, dtype='u4') ny = nxy / nx ni = np.mod(pos, nx) nj = np.mod(np.floor(pos / nx), ny) nk = np.floor(np.floor(pos / nx) / ny) net['pore.coords'] = np.array([ni, nj, nk]).T with open(th2np_file, mode='rb') as f: Nt = np.fromfile(file=f, count=1, dtype='u4')[0] net['throat.area'] = np.ones([ Nt, ], dtype=int) * (-1) for i in range(0, Nt): ID = np.fromfile(file=f, count=1, dtype='u4') net['throat.area'][i] = np.fromfile(file=f, count=1, dtype='f4') # numvox = np.fromfile(file=f, count=1, dtype='u4') att_pores = np.fromfile(file=f, count=2, dtype='u4') nx = np.fromfile(file=f, count=1, dtype='u4') nxy = np.fromfile(file=f, count=1, dtype='u4') pos = np.fromfile(file=f, count=Nt, dtype='u4') ny = nxy / nx ni = np.mod(pos, nx) nj = np.mod(np.floor(pos / nx), ny) nk = np.floor(np.floor(pos / nx) / ny) net['throat.coords'] = np.array([ni, nj, nk]).T net['pore.internal'] = net['pore.boundary_type'] == 0 # Convert voxel area and volume to actual dimensions net['throat.area'] = (voxel_size**2) * net['throat.area'] net['pore.volume'] = (voxel_size**3) * net['pore.volume'] if project is None: project = Project(name=path) network = GenericNetwork(project=project) network = cls._update_network(network=network, net=net) # Trim headless throats before returning ind = np.where(network['throat.conns'][:, 0] == -1)[0] trim(network=network, throats=ind) return project