def _facesPerCell(self): if numerix.MA.is_masked(self.cellFaceIDs): facesPerCell = (~numerix.MA.getmask(self.cellFaceIDs)).sum(axis=0) else: facesPerCell = numerix.empty((self.numberOfCells,), dtype=numerix.INT_DTYPE) facesPerCell[:] = self._maxFacesPerCell return facesPerCell
def _cellTopology(self): """return a map of the topology of each cell of grid""" cellTopology = numerix.empty((self.mesh.numberOfCells, ), dtype=numerix.ubyte) cellTopology[:] = self._elementTopology["voxel"] return cellTopology
def _unsortedNodesPerFace(self): cellFaceVertices = self._cellFaceVertices if numerix.MA.is_masked(cellFaceVertices): nodesPerFace = (~cellFaceVertices.mask).sum(axis=0) else: nodesPerFace = numerix.empty(cellFaceVertices.shape[1:], dtype=numerix.INT_DTYPE) nodesPerFace[:] = self.faceVertexIDs.shape[0] return nodesPerFace
def _cellTopology(self): """return a map of the topology of each cell""" cellTopology = numerix.empty((self.mesh.numberOfCells,), dtype=numerix.ubyte) t = self._elementTopology cellTopology[:] = t["polygon"] facesPerCell = self.mesh._facesPerCell cellTopology[facesPerCell == 3] = t["triangle"] cellTopology[facesPerCell == 4] = t["quadrangle"] return cellTopology
def _getGlobalValue(self, localIDs, globalIDs): localValue = self.getValue() if self.getMesh().communicator.Nproc > 1: if localValue.shape[-1] != 0: localValue = localValue[..., localIDs] globalIDs = numerix.concatenate(self.getMesh().communicator.allgather(globalIDs)) globalValue = numerix.empty(localValue.shape[:-1] + (max(globalIDs) + 1,), dtype=numerix.obj2sctype(localValue)) globalValue[..., globalIDs] = numerix.concatenate(self.getMesh().communicator.allgather(localValue), axis=-1) return globalValue else: return localValue
def setValue(self, value, unit=None, where=None): """ Set the value of the Variable. Can take a masked array. >>> a = Variable((1,2,3)) >>> a.setValue(5, where=(1, 0, 1)) >>> print a [5 2 5] >>> b = Variable((4,5,6)) >>> a.setValue(b, where=(1, 0, 1)) >>> print a [4 2 6] >>> print b [4 5 6] >>> a.setValue(3) >>> print a [3 3 3] >>> b = numerix.array((3,4,5)) >>> a.setValue(b) >>> a[:] = 1 >>> print b [3 4 5] >>> a.setValue((4,5,6), where=(1, 0)) Traceback (most recent call last): .... ValueError: shape mismatch: objects cannot be broadcast to a single shape """ if where is not None: tmp = numerix.empty(numerix.getShape(where), self.getsctype()) tmp[:] = value tmp = numerix.where(where, tmp, self.getValue()) else: if hasattr(value, 'copy'): tmp = value.copy() else: tmp = value value = self._makeValue(value=tmp, unit=unit, array=None) if numerix.getShape(self.value) == (): self.value.itemset(value) else: self.value[:] = value self._markFresh()
def __mul__(self, other): """ Multiply a sparse matrix by another sparse matrix >>> L1 = _PysparseMatrixFromShape(rows=3, cols=3) >>> L1.put([3.,10.,numerix.pi,2.5], [0,0,1,2], [2,1,1,0]) >>> L2 = _PysparseIdentityMatrix(size=3) >>> L2.put([4.38,12357.2,1.1], [2,1,0], [1,0,2]) >>> tmp = numerix.array(((1.23572000e+05, 2.31400000e+01, 3.00000000e+00), ... (3.88212887e+04, 3.14159265e+00, 0.00000000e+00), ... (2.50000000e+00, 0.00000000e+00, 2.75000000e+00))) >>> numerix.allclose((L1 * L2).numpyArray, tmp) 1 or a sparse matrix by a vector >>> tmp = numerix.array((29., 6.28318531, 2.5)) >>> numerix.allclose(L1 * numerix.array((1,2,3),'d'), tmp) 1 or a vector by a sparse matrix >>> tmp = numerix.array((7.5, 16.28318531, 3.)) >>> numerix.allclose(numerix.array((1,2,3),'d') * L1, tmp) ## The multiplication is broken. Numpy is calling __rmul__ for every element instead of with the whole array. 1 """ N = self.matrix.shape[1] if isinstance(other, _PysparseMatrix): return _PysparseMatrix( matrix=spmatrix.matrixmultiply(self.matrix, other.matrix)) else: shape = numerix.shape(other) if shape == (): L = spmatrix.ll_mat(N, N, N) L.put(other * numerix.ones(N, 'l')) return _PysparseMatrix( matrix=spmatrix.matrixmultiply(self.matrix, L)) elif shape == (N, ): y = numerix.empty((self.matrix.shape[0], )) self.matrix.matvec(other, y) return y else: raise TypeError
def _getGlobalValue(self, localIDs, globalIDs): localValue = self.value if self.mesh.communicator.Nproc > 1: if localValue.shape[-1] != 0: localValue = localValue[..., localIDs] globalIDs = numerix.concatenate( self.mesh.communicator.allgather(globalIDs)) globalValue = numerix.empty(localValue.shape[:-1] + (max(globalIDs) + 1, ), dtype=numerix.obj2sctype(localValue)) globalValue[..., globalIDs] = numerix.concatenate( self.mesh.communicator.allgather(localValue), axis=-1) return globalValue else: return localValue
def __mul__(self, other): """ Multiply a sparse matrix by another sparse matrix >>> L1 = _PysparseMatrixFromShape(rows=3, cols=3) >>> L1.put([3., 10., numerix.pi, 2.5], [0, 0, 1, 2], [2, 1, 1, 0]) >>> L2 = _PysparseIdentityMatrix(size=3) >>> L2.put([4.38, 12357.2, 1.1], [2, 1, 0], [1, 0, 2]) >>> tmp = numerix.array(((1.23572000e+05, 2.31400000e+01, 3.00000000e+00), ... (3.88212887e+04, 3.14159265e+00, 0.00000000e+00), ... (2.50000000e+00, 0.00000000e+00, 2.75000000e+00))) >>> numerix.allclose((L1 * L2).numpyArray, tmp) 1 or a sparse matrix by a vector >>> tmp = numerix.array((29., 6.28318531, 2.5)) >>> numerix.allclose(L1 * numerix.array((1, 2, 3), 'd'), tmp) 1 or a vector by a sparse matrix >>> tmp = numerix.array((7.5, 16.28318531, 3.)) >>> numerix.allclose(numerix.array((1, 2, 3), 'd') * L1, tmp) ## The multiplication is broken. Numpy is calling __rmul__ for every element instead of with the whole array. 1 """ N = self.matrix.shape[1] if isinstance(other, _PysparseMatrix): return _PysparseMatrix(matrix=spmatrix.matrixmultiply(self.matrix, other.matrix)) else: shape = numerix.shape(other) if shape == (): L = spmatrix.ll_mat(N, N, N) L.put(other * numerix.ones(N, 'l')) return _PysparseMatrix(matrix=spmatrix.matrixmultiply(self.matrix, L)) elif shape == (N,): y = numerix.empty((self.matrix.shape[0],)) self.matrix.matvec(other, y) return y else: raise TypeError
def _getGlobalValue(self, localIDs, globalIDs): from fipy.tools import parallel localValue = self.getValue() if parallel.Nproc > 1: from mpi4py import MPI comm = MPI.COMM_WORLD if localValue.shape[-1] != 0: localValue = localValue[..., localIDs] globalIDs = numerix.concatenate(comm.allgather(globalIDs)) globalValue = numerix.empty(localValue.shape[:-1] + (max(globalIDs) + 1,), dtype=numerix.obj2sctype(localValue)) globalValue[..., globalIDs] = numerix.concatenate(comm.allgather(localValue), axis=-1) return globalValue else: return localValue
def _maxminparallel_(self, a, axis, default, fn, fnParallel): a = a[..., self._localNonOverlappingIDs] if numerix.multiply.reduce(a.shape) == 0: if axis is None: opShape = () else: opShape = self.shape[:axis] + self.shape[axis + 1:] if len(opShape) == 0: nodeVal = default else: nodeVal = numerix.empty(opShape) nodeVal[:] = default else: nodeVal = fn(axis=axis) return fnParallel(nodeVal)
def _maxminparallel_(self, a, axis, default, fn, fnParallel): a = a[self._getLocalNonOverlappingIDs()] if numerix.multiply.reduce(a.shape) == 0: if axis is None: opShape = () else: opShape=self.shape[:axis] + self.shape[axis+1:] if len(opShape) == 0: nodeVal = default else: nodeVal = numerix.empty(opShape) nodeVal[:] = default else: nodeVal = fn(axis=axis) return fnParallel(nodeVal)
def _petsc2fipyGhost(self, vec): """Convert a PETSc `GhostVec` to a FiPy Variable (form) Moves the ghosts from the end, as necessary. The return Variable may be coupled/vector and so moving the ghosts is a bit subtle. Given an 8-element `GhostVec` `vj` ``` v0 v1 v2 v3 (v4) (v6) [4, 6] processor 0 v4 v5 v6 v7 (v1) (v3) [1, 3] processor 1 ``` where j is the global index and the `[a, b]` are the global ghost indices. Elements in () are ghosted We end up with the (2x4) FiPy Variable ``` v0 v1 (v4) processor 0 v2 v3 (v6) (v1) v4 v4 processor 1 (v3) v6 v7 ``` """ N = len(self.mesh._globalOverlappingCellIDs) M = self.numberOfEquations var = numerix.empty((M, N)) bodies = numerix.array(vec) if M > 1: bodies = numerix.reshape(bodies, (M, -1)) var[..., self._bodies] = bodies vec.ghostUpdate() with vec.localForm() as lf: if len(self._ghosts) > 0: ids = numerix.arange(-len(self._ghosts), 0) ghosts = numerix.reshape(numerix.array(lf)[ids], (M, -1)) var[..., ~self._bodies] = ghosts return var.flatten()
def _cellTopology(self): """return a map of the topology of each cell""" facesPerCell = self.mesh._facesPerCell nodesPerFace = self.mesh._nodesPerFace def faceCountsMatch(targetCounts): if len(targetCounts) > nodesPerFace.shape[0]: # pad nodesPerFace with zeros paddedNodesPerFace = numerix.zeros((len(targetCounts), nodesPerFace.shape[1]), dtype=numerix.INT_DTYPE) paddedNodesPerFace[:nodesPerFace.shape[0],:] = nodesPerFace paddedTargetCounts = numerix.array(targetCounts)[..., numerix.newaxis] else: # pad target face node count with zeros paddedTargetCounts = numerix.concatenate((targetCounts, [0] * (self.mesh._maxFacesPerCell - len(targetCounts)))) paddedTargetCounts = paddedTargetCounts[..., numerix.newaxis] paddedNodesPerFace = nodesPerFace return ((facesPerCell == len(targetCounts)) & (paddedNodesPerFace == paddedTargetCounts).all(axis=0)) cellTopology = numerix.empty((self.mesh.numberOfCells,), dtype=numerix.ubyte) t = self._elementTopology if self.mesh.dim == 1: cellTopology[:] = t["line"] elif self.mesh.dim == 2: cellTopology[:] = t["polygon"] cellTopology[faceCountsMatch([2, 2, 2])] = t["triangle"] cellTopology[faceCountsMatch([2, 2, 2, 2])] = t["quadrangle"] else: cellTopology[:] = t["unknown"] cellTopology[faceCountsMatch([3, 3, 3, 3])] = t["tetrahedron"] cellTopology[faceCountsMatch([4, 4, 4, 4, 4, 4])] = t["hexahedron"] cellTopology[faceCountsMatch([4, 4, 4, 3, 3])] = t["prism"] cellTopology[faceCountsMatch([4, 3, 3, 3, 3])] = t["pyramid"] return cellTopology
def _cellTopology(self): """return a map of the topology of each cell""" facesPerCell = self.mesh._facesPerCell nodesPerFace = self.mesh._nodesPerFace def faceCountsMatch(targetCounts): if len(targetCounts) > nodesPerFace.shape[0]: # pad nodesPerFace with zeros paddedNodesPerFace = numerix.zeros((len(targetCounts), nodesPerFace.shape[1]), dtype=numerix.INT_DTYPE) paddedNodesPerFace[:nodesPerFace.shape[0], :] = nodesPerFace paddedTargetCounts = numerix.array(targetCounts)[..., numerix.newaxis] else: # pad target face node count with zeros paddedTargetCounts = numerix.concatenate((targetCounts, [0] * (self.mesh._maxFacesPerCell - len(targetCounts)))) paddedTargetCounts = paddedTargetCounts[..., numerix.newaxis] paddedNodesPerFace = nodesPerFace return ((facesPerCell == len(targetCounts)) & (paddedNodesPerFace == paddedTargetCounts).all(axis=0)) cellTopology = numerix.empty((self.mesh.numberOfCells,), dtype=numerix.ubyte) t = self._elementTopology if self.mesh.dim == 1: cellTopology[:] = t["line"] elif self.mesh.dim == 2: cellTopology[:] = t["polygon"] cellTopology[faceCountsMatch([2, 2, 2])] = t["triangle"] cellTopology[faceCountsMatch([2, 2, 2, 2])] = t["quadrangle"] else: cellTopology[:] = t["unknown"] cellTopology[faceCountsMatch([3, 3, 3, 3])] = t["tetrahedron"] cellTopology[faceCountsMatch([4, 4, 4, 4, 4, 4])] = t["hexahedron"] cellTopology[faceCountsMatch([4, 4, 4, 3, 3])] = t["prism"] cellTopology[faceCountsMatch([4, 3, 3, 3, 3])] = t["pyramid"] return cellTopology
def _execInline(self, comment=None): """ Gets the stack from _getCstring() which calls _getRepresentation() >>> (Variable((1,2,3,4)) * Variable((5,6,7,8)))._getCstring() '(var0[i] * var1[i])' >>> (Variable(((1,2),(3,4))) * Variable(((5,6),(7,8))))._getCstring() '(var0[i + j * ni] * var1[i + j * ni])' >>> (Variable((1,2)) * Variable((5,6)) * Variable((7,8)))._getCstring() '((var00[i] * var01[i]) * var1[i])' The following test was implemented due to a problem with contiguous arrays. The `mesh.getCellCenters()[1]` command introduces a non-contiguous array into the `Variable` and this causes the inline routine to return senseless results. >>> from fipy import Grid2D, CellVariable >>> mesh = Grid2D(dx=1., dy=1., nx=2, ny=2) >>> var = CellVariable(mesh=mesh, value=0.) >>> Y = mesh.getCellCenters()[1] >>> var.setValue(Y + 1.0) >>> print var - Y [ 1. 1. 1. 1.] """ from fipy.tools import inline argDict = {} string = self._getCstring(argDict=argDict, freshen=True) + ';' try: shape = self.opShape except AttributeError: shape = self.shape dimensions = len(shape) if dimensions == 0: string = 'result[0] = ' + string dim = () else: string = 'result' + self._getCIndexString(shape) + ' = ' + string ni = self.opShape[-1] argDict['ni'] = ni if dimensions == 1: dim = (ni) else: nj = self.opShape[-2] argDict['nj'] = nj if dimensions == 2: dim =(nj,ni) elif dimensions == 3: nk = self.opShape[-3] dim = (nk,nj,ni) argDict['nk'] = nk else: raise DimensionError, 'Impossible Dimensions' ## Following section makes sure that the result array has a ## valid typecode. If self.value is None then a typecode is ## assigned to the Variable by running the calculation without ## inlining. The non-inlined result is thus used the first ## time through. if self.value is None and not hasattr(self, 'typecode'): self.canInline = False argDict['result'] = self.getValue() self.canInline = True self.typecode = numerix.obj2sctype(argDict['result']) else: if self.value is None: if self.getsctype() == numerix.bool_: argDict['result'] = numerix.empty(dim, numerix.int8) else: argDict['result'] = numerix.empty(dim, self.getsctype()) else: argDict['result'] = self.value resultShape = argDict['result'].shape if resultShape == (): argDict['result'] = numerix.reshape(argDict['result'], (1,)) inline._runInline(string, converters=None, comment=comment, **argDict) if resultShape == (): argDict['result'] = numerix.reshape(argDict['result'], resultShape) return argDict['result']
def _cellTopology(self): """return a map of the topology of each cell of grid""" cellTopology = numerix.empty((self.mesh.numberOfCells,), dtype=numerix.ubyte) cellTopology[:] = self._elementTopology["line"] return cellTopology
def _getAddedMeshValues(self, other, resolution=1e-2): """Calculate the parameters to define a concatenation of `other` with `self` Parameters ---------- other : ~fipy.meshes.mesh.Mesh The `Mesh` to concatenate with `self` resolution : float How close vertices have to be (relative to the smallest cell-to-cell distance in either mesh) to be considered the same Returns ------- dict (`vertexCoords`, `faceVertexIDs`, `cellFaceIDs`) for the new mesh. """ selfc = self._concatenableMesh otherc = other._concatenableMesh selfNumFaces = selfc.faceVertexIDs.shape[-1] selfNumVertices = selfc.vertexCoords.shape[-1] otherNumFaces = otherc.faceVertexIDs.shape[-1] otherNumVertices = otherc.vertexCoords.shape[-1] ## check dimensions if(selfc.vertexCoords.shape[0] != otherc.vertexCoords.shape[0]): raise MeshAdditionError("Dimensions do not match") ## compute vertex correlates # from fipy.tools.debug import PRINT # PRINT("selfNumFaces", selfNumFaces) # PRINT("otherNumFaces", otherNumVertices) # PRINT("selfNumVertices", selfNumVertices) # PRINT("otherNumVertices", otherNumVertices) # # from fipy.tools.debug import PRINT # from fipy.tools.debug import PRINT # PRINT("otherExt", otherc.exteriorFaces.value) # raw_input() # PRINT("selfExt", selfc.exteriorFaces.value) # # PRINT("self filled", selfc.faceVertexIDs.filled()) # PRINT("othe filled", otherc.faceVertexIDs.filled()) # raw_input() # # PRINT("selfc.faceVertexIDs.filled()\n",selfc.faceVertexIDs.filled()) # PRINT("flat\n",selfc.faceVertexIDs.filled()[..., # selfc.exteriorFaces.value].flatten()) # PRINT("selfc.exteriorFaces.value\n",selfc.exteriorFaces.value) # PRINT("extfaces type", type(selfc.exteriorFaces)) # PRINT("extfaces mesh", selfc.exteriorFaces.mesh) ## only try to match along the operation manifold if hasattr(self, "opManifold"): self_faces = self.opManifold(selfc) else: self_faces = selfc.exteriorFaces.value if hasattr(other, "opManifold"): other_faces = other.opManifold(otherc) else: other_faces = otherc.exteriorFaces.value ## only try to match exterior (X) vertices self_Xvertices = numerix.unique(selfc.faceVertexIDs.filled()[..., self_faces].flatten()) other_Xvertices = numerix.unique(otherc.faceVertexIDs.filled()[..., other_faces].flatten()) self_XvertexCoords = selfc.vertexCoords[..., self_Xvertices] other_XvertexCoords = otherc.vertexCoords[..., other_Xvertices] closest = numerix.nearest(self_XvertexCoords, other_XvertexCoords) # just because they're closest, doesn't mean they're close tmp = self_XvertexCoords[..., closest] - other_XvertexCoords distance = numerix.sqrtDot(tmp, tmp) # only want vertex pairs that are 100x closer than the smallest # cell-to-cell distance close = distance < resolution * min(selfc._cellToCellDistances.min(), otherc._cellToCellDistances.min()) vertexCorrelates = numerix.array((self_Xvertices[closest[close]], other_Xvertices[close])) # warn if meshes don't touch, but allow it if (selfc._numberOfVertices > 0 and otherc._numberOfVertices > 0 and vertexCorrelates.shape[-1] == 0): import warnings warnings.warn("Vertices are not aligned", UserWarning, stacklevel=4) ## compute face correlates # ensure that both sets of faceVertexIDs have the same maximum number of (masked) elements self_faceVertexIDs = selfc.faceVertexIDs other_faceVertexIDs = otherc.faceVertexIDs diff = self_faceVertexIDs.shape[0] - other_faceVertexIDs.shape[0] if diff > 0: other_faceVertexIDs = numerix.append(other_faceVertexIDs, -1 * numerix.ones((diff,) + other_faceVertexIDs.shape[1:], 'l'), axis=0) other_faceVertexIDs = MA.masked_values(other_faceVertexIDs, -1) elif diff < 0: self_faceVertexIDs = numerix.append(self_faceVertexIDs, -1 * numerix.ones((-diff,) + self_faceVertexIDs.shape[1:], 'l'), axis=0) self_faceVertexIDs = MA.masked_values(self_faceVertexIDs, -1) # want self's Faces for which all faceVertexIDs are in vertexCorrelates self_matchingFaces = numerix.in1d(self_faceVertexIDs, vertexCorrelates[0]).reshape(self_faceVertexIDs.shape).all(axis=0).nonzero()[0] # want other's Faces for which all faceVertexIDs are in vertexCorrelates other_matchingFaces = numerix.in1d(other_faceVertexIDs, vertexCorrelates[1]).reshape(other_faceVertexIDs.shape).all(axis=0).nonzero()[0] # map other's Vertex IDs to new Vertex IDs, # accounting for overlaps with self's Vertex IDs vertex_map = numerix.empty(otherNumVertices, dtype=numerix.INT_DTYPE) verticesToAdd = numerix.delete(numerix.arange(otherNumVertices), vertexCorrelates[1]) vertex_map[verticesToAdd] = numerix.arange(otherNumVertices - len(vertexCorrelates[1])) + selfNumVertices vertex_map[vertexCorrelates[1]] = vertexCorrelates[0] # calculate hashes of faceVertexIDs for comparing Faces if self_matchingFaces.shape[-1] == 0: self_faceHash = numerix.empty(self_matchingFaces.shape[:-1] + (0,), dtype="str") else: # sort each of self's Face's vertexIDs for canonical comparison self_faceHash = numerix.sort(self_faceVertexIDs[..., self_matchingFaces], axis=0) # then hash the Faces for comparison (NumPy set operations are only for 1D arrays) self_faceHash = numerix.apply_along_axis(str, axis=0, arr=self_faceHash) face_sort = numerix.argsort(self_faceHash) self_faceHash = self_faceHash[face_sort] self_matchingFaces = self_matchingFaces[face_sort] if other_matchingFaces.shape[-1] == 0: other_faceHash = numerix.empty(other_matchingFaces.shape[:-1] + (0,), dtype="str") else: # convert each of other's Face's vertexIDs to new IDs other_faceHash = vertex_map[other_faceVertexIDs[..., other_matchingFaces]] # sort each of other's Face's vertexIDs for canonical comparison other_faceHash = numerix.sort(other_faceHash, axis=0) # then hash the Faces for comparison (NumPy set operations are only for 1D arrays) other_faceHash = numerix.apply_along_axis(str, axis=0, arr=other_faceHash) face_sort = numerix.argsort(other_faceHash) other_faceHash = other_faceHash[face_sort] other_matchingFaces = other_matchingFaces[face_sort] self_matchingFaces = self_matchingFaces[numerix.in1d(self_faceHash, other_faceHash)] other_matchingFaces = other_matchingFaces[numerix.in1d(other_faceHash, self_faceHash)] faceCorrelates = numerix.array((self_matchingFaces, other_matchingFaces)) # warn if meshes don't touch, but allow it if (selfc.numberOfFaces > 0 and otherc.numberOfFaces > 0 and faceCorrelates.shape[-1] == 0): import warnings warnings.warn("Faces are not aligned", UserWarning, stacklevel=4) # map other's Face IDs to new Face IDs, # accounting for overlaps with self's Face IDs face_map = numerix.empty(otherNumFaces, dtype=numerix.INT_DTYPE) facesToAdd = numerix.delete(numerix.arange(otherNumFaces), faceCorrelates[1]) face_map[facesToAdd] = numerix.arange(otherNumFaces - len(faceCorrelates[1])) + selfNumFaces face_map[faceCorrelates[1]] = faceCorrelates[0] other_faceVertexIDs = vertex_map[otherc.faceVertexIDs[..., facesToAdd]] # ensure that both sets of cellFaceIDs have the same maximum number of (masked) elements self_cellFaceIDs = selfc.cellFaceIDs other_cellFaceIDs = face_map[otherc.cellFaceIDs] diff = self_cellFaceIDs.shape[0] - other_cellFaceIDs.shape[0] if diff > 0: other_cellFaceIDs = numerix.append(other_cellFaceIDs, -1 * numerix.ones((diff,) + other_cellFaceIDs.shape[1:], 'l'), axis=0) other_cellFaceIDs = MA.masked_values(other_cellFaceIDs, -1) elif diff < 0: self_cellFaceIDs = numerix.append(self_cellFaceIDs, -1 * numerix.ones((-diff,) + self_cellFaceIDs.shape[1:], 'l'), axis=0) self_cellFaceIDs = MA.masked_values(self_cellFaceIDs, -1) # concatenate everything and return return { 'vertexCoords': numerix.concatenate((selfc.vertexCoords, otherc.vertexCoords[..., verticesToAdd]), axis=1), 'faceVertexIDs': numerix.concatenate((self_faceVertexIDs, other_faceVertexIDs), axis=1), 'cellFaceIDs': MA.concatenate((self_cellFaceIDs, other_cellFaceIDs), axis=1) }
def _getAddedMeshValues(self, other, resolution=1e-2): """Calculate the parameters to define a concatenation of `other` with `self` :Parameters: - `other`: The :class:`~fipy.meshes.numMesh.Mesh` to concatenate with `self` - `resolution`: How close vertices have to be (relative to the smallest cell-to-cell distance in either mesh) to be considered the same :Returns: A `dict` with 3 elements: the new mesh vertexCoords, faceVertexIDs, and cellFaceIDs. """ selfc = self._getConcatenableMesh() other = other._getConcatenableMesh() selfNumFaces = selfc.faceVertexIDs.shape[-1] selfNumVertices = selfc.vertexCoords.shape[-1] otherNumFaces = other.faceVertexIDs.shape[-1] otherNumVertices = other.vertexCoords.shape[-1] ## check dimensions if(selfc.vertexCoords.shape[0] != other.vertexCoords.shape[0]): raise MeshAdditionError, "Dimensions do not match" ## compute vertex correlates ## only try to match exterior (X) vertices self_Xvertices = numerix.unique(selfc._getFaceVertexIDs().filled()[..., selfc.getExteriorFaces().getValue()].flatten()) other_Xvertices = numerix.unique(other._getFaceVertexIDs().filled()[..., other.getExteriorFaces().getValue()].flatten()) self_XvertexCoords = selfc.vertexCoords[..., self_Xvertices] other_XvertexCoords = other.vertexCoords[..., other_Xvertices] # lifted from Mesh._getNearestCellID() other_vertexCoordMap = numerix.resize(other_XvertexCoords, (self_XvertexCoords.shape[-1], other_XvertexCoords.shape[0], other_XvertexCoords.shape[-1])).swapaxes(0,1) tmp = self_XvertexCoords[..., numerix.newaxis] - other_vertexCoordMap closest = numerix.argmin(numerix.dot(tmp, tmp), axis=0) # just because they're closest, doesn't mean they're close tmp = self_XvertexCoords[..., closest] - other_XvertexCoords distance = numerix.sqrtDot(tmp, tmp) # only want vertex pairs that are 100x closer than the smallest # cell-to-cell distance close = distance < resolution * min(selfc._getCellToCellDistances().min(), other._getCellToCellDistances().min()) vertexCorrelates = numerix.array((self_Xvertices[closest[close]], other_Xvertices[close])) # warn if meshes don't touch, but allow it if (selfc._getNumberOfVertices() > 0 and other._getNumberOfVertices() > 0 and vertexCorrelates.shape[-1] == 0): import warnings warnings.warn("Vertices are not aligned", UserWarning, stacklevel=4) ## compute face correlates # ensure that both sets of faceVertexIDs have the same maximum number of (masked) elements self_faceVertexIDs = selfc.faceVertexIDs other_faceVertexIDs = other.faceVertexIDs diff = self_faceVertexIDs.shape[0] - other_faceVertexIDs.shape[0] if diff > 0: other_faceVertexIDs = numerix.append(other_faceVertexIDs, -1 * numerix.ones((diff,) + other_faceVertexIDs.shape[1:]), axis=0) other_faceVertexIDs = MA.masked_values(other_faceVertexIDs, -1) elif diff < 0: self_faceVertexIDs = numerix.append(self_faceVertexIDs, -1 * numerix.ones((-diff,) + self_faceVertexIDs.shape[1:]), axis=0) self_faceVertexIDs = MA.masked_values(self_faceVertexIDs, -1) # want self's Faces for which all faceVertexIDs are in vertexCorrelates self_matchingFaces = numerix.in1d(self_faceVertexIDs, vertexCorrelates[0]).reshape(self_faceVertexIDs.shape).all(axis=0).nonzero()[0] # want other's Faces for which all faceVertexIDs are in vertexCorrelates other_matchingFaces = numerix.in1d(other_faceVertexIDs, vertexCorrelates[1]).reshape(other_faceVertexIDs.shape).all(axis=0).nonzero()[0] # map other's Vertex IDs to new Vertex IDs, # accounting for overlaps with self's Vertex IDs vertex_map = numerix.empty(otherNumVertices, dtype=int) verticesToAdd = numerix.delete(numerix.arange(otherNumVertices), vertexCorrelates[1]) vertex_map[verticesToAdd] = numerix.arange(otherNumVertices - len(vertexCorrelates[1])) + selfNumVertices vertex_map[vertexCorrelates[1]] = vertexCorrelates[0] # calculate hashes of faceVertexIDs for comparing Faces if self_matchingFaces.shape[-1] == 0: self_faceHash = numerix.empty(self_matchingFaces.shape[:-1] + (0,), dtype="str") else: # sort each of self's Face's vertexIDs for canonical comparison self_faceHash = numerix.sort(self_faceVertexIDs[..., self_matchingFaces], axis=0) # then hash the Faces for comparison (NumPy set operations are only for 1D arrays) self_faceHash = numerix.apply_along_axis(str, axis=0, arr=self_faceHash) face_sort = numerix.argsort(self_faceHash) self_faceHash = self_faceHash[face_sort] self_matchingFaces = self_matchingFaces[face_sort] if other_matchingFaces.shape[-1] == 0: other_faceHash = numerix.empty(other_matchingFaces.shape[:-1] + (0,), dtype="str") else: # convert each of other's Face's vertexIDs to new IDs other_faceHash = vertex_map[other_faceVertexIDs[..., other_matchingFaces]] # sort each of other's Face's vertexIDs for canonical comparison other_faceHash = numerix.sort(other_faceHash, axis=0) # then hash the Faces for comparison (NumPy set operations are only for 1D arrays) other_faceHash = numerix.apply_along_axis(str, axis=0, arr=other_faceHash) face_sort = numerix.argsort(other_faceHash) other_faceHash = other_faceHash[face_sort] other_matchingFaces = other_matchingFaces[face_sort] self_matchingFaces = self_matchingFaces[numerix.in1d(self_faceHash, other_faceHash)] other_matchingFaces = other_matchingFaces[numerix.in1d(other_faceHash, self_faceHash)] faceCorrelates = numerix.array((self_matchingFaces, other_matchingFaces)) # warn if meshes don't touch, but allow it if (selfc._getNumberOfFaces() > 0 and other._getNumberOfFaces() > 0 and faceCorrelates.shape[-1] == 0): import warnings warnings.warn("Faces are not aligned", UserWarning, stacklevel=4) # map other's Face IDs to new Face IDs, # accounting for overlaps with self's Face IDs face_map = numerix.empty(otherNumFaces, dtype=int) facesToAdd = numerix.delete(numerix.arange(otherNumFaces), faceCorrelates[1]) face_map[facesToAdd] = numerix.arange(otherNumFaces - len(faceCorrelates[1])) + selfNumFaces face_map[faceCorrelates[1]] = faceCorrelates[0] other_faceVertexIDs = vertex_map[other.faceVertexIDs[..., facesToAdd]] # ensure that both sets of cellFaceIDs have the same maximum number of (masked) elements self_cellFaceIDs = selfc.cellFaceIDs other_cellFaceIDs = face_map[other.cellFaceIDs] diff = self_cellFaceIDs.shape[0] - other_cellFaceIDs.shape[0] if diff > 0: other_cellFaceIDs = numerix.append(other_cellFaceIDs, -1 * numerix.ones((diff,) + other_cellFaceIDs.shape[1:]), axis=0) other_cellFaceIDs = MA.masked_values(other_cellFaceIDs, -1) elif diff < 0: self_cellFaceIDs = numerix.append(self_cellFaceIDs, -1 * numerix.ones((-diff,) + self_cellFaceIDs.shape[1:]), axis=0) self_cellFaceIDs = MA.masked_values(self_cellFaceIDs, -1) # concatenate everything and return return { 'vertexCoords': numerix.concatenate((selfc.vertexCoords, other.vertexCoords[..., verticesToAdd]), axis=1), 'faceVertexIDs': numerix.concatenate((self_faceVertexIDs, other_faceVertexIDs), axis=1), 'cellFaceIDs': MA.concatenate((self_cellFaceIDs, other_cellFaceIDs), axis=1) }