def _getOrderedLines(IDs, coordinates, thresholdDistance = 0.0): """ This function takes a set of IDs and corresponding coordinates and makes a set of closed curves. :Parameters: - `IDs`: An array of integers. - `coordinates`: An array of coordinates of the same length as IDs. The following are a general set of test cases. >>> _getOrderedLines((0, 1, 2, 3), ((0, 0), (2, 0), (0, 1), (2, 1))) [[0, 2, 3, 1]] >>> _getOrderedLines((0, 1, 2, 3, 4), ((-10, -10), (0, 0), (2, 0), (0, 1), (2, 1))) [[0], [1, 3, 4, 2]] >>> _getOrderedLines((0, 1, 2, 3), ((0, 0), (0.9, 0), (0, 1), (1, 1))) [[0, 1, 3, 2]] >>> _getOrderedLines((0, 1, 2, 3, 4, 5), ((0, 0), (1, 0), (2, 0), (0, 1.1), (1, 1.1), (2, 1.1))) [[0, 1, 2, 5, 4, 3]] >>> _getOrderedLines((4, 5, 0, 1, 3, 2, 6), ((0, 0), (1, 0), (3, 0), (0, 1.1), (1, 1.1), (3, 1), (4, 1))) [[4, 5, 3, 1], [0, 2, 6]] >>> _getOrderedLines((4, 5, 3, 2, 1, 0, 9, 8, 7, 6), ((0, 0), (1, 0), (0, 1.1), (1, 1.1), (0, 3), (1, 3), (-1, 4), (2, 4), (-2, 4), (3, 4))) [[4, 5, 2, 3], [7, 9, 1, 0, 8, 6]] >>> from builtins import range >>> _getOrderedLines(list(range(7)), ((-7, 0), (-6, 0), (-5, 0), (0, 0), (5, 0), (6, 0), (7, 0))) [[0, 1, 2], [3], [4, 5, 6]] >>> from builtins import range >>> _getOrderedLines(list(range(7)), ((-7, 0), (-6, 0), (-5, 0), (0, 0), (5, 0), (6, 0), (7, 0)), thresholdDistance = 5.5) [[0, 1, 2, 3, 4, 5, 6]] """ from fipy.tools import numerix coordinates = numerix.array(coordinates) closeIDs = numerix.zeros((len(IDs), len(IDs)), 'l') vertices = [] for ID in IDs: distances = numerix.zeros(len(IDs), 'd') for i in range(len(coordinates[0,:])): distances += (coordinates[:, i] - coordinates[ID, i])**2 vertices.append(_Vertex(ID, coordinates[ID, 0], coordinates[ID, 1])) closeIDs[ID,:] = numerix.argsort(distances) for ID in IDs: i = 1 closeVertices = [] while i < 3 or vertices[ID].distance(vertices[closeIDs[ID, i]]) < thresholdDistance: closeVertices.append(vertices[closeIDs[ID, i]]) i += 1 vertices[ID].setCloseVertices(closeVertices) listOfVertexLists = [] for vertex in vertices: if not vertex.getInLine(): listOfVertexLists.append(_Line(vertex).getVertexListIDs()) return listOfVertexLists
def _orderVertices(vertexCoords, vertices): coordinates = numerix.take(vertexCoords, vertices, axis=1) centroid = numerix.add.reduce(coordinates, axis=1) / coordinates.shape[1] coordinates = coordinates - centroid[..., numerix.newaxis] coordinates = numerix.where(coordinates == 0, 1.e-10, coordinates) ## to prevent division by zero angles = numerix.arctan(coordinates[1] / coordinates[0]) + numerix.where(coordinates[0] < 0, numerix.pi, 0) ## angles go from -pi / 2 to 3*pi / 2 sortorder = numerix.argsort(angles) return numerix.take(vertices, sortorder)
def _orderVertices(vertexCoords, vertices): coordinates = numerix.take(vertexCoords, vertices) centroid = numerix.add.reduce(coordinates) / coordinates.shape[0] coordinates = coordinates - centroid # to prevent division by zero coordinates = numerix.where(coordinates == 0, 1.e-100, coordinates) # angles go from -pi / 2 to 3*pi / 2 angles = numerix.arctan(coordinates[:, 1] / coordinates[:, 0]) \ + numerix.where(coordinates[:, 0] < 0, numerix.pi, 0) sortorder = numerix.argsort(angles) return numerix.take(vertices, sortorder)
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 _faceOrder(self): # note: argsort does not work as documented: # Array of indices that sort a along the specified axis. In other words, a[index_array] yields a sorted a. # No, it's not. # We need both the sorted values and the sort order. return numerix.argsort(self._unsortedNodesPerFace, axis=0)[::-1]
def _calcTrialValue(self, id, evaluatedFlag, extensionVariable): adjIDs = self.cellToCellIDs[...,id] adjEvaluatedFlag = numerix.take(evaluatedFlag, adjIDs) adjValues = numerix.take(self.value, adjIDs) adjValues = numerix.where(adjEvaluatedFlag, adjValues, 1e+10) try: indices = numerix.argsort(abs(adjValues)) except TypeError: # numpy 1.1 raises a TypeError when using argsort function indices = abs(adjValues).argsort() sign = (self.value[id] > 0) * 2 - 1 d0 = self.cellToCellDistances[indices[0], id] v0 = self.value[..., adjIDs[indices[0]]] e0 = extensionVariable[..., adjIDs[indices[0]]] N = numerix.sum(adjEvaluatedFlag) index0 = indices[0] index1 = indices[1] index2 = indices[self.mesh.getDim()] if N > 1: n0 = self.cellNormals[..., index0, id] n1 = self.cellNormals[..., index1, id] if self.mesh.getDim() == 2: cross = (n0[0] * n1[1] - n0[1] * n1[0]) else: cross = 0.0 if abs(cross) < 0.1: if N == 2: N = 1 elif N == 3: index1 = index2 if N == 0: raise Exception elif N == 1: return v0 + sign * d0, e0 else: d1 = self.cellToCellDistances[index1, id] n0 = self.cellNormals[..., index0, id] n1 = self.cellNormals[..., index1, id] v1 = self.value[..., adjIDs[index1]] crossProd = d0 * d1 * (n0[0] * n1[1] - n0[1] * n1[0]) dotProd = d0 * d1 * numerix.dot(n0, n1) dsq = d0**2 + d1**2 - 2 * dotProd top = -v0 * (dotProd - d1**2) - v1 * (dotProd - d0**2) sqrt = crossProd**2 *(dsq - (v0 - v1)**2) sqrt = numerix.sqrt(max(sqrt, 0)) dis = (top + sign * sqrt) / dsq ## extension variable e1 = extensionVariable[..., adjIDs[index1]] a0 = self.cellAreas[index0, id] a1 = self.cellAreas[index1, id] if self.value[id] > 0: phi = max(dis, 0) else: phi = min(dis, 0) n0grad = a0 * abs(v0 - phi) / d0 n1grad = a1 * abs(v1 - phi) / d1 return dis, (e0 * n0grad + e1 * n1grad) / (n0grad + n1grad)
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) }