Exemple #1
0
    def __init__(self, problem: Elasticity):
        self.problem = problem
        self.points = problem.points
        self.triangles = problem.triangles
        Cell2.points = problem.points

        quad_degree = 4
        gauss = QGauss(dim=2, n=quad_degree)
        self.fe_values = FEValues(self.problem.fe, gauss, self.points,
                                  self.problem.edges, lambda p: True,
                                  update_gradients=True)

        self.cached_gradient_point = None
        self.cached_gradient = None
Exemple #2
0
    def setUp(self):
        dim = 2
        degree = 1
        quad_degree = 1

        fe_q = FE_Q(dim, degree)
        quadrature = QGauss(dim, quad_degree)
        self.fe_values = FEValues(fe_q,
                                  quadrature,
                                  points=self.triangle_corners,
                                  edges=self.edges,
                                  is_dirichlet=lambda p: False,
                                  update_values=True,
                                  update_gradients=True)
        cell = Cell(dim, self.triangles[0])
        self.fe_values.reinit(cell)
Exemple #3
0
    def test_gauss_degree_of_exactness_on_triangle(self):
        dim = 2

        lower_x = 3
        lower_y = 0
        upper_x = lower_x  # for easy scipy integration
        upper_y = 3

        triangle_corners = np.array([[0, 0], [lower_x, lower_y],
                                     [upper_x, upper_y]])

        def first_deg(p):
            return 2 * p[0] + 4 * p[1] - 1

        def second_deg(p):
            return p[0]**2 - 2 * p[0] * p[1] - p[1]**2 / 2 + 1

        def third_deg(p):
            return 4 * p[1]**3 + p[0]**2 - 3 * p[1]

        def four_deg(p):
            return p[0]**4 - third_deg(p)

        def five_deg(p):
            return p[1]**5 - four_deg(p)

        # Number of points to max polynomial degree
        degree_of_exactness = {1: 1, 3: 2, 4: 3, 7: 4}

        message = ""

        for func, degree in [(first_deg, 1), (second_deg, 2), (third_deg, 3),
                             (four_deg, 4), (five_deg, 5)]:
            print("\nFunc", func.__name__)
            integral = dblquad(lambda x, y: func([y, x]), 0, lower_x,
                               lambda x: lower_y / lower_x * x,
                               lambda x: upper_y / upper_x * x)[0]
            print("INTEGRATED:", integral)

            for quad_degree in [1, 3, 4, 7]:
                fe_q = FE_Q(dim, degree=1)
                quadrature = QGauss(dim, quad_degree)
                fe_values = FEValues(fe_q,
                                     quadrature,
                                     points=triangle_corners,
                                     edges=self.edges,
                                     is_dirichlet=lambda p: False,
                                     update_values=True,
                                     update_gradients=True)
                cell = Cell(dim, self.triangles[0])
                fe_values.reinit(cell)

                value = 0
                for q_index in range(quad_degree):
                    x_q = fe_values.quadrature_point(q_index)
                    value += func(x_q) * fe_values.JxW(q_index)

                print("gauss points", quad_degree, "polydeg", degree, "value",
                      value)

                try:
                    if degree <= degree_of_exactness[quad_degree]:
                        self.assertAlmostEqual(
                            value,
                            integral,
                            3,
                            msg=f"A polynomial of degree {degree} should be "
                            f"integrated exactly by a gaussian "
                            f"{quad_degree} point quadrature.")
                    else:
                        self.assertNotEqual(
                            value,
                            integral,
                            msg=f"{quad_degree} point Gauss should not handle "
                            f"polynomial of degree {degree}.")

                except AssertionError as e:
                    message += f"\n{e}"

            if message:
                raise AssertionError(message)
Exemple #4
0
class InterpolatedSolution(Function):
    def __init__(self, problem: Elasticity):
        self.problem = problem
        self.points = problem.points
        self.triangles = problem.triangles
        Cell2.points = problem.points

        quad_degree = 4
        gauss = QGauss(dim=2, n=quad_degree)
        self.fe_values = FEValues(self.problem.fe, gauss, self.points,
                                  self.problem.edges, lambda p: True,
                                  update_gradients=True)

        self.cached_gradient_point = None
        self.cached_gradient = None

    def value(self, p):
        for triangle in self.triangles:
            corners = self.points[triangle]
            if self.inside_triangle(p, corners):
                return self.interpolate(p, triangle)
        else:
            raise Exception("Point not inside mesh??")

    def gradient(self, p, value=None):
        if np.all(p == self.cached_gradient_point):
            return self.cached_gradient
        else:
            raise Exception("wut")

    def interpolate(self, p, triangle):

        cell = Cell2(2, triangle)
        self.fe_values.reinit(cell)

        numerical_sol = 0
        numerical_grad = 0

        # Interpolate the solution value and gradient in the quadrature
        # point.
        phis = []
        grad_phis = []

        for k in self.fe_values.dof_indices():
            # This is the global point index

            if k % 2 == 0:
                # k is even
                # Then the test function should be phi_k = [v_k, 0]
                value = [self.fe_values[0].fe.shape_value(k // 2, p), 0]
                grad = [self.fe_values[0].fe.shape_grad(k // 2), [0, 0]]
            else:
                # k is odd
                # Then the test function should be phi_k+1 = [0, v_k]
                value = [0, self.fe_values[1].fe.shape_value(k // 2, p)]
                # TODO ser gradientene riktige ut? har de litt små
                # verdier? skal vel være på størrelsen 1/h
                grad = [[0, 0], self.fe_values[1].fe.shape_grad(k // 2)]
            phis.append(np.array(value))
            grad_phis.append(np.array(grad))

        for i in self.fe_values.dof_indices():
            global_index = self.fe_values.loc2glob_dofs[i]
            numerical_sol += self.problem.solution[global_index] * phis[i]
            numerical_grad += self.problem.solution[global_index] * grad_phis[i]

        self.cached_gradient_point = p
        self.cached_gradient = numerical_grad
        return numerical_sol

    @staticmethod
    def inside_triangle(p, corners):
        indices = {0: [1, 2], 1: [0, 2], 2: [0, 1]}
        for i, corner in enumerate(corners):
            a = corners[indices[i][0]]
            b = corners[indices[i][1]]
            res = InterpolatedSolution.half_plane(p, [a, b], corners[i])
            if res < 0:
                return False
        return True

    @staticmethod
    def half_plane(p: np.ndarray, plane, third):
        """
        Returns a positive number if the point p is on the correct side of
        the plane.

        :param p: a point
        :param plane: as list of two numpy points
        :param third: the third point of the triangle
        :return:
        """

        normal = np.array([-(plane[1][1] - plane[0][1]),
                           plane[1][0] - plane[0][0]])
        ac = third - plane[0]
        if normal @ ac < 0:
            # Make sure the normal vector points into the triangle
            normal *= -1

        return normal @ (p - plane[0])
    def compute_error(self):
        print("Compute error")
        analytical_soln: Function = self.AnalyticalSoln()

        gauss = QGauss(dim=self.dim, n=self.degree)
        fe_values = FEValues(self.fe,
                             gauss,
                             self.points,
                             self.edges,
                             self.is_dirichlet,
                             update_gradients=True)

        l2_diff_integral = 0
        h1_diff_integral = 0
        for triangle in self.triangles:
            cell = Cell(self.dim, triangle)
            fe_values.reinit(cell)

            for q_index in fe_values.quadrature_point_indices():

                numerical_sol = 0
                numerical_grad = 0
                # Interpolate the solution value and gradient in the quadrature
                # point.
                for i in fe_values.dof_indices():
                    global_index = fe_values.local2global[i]
                    numerical_sol += self.solution[global_index] \
                                     * fe_values.shape_value(i, q_index)

                    numerical_grad += self.solution[global_index] \
                                      * fe_values.shape_grad(i, q_index)

                x_q = fe_values.quadrature_point(q_index)
                exact_solution = analytical_soln.value(x_q)
                exact_grad = analytical_soln.gradient(x_q)

                # Integrate the square difference of the two
                l2_diff_integral += (numerical_sol - exact_solution) ** 2 * \
                                    fe_values.JxW(q_index)

                # TODO calculate error in H1-norm
                # Integrate the square difference of the gradients.
                gradient_diff = numerical_grad - exact_grad
                h1_diff_integral += gradient_diff @ gradient_diff * \
                                    fe_values.JxW(q_index)

        l2_error = np.sqrt(l2_diff_integral)
        h1_error = np.sqrt(l2_diff_integral + h1_diff_integral)
        h1_error_semi_norm = np.sqrt(h1_diff_integral)

        Error = namedtuple("Error",
                           ["L2_error", "H1_error", "H1_semi_error", "h"])

        print("L2", l2_error)
        print("H1", h1_error)
        print("H1-semi", h1_error_semi_norm)
        print("h", self.h)
        return Error(L2_error=l2_error,
                     H1_error=h1_error,
                     H1_semi_error=h1_error_semi_norm,
                     h=self.h)
Exemple #6
0
    def assemble_system(self):
        """
        Set up the stiffness matrix.
        :return:
        """
        print("Assemble system")
        gauss = QGauss(dim=self.dim, n=self.quad_degree)
        fe_values = FEValues(self.fe,
                             gauss,
                             self.points,
                             self.edges,
                             self.is_dirichlet,
                             update_gradients=True)

        face_gauss = QGauss(dim=self.dim - 1, n=self.quad_degree)
        fe_face_values = FEFaceValues(self.fe, face_gauss, self.points,
                                      self.edges, self.is_dirichlet)

        rhs = self.RHS()
        # TODO only homogeneous Dirichlet supperted
        boundary_values = BoundaryValues()
        neumann_bdd_values = self.NeumannBD()

        for triangle in self.triangles:
            # Nå er vi i en celle
            # lar indeksen i punktlista være den globale nummereringen til
            # shape funksjoner
            # TODO build this initialization into the for-loop.
            cell = Cell(self.dim, triangle)
            fe_values.reinit(cell)

            for q_index in fe_values.quadrature_point_indices():
                x_q = fe_values.quadrature_point(q_index)

                for i in fe_values.dof_indices():
                    for j in fe_values.dof_indices():
                        val = fe_values.shape_grad(i, q_index) \
                              @ fe_values.shape_grad(j, q_index) \
                              * fe_values.JxW(q_index)  # (∇u_i, ∇v_j)

                        self.system_matrix[fe_values.loc2glob_dofs[i],
                                           fe_values.loc2glob_dofs[j]] += val

                    val = fe_values.shape_value(i, q_index) * rhs.value(x_q) \
                          * fe_values.JxW(q_index)  # (v_i, f)
                    self.system_rhs[fe_values.loc2glob_dofs[i]] += val

            for face in cell.face_iterators():
                if not face.at_boundary():
                    continue

                fe_face_values.reinit(cell, face)

                for q_index in fe_face_values.quadrature_point_indices():
                    x_q = fe_face_values.quadrature_point(q_index)

                    for j in fe_face_values.dof_indices():
                        g = neumann_bdd_values.value(x_q)
                        val = fe_face_values.shape_value(j, q_index) * g \
                              * fe_face_values.JxW(q_index)  # (g, v_j)
                        self.system_rhs[fe_face_values.loc2glob_dofs[j]] += val

        # TODO this fixes so the matrix is invertible, but could just have
        # removed those dofs that are not a dof from the matrix, so this
        # wouldn't be needed. Would then have to set boundary values in a
        # different way after the system is solved.
        for i, point in enumerate(self.points):
            if fe_values.is_boundary[i] and self.is_dirichlet(point):
                self.system_matrix[i, i] = 1
                # TODO this doesn't implement lifting functions, so this
                # only works with homogeneous dirichlet boundary conditions.
                self.system_rhs[i] = boundary_values.value(point)