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 _newton_solve(z, E, scale, tolerance=1e-6, armijo=1e-4, max_iterations=50):
    F = derivative(E, z)
    H = derivative(F, z)

    Q = z.function_space()
    bc = firedrake.DirichletBC(Q, 0, 'on_boundary')
    p = firedrake.Function(Q)
    for iteration in range(max_iterations):
        firedrake.solve(H == -F, p, bc,
            solver_parameters={'ksp_type': 'preonly', 'pc_type': 'lu'})

        dE_dp = assemble(action(F, p))
        α = 1.0
        E0 = assemble(E)
        Ez = assemble(replace(E, {z: z + firedrake.Constant(α) * p}))
        while (Ez > E0 + armijo * α * dE_dp) or np.isnan(Ez):
            α /= 2
            Ez = assemble(replace(E, {z: z + firedrake.Constant(α) * p}))

        z.assign(z + firedrake.Constant(α) * p)
        if abs(dE_dp) < tolerance * assemble(scale):
            return z

    raise ValueError("Newton solver failed to converge after {0} iterations"
                     .format(max_iterations))
Beispiel #3
0
    def __init__(self, problem, tolerance, solver_parameters=None, **kwargs):
        r"""Solve a MinimizationProblem using Newton's method with backtracking
        line search

        Parameters
        ----------
        problem : MinimizationProblem
            The particular problem instance to solve
        tolerance : float
            dimensionless tolerance for when to stop iterating, measured with
            with respect to the problem's scale functional
        solver_parameters : dict (optional)
            Linear solve parameters for computing the search direction
        armijo : float (optional)
            Parameter in the Armijo condition for line search; defaults to
            1e-4, see Nocedal and Wright
        contraction : float (optional)
            shrinking factor for backtracking line search; defaults to .5
        max_iterations : int (optional)
            maximum number of outer-level Newton iterations; defaults to 50
        """
        self.problem = problem
        self.tolerance = tolerance
        if solver_parameters is None:
            solver_parameters = default_solver_parameters

        self.armijo = kwargs.pop("armijo", 1e-4)
        self.contraction = kwargs.pop("contraction", 0.5)
        self.max_iterations = kwargs.pop("max_iterations", 50)

        u = self.problem.u
        V = u.function_space()
        v = firedrake.Function(V)
        self.v = v

        E = self.problem.E
        self.F = firedrake.derivative(E, u)
        self.J = firedrake.derivative(self.F, u)
        self.dE_dv = firedrake.action(self.F, v)

        bcs = None
        if self.problem.bcs:
            bcs = firedrake.homogenize(self.problem.bcs)
        problem = firedrake.LinearVariationalProblem(
            self.J,
            -self.F,
            v,
            bcs,
            constant_jacobian=False,
            form_compiler_parameters=self.problem.form_compiler_parameters,
        )
        self.search_direction_solver = firedrake.LinearVariationalSolver(
            problem, solver_parameters=solver_parameters
        )

        self.search_direction_solver.solve()
        self.t = firedrake.Constant(0.0)
        self.iteration = 0
Beispiel #4
0
    def evaluate_adj_component(self,
                               inputs,
                               adj_inputs,
                               block_variable,
                               idx,
                               prepared=None):
        if not self.linear and self.func == block_variable.output:
            # We are not able to calculate derivatives wrt initial guess.
            return None
        F_form = prepared["form"]
        adj_sol = prepared["adj_sol"]
        adj_sol_bdy = prepared["adj_sol_bdy"]
        c = block_variable.output
        c_rep = block_variable.saved_output

        if isinstance(c, firedrake.Function):
            trial_function = firedrake.TrialFunction(c.function_space())
        elif isinstance(c, firedrake.Constant):
            mesh = self.compat.extract_mesh_from_form(F_form)
            trial_function = firedrake.TrialFunction(
                c._ad_function_space(mesh))
        elif isinstance(c, firedrake.DirichletBC):
            tmp_bc = self.compat.create_bc(
                c,
                value=self.compat.extract_subfunction(adj_sol_bdy,
                                                      c.function_space()))
            return [tmp_bc]
        elif isinstance(c, self.compat.MeshType):
            # Using CoordianteDerivative requires us to do action before
            # differentiating, might change in the future.
            F_form_tmp = firedrake.action(F_form, adj_sol)
            X = firedrake.SpatialCoordinate(c_rep)
            dFdm = firedrake.derivative(
                -F_form_tmp, X, firedrake.TestFunction(c._ad_function_space()))

            dFdm = self.compat.assemble_adjoint_value(dFdm,
                                                      **self.assemble_kwargs)
            return dFdm

        # dFdm_cache works with original variables, not block saved outputs.
        if c in self._dFdm_cache:
            dFdm = self._dFdm_cache[c]
        else:
            dFdm = -firedrake.derivative(self.lhs, c, trial_function)
            dFdm = firedrake.adjoint(dFdm)
            self._dFdm_cache[c] = dFdm

        # Replace the form coefficients with checkpointed values.
        replace_map = self._replace_map(dFdm)
        replace_map[self.func] = self.get_outputs()[0].saved_output
        dFdm = replace(dFdm, replace_map)

        dFdm = dFdm * adj_sol
        dFdm = self.compat.assemble_adjoint_value(dFdm, **self.assemble_kwargs)

        return dFdm
Beispiel #5
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 #6
0
    def solve(
        self,
        parameters={
            "snes_type": "newtonls",
            "snes_monitor": True,
            "ksp_type": "preonly",
            "pc_type": "lu",
            "mat_type": "aij",
            "pc_factor_mat_solver_type": "mumps"
        }):

        problem = fe.NonlinearVariationalProblem(
            F=self.variational_form_residual,
            u=self.solution,
            bcs=self.dirichlet_boundary_conditions,
            J=fe.derivative(self.variational_form_residual, self.solution))

        solver = fe.NonlinearVariationalSolver(problem=problem,
                                               solver_parameters=parameters)

        solver.solve()

        self.snes_iteration_count += solver.snes.getIterationNumber()

        return self.solution
Beispiel #7
0
    def form(self, pc, test, trial):
        """Implements the interface for AuxiliaryOperatorPC."""
        appctx = self.get_appctx(pc)
        F = appctx["F"]
        butcher_tableau = appctx["butcher_tableau"]
        t = appctx["t"]
        dt = appctx["dt"]
        u0 = appctx["u0"]
        bcs = appctx["bcs"]
        bc_type = appctx["bc_type"]
        splitting = appctx["splitting"]
        nullspace = appctx["nullspace"]

        # Make a modified Butcher tableau, probably with some kind
        # of sparser structure (e.g. LD part of LDU factorization)
        Atilde = self.getAtilde(butcher_tableau.A)
        butcher_new = copy.deepcopy(butcher_tableau)
        butcher_new.A = Atilde

        # Get the UFL for the system with the modified Butcher tableau
        Fnew, w, bcnew, bignsp, _ = getForm(F, butcher_new, t, dt, u0, bcs,
                                            bc_type, splitting, nullspace)

        # Now we get the Jacobian for the modified system,
        # which becomes the auxiliary operator!
        test_old = Fnew.arguments()[0]
        a = replace(derivative(Fnew, w, du=trial),
                    {test_old: test})

        return a, bcnew
Beispiel #8
0
    def setup(self, **kwargs):
        for name, field in kwargs.items():
            if name in self._fields.keys():
                self._fields[name].assign(field)
            else:
                self._fields[name] = utilities.copy(field)

        # Create homogeneous BCs for the Dirichlet part of the boundary
        u = self._fields.get('velocity', self._fields.get('u'))
        V = u.function_space()
        bcs = firedrake.DirichletBC(V, u, self._dirichlet_ids)
        if not self._dirichlet_ids:
            bcs = None

        # Find the numeric IDs for the ice front
        boundary_ids = u.ufl_domain().exterior_facets.unique_markers
        ice_front_ids_comp = set(self._dirichlet_ids + self._side_wall_ids)
        ice_front_ids = list(set(boundary_ids) - ice_front_ids_comp)

        # Create the action and scale functionals
        _kwargs = {
            'side_wall_ids': self._side_wall_ids,
            'ice_front_ids': ice_front_ids
        }
        action = self._model.action(**self._fields, **_kwargs)
        F = firedrake.derivative(action, u)

        degree = self._model.quadrature_degree(**self._fields)
        params = {'form_compiler_parameters': {'quadrature_degree': degree}}
        problem = firedrake.NonlinearVariationalProblem(F, u, bcs, **params)
        self._solver = firedrake.NonlinearVariationalSolver(
            problem, solver_parameters=self._solver_parameters)
def initial_values(sim):

    print("Solving steady heat driven cavity to obtain initial values")

    Ra = 2.518084e6

    Pr = 6.99

    sim.grashof_number = sim.grashof_number.assign(Ra / Pr)

    sim.prandtl_number = sim.prandtl_number.assign(Pr)

    dim = sim.mesh.geometric_dimension()

    T_c = sim.cold_wall_temperature.__float__()

    w = fe.interpolate(
        fe.Expression((0., ) + (0., ) * dim + (T_c, ), element=sim.element),
        sim.function_space)

    F = heat_driven_cavity_variational_form_residual(
        sim=sim, solution=w) * fe.dx(degree=sim.quadrature_degree)

    T_h = sim.hot_wall_temperature.__float__()

    problem = fe.NonlinearVariationalProblem(
        F=F,
        u=w,
        bcs=dirichlet_boundary_conditions(sim),
        J=fe.derivative(F, w))

    solver = fe.NonlinearVariationalSolver(problem=problem,
                                           solver_parameters={
                                               "snes_type": "newtonls",
                                               "snes_monitor": True,
                                               "ksp_type": "preonly",
                                               "pc_type": "lu",
                                               "mat_type": "aij",
                                               "pc_factor_mat_solver_type":
                                               "mumps"
                                           })

    def solve():

        solver.solve()

        return w

    w, _ = \
        sapphire.continuation.solve_with_bounded_regularization_sequence(
            solve = solve,
            solution = w,
            backup_solution = fe.Function(w),
            regularization_parameter = sim.grashof_number,
            initial_regularization_sequence = (
                0., sim.grashof_number.__float__()))

    return w
Beispiel #10
0
 def derivative_form(self, w):
     if args.discretisation == "pkp0":
         fd.warning(fd.RED % "Using residual without grad-div")
         F = solver.F_nograddiv
     else:
         F = solver.F
     F = fd.replace(F, {F.arguments()[0]: solver.z_adj})
     L = F + self.value_form()
     X = fd.SpatialCoordinate(solver.z.ufl_domain())
     return fd.derivative(L, X, w)
Beispiel #11
0
    def initialize(self, init_solution):
        self.solution_old.assign(init_solution)
        u, p = firedrake.split(self.solution)
        u_old, p_old = self.solution_old.split()
        u_star_theta = (1-self.theta)*u_old + self.theta*self.u_star
        u_theta = (1-self.theta)*u_old + self.theta*u
        p_theta = (1-self.theta_p)*p_old + self.theta_p*p
        u_lag, p_lag = self.solution_lag.split()
        u_lag_theta = (1-self.theta)*u_old + self.theta*u_lag
        p_lag_theta = (1-self.theta_p)*p_old + self.theta_p*p_lag

        # setup predictor solve, this solves for u_start only using a fixed p_lag_theta for pressure
        self.fields_star = self.fields.copy()
        self.fields_star['pressure'] = p_lag_theta

        self.Fstar = self.equations[0].mass_term(self.u_star_test, self.u_star-u_old)
        self.Fstar -= self.dt_const*self.equations[0].residual(self.u_star_test, u_star_theta, u_lag_theta, self.fields_star, bcs=self.bcs)
        self.predictor_problem = firedrake.NonlinearVariationalProblem(self.Fstar, self.u_star)
        self.predictor_solver = firedrake.NonlinearVariationalSolver(self.predictor_problem,
                                                                     solver_parameters=self.predictor_solver_parameters,
                                                                     options_prefix='predictor_momentum')

        # the correction solve, solving the coupled system:
        #   u1 = u* - dt*G ( p_theta - p_lag_theta)
        #   div(u1) = 0
        self.F = self.equations[0].mass_term(self.u_test, u-self.u_star)

        pg_term = [term for term in self.equations[0]._terms if isinstance(term, PressureGradientTerm)][0]
        pg_fields = self.fields.copy()
        # note that p_theta-p_lag_theta = theta_p*(p1-p_lag)
        pg_fields['pressure'] = self.theta_p * (p - p_lag)
        self.F -= self.dt_const*pg_term.residual(self.u_test, u_theta, u_lag_theta, pg_fields, bcs=self.bcs)

        div_term = [term for term in self.equations[1]._terms if isinstance(term, DivergenceTerm)][0]
        div_fields = self.fields.copy()
        div_fields['velocity'] = u
        self.F -= self.dt_const*div_term.residual(self.p_test, p_theta, p_lag_theta, div_fields, bcs=self.bcs)

        W = self.solution.function_space()
        if self.pressure_nullspace is None:
            mixed_nullspace = None
        else:
            mixed_nullspace = firedrake.MixedVectorSpaceBasis(W, [W.sub(0), self.pressure_nullspace])

        self.problem = firedrake.NonlinearVariationalProblem(self.F, self.solution)
        self.solver = firedrake.NonlinearVariationalSolver(self.problem,
                                                           solver_parameters=self.solver_parameters,
                                                           appctx={'a': firedrake.derivative(self.F, self.solution),
                                                                   'schur_nullspace': self.pressure_nullspace,
                                                                   'dt': self.dt_const, 'dx': self.equations[1].dx,
                                                                   'ds': self.equations[1].ds, 'bcs': self.bcs, 'n': div_term.n},
                                                           nullspace=mixed_nullspace, transpose_nullspace=mixed_nullspace,
                                                           options_prefix=self.name)

        self._initialized = True
Beispiel #12
0
    def initialize_solvers(self):
        # Kinematics                # Right Cauchy-Green tensor
        if self.nonlin:
            d = self.X.geometric_dimension()
            I = fd.Identity(d)  # Identity tensor
            F = I + fd.grad(self.X)  # Deformation gradient
            C = F.T * F
            E = (C - I) / 2.  # Green-Lagrangian strain
#            E = 1./2.*( fd.grad(self.X).T + fd.grad(self.X) + fd.grad(self.X).T * fd.grad(self.X) ) # alternative equivalent definition
        else:
            E = 1. / 2. * (fd.grad(self.X).T + fd.grad(self.X)
                           )  # linear strain

        self.W = (self.lam / 2.) * (fd.tr(E))**2 + self.mu * fd.tr(E * E)
        #        f = fd.Constant((0, 0, -self.g)) # body force / rho
        #        T = self.surface_force()

        # Total potential energy
        Pi = self.W * fd.dx
        # Compute first variation of Pi (directional derivative about X in the direction of v)
        F_expr = fd.derivative(Pi, self.X, self.v)

        self.DBC = fd.DirichletBC(self.V, fd.as_vector([0., 0., 0.]),
                                  self.bottom_id)

        #        delX = fd.nabla_grad(self.X)
        #        delv_B = fd.nabla_grad(self.v)
        #        T_x_dv = self.lam * fd.div(self.X) * fd.div(self.v) \
        #                + self.mu * ( fd.inner( delX, delv_B + fd.transpose(delv_B) ) )

        self.a_U = fd.dot(self.trial, self.v) * fd.dx
        #        self.L_U = ( fd.dot( self.U, self.v ) - self.dt/2./self.rho * T_x_dv ) * fd.dx
        self.L_U = fd.dot(
            self.U, self.v) * fd.dx - self.dt / 2. / self.rho * F_expr  #\
        #                  + self.dt/2./self.rho*fd.dot(T,self.v)*fd.ds(1) # surface force at x==0 plane
        # + self.dt/2.*fd.dot(f,self.v)*fd.dx # body force
        self.a_X = fd.dot(self.trial, self.v) * fd.dx
        #        self.L_interface = fd.dot(self.phi_vect, self.v) * fd.ds(self.interface_id)
        self.L_X = fd.dot((self.X + self.dt * self.U), self.v) * fd.dx  #\
        #                    - self.dt/self.rho * self.L_interface

        self.LVP_U = fd.LinearVariationalProblem(self.a_U,
                                                 self.L_U,
                                                 self.U,
                                                 bcs=[self.DBC])
        self.LVS_U = fd.LinearVariationalSolver(self.LVP_U)
        self.LVP_X = fd.LinearVariationalProblem(self.a_X,
                                                 self.L_X,
                                                 self.X,
                                                 bcs=[self.DBC])
        self.LVS_X = fd.LinearVariationalSolver(self.LVP_X)
Beispiel #13
0
    def eval_dJdw(self):
        u = self.u
        v = self.v
        J = self.J
        F = self.F
        X = self.X
        w = self.w
        V = self.V
        params = self.params

        solve(self.F == 0, u, bcs=self.bc, solver_parameters=params)
        bil_form = adjoint(derivative(F, u))
        rhs = -derivative(J, u)
        u_adj = Function(V)
        solve(assemble(bil_form),
              u_adj,
              assemble(rhs),
              bcs=self.bc,
              solver_parameters=params)
        L = J + replace(self.F, {v: u_adj})
        self.L = L
        self.bil_form = bil_form
        return assemble(derivative(L, X, w))
def solve_firedrake(q, kappa0, kappa1):

    x = firedrake.SpatialCoordinate(mesh)
    f = x[0]

    u = firedrake.Function(V)
    bcs = [firedrake.DirichletBC(V, firedrake.Constant(0.0), "on_boundary")]

    inner, grad, dx = ufl.inner, ufl.grad, ufl.dx
    JJ = 0.5 * inner(kappa0 * grad(u), grad(u)) * dx - q * kappa1 * f * u * dx
    v = firedrake.TestFunction(V)
    F = firedrake.derivative(JJ, u, v)
    firedrake.solve(F == 0, u, bcs=bcs)
    return u
Beispiel #15
0
    def gauss_newton_mult(self, q):
        """Multiply a field by the Gauss-Newton operator"""
        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)

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

        v = firedrake.Function(u.function_space())
        firedrake.solve(adjoint(dF_du) == derivative(dE, u, w),
                        v,
                        self._bc,
                        solver_parameters=self._solver_params,
                        form_compiler_parameters=self._fc_params)

        return action(adjoint(dF_dp), v) + derivative(dR, p, q)
Beispiel #16
0
 def solve(self, q, f, dirichlet_ids=[], **kwargs):
     u = firedrake.Function(f.function_space())
     L = self.action(q, u, f)
     F = firedrake.derivative(L, u)
     V = u.function_space()
     bc = firedrake.DirichletBC(V, firedrake.Constant(0), dirichlet_ids)
     firedrake.solve(F == 0,
                     u,
                     bc,
                     solver_parameters={
                         'ksp_type': 'preonly',
                         'pc_type': 'lu'
                     })
     return u
Beispiel #17
0
    def _setup(self, **kwargs):
        q = kwargs["q"].copy(deepcopy=True)
        f = kwargs["f"].copy(deepcopy=True)
        u = kwargs["u"].copy(deepcopy=True)

        L = self.model.action(u=u, q=q, f=f)
        F = firedrake.derivative(L, u)
        V = u.function_space()
        bc = firedrake.DirichletBC(V, u, self.dirichlet_ids)
        params = {
            "solver_parameters": {
                "ksp_type": "preonly",
                "pc_type": "lu"
            }
        }
        problem = firedrake.NonlinearVariationalProblem(F, u, bc)
        self._solver = firedrake.NonlinearVariationalSolver(problem, **params)
        self._fields = {"q": q, "f": f, "u": u}
Beispiel #18
0
    def _setup(self, problem, callback=(lambda s: None)):
        self._problem = problem
        self._callback = callback

        self._p = problem.parameter.copy(deepcopy=True)
        self._u = problem.state.copy(deepcopy=True)

        self._model_args = dict(**problem.model_args,
                                dirichlet_ids=problem.dirichlet_ids)
        u_name, p_name = problem.state_name, problem.parameter_name
        args = dict(**self._model_args, **{u_name: self._u, p_name: self._p})

        # Make the form compiler use a reasonable number of quadrature points
        degree = problem.model.quadrature_degree(**args)
        self._fc_params = {'quadrature_degree': degree}

        # Create the error, regularization, and barrier functionals
        self._E = problem.objective(self._u)
        self._R = problem.regularization(self._p)
        self._J = self._E + self._R

        # Create the weak form of the forward model, the adjoint state, and
        # the derivative of the objective functional
        self._F = derivative(problem.model.action(**args), self._u)
        self._dF_du = derivative(self._F, self._u)

        # Create a search direction
        dR = derivative(self._R, self._p)
        self._solver_params = {'ksp_type': 'preonly', 'pc_type': 'lu'}
        Q = self._p.function_space()
        self._q = firedrake.Function(Q)

        # Create the adjoint state variable
        V = self.state.function_space()
        self._λ = firedrake.Function(V)
        dF_dp = derivative(self._F, self._p)

        # Create Dirichlet BCs where they apply for the adjoint solve
        rank = self._λ.ufl_element().num_sub_elements()
        if rank == 0:
            zero = firedrake.Constant(0)
        else:
            zero = firedrake.as_vector((0, ) * rank)
        self._bc = firedrake.DirichletBC(V, zero, problem.dirichlet_ids)

        # Create the derivative of the objective functional
        self._dE = derivative(self._E, self._u)
        dR = derivative(self._R, self._p)
        self._dJ = (action(adjoint(dF_dp), self._λ) + dR)
Beispiel #19
0
def firedrakeSmooth(q0, alpha=2e3):
    """[summary]

    Parameters
    ----------
    q0 : firedrake function
        firedrake function to be smooth
    alpha : float, optional
        parameter that controls the amount of smoothing, which is
        approximately the smoothing lengthscale in m, by default 2e3
    Returns
    -------
    q firedrake interp function
        smoothed result
    """
    q = q0.copy(deepcopy=True)
    J = 0.5 * ((q - q0)**2 + alpha**2 * inner(grad(q), grad(q))) * dx
    F = firedrake.derivative(J, q)
    firedrake.solve(F == 0, q)
    return q
Beispiel #20
0
 def wrapper(self, *args, **kwargs):
     from firedrake import derivative, adjoint, TrialFunction
     init(self, *args, **kwargs)
     self._ad_F = self.F
     self._ad_u = self.u
     self._ad_bcs = self.bcs
     self._ad_J = self.J
     try:
         # Some forms (e.g. SLATE tensors) are not currently
         # differentiable.
         dFdu = derivative(self.F, self.u,
                           TrialFunction(self.u.function_space()))
         self._ad_adj_F = adjoint(dFdu)
     except TypeError:
         self._ad_adj_F = None
     self._ad_kwargs = {
         'Jp': self.Jp,
         'form_compiler_parameters': self.form_compiler_parameters,
         'is_linear': self.is_linear
     }
     self._ad_count_map = {}
Beispiel #21
0
def hyperelasticity(mesh, degree):
    V = VectorFunctionSpace(mesh, 'Q', degree)
    v = TestFunction(V)
    du = TrialFunction(V)  # Incremental displacement
    u = Function(V)  # Displacement from previous iteration
    B = Function(V)  # Body force per unit mass
    # Kinematics
    I = Identity(mesh.topological_dimension())
    F = I + grad(u)  # Deformation gradient
    C = F.T * F  # Right Cauchy-Green tensor
    E = (C - I) / 2  # Euler-Lagrange strain tensor
    E = variable(E)
    # Material constants
    mu = Constant(1.0)  # Lame's constants
    lmbda = Constant(0.001)
    # Strain energy function (material model)
    psi = lmbda / 2 * (tr(E)**2) + mu * tr(E * E)
    S = diff(psi, E)  # Second Piola-Kirchhoff stress tensor
    PK = F * S  # First Piola-Kirchoff stress tensor
    # Variational problem
    return derivative((inner(PK, grad(v)) - inner(B, v)) * dx, u, du)
Beispiel #22
0
    def setup(self, **kwargs):
        for name, field in kwargs.items():
            if name in self._fields.keys():
                self._fields[name].assign(field)
            else:
                if isinstance(field, firedrake.Constant):
                    self._fields[name] = firedrake.Constant(field)
                elif isinstance(field, firedrake.Function):
                    self._fields[name] = field.copy(deepcopy=True)
                else:
                    raise TypeError(
                        "Input %s field has type %s, must be Constant or Function!"
                        % (name, type(field))
                    )

        # Create homogeneous BCs for the Dirichlet part of the boundary
        u = self._fields["velocity"]
        V = u.function_space()
        bcs = firedrake.DirichletBC(V, u, self._dirichlet_ids)
        if not self._dirichlet_ids:
            bcs = None

        # Find the numeric IDs for the ice front
        boundary_ids = u.ufl_domain().exterior_facets.unique_markers
        ice_front_ids_comp = set(self._dirichlet_ids + self._side_wall_ids)
        ice_front_ids = list(set(boundary_ids) - ice_front_ids_comp)

        # Create the action and scale functionals
        _kwargs = {"side_wall_ids": self._side_wall_ids, "ice_front_ids": ice_front_ids}
        action = self._model.action(**self._fields, **_kwargs)
        F = firedrake.derivative(action, u)

        problem = firedrake.NonlinearVariationalProblem(F, u, bcs)
        self._solver = firedrake.NonlinearVariationalSolver(
            problem, solver_parameters=self._solver_parameters
        )
Beispiel #23
0
    def diagnostic_solve(self, u0, h, s, A, **kwargs):
        r"""Solve for the ice velocity from the thickness and surface
        elevation

        Parameters
        ----------
        u0 : firedrake.Function
            Ice velocity
        h : firedrake.Function
            Ice thickness
        s : firedrake.Function
            Ice surface elevation
        A : firedrake.Function or firedrake.Constant
            Rate factor

        Returns
        -------
        u : firedrake.Function
            Ice velocity

        Other parameters
        ----------------
        **kwargs
            All other keyword arguments will be passed on to the
            'mass', 'gravity' and 'penalty' functions
            that was set when this model object was initialized
        """
        u = u0.copy(deepcopy=True)
        action = self.action(u=u, h=h, s=s, A=A, **kwargs)

        F = firedrake.derivative(action, u)
        firedrake.solve(F == 0,
                        u,
                        form_compiler_parameters={'quadrature_degree': 4})

        return u
Beispiel #24
0
    def solve(self) -> fe.Function:
        """Set up the problem and solver, and solve.
        
        This is a JIT (just in time), ensuring that the problem and 
        solver setup are up-to-date before calling the solver.
        All compiled objects are cached, so the JIT problem and solver 
        setup does not have any significant performance overhead.
        """
        problem = fe.NonlinearVariationalProblem(
            F=self.weak_form_residual(),
            u=self.solution,
            bcs=self.dirichlet_boundary_conditions(),
            J=fe.derivative(self.weak_form_residual(), self.solution))

        solver = fe.NonlinearVariationalSolver(
            problem=problem,
            nullspace=self.nullspace(),
            solver_parameters=self.solver_parameters)

        solver.solve()

        self.snes_iteration_count += solver.snes.getIterationNumber()

        return self.solution
Beispiel #25
0
    def _setup(self, problem, callback=(lambda s: None)):
        self._problem = problem
        self._callback = callback

        self._p = problem.parameter.copy(deepcopy=True)
        self._u = problem.state.copy(deepcopy=True)

        self._solver = self.problem.solver_type(self.problem.model,
                                                **self.problem.solver_kwargs)
        u_name, p_name = problem.state_name, problem.parameter_name
        solve_kwargs = dict(**problem.diagnostic_solve_kwargs, **{
            u_name: self._u,
            p_name: self._p
        })

        # Make the form compiler use a reasonable number of quadrature points
        degree = problem.model.quadrature_degree(**solve_kwargs)
        self._fc_params = {'quadrature_degree': degree}

        # Create the error, regularization, and barrier functionals
        self._E = problem.objective(self._u)
        self._R = problem.regularization(self._p)
        self._J = self._E + self._R

        # Create the weak form of the forward model, the adjoint state, and
        # the derivative of the objective functional
        A = problem.model.action(**solve_kwargs)
        self._F = derivative(A, self._u)
        self._dF_du = derivative(self._F, self._u)

        # Create a search direction
        dR = derivative(self._R, self._p)
        # TODO: Make this customizable
        self._solver_params = default_solver_parameters
        Q = self._p.function_space()
        self._q = firedrake.Function(Q)

        # Create the adjoint state variable
        V = self.state.function_space()
        self._λ = firedrake.Function(V)
        dF_dp = derivative(self._F, self._p)

        # Create Dirichlet BCs where they apply for the adjoint solve
        rank = self._λ.ufl_element().num_sub_elements()
        if rank == 0:
            zero = Constant(0)
        else:
            zero = firedrake.as_vector((0, ) * rank)
        self._bc = firedrake.DirichletBC(V, zero, problem.dirichlet_ids)

        # Create the derivative of the objective functional
        self._dE = derivative(self._E, self._u)
        dR = derivative(self._R, self._p)
        self._dJ = (action(adjoint(dF_dp), self._λ) + dR)

        # Create problem and solver objects for the adjoint state
        L = adjoint(self._dF_du)
        adjoint_problem = firedrake.LinearVariationalProblem(
            L,
            -self._dE,
            self._λ,
            self._bc,
            form_compiler_parameters=self._fc_params,
            constant_jacobian=False)
        self._adjoint_solver = firedrake.LinearVariationalSolver(
            adjoint_problem, solver_parameters=self._solver_params)
Beispiel #26
0
def heat_exchanger_optimization(mu=0.03, n_iters=1000):

    output_dir = "2D/"

    path = os.path.abspath(__file__)
    dir_path = os.path.dirname(path)
    mesh = fd.Mesh(f"{dir_path}/2D_mesh.msh")
    # Perturb the mesh coordinates. Necessary to calculate shape derivatives
    S = fd.VectorFunctionSpace(mesh, "CG", 1)
    s = fd.Function(S, name="deform")
    mesh.coordinates.assign(mesh.coordinates + s)

    # Initial level set function
    x, y = fd.SpatialCoordinate(mesh)
    PHI = fd.FunctionSpace(mesh, "CG", 1)
    phi_expr = sin(y * pi / 0.2) * cos(x * pi / 0.2) - fd.Constant(0.8)
    # Avoid recording the operation interpolate into the tape.
    # Otherwise, the shape derivatives will not be correct
    with fda.stop_annotating():
        phi = fd.interpolate(phi_expr, PHI)
        phi.rename("LevelSet")
        fd.File(output_dir + "phi_initial.pvd").write(phi)

    # Physics
    mu = fd.Constant(mu)  # viscosity
    alphamin = 1e-12
    alphamax = 2.5 / (2e-4)
    parameters = {
        "mat_type": "aij",
        "ksp_type": "preonly",
        "ksp_converged_reason": None,
        "pc_type": "lu",
        "pc_factor_mat_solver_type": "mumps",
    }
    stokes_parameters = parameters
    temperature_parameters = parameters
    u_inflow = 2e-3
    tin1 = fd.Constant(10.0)
    tin2 = fd.Constant(100.0)

    P2 = fd.VectorElement("CG", mesh.ufl_cell(), 2)
    P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1)
    TH = P2 * P1
    W = fd.FunctionSpace(mesh, TH)

    U = fd.TrialFunction(W)
    u, p = fd.split(U)
    V = fd.TestFunction(W)
    v, q = fd.split(V)

    epsilon = fd.Constant(10000.0)

    def hs(phi, epsilon):
        return fd.Constant(alphamax) * fd.Constant(1.0) / (
            fd.Constant(1.0) + exp(-epsilon * phi)) + fd.Constant(alphamin)

    def stokes(phi, BLOCK_INLET_MOUTH, BLOCK_OUTLET_MOUTH):
        a_fluid = mu * inner(grad(u), grad(v)) - div(v) * p - q * div(u)
        darcy_term = inner(u, v)
        return (a_fluid * dx + hs(phi, epsilon) * darcy_term * dx(0) +
                alphamax * darcy_term *
                (dx(BLOCK_INLET_MOUTH) + dx(BLOCK_OUTLET_MOUTH)))

    # Dirichlet boundary conditions
    inflow1 = fd.as_vector([
        u_inflow * sin(
            ((y - (line_sep -
                   (dist_center + inlet_width))) * pi) / inlet_width),
        0.0,
    ])
    inflow2 = fd.as_vector([
        u_inflow * sin(((y - (line_sep + dist_center)) * pi) / inlet_width),
        0.0,
    ])

    noslip = fd.Constant((0.0, 0.0))

    # Stokes 1
    bcs1_1 = fd.DirichletBC(W.sub(0), noslip, WALLS)
    bcs1_2 = fd.DirichletBC(W.sub(0), inflow1, INLET1)
    bcs1_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET1)
    bcs1_4 = fd.DirichletBC(W.sub(0), noslip, INLET2)
    bcs1_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET2)
    bcs1 = [bcs1_1, bcs1_2, bcs1_3, bcs1_4, bcs1_5]

    # Stokes 2
    bcs2_1 = fd.DirichletBC(W.sub(0), noslip, WALLS)
    bcs2_2 = fd.DirichletBC(W.sub(0), inflow2, INLET2)
    bcs2_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET2)
    bcs2_4 = fd.DirichletBC(W.sub(0), noslip, INLET1)
    bcs2_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET1)
    bcs2 = [bcs2_1, bcs2_2, bcs2_3, bcs2_4, bcs2_5]

    # Forward problems
    U1, U2 = fd.Function(W), fd.Function(W)
    L = inner(fd.Constant((0.0, 0.0, 0.0)), V) * dx
    problem = fd.LinearVariationalProblem(stokes(-phi, INMOUTH2, OUTMOUTH2),
                                          L,
                                          U1,
                                          bcs=bcs1)
    solver_stokes1 = fd.LinearVariationalSolver(
        problem,
        solver_parameters=stokes_parameters,
        options_prefix="stokes_1")
    solver_stokes1.solve()
    problem = fd.LinearVariationalProblem(stokes(phi, INMOUTH1, OUTMOUTH1),
                                          L,
                                          U2,
                                          bcs=bcs2)
    solver_stokes2 = fd.LinearVariationalSolver(
        problem,
        solver_parameters=stokes_parameters,
        options_prefix="stokes_2")
    solver_stokes2.solve()

    # Convection difussion equation
    ks = fd.Constant(1e0)
    cp_value = 5.0e5
    cp = fd.Constant(cp_value)
    T = fd.FunctionSpace(mesh, "DG", 1)
    t = fd.Function(T, name="Temperature")
    w = fd.TestFunction(T)

    # Mesh-related functions
    n = fd.FacetNormal(mesh)
    h = fd.CellDiameter(mesh)
    u1, p1 = fd.split(U1)
    u2, p2 = fd.split(U2)

    def upwind(u):
        return (dot(u, n) + abs(dot(u, n))) / 2.0

    u1n = upwind(u1)
    u2n = upwind(u2)

    # Penalty term
    alpha = fd.Constant(500.0)
    # Bilinear form
    a_int = dot(grad(w), ks * grad(t) - cp * (u1 + u2) * t) * dx

    a_fac = (fd.Constant(-1.0) * ks * dot(avg(grad(w)), jump(t, n)) * dS +
             fd.Constant(-1.0) * ks * dot(jump(w, n), avg(grad(t))) * dS +
             ks("+") *
             (alpha("+") / avg(h)) * dot(jump(w, n), jump(t, n)) * dS)

    a_vel = (dot(
        jump(w),
        cp * (u1n("+") + u2n("+")) * t("+") - cp *
        (u1n("-") + u2n("-")) * t("-"),
    ) * dS + dot(w,
                 cp * (u1n + u2n) * t) * ds)

    a_bnd = (dot(w,
                 cp * dot(u1 + u2, n) * t) * (ds(INLET1) + ds(INLET2)) +
             w * t * (ds(INLET1) + ds(INLET2)) - w * tin1 * ds(INLET1) -
             w * tin2 * ds(INLET2) + alpha / h * ks * w * t *
             (ds(INLET1) + ds(INLET2)) - ks * dot(grad(w), t * n) *
             (ds(INLET1) + ds(INLET2)) - ks * dot(grad(t), w * n) *
             (ds(INLET1) + ds(INLET2)))

    aT = a_int + a_fac + a_vel + a_bnd

    LT_bnd = (alpha / h * ks * tin1 * w * ds(INLET1) +
              alpha / h * ks * tin2 * w * ds(INLET2) -
              tin1 * ks * dot(grad(w), n) * ds(INLET1) -
              tin2 * ks * dot(grad(w), n) * ds(INLET2))

    problem = fd.LinearVariationalProblem(derivative(aT, t), LT_bnd, t)
    solver_temp = fd.LinearVariationalSolver(
        problem,
        solver_parameters=temperature_parameters,
        options_prefix="temperature",
    )
    solver_temp.solve()
    # fd.solve(eT == 0, t, solver_parameters=temperature_parameters)

    # Cost function: Flux at the cold outlet
    scale_factor = 4e-4
    Jform = fd.assemble(
        fd.Constant(-scale_factor * cp_value) * inner(t * u1, n) * ds(OUTLET1))
    # Constraints: Pressure drop on each fluid
    power_drop = 1e-2
    Power1 = fd.assemble(p1 / power_drop * ds(INLET1))
    Power2 = fd.assemble(p2 / power_drop * ds(INLET2))

    phi_pvd = fd.File("phi_evolution.pvd")

    def deriv_cb(phi):
        with stop_annotating():
            phi_pvd.write(phi[0])

    c = fda.Control(s)

    # Reduced Functionals
    Jhat = LevelSetFunctional(Jform, c, phi, derivative_cb_pre=deriv_cb)
    P1hat = LevelSetFunctional(Power1, c, phi)
    P1control = fda.Control(Power1)

    P2hat = LevelSetFunctional(Power2, c, phi)
    P2control = fda.Control(Power2)

    Jhat_v = Jhat(phi)
    print("Initial cost function value {:.5f}".format(Jhat_v), flush=True)
    print("Power drop 1 {:.5f}".format(Power1), flush=True)
    print("Power drop 2 {:.5f}".format(Power2), flush=True)

    beta_param = 0.08
    # Regularize the shape derivatives only in the domain marked with 0
    reg_solver = RegularizationSolver(S,
                                      mesh,
                                      beta=beta_param,
                                      gamma=1e5,
                                      dx=dx,
                                      design_domain=0)

    tol = 1e-5
    dt = 0.05
    params = {
        "alphaC": 1.0,
        "debug": 5,
        "alphaJ": 1.0,
        "dt": dt,
        "K": 1e-3,
        "maxit": n_iters,
        "maxtrials": 5,
        "itnormalisation": 10,
        "tol_merit":
        5e-3,  # new merit can be within 0.5% of the previous merit
        # "normalize_tol" : -1,
        "tol": tol,
    }

    solver_parameters = {
        "reinit_solver": {
            "h_factor": 2.0,
        }
    }
    # Optimization problem
    problem = InfDimProblem(
        Jhat,
        reg_solver,
        ineqconstraints=[
            Constraint(P1hat, 1.0, P1control),
            Constraint(P2hat, 1.0, P2control),
        ],
        solver_parameters=solver_parameters,
    )
    results = nlspace_solve(problem, params)

    return results
Beispiel #27
0
def compute_time_accuracy_via_mms(
        gridsize, element_degree, timestep_sizes, endtime):
    
    mesh = fe.UnitIntervalMesh(gridsize)
    
    element = fe.FiniteElement("P", mesh.ufl_cell(), element_degree)
    
    V = fe.FunctionSpace(mesh, element)
    
    u_h = fe.Function(V)
    
    v = fe.TestFunction(V)
    
    un = fe.Function(V)
    
    u_m = manufactured_solution(mesh)
    
    bc = fe.DirichletBC(V, u_m, "on_boundary")
    
    _F = F(u_h, v, un)
    
    problem = fe.NonlinearVariationalProblem(
        _F - v*R(u_m)*dx,
        u_h,
        bc,
        fe.derivative(_F, u_h))
    
    solver = fe.NonlinearVariationalSolver(problem)
    
    t.assign(0.)
    
    initial_values = fe.interpolate(u_m, V)
    
    L2_norm_errors = []
    
    print("Delta_t, L2_norm_error")
    
    for timestep_size in timestep_sizes:
        
        Delta_t.assign(timestep_size)
        
        un.assign(initial_values)
        
        time = 0.
        
        t.assign(time)
        
        while time < (endtime - TIME_EPSILON):
            
            time += timestep_size
            
            t.assign(time)
            
            solver.solve()
            
            un.assign(u_h)

        L2_norm_errors.append(
            math.sqrt(fe.assemble(fe.inner(u_h - u_m, u_h - u_m)*dx)))
        
        print(str(timestep_size) + ", " + str(L2_norm_errors[-1]))
    
    r = timestep_sizes[-2]/timestep_sizes[-1]
    
    e = L2_norm_errors
    
    log = math.log
    
    order = log(e[-2]/e[-1])/log(r)
    
    return order
def initial_values(sim):

    print("Solving steady heat driven cavity to obtain initial values")

    Ra = 2.518084e6

    Pr = 6.99

    sim.reference_temperature_range__degC.assign(10.)

    sim.grashof_number = sim.grashof_number.assign(Ra / Pr)

    sim.prandtl_number = sim.prandtl_number.assign(Pr)

    w = fe.Function(sim.function_space)

    p, u, T = w.split()

    p.assign(0.)

    ihat, jhat = sim.unit_vectors()

    u.assign(0. * ihat + 0. * jhat)

    T.assign(sim.cold_wall_temperature)

    F = heat_driven_cavity_variational_form_residual(
        sim=sim, solution=w) * fe.dx(degree=sim.quadrature_degree)

    problem = fe.NonlinearVariationalProblem(
        F=F,
        u=w,
        bcs=dirichlet_boundary_conditions(sim),
        J=fe.derivative(F, w))

    solver = fe.NonlinearVariationalSolver(problem=problem,
                                           solver_parameters={
                                               "snes_type": "newtonls",
                                               "snes_monitor": None,
                                               "ksp_type": "preonly",
                                               "pc_type": "lu",
                                               "mat_type": "aij",
                                               "pc_factor_mat_solver_type":
                                               "mumps"
                                           })

    def solve():

        solver.solve()

        return w

    w, _ = \
        sapphire.continuation.solve_with_bounded_regularization_sequence(
            solve = solve,
            solution = w,
            backup_solution = fe.Function(w),
            regularization_parameter = sim.grashof_number,
            initial_regularization_sequence = (
                0., sim.grashof_number.__float__()))

    return w
Beispiel #29
0
n = 50
mesh = fd.UnitSquareMesh(n, n)
V = fd.VectorFunctionSpace(mesh, "CG", 2)

x = ufl.SpatialCoordinate(mesh)
expr = ufl.as_vector([ufl.sin(2 * ufl.pi * x[0]), ufl.cos(2 * ufl.pi * x[1])])
u = fd.interpolate(expr, V)

u_dot = fd.Function(V)
v = fd.TestFunction(V)

nu = fd.Constant(0.0001)  # for burgers
if equation == "heat":
    nu = fd.Constant(0.1)  # for heat

M = fd.derivative(fd.inner(u, v) * fd.dx, u)
R = -(fd.inner(fd.grad(u) * u, v) + nu * fd.inner(fd.grad(u), fd.grad(v))) * fd.dx
if equation == "heat":
    R = -nu * fd.inner(fd.grad(u), fd.grad(v)) * fd.dx
F = fd.action(M, u_dot) - R

bc = fd.DirichletBC(V, (0.0, 0.0), "on_boundary")

t = 0.0
end = 0.1
tspan = (t, end)

state_out = fd.File("result/state.pvd")


def ts_monitor(ts, steps, time, X):
Beispiel #30
0
    def __init__(self, solver):
        r"""State machine for solving the Gauss-Newton subproblem via the
        preconditioned conjugate gradient method"""
        self._assemble = solver._assemble
        u = solver.state
        p = solver.parameter
        E = solver._E
        dE = derivative(E, u)
        R = solver._R
        dR = derivative(R, p)
        F = solver._F
        dF_du = derivative(F, u)
        dF_dp = derivative(F, p)
        # TODO: Make this an arbitrary RHS -- the solver can set it to the
        # gradient if we want
        dJ = solver.gradient
        bc = solver._bc

        V = u.function_space()
        Q = p.function_space()

        # Create the preconditioned residual and solver
        z = firedrake.Function(Q)
        s = firedrake.Function(Q)
        φ, ψ = firedrake.TestFunction(Q), firedrake.TrialFunction(Q)
        M = φ * ψ * dx + derivative(dR, p)
        residual_problem = firedrake.LinearVariationalProblem(
            M,
            -dJ,
            z,
            form_compiler_parameters=solver._fc_params,
            constant_jacobian=False)
        residual_solver = firedrake.LinearVariationalSolver(
            residual_problem, solver_parameters=solver._solver_params)

        self._preconditioner = M
        self._residual = z
        self._search_direction = s
        self._residual_solver = residual_solver

        # Create a variable to store the current solution of the Gauss-Newton
        # problem and the solutions of the auxiliary tangent sub-problems
        q = firedrake.Function(Q)
        v = firedrake.Function(V)
        w = firedrake.Function(V)

        # Create linear problem and solver objects for the auxiliary tangent
        # sub-problems
        tangent_linear_problem = firedrake.LinearVariationalProblem(
            dF_du,
            action(dF_dp, s),
            w,
            bc,
            form_compiler_parameters=solver._fc_params,
            constant_jacobian=False)
        tangent_linear_solver = firedrake.LinearVariationalSolver(
            tangent_linear_problem, solver_parameters=solver._solver_params)

        adjoint_tangent_linear_problem = firedrake.LinearVariationalProblem(
            adjoint(dF_du),
            derivative(dE, u, w),
            v,
            bc,
            form_compiler_parameters=solver._fc_params,
            constant_jacobian=False)
        adjoint_tangent_linear_solver = firedrake.LinearVariationalSolver(
            adjoint_tangent_linear_problem,
            solver_parameters=solver._solver_params)

        self._rhs = dJ
        self._solution = q
        self._tangent_linear_solution = w
        self._tangent_linear_solver = tangent_linear_solver
        self._adjoint_tangent_linear_solution = v
        self._adjoint_tangent_linear_solver = adjoint_tangent_linear_solver

        self._product = action(adjoint(dF_dp), v) + derivative(dR, p, s)

        # Create the update to the residual and the associated solver
        δz = firedrake.Function(Q)
        Gs = self._product
        delta_residual_problem = firedrake.LinearVariationalProblem(
            M,
            Gs,
            δz,
            form_compiler_parameters=solver._fc_params,
            constant_jacobian=False)
        delta_residual_solver = firedrake.LinearVariationalSolver(
            delta_residual_problem, solver_parameters=solver._solver_params)

        self._delta_residual = δz
        self._delta_residual_solver = delta_residual_solver

        self._residual_energy = 0.
        self._search_direction_energy = 0.

        self.reinit()