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 __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 `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, dx=1.0, nx=1, origin=(0,), overlap=2, communicator=parallel): origin = numerix.array(origin) self.args = {"dx": dx, "nx": nx, "origin": origin, "overlap": overlap} self.dim = 1 self.dx = PhysicalField(value=dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = int(nx) (self.nx, self.overlap, self.offset) = self._calcParallelGridInfo(nx, overlap, communicator) self.origin = PhysicalField(value=origin) self.origin /= scale self.origin += self.offset * self.dx self.numberOfVertices = self.nx + 1 if self.nx == 0: self.numberOfFaces = 0 else: self.numberOfFaces = self.nx + 1 self.numberOfCells = self.nx self.exteriorFaces = self.getFacesLeft() | self.getFacesRight() self.scale = {"length": 1.0, "area": 1.0, "volume": 1.0} self.setScale(value=scale) self.communicator = communicator
def __init__(self, dx=1., nx=None, overlap=2, communicator=parallel): self.args = { 'dx': dx, 'nx': nx, 'overlap': overlap } from fipy.tools.dimensions.physicalField import PhysicalField self.dx = PhysicalField(value=dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = self._calcNumPts(d=self.dx, n=nx) (self.nx, self.overlap, self.offset) = self._calcParallelGridInfo(nx, overlap, communicator) if numerix.getShape(self.dx) is not (): Xoffset = numerix.sum(self.dx[0:self.offset]) self.dx = self.dx[self.offset:self.offset + self.nx] else: Xoffset = self.dx * self.offset vertices = self._createVertices() + ((Xoffset,),) self.numberOfVertices = len(vertices[0]) faces = self._createFaces() self.numberOfFaces = len(faces[0]) cells = self._createCells() Mesh1D.__init__(self, vertices, faces, cells, communicator=communicator) self.setScale(value = scale)
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 = 1, ny = 1): """ 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.nx = nx self.ny = ny self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale self.dy = PhysicalField(value = dy) if self.dy.getUnit().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 vertices = self._createVertices() faces = self._createFaces() cells = self._createCells() cells = numerix.sort(cells, axis=0) Mesh2D.__init__(self, vertices, faces, cells) self.setScale(value = scale)
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 __init__(self, dx = 1., dy = 1., nx = None, ny = 1, rand = 0): self.nx = nx self.ny = ny self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale from fipy import Grid2D self.grid = Grid2D(nx=nx, ny=ny, dx=dx, dy=dy) self.numberOfVertices = self.grid._getNumberOfVertices() vertices = self.grid.getVertexCoords() 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._getFaceVertexIDs() cells = self.grid._getCellFaceIDs() Mesh2D.__init__(self, changedVertices, faces, cells) self.setScale(value = scale)
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, dx=1., nx=1, origin=(0,), overlap=2, parallelModule=parallel): origin = numerix.array(origin) self.args = { 'dx': dx, 'nx': nx, 'origin': origin, 'overlap': overlap } self.dim = 1 self.dx = PhysicalField(value=dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = int(nx) (self.nx, self.overlap, self.offset) = self._calcParallelGridInfo(nx, overlap, parallelModule) self.origin = PhysicalField(value=origin) self.origin /= scale self.origin += self.offset * self.dx self.numberOfVertices = self.nx + 1 if self.nx == 0: self.numberOfFaces = 0 else: self.numberOfFaces = self.nx + 1 self.numberOfCells = self.nx self.exteriorFaces = self.getFacesLeft() | self.getFacesRight() self.scale = { 'length': 1., 'area': 1., 'volume': 1. } self.setScale(value=scale)
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 __init__(self, dx, dy, nx, ny): """Grid2D is initialized by caller :Parameters: - `dx`: dimension of each cell in **x** direction - `dy`: dimension of each cell in **y** direction - `nx`: number of cells in **x** direction - `ny`: number of cells in **y** direction """ self.nx=nx self.ny=ny self.dx=PhysicalField(value = dx) self.dy=PhysicalField(value = dy) self.scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= self.scale self.dy /= self.scale vertices = self._createVertices() rowFaces,colFaces = self._createFaces(vertices) cells = self._createCells(rowFaces,colFaces) faces,interiorFaces = self._reorderFaces(rowFaces,colFaces) Mesh.__init__(self, cells, faces, interiorFaces, vertices)
def __init__(self, dx=1., dy=1., nx=None, ny=None, overlap=2, parallelModule=parallel): self.args = { 'dx': dx, 'dy': dy, 'nx': nx, 'ny': ny, 'overlap': overlap, 'parallelModule': parallelModule } self.dx = PhysicalField(value = dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = self._calcNumPts(d=self.dx, n=nx, axis="x") self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale ny = self._calcNumPts(d=self.dy, n=ny, axis="y") (self.nx, self.ny, self.overlap, self.offset) = self._calcParallelGridInfo(nx, ny, overlap, parallelModule) if numerix.getShape(self.dx) is not (): Xoffset = numerix.sum(self.dx[0:self.offset[0]]) self.dx = self.dx[self.offset[0]:self.offset[0] + self.nx] else: Xoffset = 0 if numerix.getShape(self.dy) is not (): Yoffset = numerix.sum(self.dy[0:self.offset[1]]) self.dy = self.dy[self.offset[1]:self.offset[1] + self.ny] else: Yoffset = 0 if self.nx == 0: self.ny = 0 if self.ny == 0: self.nx = 0 if self.nx == 0 or self.ny == 0: self.numberOfHorizontalRows = 0 self.numberOfVerticalColumns = 0 else: self.numberOfHorizontalRows = (self.ny + 1) self.numberOfVerticalColumns = (self.nx + 1) self.numberOfVertices = self.numberOfHorizontalRows * self.numberOfVerticalColumns vertices = self._createVertices() + ((Xoffset,), (Yoffset,)) faces = self._createFaces() self.numberOfFaces = len(faces[0]) cells = self._createCells() Mesh2D.__init__(self, vertices, faces, cells) self.setScale(value = scale)
class Grid2D(Mesh2D): """ Creates a 2D grid mesh with horizontal faces numbered first and then vertical faces. """ def __init__(self, dx=1., dy=1., nx=None, ny=None, overlap=2, parallelModule=parallel): self.args = { 'dx': dx, 'dy': dy, 'nx': nx, 'ny': ny, 'overlap': overlap, 'parallelModule': parallelModule } self.dx = PhysicalField(value = dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = self._calcNumPts(d=self.dx, n=nx, axis="x") self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale ny = self._calcNumPts(d=self.dy, n=ny, axis="y") (self.nx, self.ny, self.overlap, self.offset) = self._calcParallelGridInfo(nx, ny, overlap, parallelModule) if numerix.getShape(self.dx) is not (): Xoffset = numerix.sum(self.dx[0:self.offset[0]]) self.dx = self.dx[self.offset[0]:self.offset[0] + self.nx] else: Xoffset = 0 if numerix.getShape(self.dy) is not (): Yoffset = numerix.sum(self.dy[0:self.offset[1]]) self.dy = self.dy[self.offset[1]:self.offset[1] + self.ny] else: Yoffset = 0 if self.nx == 0: self.ny = 0 if self.ny == 0: self.nx = 0 if self.nx == 0 or self.ny == 0: self.numberOfHorizontalRows = 0 self.numberOfVerticalColumns = 0 else: self.numberOfHorizontalRows = (self.ny + 1) self.numberOfVerticalColumns = (self.nx + 1) self.numberOfVertices = self.numberOfHorizontalRows * self.numberOfVerticalColumns vertices = self._createVertices() + ((Xoffset,), (Yoffset,)) faces = self._createFaces() self.numberOfFaces = len(faces[0]) cells = self._createCells() Mesh2D.__init__(self, vertices, faces, cells) self.setScale(value = scale) def _calcParallelGridInfo(self, nx, ny, overlap, parallelModule): procID = parallelModule.procID Nproc = parallelModule.Nproc overlap = min(overlap, ny) cellsPerNode = max(int(ny / Nproc), overlap) occupiedNodes = min(int(ny / (cellsPerNode or 1)), Nproc) overlap = { 'left': 0, 'right': 0, 'bottom': overlap * (procID > 0) * (procID < occupiedNodes), 'top': overlap * (procID < occupiedNodes - 1) } offset = (0, min(procID, occupiedNodes-1) * cellsPerNode - overlap['bottom']) local_nx = nx local_ny = cellsPerNode * (procID < occupiedNodes) if procID == occupiedNodes - 1: local_ny += (ny - cellsPerNode * occupiedNodes) local_ny = local_ny + overlap['bottom'] + overlap['top'] self.globalNumberOfCells = nx * ny self.globalNumberOfFaces = nx * (ny + 1) + ny * (nx + 1) return local_nx, local_ny, overlap, offset def __repr__(self): return "%s(dx=%s, dy=%s, nx=%d, ny=%d)" \ % (self.__class__.__name__, str(self.args["dx"]), str(self.args["dy"]), self.args["nx"], self.args["ny"]) def _createVertices(self): x = self._calcVertexCoordinates(self.dx, self.nx) x = numerix.resize(x, (self.numberOfVertices,)) y = self._calcVertexCoordinates(self.dy, self.ny) y = numerix.repeat(y, self.numberOfVerticalColumns) return numerix.array((x, y)) def _createFaces(self): """ v1, v2 refer to the vertices. Horizontal faces are first """ v1 = numerix.arange(self.numberOfVertices) v2 = v1 + 1 horizontalFaces = vector.prune(numerix.array((v1, v2)), self.numberOfVerticalColumns, self.nx, axis=1) v1 = numerix.arange(self.numberOfVertices - self.numberOfVerticalColumns) v2 = v1 + self.numberOfVerticalColumns verticalFaces = numerix.array((v1, v2)) ## The cell normals must point out of the cell. ## The left and bottom faces have only one neighboring cell, ## in the 2nd neighbor position (there is nothing in the 1st). ## ## reverse some of the face orientations to obtain the correct normals tmp = horizontalFaces.copy() horizontalFaces[0,:self.nx] = tmp[1,:self.nx] horizontalFaces[1,:self.nx] = tmp[0,:self.nx] self.numberOfHorizontalFaces = horizontalFaces.shape[-1] tmp = verticalFaces.copy() verticalFaces[0, :] = tmp[1, :] verticalFaces[1, :] = tmp[0, :] if self.numberOfVerticalColumns > 0: verticalFaces[0, ::self.numberOfVerticalColumns] = tmp[0, ::self.numberOfVerticalColumns] verticalFaces[1, ::self.numberOfVerticalColumns] = tmp[1,::self.numberOfVerticalColumns] return numerix.concatenate((horizontalFaces, verticalFaces), axis=1) def _createCells(self): """ cells = (f1, f2, f3, f4) going anticlock wise. f1 etc. refer to the faces """ return inline._optionalInline(self._createCellsIn, self._createCellsPy) def _createCellsPy(self): cellFaceIDs = numerix.zeros((4, self.nx * self.ny)) faceIDs = numerix.arange(self.numberOfFaces) if self.numberOfFaces > 0: cellFaceIDs[0,:] = faceIDs[:self.numberOfHorizontalFaces - self.nx] cellFaceIDs[2,:] = cellFaceIDs[0,:] + self.nx cellFaceIDs[1,:] = vector.prune(faceIDs[self.numberOfHorizontalFaces:], self.numberOfVerticalColumns) cellFaceIDs[3,:] = cellFaceIDs[1,:] - 1 return cellFaceIDs def _createCellsIn(self): cellFaceIDs = numerix.zeros((4, self.nx * self.ny)) inline._runInline(""" int ID = j * ni + i; int NCELLS = ni * nj; cellFaceIDs[ID + 0 * NCELLS] = ID; cellFaceIDs[ID + 2 * NCELLS] = cellFaceIDs[ID + 0 * NCELLS] + ni; cellFaceIDs[ID + 3 * NCELLS] = horizontalFaces + ID + j; cellFaceIDs[ID + 1 * NCELLS] = cellFaceIDs[ID + 3 * NCELLS] + 1; """, horizontalFaces=self.numberOfHorizontalFaces, cellFaceIDs=cellFaceIDs, ni=self.nx, nj=self.ny) return cellFaceIDs def getScale(self): return self.scale['length'] def getPhysicalShape(self): """Return physical dimensions of Grid2D. """ return PhysicalField(value = (self.nx * self.dx * self.getScale(), self.ny * self.dy * self.getScale())) def _getMeshSpacing(self): return numerix.array((self.dx,self.dy))[...,numerix.newaxis] def getShape(self): return (self.nx, self.ny) def _isOrthogonal(self): return True def _getGlobalNonOverlappingCellIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Does not include the IDs of boundary cells. E.g., would return [0, 1] for mesh A --------- | 4 | 5 | --------- B | 2 | 3 | ========= | 0 | 1 | A --------- .. note:: Trivial except for parallel meshes """ return numerix.arange((self.offset[1] + self.overlap['bottom']) * self.nx, (self.offset[1] + self.ny - self.overlap['top']) * self.nx) def _getGlobalOverlappingCellIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Includes the IDs of boundary cells. E.g., would return [0, 1, 2, 3] for mesh A --------- | 4 | 5 | --------- B | 2 | 3 | ========= | 0 | 1 | A --------- .. note:: Trivial except for parallel meshes """ return numerix.arange(self.offset[1] * self.nx, (self.offset[1] + self.ny) * self.nx) def _getLocalNonOverlappingCellIDs(self): """ Return the IDs of the local mesh in isolation. Does not include the IDs of boundary cells. E.g., would return [0, 1] for mesh A --------- | 4 | 5 | --------- B | 2 | 3 | ========= | 0 | 1 | A --------- .. note:: Trivial except for parallel meshes """ return numerix.arange(self.overlap['bottom'] * self.nx, (self.ny - self.overlap['top']) * self.nx) def _getLocalOverlappingCellIDs(self): """ Return the IDs of the local mesh in isolation. Includes the IDs of boundary cells. E.g., would return [0, 1, 2, 3] for mesh A --------- | | | --------- B | 2 | 3 | ========= | 0 | 1 | A --------- .. note:: Trivial except for parallel meshes """ return numerix.arange(0, self.ny * self.nx) ## pickling def __getstate__(self): """ Used internally to collect the necessary information to ``pickle`` the `Grid2D` to persistent storage. """ return self.args def __setstate__(self, dict): """ Used internally to create a new `Grid2D` from ``pickled`` persistent storage. """ self.__init__(**dict) def _test(self): """
def __init__(self, dx=1., dy=1., nx=1, ny=1, origin=((0,),(0,)), overlap=2, communicator=parallel): self.args = { 'dx': dx, 'dy': dy, 'nx': nx, 'ny': ny, 'origin': origin, 'overlap': overlap, 'communicator': communicator } self.dim = 2 self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale nx = int(nx) self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale ny = int(ny) (self.nx, self.ny, self.overlap, self.offset) = self._calcParallelGridInfo(nx, ny, overlap, communicator) self.origin = PhysicalField(value = origin) self.origin /= scale self.origin += ((self.offset[0] * float(self.dx),), (self.offset[1] * float(self.dy),)) if self.nx == 0: self.ny = 0 if self.ny == 0: self.nx = 0 if self.nx == 0 or self.ny == 0: self.numberOfHorizontalRows = 0 self.numberOfVerticalColumns = 0 else: self.numberOfHorizontalRows = (self.ny + 1) self.numberOfVerticalColumns = (self.nx + 1) self.numberOfVertices = self.numberOfHorizontalRows * self.numberOfVerticalColumns self.numberOfHorizontalFaces = self.nx * self.numberOfHorizontalRows self.numberOfVerticalFaces = self.numberOfVerticalColumns * self.ny self.numberOfFaces = self.numberOfHorizontalFaces + self.numberOfVerticalFaces self.numberOfCells = self.nx * self.ny self.scale = { 'length': 1., 'area': 1., 'volume': 1. } self.setScale(value = scale) self.communicator = communicator
class Grid2D(Mesh): """2D rectangular Mesh Numbering system nx=5 ny=3 Cells:: ************************************* * * * * * * * 10 * 11 * 12 * 13 * 14 * ************************************* * * * * * * * 5 * 6 * 7 * 8 * 9 * ************************************* * * * * * * * 0 * 1 * 2 * 3 * 4 * ************************************* Faces (before reordering):: ***15******16*****17******18****19*** * * * * * * 32 33 34 35 36 37 ***10******11*****12******13*****14** * * * * * * 26 27 28 29 30 31 ***5*******6******7*******8******9*** * * * * * * 20 21 22 23 24 25 ***0*******1******2*******3******4*** Faces (after reordering):: ***27******28*****29******30****31*** * * * * * * 34 18 19 20 21 37 ***5*******6******7*******8******9*** * * * * * * 33 14 15 16 17 36 ***0*******1******2*******3******4*** * * * * * * 32 10 11 12 13 35 ***22******23*****24******25*****26** Vertices:: 18*****19*****20******21*****22****23 * * * * * * * * * * * * 12*****13*****14******15*****16****17 * * * * * * * * * * * * 6******7******8*******9******10****11 * * * * * * * * * * * * 0******1******2*******3******4******5 """ def __init__(self, dx, dy, nx, ny): """Grid2D is initialized by caller :Parameters: - `dx`: dimension of each cell in **x** direction - `dy`: dimension of each cell in **y** direction - `nx`: number of cells in **x** direction - `ny`: number of cells in **y** direction """ self.nx=nx self.ny=ny self.dx=PhysicalField(value = dx) self.dy=PhysicalField(value = dy) self.scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= self.scale self.dy /= self.scale vertices = self._createVertices() rowFaces,colFaces = self._createFaces(vertices) cells = self._createCells(rowFaces,colFaces) faces,interiorFaces = self._reorderFaces(rowFaces,colFaces) Mesh.__init__(self, cells, faces, interiorFaces, vertices) def _createVertices(self): """Return list of `Vertex` objects """ vertices = () ny=self.ny nx=self.nx dx=self.dx dy=self.dy for j in range(ny+1): for i in range(nx+1): vertices += (Vertex(numerix.array([i * dx, j * dy],'d')),) ## vertices += (Vertex(PhysicalField(value = [i * dx, j * dy])),) return vertices def _createFaces(self, vertices): """Return 2-`tuple` of `Face` objects bounded by `vertices`. First `tuple` are the `Face` objects that separate rows of `Cell` objects. Second `tuple` are the `Face` objects that separate columns of `Cell objects. These initial lists are layed out for efficiency of composing and indexing into the lists to compose `Cell` objects. They will subsequently be reordered for efficiency of computations. """ nx=self.nx ny=self.ny id = 0 rowFaces = () for j in range(ny+1): oneRow = () for i in range(nx): oneRow += (Face2D((vertices[i + j * (nx + 1)],vertices[i + 1 + j * (nx + 1)]),id),) id += 1 rowFaces += (oneRow,) colFaces = [] for j in range(ny): oneCol = () for i in range(nx+1): oneCol += (Face2D((vertices[i + j * (nx + 1)],vertices[i + (j + 1) * (nx + 1)]),id),) id += 1 colFaces += (oneCol,) return (rowFaces,colFaces) def _reorderFaces(self,rowFaces,colFaces): """Return a `tuple` of `Face` objects ordered for best efficiency. Composed from `rowFaces` and `colFaces` such that all interior faces are listed contiguously, rows then columns, followed by all boundary faces, rows then columns. """ interiorFaces = () for rowFace in rowFaces[1:-1]: interiorFaces += rowFace for colFace in colFaces: interiorFaces += colFace[1:-1] faces = interiorFaces faces += rowFaces[0] + rowFaces[-1] for colFace in colFaces: faces += (colFace[0],) for colFace in colFaces: faces += (colFace[-1],) id = 0 for face in faces: face._setID(id) id += 1 return (faces, interiorFaces) def _createCells(self,rowFaces,colFaces): """Return list of `Cell` objects. """ nx=self.nx ny=self.ny cells = () for j in range(ny): for i in range(nx): id = j * nx + i cells += ( Cell( faces = (rowFaces[j][i], rowFaces[j+1][i], colFaces[j][i], colFaces[j][i+1]), faceOrientations = (-1,1,1,-1), id = id ), ) return cells def _createInteriorFaces(self,faces): """Return list of faces that are not on boundary of Grid2D. """ interiorFaces = () for face in faces: if len(face.getCells()) == 2: interiorFaces += (face,) return interiorFaces def getFacesLeft(self): """ Return list of faces on left boundary of Grid2D with the x-axis running from left to right. """ nx=self.nx ny=self.ny start = len(self.interiorFaces) + 2 * nx return self.faces[start:start + ny] def getFacesRight(self): """ Return list of faces on right boundary of Grid2D with the x-axis running from left to right. """ nx=self.nx ny=self.ny start = len(self.interiorFaces) + 2 * nx + ny return self.faces[start:start + ny] def getFacesTop(self): """ Return list of faces on top boundary of Grid2D with the y-axis running from bottom to top. """ nx=self.nx start = len(self.interiorFaces) + nx return self.faces[start:start + nx] def getFacesBottom(self): """ Return list of faces on bottom boundary of Grid2D with the y-axis running from bottom to top. """ nx=self.nx start = len(self.interiorFaces) return self.faces[start:start + nx] def getShape(self): """Return cell dimensions `Grid2D`. """ return (self.nx,self.ny) def getPhysicalShape(self): """Return physical dimensions of Grid2D. """ return PhysicalField(value = (self.nx * self.dx * self.getScale(), self.ny * self.dy * self.getScale())) def _getMaxFacesPerCell(self): return 4 def _getFaceAreas(self): return Mesh._getFaceAreas(self) * self.getScale() def getCellVolumes(self): if self.getScale() is 1: return Mesh.getCellVolumes(self) else: return Mesh.getCellVolumes(self) * self.getScale() * self.getScale() def getCellCenters(self): if self.getScale() is 1: return Mesh.getCellCenters(self) else: return Mesh.getCellCenters(self) * self.getScale() def _getCellDistances(self): if self.getScale() is 1: return Mesh._getCellDistances(self) else: return Mesh._getCellDistances(self) * self.getScale() def _getFaceToCellDistances(self): if self.getScale() is 1: return Mesh._getFaceToCellDistances(self) else: return Mesh._getFaceToCellDistances(self) * self.getScale() def _getMeshSpacing(self): return PhysicalField(value = ((self.dx * self.getScale(),),(self.dy * self.getScale(),)))
def _getPointToCellDistances(self, point): tmp = self.cellCenters - PhysicalField(point) return numerix.sqrtDot(tmp, tmp)
class UniformGrid2D(Grid2D): """ Creates a 2D grid mesh with horizontal faces numbered first and then vertical faces. """ def __init__(self, dx=1., dy=1., nx=1, ny=1, origin=((0,),(0,)), overlap=2, communicator=parallel): self.args = { 'dx': dx, 'dy': dy, 'nx': nx, 'ny': ny, 'origin': origin, 'overlap': overlap, 'communicator': communicator } self.dim = 2 self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale nx = int(nx) self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale ny = int(ny) (self.nx, self.ny, self.overlap, self.offset) = self._calcParallelGridInfo(nx, ny, overlap, communicator) self.origin = PhysicalField(value = origin) self.origin /= scale self.origin += ((self.offset[0] * float(self.dx),), (self.offset[1] * float(self.dy),)) if self.nx == 0: self.ny = 0 if self.ny == 0: self.nx = 0 if self.nx == 0 or self.ny == 0: self.numberOfHorizontalRows = 0 self.numberOfVerticalColumns = 0 else: self.numberOfHorizontalRows = (self.ny + 1) self.numberOfVerticalColumns = (self.nx + 1) self.numberOfVertices = self.numberOfHorizontalRows * self.numberOfVerticalColumns self.numberOfHorizontalFaces = self.nx * self.numberOfHorizontalRows self.numberOfVerticalFaces = self.numberOfVerticalColumns * self.ny self.numberOfFaces = self.numberOfHorizontalFaces + self.numberOfVerticalFaces self.numberOfCells = self.nx * self.ny self.scale = { 'length': 1., 'area': 1., 'volume': 1. } self.setScale(value = scale) self.communicator = communicator def _translate(self, vector): return self.__class__(dx = self.args['dx'], nx = self.args['nx'], dy = self.args['dy'], ny = self.args['ny'], origin = numerix.array(self.args['origin']) + vector, overlap=self.args['overlap']) def __mul__(self, factor): if numerix.shape(factor) is (): factor = numerix.resize(factor, (2,1)) return UniformGrid2D(dx=self.args['dx'] * numerix.array(factor[0]), nx=self.args['nx'], dy=self.args['dy'] * numerix.array(factor[1]), ny=self.args['ny'], origin=numerix.array(self.args['origin']) * factor, overlap=self.args['overlap']) def _getConcatenableMesh(self): from fipy.meshes.numMesh.grid2D import Grid2D args = self.args.copy() origin = args['origin'] from fipy.tools import serial args['communicator'] = serial del args['origin'] return Grid2D(**args) + origin ## get topology methods ## from common/mesh def _getCellFaceIDs(self): return self._createCells() def getExteriorFaces(self): """ Return only the faces that have one neighboring cell. """ exteriorIDs = numerix.concatenate((numerix.arange(0, self.nx), numerix.arange(0, self.nx) + self.nx * self.ny, numerix.arange(0, self.ny) * self.numberOfVerticalColumns + self.numberOfHorizontalFaces, numerix.arange(0, self.ny) * self.numberOfVerticalColumns + self.numberOfHorizontalFaces + self.nx)) from fipy.variables.faceVariable import FaceVariable exteriorFaces = FaceVariable(mesh=self, value=False) exteriorFaces[exteriorIDs] = True return exteriorFaces def getInteriorFaces(self): """ Return only the faces that have two neighboring cells. """ Hids = numerix.arange(0, self.numberOfHorizontalFaces) Hids = numerix.reshape(Hids, (self.numberOfHorizontalRows, self.nx)) Hids = Hids[1:-1,...] Vids = numerix.arange(self.numberOfHorizontalFaces, self.numberOfFaces) Vids = numerix.reshape(Vids, (self.ny, self.numberOfVerticalColumns)) Vids = Vids[...,1:-1] interiorIDs = numerix.concatenate((numerix.reshape(Hids, (self.nx * (self.ny - 1),)), numerix.reshape(Vids, ((self.nx - 1) * self.ny,)))) from fipy.variables.faceVariable import FaceVariable interiorFaces = FaceVariable(mesh=self, value=False) interiorFaces[interiorIDs] = True return interiorFaces def _getCellFaceOrientations(self): cellFaceOrientations = numerix.ones((4, self.numberOfCells)) if self.numberOfCells > 0: cellFaceOrientations[0, self.nx:] = -1 cellFaceOrientations[3, :] = -1 cellFaceOrientations[3, ::self.nx] = 1 return cellFaceOrientations def _getAdjacentCellIDs(self): return inline._optionalInline(self._getAdjacentCellIDsIn, self._getAdjacentCellIDsPy) def _getAdjacentCellIDsIn(self): faceCellIDs0 = numerix.zeros(self.numberOfFaces) faceCellIDs1 = numerix.zeros(self.numberOfFaces) inline._runInline(""" int ID = j * ni + i; faceCellIDs0[ID] = ID - ni; faceCellIDs1[ID] = ID; faceCellIDs0[ID + Nhor + j] = ID - 1; faceCellIDs1[ID + Nhor + j] = ID; if (j == 0) { faceCellIDs0[ID] = ID; } if (j == nj - 1) { faceCellIDs0[ID + ni] = ID; faceCellIDs1[ID + ni] = ID; } if (i == 0) { faceCellIDs0[ID + Nhor + j] = ID; } if ( i == ni - 1 ) { faceCellIDs0[ID + Nhor + j + 1] = ID; faceCellIDs1[ID + Nhor + j + 1] = ID; } """, Nhor=self.numberOfHorizontalFaces, faceCellIDs0=faceCellIDs0, faceCellIDs1=faceCellIDs1, ni=self.nx, nj=self.ny) return (faceCellIDs0, faceCellIDs1) def _getAdjacentCellIDsPy(self): Hids = numerix.zeros((self.numberOfHorizontalRows, self.nx, 2)) indices = numerix.indices((self.numberOfHorizontalRows, self.nx)) Hids[...,1] = indices[1] + indices[0] * self.nx Hids[...,0] = Hids[...,1] - self.nx if self.numberOfHorizontalRows > 0: Hids[0,...,0] = Hids[0,...,1] Hids[0,...,1] = Hids[0,...,0] Hids[-1,...,1] = Hids[-1,...,0] Vids = numerix.zeros((self.ny, self.numberOfVerticalColumns, 2)) indices = numerix.indices((self.ny, self.numberOfVerticalColumns)) Vids[...,1] = indices[1] + indices[0] * self.nx Vids[...,0] = Vids[...,1] - 1 if self.numberOfVerticalColumns > 0: Vids[...,0,0] = Vids[...,0,1] Vids[...,0,1] = Vids[...,0,0] Vids[...,-1,1] = Vids[...,-1,0] faceCellIDs = numerix.concatenate((numerix.reshape(Hids, (self.numberOfHorizontalFaces, 2)), numerix.reshape(Vids, (self.numberOfFaces - self.numberOfHorizontalFaces, 2)))) return (faceCellIDs[:,0], faceCellIDs[:,1]) def _getCellToCellIDs(self): ids = MA.zeros((4, self.nx, self.ny), 'l') indices = numerix.indices((self.nx, self.ny)) ids[0] = indices[0] + (indices[1] - 1) * self.nx ids[1] = (indices[0] + 1) + indices[1] * self.nx ids[2] = indices[0] + (indices[1] + 1) * self.nx ids[3] = (indices[0] - 1) + indices[1] * self.nx if self.ny > 0: ids[0,..., 0] = MA.masked ids[2,...,-1] = MA.masked if self.nx > 0: ids[1,-1,...] = MA.masked ids[3, 0,...] = MA.masked return MA.reshape(ids.swapaxes(1,2), (4, self.numberOfCells)) def _getCellToCellIDsFilled(self): N = self.getNumberOfCells() M = self._getMaxFacesPerCell() cellIDs = numerix.repeat(numerix.arange(N)[numerix.newaxis, ...], M, axis=0) cellToCellIDs = self._getCellToCellIDs() return MA.where(MA.getmaskarray(cellToCellIDs), cellIDs, cellToCellIDs) def _getMaxFacesPerCell(self): return 4 ## from numMesh/mesh def getVertexCoords(self): return self._createVertices() + self.origin def getFaceCellIDs(self): return inline._optionalInline(self._getFaceCellIDsIn, self._getFaceCellIDsPy) def _getFaceCellIDsIn(self): faceCellIDs = numerix.zeros((2, self.numberOfFaces)) mask = numerix.zeros((2, self.numberOfFaces)) inline._runInline(""" int ID = j * ni + i; int rowlength = ni * nj + Nhor + nj; faceCellIDs[ID + 0 * rowlength] = ID - ni; faceCellIDs[ID + 1 * rowlength] = ID; faceCellIDs[ID + Nhor + j + 0 * rowlength] = ID - 1; faceCellIDs[ID + Nhor + j + 1 * rowlength] = ID; if (j == 0) { faceCellIDs[ID + 0 * rowlength] = ID; mask[ID + 1 * rowlength] = 1; } if (j == nj - 1) { faceCellIDs[ID + ni + 0 * rowlength] = ID; mask[ID + ni + 1 * rowlength] = 1; } if (i == 0) { faceCellIDs[ID + Nhor + j + 0 * rowlength] = ID; mask[ID + Nhor + j + 1 * rowlength] = 1; } if ( i == ni - 1 ) { faceCellIDs[ID + Nhor + j + 1 + 0 * rowlength] = ID; mask[ID + Nhor + j + 1 + 1 * rowlength] = 1; } """, Nhor=self.numberOfHorizontalFaces, mask=mask, faceCellIDs=faceCellIDs, ni=self.nx, nj=self.ny) return MA.masked_where(mask, faceCellIDs) def _getFaceCellIDsPy(self): Hids = numerix.zeros((2, self.nx, self.numberOfHorizontalRows)) indices = numerix.indices((self.nx, self.numberOfHorizontalRows)) Hids[1] = indices[0] + indices[1] * self.nx Hids[0] = Hids[1] - self.nx if self.numberOfHorizontalRows > 0: Hids[0,...,0] = Hids[1,...,0] Hids[1,...,0] = -1 Hids[1,...,-1] = -1 Vids = numerix.zeros((2, self.numberOfVerticalColumns, self.ny)) indices = numerix.indices((self.numberOfVerticalColumns, self.ny)) Vids[1] = indices[0] + indices[1] * self.nx Vids[0] = Vids[1] - 1 if self.numberOfVerticalColumns > 0: Vids[0,0] = Vids[1,0] Vids[1,0] = -1 Vids[1,-1] = -1 return MA.masked_values(numerix.concatenate((Hids.reshape((2, self.numberOfHorizontalFaces), order="FORTRAN"), Vids.reshape((2, self.numberOfFaces - self.numberOfHorizontalFaces), order="FORTRAN")), axis=1), value = -1) def _getFaceAreas(self): faceAreas = numerix.zeros(self.numberOfFaces, 'd') faceAreas[:self.numberOfHorizontalFaces] = self.dx faceAreas[self.numberOfHorizontalFaces:] = self.dy return faceAreas def _getFaceNormals(self): normals = numerix.zeros((2, self.numberOfFaces), 'd') normals[1, :self.numberOfHorizontalFaces] = 1 normals[1, :self.nx] = -1 normals[0, self.numberOfHorizontalFaces:] = 1 if self.numberOfVerticalColumns > 0: normals[0, self.numberOfHorizontalFaces::self.numberOfVerticalColumns] = -1 return normals def _getFaceCellToCellNormals(self): return self._getFaceNormals() def getCellVolumes(self): return numerix.ones(self.numberOfCells, 'd') * self.dx * self.dy def _getCellCenters(self): centers = numerix.zeros((2, self.nx, self.ny), 'd') indices = numerix.indices((self.nx, self.ny)) centers[0] = (indices[0] + 0.5) * self.dx centers[1] = (indices[1] + 0.5) * self.dy return centers.reshape((2, self.numberOfCells), order="FORTRAN") + self.origin def _getCellDistances(self): Hdis = numerix.repeat((self.dy,), self.numberOfHorizontalFaces) Hdis = numerix.reshape(Hdis, (self.nx, self.numberOfHorizontalRows)) if self.numberOfHorizontalRows > 0: Hdis[...,0] = self.dy / 2. Hdis[...,-1] = self.dy / 2. Vdis = numerix.repeat((self.dx,), self.numberOfFaces - self.numberOfHorizontalFaces) Vdis = numerix.reshape(Vdis, (self.numberOfVerticalColumns, self.ny)) if self.numberOfVerticalColumns > 0: Vdis[0,...] = self.dx / 2. Vdis[-1,...] = self.dx / 2. return numerix.concatenate((numerix.reshape(numerix.swapaxes(Hdis,0,1), (self.numberOfHorizontalFaces,)), numerix.reshape(numerix.swapaxes(Vdis,0,1), (self.numberOfFaces - self.numberOfHorizontalFaces,)))) def _getFaceToCellDistanceRatio(self): faceToCellDistanceRatios = numerix.zeros(self.numberOfFaces, 'd') faceToCellDistanceRatios[:] = 0.5 faceToCellDistanceRatios[:self.nx] = 1. faceToCellDistanceRatios[self.numberOfHorizontalFaces - self.nx:self.numberOfHorizontalFaces] = 1. if self.numberOfVerticalColumns > 0: faceToCellDistanceRatios[self.numberOfHorizontalFaces::self.numberOfVerticalColumns] = 1. faceToCellDistanceRatios[(self.numberOfHorizontalFaces + self.nx)::self.numberOfVerticalColumns] = 1. return faceToCellDistanceRatios def _getFaceToCellDistances(self): faceToCellDistances = numerix.zeros((2, self.numberOfFaces), 'd') distances = self._getCellDistances() ratios = self._getFaceToCellDistanceRatio() faceToCellDistances[0] = distances * ratios faceToCellDistances[1] = distances * (1 - ratios) return faceToCellDistances def _getOrientedAreaProjections(self): return self._getAreaProjections() def _getAreaProjections(self): return inline._optionalInline(self._getAreaProjectionsIn, self._getAreaProjectionsPy) def _getAreaProjectionsPy(self): return self._getFaceNormals() * self._getFaceAreas() def _getAreaProjectionsIn(self): areaProjections = numerix.zeros((2, self.numberOfFaces), 'd') inline._runInline(""" if (i < nx) { areaProjections[i + 1 * ni] = -dx; } else if (i < Nhor) { areaProjections[i + 1 * ni] = dx; } else if ( (i - Nhor) % (nx + 1) == 0 ) { areaProjections[i + 0 * ni] = -dy; } else { areaProjections[i + 0 * ni] = dy; } """, dx = float(self.dx), # horrible hack to get around dy = float(self.dy), # http://www.scipy.org/scipy/scipy/ticket/496 nx = self.nx, Nhor = self.numberOfHorizontalFaces, areaProjections = areaProjections, ni = self.numberOfFaces) return areaProjections def _getOrientedFaceNormals(self): return self._getFaceNormals() def _getFaceTangents1(self): tangents = numerix.zeros((2,self.numberOfFaces), 'd') if self.numberOfFaces > 0: tangents[0, :self.numberOfHorizontalFaces] = -1 tangents[0, :self.nx] = 1 tangents[1, self.numberOfHorizontalFaces:] = 1 tangents[1, self.numberOfHorizontalFaces::self.numberOfVerticalColumns] = -1 return tangents def _getFaceTangents2(self): return numerix.zeros((2, self.numberOfFaces), 'd') def _getFaceAspectRatios(self): return self._getFaceAreas() / self._getCellDistances() def _getCellToCellDistances(self): distances = numerix.zeros((4, self.nx, self.ny), 'd') distances[0] = self.dy distances[1] = self.dx distances[2] = self.dy distances[3] = self.dx if self.ny > 0: distances[0,..., 0] = self.dy / 2. distances[2,...,-1] = self.dy / 2. if self.nx > 0: distances[3, 0,...] = self.dx / 2. distances[1,-1,...] = self.dx / 2. return distances.reshape((4, self.numberOfCells), order="FORTRAN") def _getCellNormals(self): normals = numerix.zeros((2, 4, self.numberOfCells), 'd') normals[:, 0] = [[ 0], [-1]] normals[:, 1] = [[ 1], [ 0]] normals[:, 2] = [[ 0], [ 1]] normals[:, 3] = [[-1], [ 0]] return normals def _getCellAreas(self): areas = numerix.ones((4, self.numberOfCells), 'd') areas[0] = self.dx areas[1] = self.dy areas[2] = self.dx areas[3] = self.dy return areas def _getCellAreaProjections(self): return self._getCellAreas() * self._getCellNormals() ## from numMesh/mesh def getFaceCenters(self): Hcen = numerix.zeros((2, self.nx, self.numberOfHorizontalRows), 'd') indices = numerix.indices((self.nx, self.numberOfHorizontalRows)) Hcen[0,...] = (indices[0] + 0.5) * self.dx Hcen[1,...] = indices[1] * self.dy Vcen = numerix.zeros((2, self.numberOfVerticalColumns, self.ny), 'd') indices = numerix.indices((self.numberOfVerticalColumns, self.ny)) Vcen[0,...] = indices[0] * self.dx Vcen[1,...] = (indices[1] + 0.5) * self.dy return numerix.concatenate((Hcen.reshape((2, self.numberOfHorizontalFaces), order="FORTRAN"), Vcen.reshape((2, self.numberOfVerticalFaces), order="FORTRAN")), axis=1) + self.origin def _getCellVertexIDs(self): ids = numerix.zeros((4, self.nx, self.ny)) indices = numerix.indices((self.nx, self.ny)) ids[1] = indices[0] + (indices[1] + 1) * self.numberOfVerticalColumns ids[0] = ids[1] + 1 ids[3] = indices[0] + indices[1] * self.numberOfVerticalColumns ids[2] = ids[3] + 1 return numerix.reshape(ids, (4, self.numberOfCells)) def _getFaceVertexIDs(self): Hids = numerix.zeros((2, self.nx, self.numberOfHorizontalRows)) indices = numerix.indices((self.nx, self.numberOfHorizontalRows)) Hids[0] = indices[0] + indices[1] * self.numberOfVerticalColumns Hids[1] = Hids[0] + 1 Vids = numerix.zeros((2, self.numberOfVerticalColumns, self.ny)) indices = numerix.indices((self.numberOfVerticalColumns, self.ny)) Vids[0] = indices[0] + indices[1] * self.numberOfVerticalColumns Vids[1] = Vids[0] + self.numberOfVerticalColumns return numerix.concatenate((Hids.reshape((2, self.numberOfHorizontalFaces), order="FORTRAN"), Vids.reshape((2, self.numberOfFaces - self.numberOfHorizontalFaces), order="FORTRAN")), axis=1) def _getOrderedCellVertexIDs(self): ids = numerix.zeros((4, self.nx, self.ny)) indices = numerix.indices((self.nx, self.ny)) ids[2] = indices[0] + (indices[1] + 1) * self.numberOfVerticalColumns ids[1] = ids[2] + 1 ids[3] = indices[0] + indices[1] * self.numberOfVerticalColumns ids[0] = ids[3] + 1 return ids.reshape((4, self.numberOfCells), order="FORTRAN") ## scaling def _calcScaledGeometry(self): pass def _getNearestCellID(self, points): """ Test cases >>> from fipy import * >>> m = Grid2D(nx=3, ny=2) >>> eps = numerix.array([[1e-5, 1e-5]]) >>> print m._getNearestCellID(((0., .9, 3.), (0., 2., 2.))) [0 3 5] >>> print m._getNearestCellID(([1.1], [1.5])) [4] >>> m0 = Grid2D(nx=2, ny=2, dx=1., dy=1.) >>> m1 = Grid2D(nx=4, ny=4, dx=.5, dy=.5) >>> print m0._getNearestCellID(m1.getCellCenters().getGlobalValue()) [0 0 1 1 0 0 1 1 2 2 3 3 2 2 3 3] """ nx = self.args['nx'] ny = self.args['ny'] if nx == 0 or ny == 0: return numerix.arange(0) x0, y0 = self.getCellCenters().getGlobalValue()[...,0] xi, yi = points dx, dy = self.dx, self.dy i = numerix.array(numerix.rint(((xi - x0) / dx)), 'l') i[i < 0] = 0 i[i > nx - 1] = nx - 1 j = numerix.array(numerix.rint(((yi - y0) / dy)), 'l') j[j < 0] = 0 j[j > ny - 1] = ny - 1 return j * nx + i def _test(self): """
def physicalShape(self): """Return physical dimensions of Grid2D. """ return PhysicalField(value=(self.nx * self.dx * self.scale, self.ny * self.dy * self.scale))
class UniformGrid1D(Grid1D): """ Creates a 1D grid mesh. >>> mesh = UniformGrid1D(nx = 3) >>> print mesh.getCellCenters() [[ 0.5 1.5 2.5]] """ def __init__(self, dx=1., nx=1, origin=(0,), overlap=2, parallelModule=parallel): origin = numerix.array(origin) self.args = { 'dx': dx, 'nx': nx, 'origin': origin, 'overlap': overlap } self.dim = 1 self.dx = PhysicalField(value=dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = int(nx) (self.nx, self.overlap, self.offset) = self._calcParallelGridInfo(nx, overlap, parallelModule) self.origin = PhysicalField(value=origin) self.origin /= scale self.origin += self.offset * self.dx self.numberOfVertices = self.nx + 1 if self.nx == 0: self.numberOfFaces = 0 else: self.numberOfFaces = self.nx + 1 self.numberOfCells = self.nx self.exteriorFaces = self.getFacesLeft() | self.getFacesRight() self.scale = { 'length': 1., 'area': 1., 'volume': 1. } self.setScale(value=scale) def _translate(self, vector): return UniformGrid1D(dx=self.dx, nx=self.args['nx'], origin=self.args['origin'] + numerix.array(vector), overlap=self.args['overlap']) def __mul__(self, factor): return UniformGrid1D(dx=self.dx * factor, nx=self.args['nx'], origin=self.args['origin'] * factor, overlap=self.args['overlap']) def _getConcatenableMesh(self): from fipy.meshes.numMesh.mesh1D import Mesh1D return Mesh1D(vertexCoords = self.getVertexCoords(), faceVertexIDs = self._createFaces(), cellFaceIDs = self._createCells()) def _concatenate(self, other, smallNumber): """ Following test was added due to a bug in adding Meshes. >>> a = UniformGrid1D(nx=10) + (10,) >>> print a.getCellCenters() [[ 10.5 11.5 12.5 13.5 14.5 15.5 16.5 17.5 18.5 19.5]] >>> b = 10 + UniformGrid1D(nx=10) >>> print b.getCellCenters() [[ 10.5 11.5 12.5 13.5 14.5 15.5 16.5 17.5 18.5 19.5]] >>> from fipy.tools import parallel >>> if parallel.Nproc == 1: ... c = UniformGrid1D(nx=10) + (UniformGrid1D(nx=10) + 10) >>> print (parallel.Nproc > 1 ... or numerix.allclose(c.getCellCenters()[0], ... [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, ... 12.5, 13.5, 14.5, 15.5, 16.5, 17.5, 18.5, 19.5])) True """ return self._getConcatenableMesh()._concatenate(other = other, smallNumber = smallNumber) ## get topology methods ## from common/mesh def _getCellFaceIDs(self): return MA.array(self._createCells()) def getInteriorFaces(self): from fipy.variables.faceVariable import FaceVariable interiorFaces = FaceVariable(mesh=self, value=False) interiorFaces[numerix.arange(self.numberOfFaces-2) + 1] = True return interiorFaces def _getCellFaceOrientations(self): orientations = numerix.ones((2, self.numberOfCells)) if self.numberOfCells > 0: orientations[0] *= -1 orientations[0,0] = 1 return orientations def _getAdjacentCellIDs(self): c1 = numerix.arange(self.numberOfFaces) ids = numerix.array((c1 - 1, c1)) if self.numberOfFaces > 0: ids[0,0] = ids[1,0] ids[1,-1] = ids[0,-1] return ids[0], ids[1] def _getCellToCellIDs(self): c1 = numerix.arange(self.numberOfCells) ids = MA.array((c1 - 1, c1 + 1)) if self.numberOfCells > 0: ids[0,0] = MA.masked ids[1,-1] = MA.masked return ids def _getCellToCellIDsFilled(self): ids = self._getCellToCellIDs().filled() if self.numberOfCells > 0: ids[0,0] = 0 ids[1,-1] = self.numberOfCells - 1 return ids def _getMaxFacesPerCell(self): return 2 ## from numMesh/mesh def getVertexCoords(self): return self.getFaceCenters() def getFaceCellIDs(self): c1 = numerix.arange(self.numberOfFaces) ids = MA.array((c1 - 1, c1)) if self.numberOfFaces > 0: ids[0,0] = ids[1,0] ids[1,0] = MA.masked ids[1,-1] = MA.masked return ids ## get geometry methods ## from common/mesh def _getFaceAreas(self): return numerix.ones(self.numberOfFaces,'d') def _getFaceNormals(self): faceNormals = numerix.ones((1, self.numberOfFaces), 'd') # The left-most face has neighboring cells None and the left-most cell. # We must reverse the normal to make fluxes work correctly. if self.numberOfFaces > 0: faceNormals[...,0] *= -1 return faceNormals def _getFaceCellToCellNormals(self): return self._getFaceNormals() def getCellVolumes(self): return numerix.ones(self.numberOfCells, 'd') * self.dx def _getCellCenters(self): return ((numerix.arange(self.numberOfCells)[numerix.NewAxis, ...] + 0.5) * self.dx + self.origin) * self.scale['length'] def _getCellDistances(self): distances = numerix.ones(self.numberOfFaces, 'd') distances *= self.dx if len(distances) > 0: distances[0] = self.dx / 2. distances[-1] = self.dx / 2. return distances def _getFaceToCellDistanceRatio(self): distances = numerix.ones(self.numberOfFaces, 'd') distances *= 0.5 if len(distances) > 0: distances[0] = 1 distances[-1] = 1 return distances def _getOrientedAreaProjections(self): return self._getAreaProjections() def _getAreaProjections(self): return self._getFaceNormals() def _getOrientedFaceNormals(self): return self._getFaceNormals() def _getFaceTangents1(self): return numerix.zeros(self.numberOfFaces, 'd')[numerix.NewAxis, ...] def _getFaceTangents2(self): return numerix.zeros(self.numberOfFaces, 'd')[numerix.NewAxis, ...] def _getFaceAspectRatios(self): return 1. / self._getCellDistances() def _getCellToCellDistances(self): distances = MA.zeros((2, self.numberOfCells), 'd') distances[:] = self.dx if self.numberOfCells > 0: distances[0,0] = self.dx / 2. distances[1,-1] = self.dx / 2. return distances def _getCellNormals(self): normals = numerix.ones((1, 2, self.numberOfCells), 'd') if self.numberOfCells > 0: normals[:,0] = -1 return normals def _getCellAreas(self): return numerix.ones((2, self.numberOfCells), 'd') def _getCellAreaProjections(self): return MA.array(self._getCellNormals()) ## from numMesh/mesh def getFaceCenters(self): return numerix.arange(self.numberOfFaces)[numerix.NewAxis, ...] * self.dx + self.origin def _getCellVertexIDs(self): c1 = numerix.arange(self.numberOfCells) return numerix.array((c1 + 1, c1)) ## scaling def _calcScaledGeometry(self): pass def _getNearestCellID(self, points): """ Test cases >>> from fipy import * >>> m = Grid1D(nx=3) >>> print m._getNearestCellID(([0., .9, 3.],)) [0 0 2] >>> print m._getNearestCellID(([1.1],)) [1] >>> m0 = Grid1D(nx=2, dx=1.) >>> m1 = Grid1D(nx=4, dx=.5) >>> print m0._getNearestCellID(m1.getCellCenters().getGlobalValue()) [0 0 1 1] """ nx = self.globalNumberOfCells if nx == 0: return numerix.arange(0) x0, = self.getCellCenters().getGlobalValue()[...,0] xi, = points dx = self.dx i = numerix.array(numerix.rint(((xi - x0) / dx)), 'l') i[i < 0] = 0 i[i > nx - 1] = nx - 1 return i def _test(self): """
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
class Tri2D(Mesh2D): """ This class creates a mesh made out of triangles. It does this by starting with a standard Cartesian mesh (`Grid2D`) and dividing each cell in that mesh (hereafter referred to as a 'box') into four equal parts with the dividing lines being the diagonals. """ def __init__(self, dx = 1., dy = 1., nx = 1, ny = 1): """ 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.nx = nx self.ny = ny self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale self.dy = PhysicalField(value = dy) if self.dy.getUnit().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 vertices = self._createVertices() faces = self._createFaces() cells = self._createCells() cells = numerix.sort(cells, axis=0) Mesh2D.__init__(self, vertices, faces, cells) self.setScale(value = scale) def _createVertices(self): x = numerix.arange(self.nx + 1) * self.dx y = numerix.arange(self.ny + 1) * self.dy x = numerix.resize(x, (self.numberOfCornerVertices,)) y = numerix.repeat(y, self.nx + 1) boxCorners = numerix.array((x, y)) x = numerix.arange(0.5, self.nx + 0.5) * self.dx y = numerix.arange(0.5, self.ny + 0.5) * self.dy x = numerix.resize(x, (self.numberOfCenterVertices,)) y = numerix.repeat(y, self.nx) boxCenters = numerix.array((x, y)) return numerix.concatenate((boxCorners, boxCenters), axis=1) def _createFaces(self): """ v1, v2 refer to the cells. Horizontel faces are first """ v1 = numerix.arange(self.numberOfCornerVertices) v2 = v1 + 1 horizontalFaces = vector.prune(numerix.array((v1, v2)), self.nx + 1, self.nx, axis=1) v1 = numerix.arange(self.numberOfCornerVertices - (self.nx + 1)) v2 = v1 + self.nx + 1 verticalFaces = numerix.array((v1, v2)) ## reverse some of the face orientations to obtain the correct normals tmp = horizontalFaces.copy() horizontalFaces[0, :self.nx] = tmp[1, :self.nx] horizontalFaces[1, :self.nx] = tmp[0, :self.nx] tmp = verticalFaces.copy() verticalFaces[0] = tmp[1] verticalFaces[1] = tmp[0] verticalFaces[0, ::(self.nx + 1)] = tmp[0, ::(self.nx + 1)] verticalFaces[1, ::(self.nx + 1)] = tmp[1, ::(self.nx + 1)] ## do the center ones now cellCenters = numerix.arange(self.numberOfCornerVertices, self.numberOfTotalVertices) lowerLefts = vector.prune(numerix.arange(self.numberOfCornerVertices - (self.nx + 1)), self.nx + 1, self.nx) lowerRights = lowerLefts + 1 upperLefts = lowerLefts + self.nx + 1 upperRights = lowerLefts + self.nx + 2 lowerLeftFaces = numerix.array((cellCenters, lowerLefts)) lowerRightFaces = numerix.array((lowerRights, cellCenters)) upperLeftFaces = numerix.array((cellCenters, upperLefts)) upperRightFaces = numerix.array((cellCenters, upperRights)) return numerix.concatenate((horizontalFaces, verticalFaces, lowerLeftFaces, lowerRightFaces, upperLeftFaces, upperRightFaces), axis=1) def _createCells(self): """ cells = (f1, f2, f3, f4) going anticlockwise. f1 etc. refer to the faces """ self.numberOfHorizontalFaces = self.nx * (self.ny + 1) self.numberOfVerticalFaces = self.ny * (self.nx + 1) self.numberOfEachDiagonalFaces = self.nx * self.ny bottomFaces = numerix.arange(0, self.numberOfHorizontalFaces - self.nx) topFaces = numerix.arange(self.nx, self.numberOfHorizontalFaces) leftFaces = vector.prune(numerix.arange(self.numberOfHorizontalFaces, self.numberOfHorizontalFaces + self.numberOfVerticalFaces), self.nx + 1, self.nx) rightFaces = vector.prune(numerix.arange(self.numberOfHorizontalFaces, self.numberOfHorizontalFaces + self.numberOfVerticalFaces), self.nx + 1, 0) lowerLeftDiagonalFaces = numerix.arange(self.numberOfHorizontalFaces + self.numberOfVerticalFaces, self.numberOfHorizontalFaces + self.numberOfVerticalFaces + self.numberOfEachDiagonalFaces) lowerRightDiagonalFaces = lowerLeftDiagonalFaces + self.numberOfEachDiagonalFaces upperLeftDiagonalFaces = lowerRightDiagonalFaces + self.numberOfEachDiagonalFaces upperRightDiagonalFaces = upperLeftDiagonalFaces + self.numberOfEachDiagonalFaces ##faces in arrays, now get the cells bottomOfBoxCells = numerix.array([bottomFaces, lowerRightDiagonalFaces, lowerLeftDiagonalFaces]) rightOfBoxCells = numerix.array([rightFaces, upperRightDiagonalFaces, lowerRightDiagonalFaces]) topOfBoxCells = numerix.array([topFaces, upperLeftDiagonalFaces, upperRightDiagonalFaces]) leftOfBoxCells = numerix.array([leftFaces, lowerLeftDiagonalFaces, upperLeftDiagonalFaces]) return numerix.concatenate((rightOfBoxCells, topOfBoxCells, leftOfBoxCells, bottomOfBoxCells), axis=1) def getScale(self): return self.scale['length'] def getPhysicalShape(self): """Return physical dimensions of Grid2D. """ return PhysicalField(value = (self.nx * self.dx * self.getScale(), self.ny * self.dy * self.getScale())) def _getMeshSpacing(self): return numerix.array((self.dx,self.dy))[...,numerix.newaxis] def getShape(self): return (self.nx, self.ny) def _isOrthogonal(self): return True ## pickling def __getstate__(self): return { 'dx' : self.dx * self.scale['length'], 'dy' : self.dy * self.scale['length'], 'nx' : self.nx, 'ny' : self.ny } def __setstate__(self, dict): self.__init__(**dict) def _test(self): """
class Grid3D(Mesh): """ 3D rectangular-prism Mesh X axis runs from left to right. Y axis runs from bottom to top. Z axis runs from front to back. Numbering System: Vertices: Numbered in the usual way. X coordinate changes most quickly, then Y, then Z. Cells: Same numbering system as vertices. Faces: XY faces numbered first, then XZ faces, then YZ faces. Within each subcategory, it is numbered in the usual way. """ def __init__(self, dx = 1., dy = 1., dz = 1., nx = None, ny = None, nz = None, overlap=2, communicator=parallel): self.args = { 'dx': dx, 'dy': dy, 'dz' :dz, 'nx': nx, 'ny': ny, 'nz': nz, 'overlap': overlap, 'communicator': communicator } self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale nx = self._calcNumPts(d = self.dx, n = nx, axis = "x") self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale ny = self._calcNumPts(d = self.dy, n = ny, axis = "y") self.dz = PhysicalField(value = dz) if self.dz.getUnit().isDimensionless(): self.dz = dz else: self.dz /= scale nz = self._calcNumPts(d = self.dz, n = nz, axis = "z") (self.nx, self.ny, self.nz, self.overlap, self.offset) = self._calcParallelGridInfo(nx, ny, nz, overlap, communicator) if numerix.getShape(self.dx) is not (): Xoffset = numerix.sum(self.dx[0:self.offset[0]]) self.dx = self.dx[self.offset[0]:self.offset[0] + self.nx] else: Xoffset = 0 if numerix.getShape(self.dy) is not (): Yoffset = numerix.sum(self.dy[0:self.offset[1]]) self.dy = self.dy[self.offset[1]:self.offset[1] + self.ny] else: Yoffset = 0 if numerix.getShape(self.dy) is not (): Zoffset = numerix.sum(self.dz[0:self.offset[2]]) self.dz = self.dz[self.offset[2]:self.offset[2] + self.nz] else: Zoffset = 0 if self.nx == 0 or self.ny == 0 or self.nz == 0: self.nx = 0 self.ny = 0 self.nz = 0 if self.nx == 0 or self.ny == 0 or self.nz == 0: self.numberOfHorizontalRows = 0 self.numberOfVerticalColumns = 0 self.numberOfLayersDeep = 0 else: self.numberOfHorizontalRows = (self.ny + 1) self.numberOfVerticalColumns = (self.nx + 1) self.numberOfLayersDeep = (self.nz + 1) self.numberOfVertices = self.numberOfHorizontalRows * self.numberOfVerticalColumns * self.numberOfLayersDeep vertices = self._createVertices() + ((Xoffset,), (Yoffset,), (Zoffset,)) faces = self._createFaces() cells = self._createCells() Mesh.__init__(self, vertices, faces, cells) self.setScale(value = scale) def _calcParallelGridInfo(self, nx, ny, nz, overlap, communicator): procID = communicator.procID Nproc = communicator.Nproc overlap = min(overlap, nz) cellsPerNode = max(int(nz / Nproc), overlap) occupiedNodes = min(int(nz / (cellsPerNode or 1)), Nproc) overlap = { 'left': 0, 'right': 0, 'bottom' : 0, 'top' : 0, 'front': overlap * (procID > 0) * (procID < occupiedNodes), 'back': overlap * (procID < occupiedNodes - 1) } offset = (0, 0, min(procID, occupiedNodes-1) * cellsPerNode - overlap['front']) local_nx = nx local_ny = ny local_nz = cellsPerNode * (procID < occupiedNodes) if procID == occupiedNodes - 1: local_nz += (nz - cellsPerNode * occupiedNodes) local_nz = local_nz + overlap['front'] + overlap['back'] self.globalNumberOfCells = nx * ny * nz self.globalNumberOfFaces = nx * nz * (ny + 1) + ny * nz * (nx + 1) + nx * ny * (nz + 1) return local_nx, local_ny, local_nz, overlap, offset def __repr__(self): dnstr = [] for d, n in [("dx", "nx"), ("dy", "ny"), ("dz", "nz")]: dnstr.append(d + "=" + str(self.args[d])) if self.args[n] is not None: dnstr.append(n + "=" + str(self.args[n])) return "%s(%s)" % (self.__class__.__name__, ", ".join(dnstr)) def _createVertices(self): x = self._calcVertexCoordinates(self.dx, self.nx) x = numerix.resize(x, (self.numberOfVertices,)) y = self._calcVertexCoordinates(self.dy, self.ny) y = numerix.repeat(y, self.numberOfVerticalColumns) y = numerix.resize(y, (self.numberOfVertices,)) z = self._calcVertexCoordinates(self.dz, self.nz) z = numerix.repeat(z, self.numberOfHorizontalRows * self.numberOfVerticalColumns) z = numerix.resize(z, (self.numberOfVertices,)) return numerix.array((x, y, z)) def _createFaces(self): """ XY faces are first, then XZ faces, then YZ faces """ ## do the XY faces v1 = numerix.arange((self.nx + 1) * (self.ny)) v1 = vector.prune(v1, self.nx + 1, self.nx) v1 = self._repeatWithOffset(v1, (self.nx + 1) * (self.ny + 1), self.nz + 1) v2 = v1 + 1 v3 = v1 + (self.nx + 2) v4 = v1 + (self.nx + 1) XYFaces = numerix.array((v1, v2, v3, v4)) ## do the XZ faces v1 = numerix.arange((self.nx + 1) * (self.ny + 1)) v1 = vector.prune(v1, self.nx + 1, self.nx) v1 = self._repeatWithOffset(v1, (self.nx + 1) * (self.ny + 1), self.nz) v2 = v1 + 1 v3 = v1 + ((self.nx + 1)*(self.ny + 1)) + 1 v4 = v1 + ((self.nx + 1)*(self.ny + 1)) XZFaces = numerix.array((v1, v2, v3, v4)) ## do the YZ faces v1 = numerix.arange((self.nx + 1) * self.ny) v1 = self._repeatWithOffset(v1, (self.nx + 1) * (self.ny + 1), self.nz) v2 = v1 + (self.nx + 1) v3 = v1 + ((self.nx + 1)*(self.ny + 1)) + (self.nx + 1) v4 = v1 + ((self.nx + 1)*(self.ny + 1)) YZFaces = numerix.array((v1, v2, v3, v4)) ## reverse some of the face orientations to obtain the correct normals ##tmp = horizontalFaces.copy() ##horizontalFaces[:self.nx, 0] = tmp[:self.nx, 1] ##horizontalFaces[:self.nx, 1] = tmp[:self.nx, 0] ##tmp = verticalFaces.copy() ##verticalFaces[:, 0] = tmp[:, 1] ##verticalFaces[:, 1] = tmp[:, 0] ##verticalFaces[::(self.nx + 1), 0] = tmp[::(self.nx + 1), 0] ##verticalFaces[::(self.nx + 1), 1] = tmp[::(self.nx + 1), 1] self.numberOfXYFaces = (self.nx * self.ny * (self.nz + 1)) self.numberOfXZFaces = (self.nx * (self.ny + 1) * self.nz) self.numberOfYZFaces = ((self.nx + 1) * self.ny * self.nz) self.numberOfFaces = self.numberOfXYFaces + self.numberOfXZFaces + self.numberOfYZFaces return numerix.concatenate((XYFaces, XZFaces, YZFaces), axis=1) def _createCells(self): """ cells = (front face, back face, left face, right face, bottom face, top face) front and back faces are YZ faces left and right faces are XZ faces top and bottom faces are XY faces """ self.numberOfCells = self.nx * self.ny * self.nz ## front and back faces frontFaces = numerix.arange(self.numberOfYZFaces) frontFaces = vector.prune(frontFaces, self.nx + 1, self.nx) frontFaces = frontFaces + self.numberOfXYFaces + self.numberOfXZFaces backFaces = frontFaces + 1 ## left and right faces leftFaces = numerix.arange(self.nx * self.ny) leftFaces = self._repeatWithOffset(leftFaces, self.nx * (self.ny + 1), self.nz) leftFaces = numerix.ravel(leftFaces) leftFaces = leftFaces + self.numberOfXYFaces rightFaces = leftFaces + self.nx ## bottom and top faces bottomFaces = numerix.arange(self.nx * self.ny * self.nz) topFaces = bottomFaces + (self.nx * self.ny) return numerix.array((frontFaces, backFaces, leftFaces, rightFaces, bottomFaces, topFaces)) def getScale(self): return self.scale['length'] def getPhysicalShape(self): """Return physical dimensions of Grid3D. """ return PhysicalField(value = (self.nx * self.dx * self.getScale(), self.ny * self.dy * self.getScale(), self.nz * self.dz * self.getScale())) def _getMeshSpacing(self): return numerix.array((self.dx, self.dy, self.dz))[...,numerix.newaxis] def getShape(self): return (self.nx, self.ny, self.nz) def _repeatWithOffset(self, array, offset, reps): a = numerix.fromfunction(lambda rnum, x: array + (offset * rnum), (reps, numerix.size(array))).astype('l') return numerix.ravel(a) ## The following method is broken when dx, dy or dz are not scalar. Simpler to use the generic ## _calcFaceAreas rather than do the required type checking, resizing and outer product. ## ## def _calcFaceAreas(self): ## XYFaceAreas = numerix.ones(self.numberOfXYFaces) ## XYFaceAreas = XYFaceAreas * self.dx * self.dy ## XZFaceAreas = numerix.ones(self.numberOfXZFaces) ## XZFaceAreas = XZFaceAreas * self.dx * self.dz ## YZFaceAreas = numerix.ones(self.numberOfYZFaces) ## YZFaceAreas = YZFaceAreas * self.dy * self.dz ## self.faceAreas = numerix.concatenate((XYFaceAreas, XZFaceAreas, YZFaceAreas)) def _calcFaceNormals(self): XYFaceNormals = numerix.zeros((3, self.numberOfXYFaces)) XYFaceNormals[2, (self.nx * self.ny):] = 1 XYFaceNormals[2, :(self.nx * self.ny)] = -1 XZFaceNormals = numerix.zeros((3, self.numberOfXZFaces)) xzd = numerix.arange(self.numberOfXZFaces) xzd = xzd % (self.nx * (self.ny + 1)) xzd = (xzd < self.nx) xzd = 1 - (2 * xzd) XZFaceNormals[1, :] = xzd YZFaceNormals = numerix.zeros((3, self.numberOfYZFaces)) YZFaceNormals[0, :] = 1 YZFaceNormals[0, ::self.nx + 1] = -1 self.faceNormals = numerix.concatenate((XYFaceNormals, XZFaceNormals, YZFaceNormals), axis=-1) def _calcFaceTangents(self): ## need to see whether order matters. faceTangents1 = numerix.zeros((3, self.numberOfFaces), 'd') faceTangents2 = numerix.zeros((3, self.numberOfFaces), 'd') ## XY faces faceTangents1[0, :self.numberOfXYFaces] = 1. faceTangents2[1, :self.numberOfXYFaces] = 1. ## XZ faces faceTangents1[0, self.numberOfXYFaces:self.numberOfXYFaces + self.numberOfXZFaces] = 1. faceTangents2[2, self.numberOfXYFaces:self.numberOfXYFaces + self.numberOfXZFaces] = 1. ## YZ faces faceTangents1[1, self.numberOfXYFaces + self.numberOfXZFaces:] = 1. faceTangents2[2, self.numberOfXYFaces + self.numberOfXZFaces:] = 1. self.faceTangents1 = faceTangents1 self.faceTangents2 = faceTangents2 def _calcHigherOrderScalings(self): self.scale['area'] = self.scale['length']**2 self.scale['volume'] = self.scale['length']**3 def _isOrthogonal(self): return True def _getGlobalNonOverlappingCellIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Does not include the IDs of boundary cells. .. note:: Trivial except for parallel meshes """ return numerix.arange((self.offset[2] + self.overlap['front']) * self.nx * self.ny, (self.offset[2] + self.nz - self.overlap['back']) * self.nx * self.ny) def _getGlobalOverlappingCellIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Includes the IDs of boundary cells. .. note:: Trivial except for parallel meshes """ return numerix.arange(self.offset[2] * self.nx * self.ny, (self.offset[2] + self.nz) * self.nx * self.ny) def _getLocalNonOverlappingCellIDs(self): """ Return the IDs of the local mesh in isolation. Does not include the IDs of boundary cells. .. note:: Trivial except for parallel meshes """ return numerix.arange(self.overlap['front'] * self.nx * self.ny, (self.nz - self.overlap['back']) * self.nx * self.ny) def _getLocalOverlappingCellIDs(self): """ Return the IDs of the local mesh in isolation. Includes the IDs of boundary cells. .. note:: Trivial except for parallel meshes """ return numerix.arange(0, self.ny * self.nx * self.nz) ## pickling def __getstate__(self): return self.args def __setstate__(self, dict): self.__init__(**dict) def _test(self): """
def _calcValue(self): P = self.P.numericValue alpha = numerix.where(P > 0., 1., 0.) return PhysicalField(value=alpha)
def __init__(self, dx = 1., dy = 1., dz = 1., nx = None, ny = None, nz = None, overlap=2, communicator=parallel): self.args = { 'dx': dx, 'dy': dy, 'dz' :dz, 'nx': nx, 'ny': ny, 'nz': nz, 'overlap': overlap, 'communicator': communicator } self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale nx = self._calcNumPts(d = self.dx, n = nx, axis = "x") self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale ny = self._calcNumPts(d = self.dy, n = ny, axis = "y") self.dz = PhysicalField(value = dz) if self.dz.getUnit().isDimensionless(): self.dz = dz else: self.dz /= scale nz = self._calcNumPts(d = self.dz, n = nz, axis = "z") (self.nx, self.ny, self.nz, self.overlap, self.offset) = self._calcParallelGridInfo(nx, ny, nz, overlap, communicator) if numerix.getShape(self.dx) is not (): Xoffset = numerix.sum(self.dx[0:self.offset[0]]) self.dx = self.dx[self.offset[0]:self.offset[0] + self.nx] else: Xoffset = 0 if numerix.getShape(self.dy) is not (): Yoffset = numerix.sum(self.dy[0:self.offset[1]]) self.dy = self.dy[self.offset[1]:self.offset[1] + self.ny] else: Yoffset = 0 if numerix.getShape(self.dy) is not (): Zoffset = numerix.sum(self.dz[0:self.offset[2]]) self.dz = self.dz[self.offset[2]:self.offset[2] + self.nz] else: Zoffset = 0 if self.nx == 0 or self.ny == 0 or self.nz == 0: self.nx = 0 self.ny = 0 self.nz = 0 if self.nx == 0 or self.ny == 0 or self.nz == 0: self.numberOfHorizontalRows = 0 self.numberOfVerticalColumns = 0 self.numberOfLayersDeep = 0 else: self.numberOfHorizontalRows = (self.ny + 1) self.numberOfVerticalColumns = (self.nx + 1) self.numberOfLayersDeep = (self.nz + 1) self.numberOfVertices = self.numberOfHorizontalRows * self.numberOfVerticalColumns * self.numberOfLayersDeep vertices = self._createVertices() + ((Xoffset,), (Yoffset,), (Zoffset,)) faces = self._createFaces() cells = self._createCells() Mesh.__init__(self, vertices, faces, cells) self.setScale(value = scale)
class Grid1D(Mesh1D): """ Creates a 1D grid mesh. >>> mesh = Grid1D(nx = 3) >>> print mesh.getCellCenters() [[ 0.5 1.5 2.5]] >>> mesh = Grid1D(dx = (1, 2, 3)) >>> print mesh.getCellCenters() [[ 0.5 2. 4.5]] >>> mesh = Grid1D(nx = 2, dx = (1, 2, 3)) Traceback (most recent call last): ... IndexError: nx != len(dx) """ def __init__(self, dx=1., nx=None, overlap=2, communicator=parallel): self.args = { 'dx': dx, 'nx': nx, 'overlap': overlap } from fipy.tools.dimensions.physicalField import PhysicalField self.dx = PhysicalField(value=dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = self._calcNumPts(d=self.dx, n=nx) (self.nx, self.overlap, self.offset) = self._calcParallelGridInfo(nx, overlap, communicator) if numerix.getShape(self.dx) is not (): Xoffset = numerix.sum(self.dx[0:self.offset]) self.dx = self.dx[self.offset:self.offset + self.nx] else: Xoffset = self.dx * self.offset vertices = self._createVertices() + ((Xoffset,),) self.numberOfVertices = len(vertices[0]) faces = self._createFaces() self.numberOfFaces = len(faces[0]) cells = self._createCells() Mesh1D.__init__(self, vertices, faces, cells, communicator=communicator) self.setScale(value = scale) def _getOverlap(self, overlap, procID, occupiedNodes): return {'left': overlap * (procID > 0) * (procID < occupiedNodes), 'right': overlap * (procID < occupiedNodes - 1)} def _calcParallelGridInfo(self, nx, overlap, communicator): procID = communicator.procID Nproc = communicator.Nproc overlap = min(overlap, nx) cellsPerNode = max(int(nx / Nproc), overlap) occupiedNodes = min(int(nx / (cellsPerNode or 1)), Nproc) overlap = self._getOverlap(overlap, procID, occupiedNodes) offset = min(procID, occupiedNodes-1) * cellsPerNode - overlap['left'] local_nx = cellsPerNode * (procID < occupiedNodes) if procID == occupiedNodes - 1: local_nx += (nx - cellsPerNode * occupiedNodes) local_nx = local_nx + overlap['left'] + overlap['right'] self.globalNumberOfCells = nx self.globalNumberOfFaces = nx + 1 return local_nx, overlap, offset def __repr__(self): dnstr = [] for d, n in [("dx", "nx")]: dnstr.append(d + "=" + str(self.args[d])) if self.args[n] is not None: dnstr.append(n + "=" + str(self.args[n])) return "%s(%s)" % (self.__class__.__name__, ", ".join(dnstr)) def _createVertices(self): x = self._calcVertexCoordinates(self.dx, self.nx) return x[numerix.newaxis,...] def _createFaces(self): if self.numberOfVertices == 1: return numerix.arange(0)[numerix.newaxis, ...] else: return numerix.arange(self.numberOfVertices)[numerix.newaxis, ...] def _createCells(self): """ cells = (f1, f2) going left to right. f1 etc. refer to the faces """ f1 = numerix.arange(self.nx) f2 = f1 + 1 return numerix.array((f1, f2)) def getDim(self): return 1 def getScale(self): return self.scale['length'] def getPhysicalShape(self): """Return physical dimensions of Grid1D. """ from fipy.tools.dimensions.physicalField import PhysicalField return PhysicalField(value = (self.nx * self.dx * self.getScale(),)) def _getMeshSpacing(self): return numerix.array((self.dx,))[...,numerix.newaxis] def getShape(self): return (self.nx,) def _getGlobalNonOverlappingCellIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Does not include the IDs of boundary cells. E.g., would return [0, 1] for mesh A A B ------------------ | 0 | 1 || 2 | 3 | ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(self.offset + self.overlap['left'], self.offset + self.nx - self.overlap['right']) def _getGlobalOverlappingCellIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Includes the IDs of boundary cells. E.g., would return [0, 1, 2] for mesh A A B ------------------ | 0 | 1 || 2 | 3 | ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(self.offset, self.offset + self.nx) def _getLocalNonOverlappingCellIDs(self): """ Return the IDs of the local mesh in isolation. Does not include the IDs of boundary cells. E.g., would return [0, 1] for mesh A A B ------------------ | 0 | 1 || 1 | 2 | ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(self.overlap['left'], self.nx - self.overlap['right']) def _getLocalOverlappingCellIDs(self): """ Return the IDs of the local mesh in isolation. Includes the IDs of boundary cells. E.g., would return [0, 1, 2] for mesh A A B ------------------ | 0 | 1 || 2 | | ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(0, self.nx) def _getGlobalNonOverlappingFaceIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Does not include the IDs of boundary cells. E.g., would return [0, 1, 2] for mesh A A || B ------------------ 0 1 2 3 4 ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(self.offset + self.overlap['left'], self.offset + self.numberOfFaces - self.overlap['right']) def _getGlobalOverlappingFaceIDs(self): """ Return the IDs of the local mesh in the context of the global parallel mesh. Includes the IDs of boundary cells. E.g., would return [0, 1, 2, 3] for mesh A A || B ------------------ 0 1 2 3 4 ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(self.offset, self.offset + self.numberOfFaces) def _getLocalNonOverlappingFaceIDs(self): """ Return the IDs of the local mesh in isolation. Does not include the IDs of boundary cells. E.g., would return [0, 1, 2] for mesh A A || B ------------------ 0 1 2/1 2 3 ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(self.overlap['left'], self.numberOfFaces - self.overlap['right']) def _getLocalOverlappingFaceIDs(self): """ Return the IDs of the local mesh in isolation. Includes the IDs of boundary cells. E.g., would return [0, 1, 2, 3] for mesh A A || B ------------------ 0 1 2 3 | ------------------ .. note:: Trivial except for parallel meshes """ return numerix.arange(0, self.numberOfFaces) ## pickling def __getstate__(self): return self.args def __setstate__(self, dict): self.__init__(**dict) def _test(self): """
class UniformGrid1D(Grid1D): """ Creates a 1D grid mesh. >>> mesh = UniformGrid1D(nx = 3) >>> print mesh.getCellCenters() [[ 0.5 1.5 2.5]] """ def __init__(self, dx=1.0, nx=1, origin=(0,), overlap=2, communicator=parallel): origin = numerix.array(origin) self.args = {"dx": dx, "nx": nx, "origin": origin, "overlap": overlap} self.dim = 1 self.dx = PhysicalField(value=dx) scale = PhysicalField(value=1, unit=self.dx.getUnit()) self.dx /= scale nx = int(nx) (self.nx, self.overlap, self.offset) = self._calcParallelGridInfo(nx, overlap, communicator) self.origin = PhysicalField(value=origin) self.origin /= scale self.origin += self.offset * self.dx self.numberOfVertices = self.nx + 1 if self.nx == 0: self.numberOfFaces = 0 else: self.numberOfFaces = self.nx + 1 self.numberOfCells = self.nx self.exteriorFaces = self.getFacesLeft() | self.getFacesRight() self.scale = {"length": 1.0, "area": 1.0, "volume": 1.0} self.setScale(value=scale) self.communicator = communicator def _translate(self, vector): return UniformGrid1D( dx=self.dx, nx=self.args["nx"], origin=self.args["origin"] + numerix.array(vector), overlap=self.args["overlap"], ) def __mul__(self, factor): return UniformGrid1D( dx=self.dx * factor, nx=self.args["nx"], origin=self.args["origin"] * factor, overlap=self.args["overlap"] ) def _getConcatenableMesh(self): from fipy.meshes.numMesh.mesh1D import Mesh1D return Mesh1D( vertexCoords=self.getVertexCoords(), faceVertexIDs=self._createFaces(), cellFaceIDs=self._createCells() ) ## get topology methods ## from common/mesh def _getCellFaceIDs(self): return MA.array(self._createCells()) def getInteriorFaces(self): from fipy.variables.faceVariable import FaceVariable interiorFaces = FaceVariable(mesh=self, value=False) interiorFaces[numerix.arange(self.numberOfFaces - 2) + 1] = True return interiorFaces def _getCellFaceOrientations(self): orientations = numerix.ones((2, self.numberOfCells)) if self.numberOfCells > 0: orientations[0] *= -1 orientations[0, 0] = 1 return orientations def _getAdjacentCellIDs(self): c1 = numerix.arange(self.numberOfFaces) ids = numerix.array((c1 - 1, c1)) if self.numberOfFaces > 0: ids[0, 0] = ids[1, 0] ids[1, -1] = ids[0, -1] return ids[0], ids[1] def _getCellToCellIDs(self): c1 = numerix.arange(self.numberOfCells) ids = MA.array((c1 - 1, c1 + 1)) if self.numberOfCells > 0: ids[0, 0] = MA.masked ids[1, -1] = MA.masked return ids def _getCellToCellIDsFilled(self): ids = self._getCellToCellIDs().filled() if self.numberOfCells > 0: ids[0, 0] = 0 ids[1, -1] = self.numberOfCells - 1 return ids def _getMaxFacesPerCell(self): return 2 ## from numMesh/mesh def getVertexCoords(self): return self.getFaceCenters() def getFaceCellIDs(self): c1 = numerix.arange(self.numberOfFaces) ids = MA.array((c1 - 1, c1)) if self.numberOfFaces > 0: ids[0, 0] = ids[1, 0] ids[1, 0] = MA.masked ids[1, -1] = MA.masked return ids ## get geometry methods ## from common/mesh def _getFaceAreas(self): return numerix.ones(self.numberOfFaces, "d") def _getFaceNormals(self): faceNormals = numerix.ones((1, self.numberOfFaces), "d") # The left-most face has neighboring cells None and the left-most cell. # We must reverse the normal to make fluxes work correctly. if self.numberOfFaces > 0: faceNormals[..., 0] *= -1 return faceNormals def _getFaceCellToCellNormals(self): return self._getFaceNormals() def getCellVolumes(self): return numerix.ones(self.numberOfCells, "d") * self.dx def _getCellCenters(self): return ((numerix.arange(self.numberOfCells)[numerix.NewAxis, ...] + 0.5) * self.dx + self.origin) * self.scale[ "length" ] def _getCellDistances(self): distances = numerix.ones(self.numberOfFaces, "d") distances *= self.dx if len(distances) > 0: distances[0] = self.dx / 2.0 distances[-1] = self.dx / 2.0 return distances def _getFaceToCellDistanceRatio(self): distances = numerix.ones(self.numberOfFaces, "d") distances *= 0.5 if len(distances) > 0: distances[0] = 1 distances[-1] = 1 return distances def _getOrientedAreaProjections(self): return self._getAreaProjections() def _getAreaProjections(self): return self._getFaceNormals() def _getOrientedFaceNormals(self): return self._getFaceNormals() def _getFaceTangents1(self): return numerix.zeros(self.numberOfFaces, "d")[numerix.NewAxis, ...] def _getFaceTangents2(self): return numerix.zeros(self.numberOfFaces, "d")[numerix.NewAxis, ...] def _getFaceAspectRatios(self): return 1.0 / self._getCellDistances() def _getCellToCellDistances(self): distances = MA.zeros((2, self.numberOfCells), "d") distances[:] = self.dx if self.numberOfCells > 0: distances[0, 0] = self.dx / 2.0 distances[1, -1] = self.dx / 2.0 return distances def _getCellNormals(self): normals = numerix.ones((1, 2, self.numberOfCells), "d") if self.numberOfCells > 0: normals[:, 0] = -1 return normals def _getCellAreas(self): return numerix.ones((2, self.numberOfCells), "d") def _getCellAreaProjections(self): return MA.array(self._getCellNormals()) ## from numMesh/mesh def getFaceCenters(self): return numerix.arange(self.numberOfFaces)[numerix.NewAxis, ...] * self.dx + self.origin def _getCellVertexIDs(self): c1 = numerix.arange(self.numberOfCells) return numerix.array((c1 + 1, c1)) ## scaling def _calcScaledGeometry(self): pass def _getNearestCellID(self, points): """ Test cases >>> from fipy import * >>> m = Grid1D(nx=3) >>> print m._getNearestCellID(([0., .9, 3.],)) [0 0 2] >>> print m._getNearestCellID(([1.1],)) [1] >>> m0 = Grid1D(nx=2, dx=1.) >>> m1 = Grid1D(nx=4, dx=.5) >>> print m0._getNearestCellID(m1.getCellCenters().getGlobalValue()) [0 0 1 1] """ nx = self.globalNumberOfCells if nx == 0: return numerix.arange(0) x0, = self.getCellCenters().getGlobalValue()[..., 0] xi, = points dx = self.dx i = numerix.array(numerix.rint(((xi - x0) / dx)), "l") i[i < 0] = 0 i[i > nx - 1] = nx - 1 return i def _test(self): """
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
class SkewedGrid2D(Mesh2D): """ Creates a 2D grid mesh with horizontal faces numbered first and then vertical faces. The points are skewed by a random amount (between `rand` and `-rand`) in the X and Y directions. """ def __init__(self, dx = 1., dy = 1., nx = None, ny = 1, rand = 0): self.nx = nx self.ny = ny self.dx = PhysicalField(value = dx) scale = PhysicalField(value = 1, unit = self.dx.getUnit()) self.dx /= scale self.dy = PhysicalField(value = dy) if self.dy.getUnit().isDimensionless(): self.dy = dy else: self.dy /= scale from fipy import Grid2D self.grid = Grid2D(nx=nx, ny=ny, dx=dx, dy=dy) self.numberOfVertices = self.grid._getNumberOfVertices() vertices = self.grid.getVertexCoords() 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._getFaceVertexIDs() cells = self.grid._getCellFaceIDs() Mesh2D.__init__(self, changedVertices, faces, cells) self.setScale(value = scale) def getScale(self): return self.scale['length'] def getPhysicalShape(self): """Return physical dimensions of Grid2D. """ return PhysicalField(value = (self.nx * self.dx * self.getScale(), self.ny * self.dy * self.getScale())) def _getMeshSpacing(self): return numerix.array((self.dx,self.dy))[...,numerix.newaxis] def getShape(self): return (self.nx, self.ny) ## pickling def __getstate__(self): return { 'dx' : self.dx * self.scale['length'], 'dy' : self.dy * self.scale['length'], 'nx' : self.nx, 'ny' : self.ny } def __setstate__(self, dict): self.__init__(**dict)