def __init__(self, dx = 1., dy = 1., nx = 1, ny = 1, _RepresentationClass=_Grid2DRepresentation, _TopologyClass=_Mesh2DTopology): """ Creates a 2D triangular mesh with horizontal faces numbered first then vertical faces, then diagonal faces. Vertices are numbered starting with the vertices at the corners of boxes and then the vertices at the centers of boxes. Cells on the right of boxes are numbered first, then cells on the top of boxes, then cells on the left of boxes, then cells on the bottom of boxes. Within each of the 'sub-categories' in the above, the vertices, cells and faces are numbered in the usual way. :Parameters: - `dx, dy`: The X and Y dimensions of each 'box'. If `dx` <> `dy`, the line segments connecting the cell centers will not be orthogonal to the faces. - `nx, ny`: The number of boxes in the X direction and the Y direction. The total number of boxes will be equal to `nx * ny`, and the total number of cells will be equal to `4 * nx * ny`. """ self.args = { 'dx': dx, 'dy': dy, 'nx': nx, 'ny': ny } self.nx = nx self.ny = ny self.numberOfHorizontalFaces = self.nx * (self.ny + 1) self.numberOfVerticalFaces = self.ny * (self.nx + 1) self.numberOfEachDiagonalFaces = self.nx * self.ny self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.unit) self.dx /= scale self.dy = PhysicalField(value = dy) if self.dy.unit.isDimensionless(): self.dy = dy else: self.dy /= scale self.numberOfCornerVertices = (self.nx + 1) * (self. ny + 1) self.numberOfCenterVertices = self.nx * self.ny self.numberOfTotalVertices = self.numberOfCornerVertices + self.numberOfCenterVertices self.offset = (0, 0) vertices = self._createVertices() faces = self._createFaces() cells = self._createCells() cells = numerix.sort(cells, axis=0) Mesh2D.__init__(self, vertices, faces, cells, _RepresentationClass=_RepresentationClass, _TopologyClass=_TopologyClass) self.scale = scale
def __init__(self, dx=1., nx=None, origin=(0,), overlap=2, communicator=parallelComm, *args, **kwargs): scale = PhysicalField(value=1, unit=PhysicalField(value=dx).unit) self.origin = PhysicalField(value=origin) self.origin /= scale super(CylindricalNonUniformGrid1D, self).__init__(dx=dx, nx=nx, overlap=overlap, communicator=communicator, *args, **kwargs) self.vertexCoords += origin self.args['origin'] = origin
def __init__(self, dx=1., dy=1., nx=None, ny=1, rand=0, *args, **kwargs): self.args = {'dx': dx, 'dy': dy, 'nx': nx, 'ny': ny, 'rand': rand} self.nx = nx self.ny = ny self.dx = PhysicalField(value=dx) scale = PhysicalField(value=1, unit=self.dx.unit) self.dx /= scale self.dy = PhysicalField(value=dy) if self.dy.unit.isDimensionless(): self.dy = dy else: self.dy /= scale self.grid = Grid2D(nx=nx, ny=ny, dx=dx, dy=dy, communicator=serialComm) self.numberOfVertices = self.grid._numberOfVertices vertices = self.grid.vertexCoords changedVertices = numerix.zeros(vertices.shape, 'd') for i in range(len(vertices[0])): if ((i % (nx + 1)) != 0 and (i % (nx + 1)) != nx and (i // nx + 1) != 0 and (i // nx + 1) != ny): changedVertices[0, i] = vertices[0, i] + (rand * ( (random.random() * 2) - 1)) changedVertices[1, i] = vertices[1, i] + (rand * ( (random.random() * 2) - 1)) else: changedVertices[0, i] = vertices[0, i] changedVertices[1, i] = vertices[1, i] faces = self.grid.faceVertexIDs cells = self.grid.cellFaceIDs Mesh2D.__init__(self, changedVertices, faces, cells, communicator=serialComm, *args, **kwargs) self.scale = scale
def _calcPhysicalShape(self): """Return physical dimensions of `Grid3D` """ from fipy.tools.dimensions.physicalField import PhysicalField return PhysicalField(value=(self.ns[0] * self.ds[0] * self.scale, self.ns[1] * self.ds[1] * self.scale, self.ns[2] * self.ds[2] * self.scale))
def __init__(self, faces, value): """ Parameters ---------- faces : :obj:`~fipy.variables.faceVariable.FaceVariable` of :obj:`bool` Mask of faces where this condition applies. value : float Value to impose. """ if self.__class__ is BoundaryCondition: raise NotImplementedError("can't instantiate abstract base class") self.faces = faces if not (isinstance(value, PhysicalField) or isinstance(value, Variable)): value = PhysicalField(value) self.value = value if not (self.faces | self.faces.mesh.exteriorFaces == self.faces.mesh.exteriorFaces).value.all(): raise IndexError('Face list has interior faces') self.adjacentCellIDs = self.faces.mesh._adjacentCellIDs[0][ self.faces.value] self.boundaryConditionApplied = False
def _calcPhysicalShape(self): """Return physical dimensions of Grid2D.""" from fipy.tools.dimensions.physicalField import PhysicalField if self._dsUniformLen(): return PhysicalField(value=(self.ns[0] * self.ds[0] * self.scale, self.ns[1] * self.ds[1] * self.scale)) else: return None
def calcOrigin(origin, offset, ds, scale): newOrigin = PhysicalField(value=origin) newOrigin /= scale if type(offset) in [int, float]: newOrigin += offset * ds[0] else: newOrigin += [[o*float(d)] for o, d in zip(offset, ds)] return newOrigin
def __init__(self, dx=1., dy=1., nx=None, ny=None, origin=((0.,), (0.,)), overlap=2, communicator=parallelComm, *args, **kwargs): scale = PhysicalField(value=1, unit=PhysicalField(value=dx).unit) self.origin = PhysicalField(value=origin) self.origin /= scale super(CylindricalNonUniformGrid2D, self).__init__(dx=dx, dy=dy, nx=nx, ny=ny, overlap=overlap, communicator=communicator, *args, **kwargs) self._faceAreas *= self.faceCenters[0] self._scaledFaceAreas = self._scale['area'] * self._faceAreas self._areaProjections = self.faceNormals * self._faceAreas self._orientedAreaProjections = self._calcOrientedAreaProjections() self._faceAspectRatios = self._calcFaceAspectRatios() self._cellAreas = self._calcCellAreas() self._cellNormals = self._calcCellNormals() self.vertexCoords += self.origin self.args['origin'] = self.origin
def sqrtDot(a1, a2): """Return array of square roots of vector dot-products for arrays a1 and a2 of vectors v1 and v2 Usually used with v1==v2 to return magnitude of v1. """ from fipy.tools.dimensions import physicalField unit1 = unit2 = physicalField._unity def dimensionlessUnmasked(a): unit = physicalField._unity mask = False if _isPhysical(a): unit = a.inBaseUnits().unit a = a.numericValue if MA.isMaskedArray(a): mask = a.mask a = a.filled(fill_value=1) if not a.flags['C_CONTIGUOUS']: a = a.copy('C') return (a, unit, mask) a1, unit1, mask1 = dimensionlessUnmasked(a1) a2, unit2, mask2 = dimensionlessUnmasked(a2) NJ, ni = NUMERIX.shape(a1) result1 = NUMERIX.zeros((ni, ), 'd') inline._runInline(""" int j; result1[i] = 0.; for (j = 0; j < NJ; j++) { result1[i] += a1[i + j * ni] * a2[i + j * ni]; } result1[i] = sqrt(result1[i]); """, result1=result1, a1=a1, a2=a2, ni=ni, NJ=NJ) if NUMERIX.any(mask1) or NUMERIX.any(mask2): result1 = MA.array(result1, mask=NUMERIX.logical_or(mask1, mask2)) if unit1 != physicalField._unity or unit2 != physicalField._unity: from fipy.tools.dimensions.physicalField import PhysicalField result1 = PhysicalField(value=result, unit=(unit1 * unit2)**0.5) return result1
def _setScaledGeometry(self, val): """ Set the scale by length. :Parameters: - `val`: The new scale length. """ self._scale['length'] = PhysicalField(value=val) if self._scale['length'].unit.isDimensionless(): self._scale['length'] = 1 self._scale['area'] = self._calcAreaScale() self._scale['volume'] = self._calcVolumeScale() self._setScaledValues()
def _calcValue(self): eps = self.eps P = self.P.numericValue P = numerix.where(abs(P) < eps, eps, P) alpha = numerix.where( P > 10., (P - 1.) / P, 0.5) tmp = (1. - P / 10.) tmpSqr = tmp * tmp alpha = numerix.where( (10. >= P) & (P > eps), ((P-1.) + tmpSqr*tmpSqr*tmp) / P, alpha) tmp = (1. + P / 10.) tmpSqr = tmp * tmp alpha = numerix.where((-eps > P) & (P >= -10.), (tmpSqr*tmpSqr*tmp - 1.) / P, alpha) alpha = numerix.where( P < -10., -1. / P, alpha) return PhysicalField(value = alpha)
def __init__(self,faces,value): """ :Parameters: - `faces`: A `list` or `tuple` of exterior `Face` objects to which this condition applies. - `value`: The value to impose. """ if self.__class__ is BoundaryCondition: raise NotImplementedError, "can't instantiate abstract base class" self.faces = faces if not (isinstance(value, PhysicalField) or isinstance(value, Variable)): value = PhysicalField(value) self.value = value if not (self.faces | self.faces.mesh.exteriorFaces == self.faces.mesh.exteriorFaces).value.all(): raise IndexError, 'Face list has interior faces' self.adjacentCellIDs = self.faces.mesh._adjacentCellIDs[0][self.faces.value] self.boundaryConditionApplied = False
def buildGridData(self, ds, ns, overlap, communicator, cacheOccupiedNodes=False): """ Build and save any information relevant to the construction of a grid. Generalized to handle any dimension. Has side-effects. Dimension specific functionality is built into `_buildOverlap` and `_packOffset`, which are overridden by children of this class. Often, this method is overridden (but always called) by children classes who must distinguish between uniform and non-uniform behavior. :Note: - `spatialNums` is a list whose elements are analogous to * numberOfHorizontalRows * numberOfVerticalColumns * numberOfLayersDeep though `spatialNums` may be of length 1, 2, or 3 depending on dimensionality. :Parameters: - `ds` - A list containing grid spacing information, e.g. [dx, dy] - `ns` - A list containing number of grid points, e.g. [nx, ny, nz] - `overlap` """ dim = len(ns) newDs = [] newdx = PhysicalField(value=ds[0]) scale = PhysicalField(value=1, unit=newdx.unit) newdx /= scale newDs.append(newdx) for d in ds[1:]: # for remaining ds newD = PhysicalField(value=d) if newD.unit.isDimensionless(): if type(d) in [list, tuple]: newD = numerix.array(d) else: newD = d else: newD /= scale newDs.append(newD) newNs = self._calcNs(ns, newDs) globalNumCells = reduce(self._mult, newNs) globalNumFaces = self._calcGlobalNumFaces(newNs) """ parallel stuff """ newNs = list(newNs) procID = communicator.procID Nproc = communicator.Nproc overlap = min(overlap, newNs[-1]) cellsPerNode = max(newNs[-1] // Nproc, overlap) occupiedNodes = min(newNs[-1] // (cellsPerNode or 1), Nproc) (firstOverlap, secOverlap, overlap) = self._buildOverlap(overlap, procID, occupiedNodes) offsetArg = min(procID, occupiedNodes - 1) * cellsPerNode - firstOverlap offset = self._packOffset(offsetArg) """ local nx, [ny, [nz]] calculation """ local_n = cellsPerNode * (procID < occupiedNodes) if procID == occupiedNodes - 1: local_n += (newNs[-1] - cellsPerNode * occupiedNodes) local_n += firstOverlap + secOverlap newNs = tuple(newNs[:-1] + [local_n]) """ post-parallel """ # "what is spatialNums?" -> see docstring if 0 in newNs: newNs = [0 for n in newNs] spatialNums = [0 for n in newNs] else: spatialNums = [n + 1 for n in newNs] spatialDict = dict( zip(["numVerticalCols", "numHorizontalRows", "numLayersDeep"][:len(spatialNums)], spatialNums)) numVertices = reduce(self._mult, spatialNums) numCells = reduce(self._mult, newNs) """ Side-effects """ self.dim = dim self.ds = newDs self.ns = newNs self.scale = scale self.globalNumberOfCells = globalNumCells self.globalNumberOfFaces = globalNumFaces self.offset = offset self.overlap = overlap self.spatialDict = spatialDict self.numberOfVertices = numVertices self.numberOfCells = numCells if cacheOccupiedNodes: self.occupiedNodes = occupiedNodes
def nearest(data, points, max_mem=1e8): """find the indices of `data` that are closest to `points` >>> from fipy import * >>> m0 = Grid2D(dx=(.1, 1., 10.), dy=(.1, 1., 10.)) >>> m1 = Grid2D(nx=2, ny=2, dx=5., dy=5.) >>> print nearest(m0.cellCenters.globalValue, m1.cellCenters.globalValue) [4 5 7 8] >>> print nearest(m0.cellCenters.globalValue, m1.cellCenters.globalValue, max_mem=100) [4 5 7 8] >>> print nearest(m0.cellCenters.globalValue, m1.cellCenters.globalValue, max_mem=10000) [4 5 7 8] """ data = asanyarray(data) points = asanyarray(points) D = data.shape[0] N = data.shape[-1] M = points.shape[-1] if N == 0: return arange(0) # given (D, N) data and (D, M) points, # break points into (D, C) chunks of points # calculatate the full factorial (D, N, C) distances between them # and then reduce to the indices of the C closest values of data # then assemble chunks C into total M closest indices # (D, N) -> (D, N, 1) data = data[..., newaxis] # there appears to be no benefit to taking chunks that use more # than about 100 MiB for D x N x C, and there is a substantial penalty # for going much above that (presumably due to swapping, even # though this is vastly less than the 4 GiB I had available) # see ticket:348 numChunks = int(round(D * N * data.itemsize * M / max_mem + 0.5)) nearestIndices = empty((M, ), dtype=INT_DTYPE) for chunk in array_split(arange(points.shape[-1]), numChunks): # last chunk can be empty, but numpy (1.5.0.dev8716, anyway) # returns array([], dtype=float64), which can't be used for indexing chunk = chunk.astype(int) # (D, M) -> (D, C) chunkOfPoints = points[..., chunk] # (D, C) -> (D, 1, C) chunkOfPoints = chunkOfPoints[..., newaxis, :] # (D, 1, C) -> (D, N, C) chunkOfPoints = NUMERIX.repeat(chunkOfPoints, N, axis=1) # print "chunkOfPoints size: ", chunkOfPoints.shape, chunkOfPoints.size, chunkOfPoints.itemsize, chunkOfPoints.size * chunkOfPoints.itemsize try: tmp = data - chunkOfPoints except TypeError: tmp = data - PhysicalField(chunkOfPoints) # (D, N, C) -> (N, C) tmp = dot(tmp, tmp, axis=0) # (N, C) -> C nearestIndices[chunk] = argmin(tmp, axis=0) return nearestIndices
def _getPointToCellDistances(self, point): tmp = self.cellCenters - PhysicalField(point) return numerix.sqrtDot(tmp, tmp)
def _calcValue(self): P = self.P.numericValue alpha = numerix.where(P > 0., 1., 0.) return PhysicalField(value=alpha)
def physicalShape(self): """Return physical dimensions of Grid2D. """ return PhysicalField(value=(self.nx * self.dx * self.scale, self.ny * self.dy * self.scale))