def crop(network, shape, mode='full'): r""" Removes vertices that lie outside the specifed shape Parameters ---------- network : dict Dictionary containing ``vert.coords`` and ``edge.conns`` arrays shape : array_like The [x, y, z] shape of the domain beyond which trimming should be applied mode : str Controls how vertices to be trimmed is determined. Options are: * 'full': Any vertices lying outside the domain are trimmed * 'mixed' Vertices with at least one neighbor lying inside the domain are kept """ Pdrop = isoutside(network['vert.coords'], shape=shape, thresh=0) if mode == 'full': network = trim(network=network, pores=np.where(Pdrop)[0]) elif mode == 'mixed': # Find throats connecting internal to external pores Ts = np.sum(Pdrop[network['edge.conns']], axis=1) == 1 # Keep the pores on the ends of these throats Pkeep = np.unique(network['edge.conns'][Ts]) Ps = np.array(list(set(np.where(Pdrop)[0]).difference(set(Pkeep)))).astype(int) network = trim(network=network, pores=Ps) # Remove throats between these surviving external pores Pdrop = isoutside(network['vert.coords'], shape=shape) Ts = np.all(Pdrop[network['edge.conns']], axis=1) network = trim(network=network, throats=Ts) # Lastly label the surviving pores as outside for further processing network['vert.outside'] = np.zeros(network['vert.coords'].shape[0], dtype=bool) network['vert.outside'][Pdrop] = True return network
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=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 _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 voronoi_delaunay_dual(points, shape, crop=False): r""" Generate a dual Voronoi-Delaunay network from given base points Parameters ---------- points : array_like or scalar The points to be tessellated. If a scalar is given a set of points of that size is generated. shape : array_like The size of the domain in which the points lie crop : bool, optional (default is ``False``) If ``True`` then all points lying beyond the given domain shape will be removed Returns ------- network : dict A dictionary containing 'vert.coords' and 'edge.conns' vor : Voronoi object The Voronoi tessellation object produced by ``scipy.spatial.Voronoi`` tri : Delaunay object The Delaunay triangulation object produced ``scipy.spatial.Delaunay`` """ # Generate a set of base points if number was given points = tools.parse_points(points=points, shape=shape) mask = ~np.all(points == 0, axis=0) # Perform tessellations vor = sptl.Voronoi(points=points[:, mask]) tri = sptl.Delaunay(points=points[:, mask]) # Combine points pts_all = np.vstack((vor.points, vor.vertices)) Nall = np.shape(pts_all)[0] # Create adjacency matrix in lil format for quick construction am = sprs.lil_matrix((Nall, Nall)) for ridge in vor.ridge_dict.keys(): # Make Delaunay-to-Delaunay connections for i in ridge: am.rows[i].extend([ridge[0], ridge[1]]) # Get Voronoi vertices for current ridge row = vor.ridge_dict[ridge].copy() # Index Voronoi vertex numbers by number of Delaunay points row = [i + vor.npoints for i in row if i > -1] # Make Voronoi-to-Delaunay connections for i in ridge: am.rows[i].extend(row) # Make Voronoi-to-Voronoi connections row.append(row[0]) for i in range(len(row)-1): am.rows[row[i]].append(row[i+1]) # Finalize adjacency matrix by assigning data values am.data = am.rows # Values don't matter, only shape, so use 'rows' # Convert to COO format for direct acces to row and col am = am.tocoo() # Extract rows and cols conns = np.vstack((am.row, am.col)).T # Convert to sanitized adjacency matrix am = conns_to_am(conns) # Finally, retrieve conns back from am conns = np.vstack((am.row, am.col)).T # Convert coords to 3D by adding col of 0's if necessary coords = np.around(pts_all, decimals=10) if np.any(mask == False): verts = np.zeros([np.shape(coords)[0], 3]) for i, col in enumerate(np.where(mask)[0]): verts[:, col] = coords[:, i] else: verts = np.copy(coords) # Assign coords and conns to network dict network = {} network['vert.coords'] = verts network['edge.conns'] = conns # Identify and trim pores outside the domain if requested if crop: Ps = isoutside(verts, shape=shape) network = tools.trim(network=network, vert_ids=np.where(Ps)[0]) return network, vor, tri