def __init__(self, shape=[1, 1, 1], num_points=None, **kwargs): points = kwargs.pop('points', None) points = self._parse_points(shape=shape, num_points=num_points, points=points) # Deal with points that are only 2D...they break tessellations if points.shape[1] == 3 and len(sp.unique(points[:, 2])) == 1: points = points[:, :2] # Perform tessellation vor = sptl.Voronoi(points=points) self._vor = vor # Combine points pts_all = sp.vstack((vor.points, vor.vertices)) Nall = sp.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-Delauny connections [am.rows[i].extend([ridge[0], ridge[1]]) for i in ridge] # 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 [am.rows[i].extend(row) for i in ridge] # Make Voronoi-to-Voronoi connections row.append(row[0]) [am.rows[row[i]].append(row[i+1]) for i in range(len(row)-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 = sp.vstack((am.row, am.col)).T # Convert to sanitized adjacency matrix am = topotools.conns_to_am(conns) # Finally, retrieve conns back from am conns = sp.vstack((am.row, am.col)).T # Translate adjacency matrix and points to OpenPNM format coords = sp.around(pts_all, decimals=10) if coords.shape[1] == 2: # Make points back into 3D if necessary coords = sp.vstack((coords.T, sp.zeros((coords.shape[0], )))).T super().__init__(conns=conns, coords=coords, **kwargs) # Label all pores and throats by type self['pore.delaunay'] = False self['pore.delaunay'][0:vor.npoints] = True self['pore.voronoi'] = False self['pore.voronoi'][vor.npoints:] = True # Label throats between Delaunay pores self['throat.delaunay'] = False Ts = sp.all(self['throat.conns'] < vor.npoints, axis=1) self['throat.delaunay'][Ts] = True # Label throats between Voronoi pores self['throat.voronoi'] = False Ts = sp.all(self['throat.conns'] >= vor.npoints, axis=1) self['throat.voronoi'][Ts] = True # Label throats connecting a Delaunay and a Voronoi pore self['throat.interconnect'] = False Ts = self.throats(labels=['delaunay', 'voronoi'], mode='not') self['throat.interconnect'][Ts] = True # Trim all pores that lie outside of the specified domain self._trim_external_pores(shape=shape) self._label_faces()
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