def face_y_areas(self): """Returns the areas of the y-faces Calling this property will compute and return the areas of faces whose normal vector is along the y-axis. Note that only 2D and 3D tensor meshes have z-faces. Returns ------- (n_faces_y) numpy.ndarray The quantity returned depends on the dimensions of the mesh: - *1D:* N/A since 1D meshes do not have y-faces - *2D:* Areas of y-faces (equivalent to the lengths of x-edges) - *3D:* Areas of y-faces """ if getattr(self, "_face_y_areas", None) is None: # Ensure that we are working with column vectors vh = self.h # The number of cell centers in each direction n = self.vnC # Compute areas of cell faces if self.dim == 1: raise Exception("1D meshes do not have y-Faces") elif self.dim == 2: areaFy = np.outer(vh[0], np.ones(n[1] + 1)) elif self.dim == 3: areaFy = np.outer(vh[0], mkvc(np.outer(np.ones(n[1] + 1), vh[2]))) self._face_y_areas = mkvc(areaFy) return self._face_y_areas
def face_z_areas(self): """Returns the areas of the z-faces Calling this property will compute and return the areas of faces whose normal vector is along the z-axis. Note that only 3D tensor meshes will have z-faces. Returns ------- (n_faces_z) numpy.ndarray The quantity returned depends on the dimensions of the mesh: - *1D:* N/A since 1D meshes do not have z-faces - *2D:* N/A since 2D meshes do not have z-faces - *3D:* Areas of z-faces """ if getattr(self, "_face_z_areas", None) is None: # Ensure that we are working with column vectors vh = self.h # The number of cell centers in each direction n = self.vnC # Compute areas of cell faces if self.dim == 1 or self.dim == 2: raise Exception("{}D meshes do not have z-Faces".format(self.dim)) elif self.dim == 3: areaFz = np.outer(vh[0], mkvc(np.outer(vh[1], np.ones(n[2] + 1)))) self._face_z_areas = mkvc(areaFz) return self._face_z_areas
def faces_y(self): """ Face staggered grid in the y direction. """ if getattr(self, "_faces_y", None) is None: N = self.reshape(self.gridN, "N", "N", "M") if self.dim == 2: XY = [mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N] self._faces_y = np.c_[XY[0], XY[1]] elif self.dim == 3: XYZ = [ mkvc( 0.25 * ( n[:-1, :, :-1] + n[:-1, :, 1:] + n[1:, :, :-1] + n[1:, :, 1:] ) ) for n in N ] self._faces_y = np.c_[XYZ[0], XYZ[1], XYZ[2]] return self._faces_y
def edge_x_lengths(self): """Returns the x-edge lengths Calling this property will compute and return the lengths of edges parallel to the x-axis. Returns ------- (n_edges_x) numpy.ndarray X-edge lengths """ if getattr(self, "_edge_x_lengths", None) is None: # Ensure that we are working with column vectors vh = self.h # The number of cell centers in each direction n = self.vnC # Compute edge lengths if self.dim == 1: edgeEx = vh[0] elif self.dim == 2: edgeEx = np.outer(vh[0], np.ones(n[1] + 1)) elif self.dim == 3: edgeEx = np.outer( vh[0], mkvc(np.outer(np.ones(n[1] + 1), np.ones(n[2] + 1))) ) self._edge_x_lengths = mkvc(edgeEx) return self._edge_x_lengths
def cell_volumes(self): """Return cell volumes Calling this property will compute and return the volumes of the tensor mesh cells. Returns ------- (n_cells) numpy.ndarray The quantity returned depends on the dimensions of the mesh: - *1D:* Returns the cell widths - *2D:* Returns the cell areas - *3D:* Returns the cell volumes """ if getattr(self, "_cell_volumes", None) is None: vh = self.h # Compute cell volumes if self.dim == 1: self._cell_volumes = mkvc(vh[0]) elif self.dim == 2: # Cell sizes in each direction self._cell_volumes = mkvc(np.outer(vh[0], vh[1])) elif self.dim == 3: # Cell sizes in each direction self._cell_volumes = mkvc(np.outer(mkvc(np.outer(vh[0], vh[1])), vh[2])) return self._cell_volumes
def edge_z_lengths(self): """Returns the z-edge lengths Calling this property will compute and return the lengths of edges parallel to the z-axis. Returns ------- (n_edges_z) numpy.ndarray The quantity returned depends on the dimensions of the mesh: - *1D:* N/A since 1D meshes do not have z-edges - *2D:* N/A since 2D meshes do not have z-edges - *3D:* Returns z-edge lengths """ if getattr(self, "_edge_z_lengths", None) is None: # Ensure that we are working with column vectors vh = self.h # The number of cell centers in each direction n = self.vnC # Compute edge lengths if self.dim == 1 or self.dim == 2: raise Exception("{}D meshes do not have y-edges".format(self.dim)) elif self.dim == 3: edgeEz = np.outer( np.ones(n[0] + 1), mkvc(np.outer(np.ones(n[1] + 1), vh[2])) ) self._edge_z_lengths = mkvc(edgeEz) return self._edge_z_lengths
def edge_lengths(self): if getattr(self, "_edge_lengths", None) is None: if self.dim == 2: xy = self.gridN A, D = index_cube("AD", self.vnN, self.vnEx) edge1 = xy[D, :] - xy[A, :] A, B = index_cube("AB", self.vnN, self.vnEy) edge2 = xy[B, :] - xy[A, :] self._edge_lengths = np.r_[ mkvc(_length2D(edge1)), mkvc(_length2D(edge2)) ] self._edge_tangents = ( np.r_[edge1, edge2] / np.c_[self._edge_lengths, self._edge_lengths] ) elif self.dim == 3: xyz = self.gridN A, D = index_cube("AD", self.vnN, self.vnEx) edge1 = xyz[D, :] - xyz[A, :] A, B = index_cube("AB", self.vnN, self.vnEy) edge2 = xyz[B, :] - xyz[A, :] A, E = index_cube("AE", self.vnN, self.vnEz) edge3 = xyz[E, :] - xyz[A, :] self._edge_lengths = np.r_[ mkvc(_length3D(edge1)), mkvc(_length3D(edge2)), mkvc(_length3D(edge3)), ] self._edge_tangents = ( np.r_[edge1, edge2, edge3] / np.c_[self._edge_lengths, self._edge_lengths, self._edge_lengths] ) return self._edge_lengths
def face_x_areas(self): """Returns the areas of the x-faces Calling this property will compute and return the areas of faces whose normal vector is along the x-axis. Returns ------- (n_faces_x) numpy.ndarray The quantity returned depends on the dimensions of the mesh: - *1D:* Numpy array of ones whose length is equal to the number of nodes - *2D:* Areas of x-faces (equivalent to the lengths of y-edges) - *3D:* Areas of x-faces """ if getattr(self, "_face_x_areas", None) is None: # Ensure that we are working with column vectors vh = self.h # The number of cell centers in each direction n = self.vnC # Compute areas of cell faces if self.dim == 1: areaFx = np.ones(n[0] + 1) elif self.dim == 2: areaFx = np.outer(np.ones(n[0] + 1), vh[1]) elif self.dim == 3: areaFx = np.outer(np.ones(n[0] + 1), mkvc(np.outer(vh[1], vh[2]))) self._face_x_areas = mkvc(areaFx) return self._face_x_areas
def edges_y(self): """Gridded y-edge locations (staggered grid) This property returns a numpy array of shape (n_edges_y, dim) containing gridded locations for all y-edges in the mesh (staggered grid). For curvilinear meshes whose structure is minimally staggered, the y-edges are edges oriented primarily along the y-direction. For highly irregular meshes however, this is not the case; see the examples below. Returns ------- (n_edges_y, dim) numpy.ndarray of float Gridded y-edge locations (staggered grid) Examples -------- Here, we provide an example of a minimally staggered curvilinear mesh. In this case, the y-edges are primarily oriented along the y-direction. >>> from discretize import CurvilinearMesh >>> from discretize.utils import example_curvilinear_grid, mkvc >>> from matplotlib import pyplot as plt >>> x, y = example_curvilinear_grid([10, 10], "rotate") >>> mesh1 = CurvilinearMesh([x, y]) >>> y_edges = mesh1.edges_y >>> fig1 = plt.figure(figsize=(5, 5)) >>> ax1 = fig1.add_subplot(111) >>> mesh1.plot_grid(ax=ax1) >>> ax1.scatter(y_edges[:, 0], y_edges[:, 1], 30, 'r') >>> ax1.legend(['Mesh', 'Y-edges'], fontsize=16) >>> plt.plot() Here, we provide an example of a highly irregular curvilinear mesh. In this case, the y-edges are not aligned primarily along a particular direction. >>> x, y = example_curvilinear_grid([10, 10], "sphere") >>> mesh2 = CurvilinearMesh([x, y]) >>> y_edges = mesh2.edges_y >>> fig2 = plt.figure(figsize=(5, 5)) >>> ax2 = fig2.add_subplot(111) >>> mesh2.plot_grid(ax=ax2) >>> ax2.scatter(y_edges[:, 0], y_edges[:, 1], 30, 'r') >>> ax2.legend(['Mesh', 'X-edges'], fontsize=16) >>> plt.plot() """ if getattr(self, "_edges_y", None) is None: N = self.reshape(self.gridN, "N", "N", "M") if self.dim == 2: XY = [mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N] self._edges_y = np.c_[XY[0], XY[1]] elif self.dim == 3: XYZ = [mkvc(0.5 * (n[:, :-1, :] + n[:, 1:, :])) for n in N] self._edges_y = np.c_[XYZ[0], XYZ[1], XYZ[2]] return self._edges_y
def face_areas(self): """Returns the areas of all faces in the mesh Calling this property will compute and return the areas of all faces as a 1D numpy array. The returned quantity is ordered x-face areas, then y-face areas, then z-face areas. Returns ------- (n_faces) numpy.ndarray The length of the quantity returned depends on the dimensions of the mesh: - *1D:* returns the x-face areas - *2D:* returns the x-face and y-face areas in order; i.e. y-edge and x-edge lengths, respectively - *3D:* returns the x, y and z-face areas in order """ if ( getattr(self, "_face_areas", None) is None or getattr(self, "_normals", None) is None ): # Compute areas of cell faces if self.dim == 2: xy = self.gridN A, B = index_cube("AB", self.vnN, self.vnFx) edge1 = xy[B, :] - xy[A, :] normal1 = np.c_[edge1[:, 1], -edge1[:, 0]] area1 = _length2D(edge1) A, D = index_cube("AD", self.vnN, self.vnFy) # Note that we are doing A-D to make sure the normal points the # right way. # Think about it. Look at the picture. Normal points towards C # iff you do this. edge2 = xy[A, :] - xy[D, :] normal2 = np.c_[edge2[:, 1], -edge2[:, 0]] area2 = _length2D(edge2) self._face_areas = np.r_[mkvc(area1), mkvc(area2)] self._normals = [_normalize2D(normal1), _normalize2D(normal2)] elif self.dim == 3: A, E, F, B = index_cube("AEFB", self.vnN, self.vnFx) normal1, area1 = face_info( self.gridN, A, E, F, B, average=False, normalizeNormals=False ) A, D, H, E = index_cube("ADHE", self.vnN, self.vnFy) normal2, area2 = face_info( self.gridN, A, D, H, E, average=False, normalizeNormals=False ) A, B, C, D = index_cube("ABCD", self.vnN, self.vnFz) normal3, area3 = face_info( self.gridN, A, B, C, D, average=False, normalizeNormals=False ) self._face_areas = np.r_[mkvc(area1), mkvc(area2), mkvc(area3)] self._normals = [normal1, normal2, normal3] return self._face_areas
def getError(self): #Test function phi = lambda x: np.cos(0.5 * np.pi * x) j_fun = lambda x: -0.5 * np.pi * np.sin(0.5 * np.pi * x) q_fun = lambda x: -0.25 * (np.pi**2) * np.cos(0.5 * np.pi * x) xc_ana = phi(self.M.gridCC) q_ana = q_fun(self.M.gridCC) j_ana = j_fun(self.M.gridFx) #TODO: Check where our boundary conditions are CCx or Nx vecN = self.M.vectorNx vecC = self.M.vectorCCx phi_bc = phi(vecC[[0, -1]]) j_bc = j_fun(vecN[[0, -1]]) P, Pin, Pout = self.M.getBCProjWF([['dirichlet', 'neumann']]) Mc = self.M.getFaceInnerProduct() McI = utils.sdInv(self.M.getFaceInnerProduct()) V = utils.sdiag(self.M.vol) G = -Pin.T * Pin * self.M.faceDiv.T * V D = self.M.faceDiv j = McI * (G * xc_ana + P * phi_bc) q = V * D * Pin.T * Pin * j + V * D * Pout.T * j_bc # Rearrange if we know q to solve for x A = V * D * Pin.T * Pin * McI * G rhs = V * q_ana - V * D * Pin.T * Pin * McI * P * phi_bc - V * D * Pout.T * j_bc # A = D*McI*G # rhs = q_ana - D*McI*P*phi_bc if self.myTest == 'j': err = np.linalg.norm((Pin * j - Pin * j_ana), np.inf) elif self.myTest == 'q': err = np.linalg.norm((q - V * q_ana), np.inf) elif self.myTest == 'xc': #TODO: fix the null space xc, info = sp.linalg.minres(A, rhs, tol=1e-6) err = np.linalg.norm((xc - xc_ana), np.inf) if info > 0: print('Solve does not work well') print('ACCURACY', np.linalg.norm(utils.mkvc(A * xc) - rhs)) elif self.myTest == 'xcJ': #TODO: fix the null space xc, info = sp.linalg.minres(A, rhs, tol=1e-6) j = McI * (G * xc + P * phi_bc) err = np.linalg.norm((Pin * j - Pin * j_ana), np.inf) if info > 0: print('Solve does not work well') print('ACCURACY', np.linalg.norm(utils.mkvc(A * xc) - rhs)) return err
def test_rotatePointsFromNormals(self): np.random.seed(10) v0 = np.random.rand(3) v0 *= 1.0 / np.linalg.norm(v0) np.random.seed(15) v1 = np.random.rand(3) v1 *= 1.0 / np.linalg.norm(v1) v2 = utils.mkvc( utils.rotatePointsFromNormals(utils.mkvc(v0, 2).T, v0, v1)) self.assertTrue(np.linalg.norm(v2 - v1) < tol)
def gridEy(self): """ Edge staggered grid in the y direction. """ if getattr(self, '_gridEy', None) is None: N = self.r(self.gridN, 'N', 'N', 'M') if self.dim == 2: XY = [utils.mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N] self._gridEy = np.c_[XY[0], XY[1]] elif self.dim == 3: XYZ = [utils.mkvc(0.5 * (n[:, :-1, :] + n[:, 1:, :])) for n in N] self._gridEy = np.c_[XYZ[0], XYZ[1], XYZ[2]] return self._gridEy
def edges_x(self): """ Edge staggered grid in the x direction. """ if getattr(self, "_edges_x", None) is None: N = self.reshape(self.gridN, "N", "N", "M") if self.dim == 2: XY = [mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N] self._edges_x = np.c_[XY[0], XY[1]] elif self.dim == 3: XYZ = [mkvc(0.5 * (n[:-1, :, :] + n[1:, :, :])) for n in N] self._edges_x = np.c_[XYZ[0], XYZ[1], XYZ[2]] return self._edges_x
def getError(self): #Test function phi = lambda x: np.cos(np.pi * x) j_fun = lambda x: -np.pi * np.sin(np.pi * x) q_fun = lambda x: -(np.pi**2) * np.cos(np.pi * x) xc_ana = phi(self.M.gridCC) q_ana = q_fun(self.M.gridCC) j_ana = j_fun(self.M.gridFx) #TODO: Check where our boundary conditions are CCx or Nx # vec = self.M.vectorNx vec = self.M.vectorCCx phi_bc = phi(vec[[0, -1]]) j_bc = j_fun(vec[[0, -1]]) P, Pin, Pout = self.M.getBCProjWF([['dirichlet', 'dirichlet']]) Mc = self.M.getFaceInnerProduct() McI = utils.sdInv(self.M.getFaceInnerProduct()) V = utils.sdiag(self.M.vol) G = -Pin.T * Pin * self.M.faceDiv.T * V D = self.M.faceDiv j = McI * (G * xc_ana + P * phi_bc) q = V * D * Pin.T * Pin * j + V * D * Pout.T * j_bc # Rearrange if we know q to solve for x A = V * D * Pin.T * Pin * McI * G rhs = V * q_ana - V * D * Pin.T * Pin * McI * P * phi_bc - V * D * Pout.T * j_bc # A = D*McI*G # rhs = q_ana - D*McI*P*phi_bc if self.myTest == 'j': err = np.linalg.norm((j - j_ana), np.inf) elif self.myTest == 'q': err = np.linalg.norm((q - V * q_ana), np.inf) elif self.myTest == 'xc': #TODO: fix the null space solver = SolverCG(A, maxiter=1000) xc = solver * (rhs) print('ACCURACY', np.linalg.norm(utils.mkvc(A * xc) - rhs)) err = np.linalg.norm((xc - xc_ana), np.inf) elif self.myTest == 'xcJ': #TODO: fix the null space xc = Solver(A) * (rhs) print(np.linalg.norm(utils.mkvc(A * xc) - rhs)) j = McI * (G * xc + P * phi_bc) err = np.linalg.norm((j - j_ana), np.inf) return err
def test_rotationMatrixFromNormals(self): np.random.seed(0) v0 = np.random.rand(3) v0 *= 1. / np.linalg.norm(v0) np.random.seed(5) v1 = np.random.rand(3) v1 *= 1. / np.linalg.norm(v1) Rf = utils.coordutils.rotationMatrixFromNormals(v0, v1) Ri = utils.coordutils.rotationMatrixFromNormals(v1, v0) self.assertTrue(np.linalg.norm(utils.mkvc(Rf.dot(v0) - v1)) < tol) self.assertTrue(np.linalg.norm(utils.mkvc(Ri.dot(v1) - v0)) < tol)
def cell_volumes(self): if getattr(self, "_cell_volumes", None) is None: vh = self.h # Compute cell volumes if self.dim == 1: self._cell_volumes = mkvc(vh[0]) elif self.dim == 2: # Cell sizes in each direction self._cell_volumes = mkvc(np.outer(vh[0], vh[1])) elif self.dim == 3: # Cell sizes in each direction self._cell_volumes = mkvc( np.outer(mkvc(np.outer(vh[0], vh[1])), vh[2])) return self._cell_volumes
def getj3Dthetaslice(self, j3D, theta_ind=0): """ grab theta slice through j """ j3D_x = j3D[:self.mesh3D.nFx].reshape(self.mesh3D.vnFx, order='F') j3D_z = j3D[self.mesh3D.vnF[:2].sum():].reshape(self.mesh3D.vnFz, order='F') j3Dslice = np.vstack([ utils.mkvc(j3D_x[:, theta_ind, :], 2), utils.mkvc(j3D_z[:, theta_ind, :], 2) ]) return j3Dslice
def cell_volumes(self): """Construct cell volumes of the 3D model as 1d array.""" if getattr(self, "_cell_volumes", None) is None: vh = self.h # Compute cell volumes if self.dim == 1: self._cell_volumes = mkvc(vh[0]) elif self.dim == 2: # Cell sizes in each direction self._cell_volumes = mkvc(np.outer(vh[0], vh[1])) elif self.dim == 3: # Cell sizes in each direction self._cell_volumes = mkvc(np.outer(mkvc(np.outer(vh[0], vh[1])), vh[2])) return self._cell_volumes
def edge_z_lengths(self): """z-edge lengths""" if getattr(self, "_edge_z_lengths", None) is None: # Ensure that we are working with column vectors vh = self.h # The number of cell centers in each direction n = self.vnC # Compute edge lengths if self.dim == 1 or self.dim == 2: raise Exception("{}D meshes do not have y-edges".format(self.dim)) elif self.dim == 3: edgeEz = np.outer( np.ones(n[0] + 1), mkvc(np.outer(np.ones(n[1] + 1), vh[2])) ) self._edge_z_lengths = mkvc(edgeEz) return self._edge_z_lengths
def refine_octree_surface(mesh, f): """ Refine an octree mesh around the given surface :param mesh: TreeMesh instance to refine :param f: function giving z(x,y) in physical units :return: TreeMesh instance """ xx, yy = np.meshgrid(mesh.vectorNx, mesh.vectorNy) zz = f(xx, yy) idx_valid = ~np.isnan(zz) xx, yy, zz = xx[idx_valid], yy[idx_valid], zz[idx_valid] surf = np.c_[mkvc(xx), mkvc(yy), mkvc(zz)] # Play with different octree_levels=[lx,ly,lz] settings below return refine_tree_xyz( mesh, surf, octree_levels=[1,1,1], method="surface", finalize=False )
def write_model_UBC(mesh, file_name, model, directory=""): """Write 2D or 3D tensor model to UBC-GIF formatted file. Parameters ---------- file_name : str or file name full path for the output mesh file or just its name if directory is specified model : (n_cells) numpy.ndarray directory : str, optional output directory """ fname = os.path.join(directory, file_name) if mesh.dim == 3: # Reshape model to a matrix modelMat = mesh.reshape(model, "CC", "CC", "M") # Transpose the axes modelMatT = modelMat.transpose((2, 0, 1)) # Flip z to positive down modelMatTR = mkvc(modelMatT[::-1, :, :]) np.savetxt(fname, modelMatTR.ravel()) elif mesh.dim == 2: modelMat = mesh.reshape(model, "CC", "CC", "M").T[::-1] f = open(fname, "w") f.write("{:d} {:d}\n".format(*mesh.shape_cells)) f.close() f = open(fname, "ab") np.savetxt(f, modelMat) f.close() else: raise Exception("mesh must be a Tensor Mesh 2D or 3D")
def write_model_UBC(mesh, file_name, model, directory=""): """Writes a model associated with a TensorMesh to a UBC-GIF format model file. Input: :param str file_name: File to write to or just its name if directory is specified :param str directory: directory where the UBC GIF file lives :param numpy.ndarray model: The model """ fname = os.path.join(directory, file_name) if mesh.dim == 3: # Reshape model to a matrix modelMat = mesh.reshape(model, "CC", "CC", "M") # Transpose the axes modelMatT = modelMat.transpose((2, 0, 1)) # Flip z to positive down modelMatTR = mkvc(modelMatT[::-1, :, :]) np.savetxt(fname, modelMatTR.ravel()) elif mesh.dim == 2: modelMat = mesh.reshape(model, "CC", "CC", "M").T[::-1] f = open(fname, "w") f.write("{:d} {:d}\n".format(*mesh.shape_cells)) f.close() f = open(fname, "ab") np.savetxt(f, modelMat) f.close() else: raise Exception("mesh must be a Tensor Mesh 2D or 3D")
def test_zero(self): z = Zero() assert z == 0 assert not (z < 0) assert z <= 0 assert not (z > 0) assert z >= 0 assert +z == z assert -z == z assert z + 1 == 1 assert z + 3 + z == 3 assert z - 3 == -3 assert z - 3 - z == -3 assert 3 * z == 0 assert z * 3 == 0 assert z / 3 == 0 a = 1 a += z assert a == 1 a = 1 a += z assert a == 1 self.assertRaises(ZeroDivisionError, lambda: 3 / z) assert mkvc(z) == 0 assert sdiag(z) * a == 0 assert z.T == 0 assert z.transpose() == 0
def __init__(self, node_list, **kwargs): if "nodes" in kwargs: node_list = kwargs.pop("nodes") node_list = tuple(np.asarray(item, dtype=np.float64) for item in node_list) # check shapes of each node array match dim = len(node_list) if dim not in [2, 3]: raise ValueError( f"Only supports 2 and 3 dimensional meshes, saw a node_list of length {dim}" ) for i, nodes in enumerate(node_list): if len(nodes.shape) != dim: raise ValueError( f"Unexpected shape of item in node list, expect array with {dim} dimensions, got {len(nodes.shape)}" ) if node_list[0].shape != nodes.shape: raise ValueError( f"The shape of nodes are not consistent, saw {node_list[0].shape} and {nodes.shape}" ) self._node_list = tuple(node_list) # Save nodes to private variable _nodes as vectors self._nodes = np.ones((self.node_list[0].size, dim)) for i, nodes in enumerate(self.node_list): self._nodes[:, i] = mkvc(nodes) shape_cells = (n - 1 for n in self.node_list[0].shape) # absorb the rest of kwargs, and do not pass to super super().__init__(shape_cells, origin=self.nodes[0])
def faces_z(self): """Gridded z-face locations (staggered grid) This property returns a numpy array of shape (n_faces_z, dim) containing gridded locations for all z-faces in the mesh (staggered grid). For curvilinear meshes whose structure is minimally staggered, the z-faces are faces whose normal vectors are primarily along the z-direction. For highly irregular meshes however, this is not the case. Returns ------- (n_faces_z, dim) numpy.ndarray of float Gridded z-face locations (staggered grid) """ if getattr(self, "_faces_z", None) is None: N = self.reshape(self.gridN, "N", "N", "M") XYZ = [ mkvc( 0.25 * (n[:-1, :-1, :] + n[:-1, 1:, :] + n[1:, :-1, :] + n[1:, 1:, :]) ) for n in N ] self._faces_z = np.c_[XYZ[0], XYZ[1], XYZ[2]] return self._faces_z
def face_z_areas(self): """ Area of the z-faces """ if getattr(self, "_face_z_areas", None) is None: # Ensure that we are working with column vectors vh = self.h # The number of cell centers in each direction n = self.vnC # Compute areas of cell faces if self.dim == 1 or self.dim == 2: raise Exception("{}D meshes do not have z-Faces".format(self.dim)) elif self.dim == 3: areaFz = np.outer(vh[0], mkvc(np.outer(vh[1], np.ones(n[2] + 1)))) self._face_z_areas = mkvc(areaFz) return self._face_z_areas
def create_box_surface(coordinates, cellwidth, axis='x', degree_rad=0): """Creates a list of coordinates of points on the surface of a box. Parameters ---------- coordinates : tuple ((xmin, xmax),(ymin, ymax),(zmin, zmax)) coordinates of box cellwidth : int width of smallest cell in the mesh axis : string 'x', 'y' or 'z' to denote the axis of rotation degree_rad : float the number of degrees to rotate the cube with Returns ------- np.ndarray a numpy array of the box surface coordinates """ x1 = coordinates[0][0] x2 = coordinates[0][1] y1 = coordinates[1][0] y2 = coordinates[1][1] z1 = coordinates[2][0] z2 = coordinates[2][1] x_num = int(np.ceil((abs(x1 - x2)) / cellwidth)) y_num = int(np.ceil((abs(y1 - y2)) / cellwidth)) z_num = int(np.ceil((abs(z1 - z2)) / cellwidth)) x_coords = np.linspace(x1, x2, x_num) y_coords = np.linspace(y1, y2, y_num) z_coords = np.linspace(z1, z2, z_num) xp, yp, zp = np.meshgrid(x_coords, y_coords, z_coords) xyz = np.c_[mkvc(xp), mkvc(yp), mkvc(zp)] surface = [] for row in xyz: if row[0] == x1 or row[0] == x2 or row[1] == y1 or row[1] == y2 or row[ 2] == z1 or row[2] == z2: surface.append(row) rotation = R.from_euler(axis, degree_rad, degrees=True).as_matrix() surface = np.asarray([np.dot(rotation, i) for i in surface]) return np.asarray(surface)
def edge_lengths(self): """Returns the lengths of all edges in the mesh Calling this property will compute and return the lengths of all edges in the mesh. The returned quantity is ordered x-edge lengths, then y-edge lengths, then z-edge lengths. Returns ------- (n_edges) numpy.ndarray The length of the quantity returned depends on the dimensions of the mesh: - *1D:* returns the x-edge lengths - *2D:* returns the x-edge and y-edge lengths in order - *3D:* returns the x, y and z-edge lengths in order """ if getattr(self, "_edge_lengths", None) is None: if self.dim == 2: xy = self.gridN A, D = index_cube("AD", self.vnN, self.vnEx) edge1 = xy[D, :] - xy[A, :] A, B = index_cube("AB", self.vnN, self.vnEy) edge2 = xy[B, :] - xy[A, :] self._edge_lengths = np.r_[ mkvc(_length2D(edge1)), mkvc(_length2D(edge2)) ] self._edge_tangents = ( np.r_[edge1, edge2] / np.c_[self._edge_lengths, self._edge_lengths] ) elif self.dim == 3: xyz = self.gridN A, D = index_cube("AD", self.vnN, self.vnEx) edge1 = xyz[D, :] - xyz[A, :] A, B = index_cube("AB", self.vnN, self.vnEy) edge2 = xyz[B, :] - xyz[A, :] A, E = index_cube("AE", self.vnN, self.vnEz) edge3 = xyz[E, :] - xyz[A, :] self._edge_lengths = np.r_[ mkvc(_length3D(edge1)), mkvc(_length3D(edge2)), mkvc(_length3D(edge3)), ] self._edge_tangents = ( np.r_[edge1, edge2, edge3] / np.c_[self._edge_lengths, self._edge_lengths, self._edge_lengths] ) return self._edge_lengths
def area(self): if (getattr(self, '_area', None) is None or getattr(self, '_normals', None) is None): # Compute areas of cell faces if(self.dim == 2): xy = self.gridN A, B = utils.indexCube('AB', self.vnC+1, np.array([self.nNx, self.nCy])) edge1 = xy[B, :] - xy[A, :] normal1 = np.c_[edge1[:, 1], -edge1[:, 0]] area1 = length2D(edge1) A, D = utils.indexCube('AD', self.vnC+1, np.array([self.nCx, self.nNy])) # Note that we are doing A-D to make sure the normal points the # right way. # Think about it. Look at the picture. Normal points towards C # iff you do this. edge2 = xy[A, :] - xy[D, :] normal2 = np.c_[edge2[:, 1], -edge2[:, 0]] area2 = length2D(edge2) self._area = np.r_[utils.mkvc(area1), utils.mkvc(area2)] self._normals = [normalize2D(normal1), normalize2D(normal2)] elif(self.dim == 3): A, E, F, B = utils.indexCube('AEFB', self.vnC+1, np.array( [self.nNx, self.nCy, self.nCz])) normal1, area1 = utils.faceInfo(self.gridN, A, E, F, B, average=False, normalizeNormals=False) A, D, H, E = utils.indexCube('ADHE', self.vnC+1, np.array( [self.nCx, self.nNy, self.nCz])) normal2, area2 = utils.faceInfo(self.gridN, A, D, H, E, average=False, normalizeNormals=False) A, B, C, D = utils.indexCube('ABCD', self.vnC+1, np.array( [self.nCx, self.nCy, self.nNz])) normal3, area3 = utils.faceInfo(self.gridN, A, B, C, D, average=False, normalizeNormals=False) self._area = np.r_[utils.mkvc(area1), utils.mkvc(area2), utils.mkvc(area3)] self._normals = [normal1, normal2, normal3] return self._area
############################################################################### # Run the long on-time simulation # ------------------------------- t = time.time() print('--- Running Long On-Time Simulation ---') prob_ramp_on.mu = mu_model fields = prob_ramp_on.fields(sigma) print(" ... done. Elapsed time {}".format(time.time() - t)) print('\n') # grab the last time-step in the simulation b_ramp_on = utils.mkvc(fields[:, 'b', -1]) ############################################################################### # Compute Magnetostatic Fields from the step-off source # ----------------------------------------------------- prob_magnetostatic.mu = mu_model prob_magnetostatic.model = sigma b_magnetostatic = src_magnetostatic.bInitial(prob_magnetostatic) ############################################################################### # Plot the results # ----------------------------------------------------- def plotBFieldResults(
def test_permeable_sources(self): target_mur = self.target_mur target_l = self.target_l target_r = self.target_r sigma_back = self.sigma_back model_names = self.model_names mesh = self.mesh radius_loop = self.radius_loop # Assign physical properties on the mesh def populate_target(mur): mu_model = np.ones(mesh.nC) x_inds = mesh.gridCC[:, 0] < target_r z_inds = ( (mesh.gridCC[:, 2] <= 0) & (mesh.gridCC[:, 2] >= -target_l) ) mu_model[x_inds & z_inds] = mur return mu_0 * mu_model mu_dict = { key: populate_target(mu) for key, mu in zip(model_names, target_mur) } sigma = np.ones(mesh.nC) * sigma_back # Plot the models if plotIt: xlim = np.r_[-200, 200] # x-limits in meters zlim = np.r_[-1.5*target_l, 10.] # z-limits in meters. (z-positive up) fig, ax = plt.subplots( 1, len(model_names), figsize=(6*len(model_names), 5) ) if len(model_names) == 1: ax = [ax] for a, key in zip(ax, model_names): plt.colorbar(mesh.plotImage( mu_dict[key], ax=a, pcolorOpts={'norm': LogNorm()}, # plot on a log-scale mirror=True )[0], ax=a) a.set_title('{}'.format(key), fontsize=13) # cylMeshGen.mesh.plotGrid(ax=a, slice='theta') # uncomment to plot the mesh on top of this a.set_xlim(xlim) a.set_ylim(zlim) plt.tight_layout() plt.show() ramp = [ (1e-5, 20), (1e-4, 20), (3e-4, 20), (1e-3, 20), (3e-3, 20), (1e-2, 20), (3e-2, 20), (1e-1, 20), (3e-1, 20), (1, 50) ] timeSteps = ramp time_mesh = discretize.TensorMesh([ramp]) offTime = 10000 waveform = TDEM.Src.QuarterSineRampOnWaveform( ramp_on=np.r_[1e-4, 20], ramp_off=offTime - np.r_[1e-4, 0] ) if plotIt: wave = np.r_[[waveform.eval(t) for t in time_mesh.gridN]] plt.plot(time_mesh.gridN, wave) plt.plot(time_mesh.gridN, np.zeros(time_mesh.nN), '-|', color='k') plt.show() src_magnetostatic = TDEM.Src.CircularLoop( [], loc=np.r_[0., 0., 0.], orientation="z", radius=100, ) src_ramp_on = TDEM.Src.CircularLoop( [], loc=np.r_[0., 0., 0.], orientation="z", radius=100, waveform=waveform ) src_list = [src_magnetostatic] src_list_late_ontime = [src_ramp_on] prob = TDEM.Problem3D_b( mesh=mesh, timeSteps=timeSteps, sigmaMap=Maps.IdentityMap(mesh), Solver=Pardiso ) prob_late_ontime = TDEM.Problem3D_b( mesh=mesh, timeSteps=timeSteps, sigmaMap=Maps.IdentityMap(mesh), Solver=Pardiso ) survey = TDEM.Survey(srcList=src_list) survey_late_ontime = TDEM.Survey(src_list_late_ontime) prob.pair(survey) prob_late_ontime.pair(survey_late_ontime) fields_dict = {} for key in model_names: t = time.time() print('--- Running {} ---'.format(key)) prob_late_ontime.mu = mu_dict[key] fields_dict[key] = prob_late_ontime.fields(sigma) print(" ... done. Elapsed time {}".format(time.time() - t)) print('\n') b_magnetostatic = {} b_late_ontime = {} for key in model_names: prob.mu = mu_dict[key] prob.sigma = sigma b_magnetostatic[key] = src_magnetostatic.bInitial(prob) prob_late_ontime.mu = mu_dict[key] b_late_ontime[key] = utils.mkvc( fields_dict[key][:, 'b', -1] ) if plotIt: fig, ax = plt.subplots( len(model_names), 2, figsize=(3*len(model_names), 5) ) for i, key in enumerate(model_names): ax[i][0].semilogy( np.absolute(b_magnetostatic[key]), label='magnetostatic' ) ax[i][0].semilogy( np.absolute(b_late_ontime[key]), label='late on-time' ) ax[i][0].legend() ax[i][1].semilogy( np.absolute(b_magnetostatic[key] - b_late_ontime[key]) ) plt.tight_layout() plt.show() print("Testing TDEM with permeable targets") passed = [] for key in model_names: norm_magneotstatic = np.linalg.norm(b_magnetostatic[key]) norm_late_ontime = np.linalg.norm(b_late_ontime[key]) norm_diff = np.linalg.norm( b_magnetostatic[key] - b_late_ontime[key] ) passed_test = ( norm_diff / (0.5*(norm_late_ontime + norm_magneotstatic)) < TOL ) print("\n{}".format(key)) print( "||magnetostatic||: {:1.2e}, " "||late on-time||: {:1.2e}, " "||difference||: {:1.2e} passed?: {}".format( norm_magneotstatic, norm_late_ontime, norm_diff, passed_test ) ) passed += [passed_test] assert all(passed) prob.sigma = 1e-4*np.ones(mesh.nC) v = utils.mkvc(np.random.rand(mesh.nE)) w = utils.mkvc(np.random.rand(mesh.nF)) assert( np.all( mesh.getEdgeInnerProduct(1e-4*np.ones(mesh.nC))*v == prob.MeSigma*v ) ) assert( np.all( mesh.getEdgeInnerProduct( 1e-4*np.ones(mesh.nC), invMat=True )*v == prob.MeSigmaI*v ) ) assert( np.all( mesh.getFaceInnerProduct(1./1e-4*np.ones(mesh.nC))*w == prob.MfRho*w ) ) assert( np.all( mesh.getFaceInnerProduct( 1./1e-4*np.ones(mesh.nC), invMat=True )*w == prob.MfRhoI*w ) ) prob.rho = 1./1e-3*np.ones(mesh.nC) v = utils.mkvc(np.random.rand(mesh.nE)) w = utils.mkvc(np.random.rand(mesh.nF)) assert( np.all( mesh.getEdgeInnerProduct(1e-3*np.ones(mesh.nC))*v == prob.MeSigma*v ) ) assert( np.all( mesh.getEdgeInnerProduct( 1e-3*np.ones(mesh.nC), invMat=True )*v == prob.MeSigmaI*v ) ) assert( np.all( mesh.getFaceInnerProduct(1./1e-3*np.ones(mesh.nC))*w == prob.MfRho*w ) ) assert( np.all( mesh.getFaceInnerProduct( 1./1e-3*np.ones(mesh.nC), invMat=True )*w == prob.MfRhoI*w ) )