Beispiel #1
0
    def gauss_newton_energy_norm(self, q):
        r"""Compute the energy norm of a field w.r.t. the Gauss-Newton operator

        The energy norm of a field :math:`q` w.r.t. the Gauss-Newton operator
        :math:`H` can be computed using one fewer linear solve than if we were
        to calculate the action of :math:`H\cdot q` on :math:`q`. This saves
        computation when using the conjugate gradient method to solve for the
        search direction.
        """
        u, p = self.state, self.parameter

        dE = derivative(self._E, u)
        dR = derivative(self._R, p)
        dF_du, dF_dp = self._dF_du, derivative(self._F, p)

        v = firedrake.Function(u.function_space())
        firedrake.solve(dF_du == action(dF_dp, q),
                        v,
                        self._bc,
                        solver_parameters=self._solver_params,
                        form_compiler_parameters=self._fc_params)

        return self._assemble(
            firedrake.energy_norm(derivative(dE, u), v) +
            firedrake.energy_norm(derivative(dR, p), q))
Beispiel #2
0
    def update_search_direction(self):
        r"""Solve the Gauss-Newton system for the new search direction using
        the preconditioned conjugate gradient method"""
        p, q, dJ = self.parameter, self.search_direction, self.gradient

        dR = derivative(self.regularization, self.parameter)
        Q = q.function_space()
        M = firedrake.TrialFunction(Q) * firedrake.TestFunction(Q) * dx + \
            derivative(dR, p)

        # Compute the preconditioned residual
        z = firedrake.Function(Q)
        firedrake.solve(M == -dJ,
                        z,
                        solver_parameters=self._solver_params,
                        form_compiler_parameters=self._fc_params)

        # This variable is a search direction for a search direction, which
        # is definitely not confusing at all.
        s = z.copy(deepcopy=True)
        q *= 0.0

        old_cost = np.inf
        while True:
            z_mnorm = self._assemble(firedrake.energy_norm(M, z))
            s_hnorm = self.gauss_newton_energy_norm(s)
            α = z_mnorm / s_hnorm

            δz = firedrake.Function(Q)
            g = self.gauss_newton_mult(s)
            firedrake.solve(M == g,
                            δz,
                            solver_parameters=self._solver_params,
                            form_compiler_parameters=self._fc_params)

            q += α * s
            z -= α * δz

            β = self._assemble(firedrake.energy_norm(M, z)) / z_mnorm
            s *= β
            s += z

            energy_norm = self.gauss_newton_energy_norm(q)
            cost = 0.5 * energy_norm + self._assemble(action(dJ, q))

            if (abs(old_cost - cost) /
                (0.5 * energy_norm) < self._search_tolerance):
                return

            old_cost = cost
Beispiel #3
0
    def step(self):
        r"""Take one step of the conjugate gradient iteration"""
        q = self.solution
        s = self.search_direction
        z = self.residual
        δz = self._delta_residual
        α = self.residual_energy / self.search_direction_energy

        Gs = self.operator_product
        dJ = self._rhs
        delta_energy = α * (self._assemble(action(Gs, q)) +
                            0.5 * α * self.search_direction_energy)
        self._energy += delta_energy
        self._objective += delta_energy + α * self._assemble(action(dJ, s))

        q.assign(q + Constant(α) * s)
        z.assign(z - Constant(α) * δz)

        M = self.preconditioner
        residual_energy = self._assemble(firedrake.energy_norm(M, z))
        β = residual_energy / self.residual_energy
        s.assign(Constant(β) * s + z)

        self.update_state()
        self._residual_energy = residual_energy
        Gs = self.operator_product
        self._search_direction_energy = self._assemble(action(Gs, s))

        self._iteration += 1
Beispiel #4
0
    def reinit(self):
        r"""Restart the solution to 0"""
        self._iteration = 0

        M = self._preconditioner
        z = self.residual
        s = self.search_direction
        Gs = self.operator_product
        Q = z.function_space()

        self.solution.assign(firedrake.Function(Q))
        self._residual_solver.solve()
        s.assign(z)
        self._residual_energy = self._assemble(firedrake.energy_norm(M, z))

        self.update_state()
        self._search_direction_energy = self._assemble(action(Gs, s))

        self._energy = 0.
        self._objective = 0.