import numpy as np from utils import mkvc class BaseMesh(object): """BaseMesh does all the counting you don't want to do. x0 origin ndarray (dim, ) n number of cells ndarray (dim, ) dim dimension of mesh int 1, 2, or 3 nCx num cells in x dir int nCy num cells in y dir int nCz num cells in z dir int nC total number of cells int nNx num nodes in x dir int nNy num nodes in y dir int nNz num nodes in z dir int nN total number of nodes int nEx num edges in x dir ndarray [nEx_x, nEx_y, nEx_z] nEy num edges in y dir ndarray [nEy_x, nEy_y, nEy_z] nEz num edges in z dir ndarray [nEz_x, nEz_y, nEz_z] nE total number of edges ndarray (dim, ) nFx num faces in x dir ndarray [nFx_x, nFx_y, nFx_z] nFy num faces in y dir ndarray [nFy_x, nFy_y, nFy_z] nFz num faces in z dir ndarray [nFz_x, nFz_y, nFz_z] nF total number of faces ndarray (dim, ) """ def __init__(self, n, x0=None): # Check inputs if x0 is None: x0 = np.zeros(len(n)) if not len(n) == len(x0): raise Exception("Dimension mismatch. x0 != len(n)") if len(n) > 3: raise Exception("Dimensions higher than 3 are not supported.") # Ensure x0 & n are 1D vectors self._n = np.array(n, dtype=int).ravel() self._x0 = np.array(x0).ravel() self._dim = len(n) def x0(): doc = "Origin of the mesh" fget = lambda self: self._x0 return locals() x0 = property(**x0()) def r(self, x, xType='CC', outType='CC', format='V'): """ Mesh.r is a quick reshape command that will do the best it can at giving you what you want. For example, you have a face variable, and you want the x component of it reshaped to a 3D matrix. Mesh.r can fulfil your dreams... mesh.r(V, 'F', 'Fx', 'M') | | | { How: 'M' or ['V'] for a matrix (ndgrid style) or a vector (n x dim) } | | { What you want: ['CC'], 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez' } | { What is it: ['CC'], 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez' } { The input: as a list or ndarray } For example: Xex, Yex, Zex = r(mesh.gridEx, 'Ex', 'Ex', 'M') # Separates each component of the Ex grid into 3 matrices XedgeVector = r(edgeVector, 'E', 'Ex', 'V') # Given an edge vector, this will return just the part on the x edges as a vector eX, eY, eZ = r(edgeVector, 'E', 'E', 'V') # Separates each component of the edgeVector into 3 vectors """ assert (type(x) == list or type(x) == np.ndarray), "x must be either a list or a ndarray" assert xType in ['CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', 'Ez'], "xType must be either 'CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez'" assert outType in ['CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', 'Ez'], "outType must be either 'CC', 'N', 'F', Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez'" assert format in ['M', 'V'], "format must be either 'M' or 'V'" assert outType[:len(xType)] == xType, "You cannot change types when reshaping." assert xType in outType, 'You cannot change type of components.' if type(x) == list: for i, xi in enumerate(x): assert type(x) == np.ndarray, "x[%i] must be a numpy array" % i assert xi.size == x[0].size, "Number of elements in list must not change." x_array = np.ones((x.size, len(x))) # Unwrap it and put it in a np array for i, xi in enumerate(x): x_array[:, i] = mkvc(xi) x = x_array assert type(x) == np.ndarray, "x must be a numpy array" x = x[:] # make a copy. xTypeIsFExyz = len(xType) > 1 and xType[0] in ['F', 'E'] and xType[1] in ['x', 'y', 'z'] def outKernal(xx, nn): """Returns xx as either a matrix (shape == nn) or a vector.""" if format == 'M': return xx.reshape(nn, order='F') elif format == 'V': return mkvc(xx) def switchKernal(xx): """Switches over the different options.""" if xType in ['CC', 'N']: nn = (self.n) if xType == 'CC' else (self.n+1) assert xx.size == np.prod(nn), "Number of elements must not change." return outKernal(xx, nn) elif xType in ['F', 'E']: # This will only deal with components of fields, not full 'F' or 'E' xx = mkvc(xx) # unwrap it in case it is a matrix nn = self.nF if xType == 'F' else self.nE nn = np.r_[0, nn] nx = [0, 0, 0] nx[0] = self.nFx if xType == 'F' else self.nEx nx[1] = self.nFy if xType == 'F' else self.nEy nx[2] = self.nFz if xType == 'F' else self.nEz for dim, dimName in enumerate(['x', 'y', 'z']): if dimName in outType: assert self.dim > dim, ("Dimensions of mesh not great enough for %s%s", (xType, dimName)) assert xx.size == np.sum(nn), 'Vector is not the right size.' start = np.sum(nn[:dim+1]) end = np.sum(nn[:dim+2]) return outKernal(xx[start:end], nx[dim]) elif xTypeIsFExyz: # This will deal with partial components (x, y or z) lying on edges or faces if 'x' in xType: nn = self.nFx if 'F' in xType else self.nEx elif 'y' in xType: nn = self.nFy if 'F' in xType else self.nEy elif 'z' in xType: nn = self.nFz if 'F' in xType else self.nEz assert xx.size == np.prod(nn), 'Vector is not the right size.' return outKernal(xx, nn) # Check if we are dealing with a vector quantity isVectorQuantity = len(x.shape) == 2 and x.shape[1] == self.dim if outType in ['F', 'E']: assert ~isVectorQuantity, 'Not sure what to do with a vector vector quantity..' outTypeCopy = outType out = () for ii, dirName in enumerate(['x', 'y', 'z'][:self.dim]): outType = outTypeCopy + dirName out += (switchKernal(x),) return out elif isVectorQuantity: out = () for ii in range(x.shape[1]): out += (switchKernal(x[:, ii]),) return out else: return switchKernal(x) def n(): doc = "Number of Cells in each dimension (array of integers)" fget = lambda self: self._n return locals() n = property(**n()) def dim(): doc = "The dimension of the mesh (1, 2, or 3)." fget = lambda self: self._dim return locals() dim = property(**dim()) def nCx(): doc = "Number oc cells in the x direction" fget = lambda self: self.n[0] return locals() nCx = property(**nCx()) def nCy(): doc = "Number of cells in the y direction" def fget(self): if self.dim > 1: return self.n[1] else: return None return locals() nCy = property(**nCy()) def nCz(): doc = "Number of cells in the z direction" def fget(self): if self.dim > 2: return self.n[2] else: return None return locals() nCz = property(**nCz()) def nC(): doc = "Total number of cells" fget = lambda self: np.prod(self.n) return locals() nC = property(**nC()) def nNx(): doc = "Number of nodes in the x-direction" fget = lambda self: self.nCx + 1 return locals() nNx = property(**nNx()) def nNy(): doc = "Number of noes in the y-direction" def fget(self): if self.dim > 1: return self.n[1] + 1 else: return None return locals() nNy = property(**nNy()) def nNz(): doc = "Number of nodes in the z-direction" def fget(self): if self.dim > 2: return self.n[2] + 1 else: return None return locals() nNz = property(**nNz()) def nN(): doc = "Total number of nodes" fget = lambda self: np.prod(self.n + 1) return locals() nN = property(**nN()) def nEx(): doc = "Number of x-edges" fget = lambda self: np.array([x for x in [self.nCx, self.nNy, self.nNz] if not x is None]) return locals() nEx = property(**nEx()) def nEy(): doc = "Number of y-edges" def fget(self): if self.dim > 1: return np.array([x for x in [self.nNx, self.nCy, self.nNz] if not x is None]) else: return None return locals() nEy = property(**nEy()) def nEz(): doc = "Number of z-edges" def fget(self): if self.dim > 2: return np.array([x for x in [self.nNx, self.nNy, self.nCz] if not x is None]) else: return None return locals() nEz = property(**nEz()) def nE(): doc = "Total number of edges" fget = lambda self: np.array([np.prod(x) for x in [self.nEx, self.nEy, self.nEz] if not x is None]) return locals() nE = property(**nE()) def nFx(): doc = "Number of x-faces" fget = lambda self: np.array([x for x in [self.nNx, self.nCy, self.nCz] if not x is None]) return locals() nFx = property(**nFx()) def nFy(): doc = "Number of y-faces" def fget(self): if self.dim > 1: return np.array([x for x in [self.nCx, self.nNy, self.nCz] if not x is None]) else: return None return locals() nFy = property(**nFy()) def nFz(): doc = "Number of z-faces" def fget(self): if self.dim > 2: return np.array([x for x in [self.nCx, self.nCy, self.nNz] if not x is None]) else: return None return locals() nFz = property(**nFz()) def nF(): doc = "Total number of faces in each dimension" fget = lambda self: np.array([np.prod(x) for x in [self.nFx, self.nFy, self.nFz] if not x is None]) return locals() nF = property(**nF()) if __name__ == '__main__': m = BaseMesh([3, 2, 4]) print m.n