Beispiel #1
0
    def setBC(self):
        mesh = self.mesh
        V = sp.diags(mesh.cell_volumes)
        self.Div = V @ mesh.face_divergence
        self.Grad = self.Div.T

        if self.bc_type == "Dirichlet":
            if self.verbose:
                print(
                    "Homogeneous Dirichlet is the natural BC for this CC discretization."
                )
            # do nothing
            return
        elif self.bc_type == "Neumann":
            alpha, beta, gamma = 0, 1, 0
        else:  # self.bc_type == "Mixed":
            boundary_faces = mesh.boundary_faces
            boundary_normals = mesh.boundary_face_outward_normals
            n_bf = len(boundary_faces)

            # Top gets 0 Nuemann
            alpha = np.zeros(n_bf)
            beta = np.ones(n_bf)
            gamma = 0

            # assume a source point at the middle of the top of the mesh
            middle = np.median(mesh.nodes, axis=0)
            top_v = np.max(mesh.nodes[:, -1])
            source_point = np.r_[middle[:-1], top_v]

            # Others: Robin: alpha * phi + d phi dn = 0
            # where alpha = 1 / r  * r_hat_dot_n
            # TODO: Implement Zhang et al. (1995)

            r_vec = boundary_faces - source_point
            r = np.linalg.norm(r_vec, axis=-1)
            r_hat = r_vec / r[:, None]
            r_dot_n = np.einsum("ij,ij->i", r_hat, boundary_normals)

            # determine faces that are on the sides and bottom of the mesh...
            if mesh._meshType.lower() == "tree":
                not_top = boundary_faces[:, -1] != top_v
            else:
                # mesh faces are ordered, faces_x, faces_y, faces_z so...
                if mesh.dim == 2:
                    is_b = make_boundary_bool(mesh.shape_faces_y)
                    is_t = np.zeros(mesh.shape_faces_y, dtype=bool, order="F")
                    is_t[:, -1] = True
                else:
                    is_b = make_boundary_bool(mesh.shape_faces_z)
                    is_t = np.zeros(mesh.shape_faces_z, dtype=bool, order="F")
                    is_t[:, :, -1] = True
                is_t = is_t.reshape(-1, order="F")[is_b]
                not_top = np.zeros(boundary_faces.shape[0], dtype=bool)
                not_top[-len(is_t):] = ~is_t
            alpha[not_top] = (r_dot_n / r)[not_top]

        B, bc = mesh.cell_gradient_weak_form_robin(alpha, beta, gamma)
        # bc should always be 0 because gamma was always 0 above
        self.Grad = self.Grad - B
Beispiel #2
0
    def boundary_edges(self):
        """Boundary edge locations

        This property returns the locations of the edges on
        the boundary of the mesh as a numpy array. The shape
        of the numpy array is the number of boundary edges by
        the dimension of the mesh.

        Returns
        -------
        (n_boundary_edges, dim) numpy.ndarray of float
            Boundary edge locations
        """
        dim = self.dim
        if dim == 1:
            return None  # no boundary edges in 1D
        if dim == 2:
            ex = ndgrid(self.cell_centers_x, self.nodes_y[[0, -1]])
            ey = ndgrid(self.nodes_x[[0, -1]], self.cell_centers_y)
            return np.r_[ex, ey]
        if dim == 3:
            ex = self.edges_x[make_boundary_bool(self.shape_edges_x, dir="yz")]
            ey = self.edges_y[make_boundary_bool(self.shape_edges_y, dir="xz")]
            ez = self.edges_z[make_boundary_bool(self.shape_edges_z, dir="xy")]
            return np.r_[ex, ey, ez]
Beispiel #3
0
 def boundary_faces(self):
     fx = self.faces_x[make_boundary_bool(self.shape_faces_x, dir="x")]
     fy = self.faces_y[make_boundary_bool(self.shape_faces_y, dir="y")]
     if self.dim == 2:
         return np.r_[fx, fy]
     elif self.dim == 3:
         fz = self.faces_z[make_boundary_bool(self.shape_faces_z, dir="z")]
         return np.r_[fx, fy, fz]
Beispiel #4
0
 def boundary_edges(self):
     if self.dim == 2:
         ex = self.edges_x[make_boundary_bool(self.shape_edges_x, dir="y")]
         ey = self.edges_y[make_boundary_bool(self.shape_edges_y, dir="x")]
         return np.r_[ex, ey]
     elif self.dim == 3:
         ex = self.edges_x[make_boundary_bool(self.shape_edges_x, dir="yz")]
         ey = self.edges_y[make_boundary_bool(self.shape_edges_y, dir="xz")]
         ez = self.edges_z[make_boundary_bool(self.shape_edges_z, dir="xy")]
         return np.r_[ex, ey, ez]
Beispiel #5
0
    def boundary_faces(self):
        """Gridded locations of non-hanging x-faces

        This property returns a numpy array of shape (n_faces_x, dim)
        containing gridded locations for all non-hanging x-faces.

        Returns
        -------
        (n_faces_x, dim) numpy.ndarray of float
            Gridded locations of all non-hanging x-faces
        """
        fx = self.faces_x[make_boundary_bool(self.shape_faces_x, dir="x")]
        fy = self.faces_y[make_boundary_bool(self.shape_faces_y, dir="y")]
        if self.dim == 2:
            return np.r_[fx, fy]
        elif self.dim == 3:
            fz = self.faces_z[make_boundary_bool(self.shape_faces_z, dir="z")]
            return np.r_[fx, fy, fz]
Beispiel #6
0
    def setBC(self, ky=None):
        if self.bc_type == "Dirichlet":
            return
        if getattr(self, "_MBC", None) is None:
            self._MBC = {}
        if ky in self._MBC:
            # I have already created the BC matrix for this wavenumber
            return
        if self.bc_type == "Neumann":
            alpha, beta, gamma = 0, 1, 0
        else:
            mesh = self.mesh
            boundary_faces = mesh.boundary_faces
            boundary_normals = mesh.boundary_face_outward_normals
            n_bf = len(boundary_faces)

            # Top gets 0 Neumann
            alpha = np.zeros(n_bf)
            beta = np.ones(n_bf)
            gamma = 0

            # assume a source point at the middle of the top of the mesh
            middle = np.median(mesh.nodes, axis=0)
            top_v = np.max(mesh.nodes[:, -1])
            source_point = np.r_[middle[:-1], top_v]

            r_vec = boundary_faces - source_point
            r = np.linalg.norm(r_vec, axis=-1)
            r_hat = r_vec / r[:, None]
            r_dot_n = np.einsum("ij,ij->i", r_hat, boundary_normals)

            # determine faces that are on the sides and bottom of the mesh...
            if mesh._meshType.lower() == "tree":
                not_top = boundary_faces[:, -1] != top_v
            else:
                # mesh faces are ordered, faces_x, faces_y, faces_z so...
                is_b = make_boundary_bool(mesh.shape_faces_y)
                is_t = np.zeros(mesh.shape_faces_y, dtype=bool, order="F")
                is_t[:, -1] = True
                is_t = is_t.reshape(-1, order="F")[is_b]
                not_top = np.zeros(boundary_faces.shape[0], dtype=bool)
                not_top[-len(is_t):] = ~is_t

            # use the exponentialy scaled modified bessel function of second kind,
            # (the division will cancel out the scaling)
            # This is more stable for large values of ky * r
            # actual ratio is k1/k0...
            alpha[not_top] = (ky * k1e(ky * r) / k0e(ky * r) *
                              r_dot_n)[not_top]

        B, bc = self.mesh.cell_gradient_weak_form_robin(alpha, beta, gamma)
        # bc should always be 0 because gamma was always 0 above
        self._MBC[ky] = B
Beispiel #7
0
    def boundary_edges(self):
        """Gridded boundary edge locations

        This property returns a numpy array of shape
        (n_boundary_edges, dim) containing the gridded locations
        of the edges on the boundary of the mesh. The returned
        quantity is organized *np.r_[edges_x, edges_y, edges_z]* .

        Returns
        -------
        (n_boundary_edges, dim) numpy.ndarray of float
            Gridded boundary edge locations
        """
        if self.dim == 2:
            ex = self.edges_x[make_boundary_bool(self.shape_edges_x, dir="y")]
            ey = self.edges_y[make_boundary_bool(self.shape_edges_y, dir="x")]
            return np.r_[ex, ey]
        elif self.dim == 3:
            ex = self.edges_x[make_boundary_bool(self.shape_edges_x, dir="yz")]
            ey = self.edges_y[make_boundary_bool(self.shape_edges_y, dir="xz")]
            ez = self.edges_z[make_boundary_bool(self.shape_edges_z, dir="xy")]
            return np.r_[ex, ey, ez]
Beispiel #8
0
    def boundary_nodes(self):
        """Gridded boundary node locations

        This property returns a numpy array of shape
        (n_boundary_nodes, dim) containing the gridded locations
        of the nodes on the boundary of the mesh.

        Returns
        -------
        (n_boundary_nodes, dim) numpy.ndarray of float
            Gridded boundary node locations
        """
        return self.nodes[make_boundary_bool(self.shape_nodes)]
Beispiel #9
0
    def boundary_face_outward_normals(self):
        is_bxm = np.zeros(self.shape_faces_x, order="F", dtype=bool)
        is_bxm[0, :] = True
        is_bxm = is_bxm.reshape(-1, order="F")

        is_bym = np.zeros(self.shape_faces_y, order="F", dtype=bool)
        is_bym[:, 0] = True
        is_bym = is_bym.reshape(-1, order="F")

        is_b = np.r_[
            make_boundary_bool(self.shape_faces_x, dir="x"),
            make_boundary_bool(self.shape_faces_y, dir="y"),
        ]
        switch = np.r_[is_bxm, is_bym]
        if self.dim == 3:
            is_bzm = np.zeros(self.shape_faces_z, order="F", dtype=bool)
            is_bzm[:, :, 0] = True
            is_bzm = is_bzm.reshape(-1, order="F")

            is_b = np.r_[is_b, make_boundary_bool(self.shape_faces_z, dir="z")]
            switch = np.r_[switch, is_bzm]
        face_normals = self.face_normals.copy()
        face_normals[switch] *= -1
        return face_normals[is_b]
Beispiel #10
0
    def boundary_face_outward_normals(self):
        """Outward normals of boundary faces

        For all boundary faces in the mesh, this property returns
        the unit vectors denoting the outward normals to the boundary.
        The returned quantity is a numpy array of shape
        (n_boundary_faces, dim).

        Returns
        -------
        (n_boundary_faces, dim) numpy.ndarray of float
            Outward normals of boundary faces
        """
        is_bxm = np.zeros(self.shape_faces_x, order="F", dtype=bool)
        is_bxm[0, :] = True
        is_bxm = is_bxm.reshape(-1, order="F")

        is_bym = np.zeros(self.shape_faces_y, order="F", dtype=bool)
        is_bym[:, 0] = True
        is_bym = is_bym.reshape(-1, order="F")

        is_b = np.r_[
            make_boundary_bool(self.shape_faces_x, dir="x"),
            make_boundary_bool(self.shape_faces_y, dir="y"),
        ]
        switch = np.r_[is_bxm, is_bym]
        if self.dim == 3:
            is_bzm = np.zeros(self.shape_faces_z, order="F", dtype=bool)
            is_bzm[:, :, 0] = True
            is_bzm = is_bzm.reshape(-1, order="F")

            is_b = np.r_[is_b, make_boundary_bool(self.shape_faces_z, dir="z")]
            switch = np.r_[switch, is_bzm]
        face_normals = self.face_normals.copy()
        face_normals[switch] *= -1
        return face_normals[is_b]
Beispiel #11
0
    def boundary_nodes(self):
        """Boundary node locations

        This property returns the locations of the nodes on
        the boundary of the mesh as a numpy array. The shape
        of the numpy array is the number of boundary nodes by
        the dimension of the mesh.

        Returns
        -------
        (n_boundary_nodes, dim) numpy.ndarray of float
            Boundary node locations
        """
        dim = self.dim
        if dim == 1:
            return self.nodes_x[[0, -1]]
        return self.nodes[make_boundary_bool(self.shape_nodes)]
Beispiel #12
0
 def boundary_nodes(self):
     return self.nodes[make_boundary_bool(self.shape_nodes)]
Beispiel #13
0
 def boundary_nodes(self):
     dim = self.dim
     if dim == 1:
         return self.nodes_x[[0, -1]]
     return self.nodes[make_boundary_bool(self.shape_nodes)]
Beispiel #14
0
    def setBC(self, ky=None):
        if self.bc_type == "Dirichlet":
            # do nothing
            raise ValueError(
                "Dirichlet conditions are not supported in the Nodal formulation"
            )
        elif self.bc_type == "Neumann":
            if self.verbose:
                print(
                    "Homogeneous Neumann is the natural BC for this Nodal discretization."
                )
            return
        else:
            if getattr(self, "_AvgBC", None) is None:
                self._AvgBC = {}
            if ky in self._AvgBC:
                return
            mesh = self.mesh
            # calculate alpha, beta, gamma at the boundary faces
            boundary_faces = mesh.boundary_faces
            boundary_normals = mesh.boundary_face_outward_normals
            n_bf = len(boundary_faces)

            alpha = np.zeros(n_bf)

            # assume a source point at the middle of the top of the mesh
            middle = np.median(mesh.nodes, axis=0)
            top_v = np.max(mesh.nodes[:, -1])
            source_point = np.r_[middle[:-1], top_v]

            r_vec = boundary_faces - source_point
            r = np.linalg.norm(r_vec, axis=-1)
            r_hat = r_vec / r[:, None]
            r_dot_n = np.einsum("ij,ij->i", r_hat, boundary_normals)

            # determine faces that are on the sides and bottom of the mesh...
            if mesh._meshType.lower() == "tree":
                not_top = boundary_faces[:, -1] != top_v
            else:
                # mesh faces are ordered, faces_x, faces_y, faces_z so...
                is_b = make_boundary_bool(mesh.shape_faces_y)
                is_t = np.zeros(mesh.shape_faces_y, dtype=bool, order="F")
                is_t[:, -1] = True
                is_t = is_t.reshape(-1, order="F")[is_b]
                not_top = np.zeros(boundary_faces.shape[0], dtype=bool)
                not_top[-len(is_t):] = ~is_t

            # use the exponentiall scaled modified bessel function of second kind,
            # (the division will cancel out the scaling)
            # This is more stable for large values of ky * r
            # actual ratio is k1/k0...
            alpha[not_top] = (ky * k1e(ky * r) / k0e(ky * r) *
                              r_dot_n)[not_top]

            P_bf = self.mesh.project_face_to_boundary_face

            AvgN2Fb = P_bf @ self.mesh.average_node_to_face
            AvgCC2Fb = P_bf @ self.mesh.average_cell_to_face

            AvgCC2Fb = sdiag(alpha * (P_bf @ self.mesh.face_areas)) @ AvgCC2Fb
            self._AvgBC[ky] = AvgN2Fb.T @ AvgCC2Fb
Beispiel #15
0
    def setBC(self):
        if self.bc_type == "Dirichlet":
            # do nothing
            raise ValueError(
                "Dirichlet conditions are not supported in the Nodal formulation"
            )
        elif self.bc_type == "Neumann":
            if self.verbose:
                print(
                    "Homogeneous Neumann is the natural BC for this nodal discretization."
                )
            return
        else:
            mesh = self.mesh
            # calculate alpha, beta, gamma at the boundary faces
            boundary_faces = mesh.boundary_faces
            boundary_normals = mesh.boundary_face_outward_normals
            n_bf = len(boundary_faces)

            # Top gets 0 Nuemann
            alpha = np.zeros(n_bf)
            # beta = np.ones(n_bf) = 1.0

            # not top get Robin condition
            # assume a source point at the middle of the top of the mesh
            middle = np.median(mesh.nodes, axis=0)
            top_v = np.max(mesh.nodes[:, -1])
            source_point = np.r_[middle[:-1], top_v]

            # Others: Robin: alpha * phi + d phi dn = 0
            # where alpha = 1 / r  * r_hat_dot_n
            # TODO: Implement Zhang et al. (1995)

            r_vec = boundary_faces - source_point
            r = np.linalg.norm(r_vec, axis=-1)
            r_hat = r_vec / r[:, None]
            r_dot_n = np.einsum("ij,ij->i", r_hat, boundary_normals)

            # determine faces that are on the sides and bottom of the mesh...
            if mesh._meshType.lower() == "tree":
                not_top = boundary_faces[:, -1] != top_v
            else:
                # mesh faces are ordered, faces_x, faces_y, faces_z so...
                if mesh.dim == 2:
                    is_b = make_boundary_bool(mesh.shape_faces_y)
                    is_t = np.zeros(mesh.shape_faces_y, dtype=bool, order="F")
                    is_t[:, -1] = True
                else:
                    is_b = make_boundary_bool(mesh.shape_faces_z)
                    is_t = np.zeros(mesh.shape_faces_z, dtype=bool, order="F")
                    is_t[:, :, -1] = True
                is_t = is_t.reshape(-1, order="F")[is_b]
                not_top = np.zeros(boundary_faces.shape[0], dtype=bool)
                not_top[-len(is_t):] = ~is_t
            alpha[not_top] = (r_dot_n / r)[not_top]

            P_bf = self.mesh.project_face_to_boundary_face

            AvgN2Fb = P_bf @ self.mesh.average_node_to_face
            AvgCC2Fb = P_bf @ self.mesh.average_cell_to_face

            AvgCC2Fb = sp.diags(alpha *
                                (P_bf @ self.mesh.face_areas)) @ AvgCC2Fb
            self._AvgBC = AvgN2Fb.T @ AvgCC2Fb