def test_check_network_health_one_isolated_cluster(self): net = op.network.Cubic(shape=[5, 5, 5]) Ps = net['pore.coords'][:, 2] == 2.5 Ps = Ps * (net['pore.coords'][:, 1] > 2.5) Ts = net.find_neighbor_throats(pores=Ps, mode='exclusive_or') trim(network=net, throats=Ts) a = net.project.check_network_health() assert len(a['disconnected_pores']) == 10
def test_check_network_health_one_isolated_cluster(self): net = op.network.Cubic(shape=[5, 5, 5]) Ps = net['pore.coords'][:, 2] == 2.5 Ps = Ps*(net['pore.coords'][:, 1] > 2.5) Ts = net.find_neighbor_throats(pores=Ps, mode='exclusive_or') trim(network=net, throats=Ts) a = net.check_network_health() assert len(a['disconnected_clusters']) == 2 assert len(a['trim_pores']) == 10
def __init__(self, shape=[1, 1, 1], points=None, **kwargs): # Clean-up input points points = self._parse_points(shape=shape, points=points) super().__init__(shape=shape, points=points, **kwargs) # Initialize network object topotools.trim(network=self, pores=self.pores('delaunay')) pop = ['pore.delaunay', 'throat.delaunay', 'throat.interconnect'] for item in pop: del self[item]
def test_check_network_health_isolated_pores(self): net = op.network.Cubic(shape=[5, 5, 5]) Ts = net.find_neighbor_throats(pores=0) trim(network=net, throats=Ts) a = net.check_network_health() assert a['isolated_pores'] == np.array([0]) trim(network=net, pores=a['trim_pores']) a = net.check_network_health() assert np.size(a['isolated_pores']) == 0
def __init__(self, template, spacing=[1, 1, 1], **kwargs): template = sp.atleast_3d(template) super().__init__(shape=template.shape, **kwargs) coords = sp.unravel_index(range(template.size), template.shape) self['pore.template_coords'] = sp.vstack(coords).T self['pore.template_indices'] = self.Ps self['pore.drop'] = template.flatten() == 0 topotools.trim(network=self, pores=self.pores('drop')) del self['pore.drop']
def __init__(self, shape=None, num_points=None, **kwargs): # Clean-up input points points = kwargs.pop('points', None) points = self._parse_points(shape=shape, num_points=num_points, points=points) # Initialize network object super().__init__(shape=shape, points=points, **kwargs) topotools.trim(network=self, pores=self.pores('delaunay')) pop = ['pore.delaunay', 'throat.delaunay', 'throat.interconnect'] for item in pop: del self[item]
def __init__(self, num_points=None, points=None, shape=[1, 1, 1], fiber_rad=None, resolution=1e-2, name=None, **kwargs): super().__init__(name=name) shape = np.array(shape) if (len(shape) != 3) or np.any(shape == 0): raise Exception('Only 3D, rectangular shapes are supported') if fiber_rad is None: logger.exception(msg='Please initialize class with a fiber_rad') net = DelaunayVoronoiDual(project=self, num_points=num_points, points=points, shape=shape, name=self.name + '_net', **kwargs) net.fiber_rad = fiber_rad net.resolution = resolution del_geom = DelaunayGeometry(project=self, network=net, pores=net.pores('delaunay'), throats=net.throats('delaunay'), name=self.name + '_del') VoronoiGeometry(project=self, network=net, pores=net.pores('voronoi'), throats=net.throats('voronoi'), name=self.name + '_vor') # Tidy up network h = net.check_network_health() if len(h['trim_pores']) > 0: topotools.trim(network=net, pores=h['trim_pores']) # Copy the Delaunay throat diameters to the boundary pores Ps = net.pores(['delaunay', 'boundary'], mode='xnor') map_Ps = del_geom.map_pores(pores=Ps, origin=net) all_Ts = net.find_neighbor_throats(pores=Ps, flatten=False) for i, Ts in enumerate(all_Ts): Ts = np.asarray(Ts) Ts = Ts[net['throat.delaunay'][Ts]] if len(Ts) > 0: map_Ts = del_geom.map_throats(throats=Ts, origin=net) td = del_geom['throat.diameter'][map_Ts] ta = del_geom['throat.area'][map_Ts] del_geom['pore.diameter'][map_Ps[i]] = td del_geom['pore.area'][map_Ps[i]] = ta del_geom.regenerate_models(propnames=['throat.equivalent_area'])
def test_trim_throats(self): np.random.seed(1) pn = op.network.Cubic(shape=[2, 2, 2], spacing=5) Ps = pn.pores()[:4] Ts1 = pn.find_neighbor_throats(pores=Ps, mode='or') geo1 = op.geometry.GenericGeometry(network=pn, pores=Ps, throats=Ts1) Ps = pn.pores()[4:] Ts2 = pn.find_neighbor_throats(pores=Ps, mode='xnor') geo2 = op.geometry.GenericGeometry(network=pn, pores=Ps, throats=Ts2) geo1['throat.random'] = np.random.random(geo1.Nt) geo2['throat.random'] = np.random.random(geo2.Nt) trimmers = pn['throat.random'] < 0.25 topotools.trim(pn, throats=pn.throats()[trimmers]) assert ~np.any(pn['throat.random'] < 0.25)
def __init__(self, template, spacing=[1, 1, 1], **kwargs): template = np.atleast_3d(template) super().__init__(shape=template.shape, spacing=spacing, **kwargs) coords = np.unravel_index(range(template.size), template.shape) self['pore.template_coords'] = np.vstack(coords).T self['pore.template_indices'] = self.Ps topotools.trim(network=self, pores=template.flatten() == 0) # Add "internal_surface" label to "fake" surface pores! ndims = topotools.dimensionality(self).sum() max_neighbors = 6 if ndims == 3 else 4 num_neighbors = np.diff(self.get_adjacency_matrix(fmt="csr").indptr) mask_surface = self["pore.surface"] mask_internal_surface = (num_neighbors < max_neighbors) & ~mask_surface self.set_label("pore.internal_surface", pores=mask_internal_surface)
def test_check_network_health_isolated_pores_and_clusters(self): net = op.network.Cubic(shape=[5, 5, 5]) # Create first isolated cluster Ps = net['pore.coords'][:, 2] == 2.5 Ps = Ps * (net['pore.coords'][:, 1] > 2.5) Ts = net.find_neighbor_throats(pores=Ps, mode='exclusive_or') trim(network=net, throats=Ts) # Create an isolated pore Ts = net.find_neighbor_throats(pores=0) trim(network=net, throats=Ts) a = net.check_network_health() # Ensure trim_pores has right length assert len(a['disconnected_pores']) == 11 # Ensure 0 is listed in trim pores assert 0 in a['disconnected_pores']
def test_check_network_health_two_isolated_clusters(self): net = op.network.Cubic(shape=[5, 5, 5]) # Create first isolated cluster Ps = net['pore.coords'][:, 2] == 2.5 Ps = Ps*(net['pore.coords'][:, 1] > 2.5) Ts = net.find_neighbor_throats(pores=Ps, mode='exclusive_or') trim(network=net, throats=Ts) # Create a second isolated cluster Ps = net['pore.coords'][:, 2] == 3.5 Ps = Ps*(net['pore.coords'][:, 1] < 2.5) Ts = net.find_neighbor_throats(pores=Ps, mode='exclusive_or') trim(network=net, throats=Ts) a = net.check_network_health() assert len(a['disconnected_clusters']) == 3 assert len(a['trim_pores']) == 20
def __init__(self, shape=[1, 1, 1], num_points=None, points=None, **kwargs): # Clean-up input points points = self._parse_points(shape=shape, num_points=num_points, points=points) super().__init__(shape=shape, points=points, **kwargs) # Initialize network object topotools.trim(network=self, pores=self.pores(['voronoi'])) pop = ['pore.voronoi', 'throat.voronoi', 'throat.interconnect', 'pore.delaunay', 'throat.delaunay'] for item in pop: del self[item] # Trim additional pores that are missed by the parent class's trimming Ps = topotools.isoutside(coords=self['pore.coords'], shape=shape) topotools.trim(network=self, pores=Ps)
def __init__(self, shape, num_points=None, **kwargs): # Generate Delaunay tessellation from super class, then trim super().__init__(shape=shape, num_points=num_points, **kwargs) points = self['pore.coords'] conns = self['throat.conns'] # Find centroid of each pair of nodes c = points[conns] m = (c[:, 0, :] + c[:, 1, :]) / 2 # Find radius of circle connecting each pair of nodes r = np.sqrt(np.sum((c[:, 0, :] - c[:, 1, :])**2, axis=1)) / 2 # Use KD-Tree to find distance to nearest neighbors tree = sptl.cKDTree(points) n = tree.query(x=m, k=1)[0] # Identify throats whose centroid is not near an unconnected node g = np.around(n, decimals=5) == np.around(r, decimals=5) trim(self, throats=~g)
def __init__(self, shape, num_points=None, **kwargs): # Generate Delaunay tessellation from super class, then trim super().__init__(shape=shape, num_points=num_points, **kwargs) points = self['pore.coords'] conns = self['throat.conns'] # Find centroid of each pair of nodes c = points[conns] m = (c[:, 0, :] + c[:, 1, :])/2 # Find radius of circle connecting each pair of nodes r = sp.sqrt(sp.sum((c[:, 0, :] - c[:, 1, :])**2, axis=1))/2 # Use KD-Tree to find distance to nearest neighbors tree = sptl.cKDTree(points) n = tree.query(x=m, k=1)[0] # Identify throats whose centroid is not near an unconnected node g = sp.around(n, decimals=5) == sp.around(r, decimals=5) trim(self, throats=~g)
def __init__(self, shape=None, num_points=None, **kwargs): # Clean-up input points points = kwargs.pop('points', None) points = self._parse_points(shape=shape, num_points=num_points, points=points) # Initialize network object super().__init__(shape=shape, points=points, **kwargs) topotools.trim(network=self, pores=self.pores(['voronoi'])) pop = ['pore.voronoi', 'throat.voronoi', 'throat.interconnect', 'pore.delaunay', 'throat.delaunay'] for item in pop: del self[item] # Trim additional pores that are missed by the parent class's trimming Ps = topotools.isoutside(coords=self['pore.coords'], shape=shape) topotools.trim(network=self, pores=Ps)
def __init__(self, template, spacing=[1, 1, 1], **kwargs): template = sp.atleast_3d(template) if 'shape' in kwargs: del kwargs['shape'] logger.warning('shape argument ignored, inferred from template') super().__init__(shape=template.shape, spacing=spacing, **kwargs) coords = sp.unravel_index(range(template.size), template.shape) self['pore.template_coords'] = sp.vstack(coords).T self['pore.template_indices'] = self.Ps self['pore.drop'] = template.flatten() == 0 topotools.trim(network=self, pores=self.pores('drop')) del self['pore.drop'] # remove labels pertaining to surface pores, then redo post-trim self.clear(mode='labels') self['pore.internal'] = True self['throat.internal'] = True topotools.find_surface_pores(self)
def test_check_network_health_isolated_pores_and_clusters(self): net = op.network.Cubic(shape=[5, 5, 5]) # Create first isolated cluster Ps = net['pore.coords'][:, 2] == 2.5 Ps = Ps*(net['pore.coords'][:, 1] > 2.5) Ts = net.find_neighbor_throats(pores=Ps, mode='exclusive_or') trim(network=net, throats=Ts) # Create an isolated pore Ts = net.find_neighbor_throats(pores=0) trim(network=net, throats=Ts) a = net.check_network_health() # Ensure pore 0 counts as a cluster assert len(a['disconnected_clusters']) == 3 # Ensure trim_pores has right length assert len(a['trim_pores']) == 11 # Ensure 0 is listed in trim pores assert 0 in a['trim_pores'] # Ensure 0 is also listed as an isolated pore assert a['isolated_pores'] == 0
def make_1D_net(config): sub = "GEOMETRY" Nunit = config.getint(sub, "nunit_OneD") spacing = config.getfloat(sub, "spacing_OneD") pos_tabs = config.getint(sub, "pos_tabs") neg_tabs = config.getint(sub, "neg_tabs") net = op.network.Cubic([Nunit + 2, 2, 1], spacing) net["pore.pos_cc"] = net["pore.front"] net["pore.neg_cc"] = net["pore.back"] T = net.find_neighbor_throats(net.pores("left"), mode="xnor") tt.trim(net, throats=T) T = net.find_neighbor_throats(net.pores("right"), mode="xnor") tt.trim(net, throats=T) pos_cc_Ts = net.find_neighbor_throats(net.pores("pos_cc"), mode="xnor") neg_cc_Ts = net.find_neighbor_throats(net.pores("neg_cc"), mode="xnor") pos_tab_nodes = net.pores()[net["pore.pos_cc"]][pos_tabs] neg_tab_nodes = net.pores()[net["pore.neg_cc"]][neg_tabs] net["pore.pos_tab"] = False net["pore.neg_tab"] = False net["pore.pos_tab"][pos_tab_nodes] = True net["pore.neg_tab"][neg_tab_nodes] = True net["throat.pos_cc"] = False net["throat.neg_cc"] = False net["throat.pos_cc"][pos_cc_Ts] = True net["throat.neg_cc"][neg_cc_Ts] = True net["throat.spm_resistor"] = True net["throat.spm_resistor"][pos_cc_Ts] = False net["throat.spm_resistor"][neg_cc_Ts] = False net["throat.spm_resistor_order"] = -1 net["throat.spm_resistor_order"][net["throat.spm_resistor"]] = np.arange( Nunit) net["throat.spm_neg_inner"] = net["throat.spm_resistor"] net["pore.free_stream"] = False del net["pore.left"] del net["pore.right"] del net["pore.front"] del net["pore.back"] del net["pore.internal"] del net["pore.surface"] del net["throat.internal"] del net["throat.surface"] phase = op.phases.GenericPhase(network=net) geo = op.geometry.GenericGeometry(network=net, pores=net.Ps, throats=net.Ts) op.physics.GenericPhysics(network=net, phase=phase, geometry=geo) net["pore.radial_position"] = net["pore.coords"][:, 0] net["pore.arc_index"] = np.indices([Nunit + 2, 2, 1])[0].flatten() net["pore.region_id"] = -1 net["pore.cell_id"] = -1 net["throat.arc_length"] = spacing net["throat.electrode_height"] = spacing # placeholder net["pore.volume"] = 1.0 net["throat.area"] = 1.0 net["throat.length"] = 1.0 plot_topology(net) return net.project, np.cumsum(net["throat.arc_length"])
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 _trim_external_pores(self, shape): r''' ''' # Find all pores within the domain Ps = topotools.isoutside(coords=self['pore.coords'], shape=shape) self['pore.external'] = False self['pore.external'][Ps] = True # Find which internal pores are delaunay Ps = (~self['pore.external'])*self['pore.delaunay'] # Find all pores connected to an internal delaunay pore Ps = self.find_neighbor_pores(pores=Ps, include_input=True) # Mark them all as keepers self['pore.keep'] = False self['pore.keep'][Ps] = True # Trim all bad pores topotools.trim(network=self, pores=~self['pore.keep']) # Now label boundary pores self['pore.boundary'] = False self['pore.boundary'] = self['pore.delaunay']*self['pore.external'] # Label Voronoi pores on boundary Ps = self.find_neighbor_pores(pores=self.pores('boundary')) Ps = self['pore.voronoi']*self.tomask(pores=Ps) self['pore.boundary'][Ps] = True # Label Voronoi and interconnect throats on boundary self['throat.boundary'] = False Ps = self.pores('boundary') Ts = self.find_neighbor_throats(pores=Ps, mode='xnor') self['throat.boundary'][Ts] = True # Trim throats between Delaunay boundary pores Ps = self.pores(labels=['boundary', 'delaunay'], mode='xnor') Ts = self.find_neighbor_throats(pores=Ps, mode='xnor') topotools.trim(network=self, throats=Ts) # Move Delaunay boundary pores to centroid of Voronoi facet Ps = self.pores(labels=['boundary', 'delaunay'], mode='xnor') for P in Ps: Ns = self.find_neighbor_pores(pores=P) Ns = Ps = self['pore.voronoi']*self.tomask(pores=Ns) coords = sp.mean(self['pore.coords'][Ns], axis=0) self['pore.coords'][P] = coords self['pore.internal'] = ~self['pore.boundary'] Ps = self.pores('internal') Ts = self.find_neighbor_throats(pores=Ps, mode='xnor') self['throat.internal'] = False self['throat.internal'][Ts] = True # Label surface pores and throats between boundary and internal Ts = self.throats(['boundary', 'internal'], mode='not') self['throat.surface'] = False self['throat.surface'][Ts] = True surf_pores = self['throat.conns'][Ts].flatten() surf_pores = sp.unique(surf_pores[~self['pore.boundary'][surf_pores]]) self['pore.surface'] = False self['pore.surface'][surf_pores] = True # Clean-up del self['pore.external'] del self['pore.keep']
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
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, 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 """ net = {} # --------------------------------------------------------------------- # Parse the link1 file path = Path(path) filename = Path(path.resolve(), prefix+'_link1.dat') with open(filename, mode='r') as f: link1 = pd.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'] = sp.vstack((link1['throat.pore1']-1, link1['throat.pore2']-1)).T net['throat.conns'] = sp.sort(net['throat.conns'], axis=1) net['throat.radius'] = sp.array(link1['throat.radius']) net['throat.shape_factor'] = sp.array(link1['throat.shape_factor']) net['throat.total_length'] = sp.array(link1['throat.total_length']) # --------------------------------------------------------------------- filename = Path(path.resolve(), prefix+'_link2.dat') with open(filename, mode='r') as f: link2 = pd.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 = sp.array(link2['throat.length']) net['throat.length'] = cl_t net['throat.conduit_lengths.throat'] = cl_t net['throat.volume'] = sp.array(link2['throat.volume']) cl_p1 = sp.array(link2['throat.pore1_length']) net['throat.conduit_lengths.pore1'] = cl_p1 cl_p2 = sp.array(link2['throat.pore2_length']) net['throat.conduit_lengths.pore2'] = cl_p2 net['throat.clay_volume'] = sp.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 = pd.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'] = sp.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 = pd.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'] = sp.array(node2['pore.volume']) net['pore.radius'] = sp.array(node2['pore.radius']) net['pore.shape_factor'] = sp.array(node2['pore.shape_factor']) net['pore.clay_volume'] = sp.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 = sp.where(sp.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 = sp.where(sp.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 = sp.hstack([trim1, trim2]) trim(network=network, throats=to_trim) return network.project
def __init__(self, shape, spacing=1.0, length=1.0, psd_params={'distribution': 'norm', 'loc': None, 'scale': None}, name=None, settings={}, **kwargs): super().__init__(name=name) self.settings.update(defsets) self.settings.update(settings) if isinstance(shape, int): shape = sp.array([shape, shape, 2]) elif len(shape) == 2: shape = sp.concatenate((sp.array(shape), [2])) else: raise Exception('shape not understood, must be int ' + ' or list of 2 ints') if isinstance(spacing, float) or isinstance(spacing, int): spacing = float(spacing) self.settings['spacing'] = spacing spacing = sp.array([spacing, spacing, length]) else: raise Exception('spacing not understood, must be float') net = Cubic(shape=shape, spacing=spacing, project=self, **kwargs) Ps_top = net.pores('top') Ps_bot = net.pores('bottom') Ts = net.find_connecting_throat(P1=Ps_top, P2=Ps_bot) Ts = net.tomask(throats=Ts) trim(network=net, throats=~Ts) geom = GenericGeometry(network=net, pores=net.Ps, throats=net.Ts) geom.add_model(propname='throat.seed', model=mods.geometry.throat_seed.random) if psd_params['loc'] is None: psd_params['loc'] = spacing[0]/2 if psd_params['scale'] is None: psd_params['scale'] = spacing[0]/10 if psd_params['distribution'] in ['norm', 'normal', 'gaussian']: geom.add_model(propname='throat.size_distribution', seeds='throat.seed', model=mods.geometry.throat_size.normal, loc=psd_params['loc'], scale=psd_params['scale']) elif psd_params['distribution'] in ['weibull']: geom.add_model(propname='throat.size_distribution', seeds='throat.seed', model=mods.geometry.throat_size.weibull, loc=psd_params['loc'], scale=psd_params['scale'], shape=psd_params['shape']) else: temp = psd_params.copy() func = getattr(spst, temp.pop('distribution')) psd = func.freeze(**temp) geom.add_model(propname='throat.size_distribution', seeds='throat.seed', model=mods.geometry.throat_size.generic_distribution, func=psd) if sp.any(geom['throat.size_distribution'] < 0): logger.warning('Given size distribution produced negative ' + 'throat diameters...these will be set to 0') geom.add_model(propname='throat.diameter', model=mods.misc.clip, prop='throat.size_distribution', xmin=1e-12, xmax=sp.inf) if self.settings['adjust_psd'] is None: if geom['throat.size_distribution'].max() > spacing[0]: logger.warning('Given size distribution produced throats ' + 'larger than the spacing.') elif self.settings['adjust_psd'] == 'clip': geom.add_model(propname='throat.diameter', model=mods.misc.clip, prop='throat.size_distribution', xmin=1e-12, xmax=spacing[0]) if geom['throat.size_distribution'].max() > spacing[0]: logger.warning('Given size distribution produced throats ' + 'larger than the spacing...tube diameters ' + 'will be clipped between 0 and given spacing') elif self.settings['adjust_psd'] == 'normalize': tmin = max(1e-12, geom['throat.size_distribution'].min()) geom.add_model(propname='throat.diameter', model=mods.misc.normalize, prop='throat.size_distribution', xmin=tmin, xmax=spacing[0]) if geom['throat.size_distribution'].max() > spacing[0]: logger.warning('Given size distribution produced throats ' + 'larger than the spacing...tube diameters ' + 'will be normalized to fit given spacing') else: logger.warning('Settings not understood, ignoring') geom.add_model(propname='pore.diameter', model=mods.geometry.pore_size.from_neighbor_throats, throat_prop='throat.diameter', mode='max') geom.add_model(propname='pore.diameter', model=mods.misc.constant, value=0.0) geom.add_model(propname='throat.length', model=mods.geometry.throat_length.ctc) geom.add_model(propname='throat.area', model=mods.geometry.throat_area.cylinder) geom.add_model(propname='pore.area', model=mods.misc.from_neighbor_throats, throat_prop='throat.area') geom.add_model(propname='pore.volume', model=mods.misc.constant, value=0.0) geom.add_model(propname='throat.volume', model=mods.geometry.throat_volume.cylinder) geom.regenerate_models() # Now create a generic phase with physics models on it phase = GenericPhase(network=net) m = mods.physics.hydraulic_conductance.classic_hagen_poiseuille phase.add_model(propname='throat.hydraulic_conductance', model=m, regen_mode='deferred') m = mods.physics.diffusive_conductance.classic_ordinary_diffusion phase.add_model(propname='throat.diffusive_conductance', model=m, regen_mode='deferred') m = mods.physics.diffusive_conductance.classic_ordinary_diffusion phase.add_model(propname='throat.entry_pressure', model=m, regen_mode='deferred')
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] = sp.fromfile(file=f, count=2, dtype='u4') net['pore.boundary_type'] = sp.ndarray([Np, ], int) net['throat.conns'] = sp.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 = sp.fromfile(file=f, count=1, dtype='u4') net['pore.ID_number'][i] = ID net['pore.boundary_type'][i] = sp.fromfile(file=f, count=1, dtype='u1') z = sp.fromfile(file=f, count=1, dtype='u4')[0] net['pore.coordination'][i] = z att_pores = sp.fromfile(file=f, count=z, dtype='u4') att_throats = sp.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'] = sp.sort(net['throat.conns'], axis=1) net['pore.volume'] = sp.fromfile(file=f, count=Np, dtype='u4') nx = sp.fromfile(file=f, count=1, dtype='u4') nxy = sp.fromfile(file=f, count=1, dtype='u4') pos = sp.fromfile(file=f, count=Np, dtype='u4') ny = nxy/nx ni = sp.mod(pos, nx) nj = sp.mod(sp.floor(pos/nx), ny) nk = sp.floor(sp.floor(pos/nx)/ny) net['pore.coords'] = sp.array([ni, nj, nk]).T with open(th2np_file, mode='rb') as f: Nt = sp.fromfile(file=f, count=1, dtype='u4')[0] net['throat.area'] = sp.ones([Nt, ], dtype=int)*(-1) for i in range(0, Nt): ID = sp.fromfile(file=f, count=1, dtype='u4') net['throat.area'][i] = sp.fromfile(file=f, count=1, dtype='f4') numvox = sp.fromfile(file=f, count=1, dtype='u4') att_pores = sp.fromfile(file=f, count=2, dtype='u4') nx = sp.fromfile(file=f, count=1, dtype='u4') nxy = sp.fromfile(file=f, count=1, dtype='u4') pos = sp.fromfile(file=f, count=Nt, dtype='u4') ny = nxy/nx ni = sp.mod(pos, nx) nj = sp.mod(sp.floor(pos/nx), ny) nk = sp.floor(sp.floor(pos/nx)/ny) net['throat.coords'] = sp.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 = sp.where(network['throat.conns'][:, 0] == -1)[0] trim(network=network, throats=ind) return 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 __init__(self, shape, spacing=1.0, length=1.0, psd_params={ 'distribution': 'norm', 'loc': None, 'scale': None }, name=None, settings={}, **kwargs): super().__init__(name=name) self.settings.update(defsets) self.settings.update(settings) if isinstance(shape, int): shape = sp.array([shape, shape, 2]) elif len(shape) == 2: shape = sp.concatenate((sp.array(shape), [2])) else: raise Exception('shape not understood, must be int ' + ' or list of 2 ints') if isinstance(spacing, float) or isinstance(spacing, int): spacing = float(spacing) self.settings['spacing'] = spacing spacing = sp.array([spacing, spacing, length]) else: raise Exception('spacing not understood, must be float') net = Cubic(shape=shape, spacing=spacing, project=self, **kwargs) Ps_top = net.pores('top') Ps_bot = net.pores('bottom') Ts = net.find_connecting_throat(P1=Ps_top, P2=Ps_bot) Ts = net.tomask(throats=Ts) trim(network=net, throats=~Ts) geom = GenericGeometry(network=net, pores=net.Ps, throats=net.Ts) geom.add_model(propname='throat.seed', model=mods.geometry.throat_seed.random) if psd_params['loc'] is None: psd_params['loc'] = spacing[0] / 2 if psd_params['scale'] is None: psd_params['scale'] = spacing[0] / 10 if psd_params['distribution'] in ['norm', 'normal', 'gaussian']: geom.add_model(propname='throat.size_distribution', seeds='throat.seed', model=mods.geometry.throat_size.normal, loc=psd_params['loc'], scale=psd_params['scale']) elif psd_params['distribution'] in ['weibull']: geom.add_model(propname='throat.size_distribution', seeds='throat.seed', model=mods.geometry.throat_size.weibull, loc=psd_params['loc'], scale=psd_params['scale'], shape=psd_params['shape']) else: temp = psd_params.copy() func = getattr(spst, temp.pop('distribution')) psd = func.freeze(**temp) geom.add_model( propname='throat.size_distribution', seeds='throat.seed', model=mods.geometry.throat_size.generic_distribution, func=psd) if sp.any(geom['throat.size_distribution'] < 0): logger.warning('Given size distribution produced negative ' + 'throat diameters...these will be set to 0') geom.add_model(propname='throat.diameter', model=mods.misc.clip, prop='throat.size_distribution', xmin=1e-12, xmax=sp.inf) if self.settings['adjust_psd'] is None: if geom['throat.size_distribution'].max() > spacing[0]: logger.warning('Given size distribution produced throats ' + 'larger than the spacing.') elif self.settings['adjust_psd'] == 'clip': geom.add_model(propname='throat.diameter', model=mods.misc.clip, prop='throat.size_distribution', xmin=1e-12, xmax=spacing[0]) if geom['throat.size_distribution'].max() > spacing[0]: logger.warning('Given size distribution produced throats ' + 'larger than the spacing...tube diameters ' + 'will be clipped between 0 and given spacing') elif self.settings['adjust_psd'] == 'normalize': tmin = max(1e-12, geom['throat.size_distribution'].min()) geom.add_model(propname='throat.diameter', model=mods.misc.normalize, prop='throat.size_distribution', xmin=tmin, xmax=spacing[0]) if geom['throat.size_distribution'].max() > spacing[0]: logger.warning('Given size distribution produced throats ' + 'larger than the spacing...tube diameters ' + 'will be normalized to fit given spacing') else: logger.warning('Settings not understood, ignoring') geom.add_model(propname='pore.diameter', model=mods.geometry.pore_size.from_neighbor_throats, throat_prop='throat.diameter', mode='max') geom.add_model(propname='pore.diameter', model=mods.misc.constant, value=0.0) geom.add_model(propname='throat.length', model=mods.geometry.throat_length.ctc) geom.add_model(propname='throat.area', model=mods.geometry.throat_area.cylinder) geom.add_model(propname='pore.area', model=mods.misc.from_neighbor_throats, throat_prop='throat.area') geom.add_model(propname='pore.volume', model=mods.misc.constant, value=0.0) geom.add_model(propname='throat.volume', model=mods.geometry.throat_volume.cylinder) geom.regenerate_models() # Now create a generic phase with physics models on it phase = GenericPhase(network=net) m = mods.physics.hydraulic_conductance.classic_hagen_poiseuille phase.add_model(propname='throat.hydraulic_conductance', model=m, regen_mode='deferred') m = mods.physics.diffusive_conductance.classic_ordinary_diffusion phase.add_model(propname='throat.diffusive_conductance', model=m, regen_mode='deferred') m = mods.physics.diffusive_conductance.classic_ordinary_diffusion phase.add_model(propname='throat.entry_pressure', model=m, regen_mode='deferred')