示例#1
0
def transpose_operators(operators):
  out = [None, None]

  for i in range(2):
    op = operators[i]

    if op is None:
      out[i] = None
    elif isinstance(op, backend.cpp.GenericMatrix):
      out[i] = op.__class__()
      backend.assemble(backend.adjoint(op.form), tensor=out[i])

      if hasattr(op, 'bcs'):
        adjoint_bcs = [backend.homogenize(bc) for bc in op.bcs if isinstance(bc, backend.cpp.DirichletBC)] + [bc for bc in op.bcs if not isinstance(bc, backend.DirichletBC)]
        [bc.apply(out[i]) for bc in adjoint_bcs]

    elif isinstance(op, backend.Form) or isinstance(op, ufl.form.Form):
      out[i] = backend.adjoint(op)

      if hasattr(op, 'bcs'):
        out[i].bcs = [backend.homogenize(bc) for bc in op.bcs if isinstance(bc, backend.cpp.DirichletBC)] + [bc for bc in op.bcs if not isinstance(bc, backend.DirichletBC)]

    elif isinstance(op, AdjointKrylovMatrix):
      pass

    else:
      print "op.__class__: ", op.__class__
      raise libadjoint.exceptions.LibadjointErrorNotImplemented("Don't know how to transpose anything else!")

  return out
示例#2
0
    def assemble_data(self):
        assert not isinstance(self.data, IdentityMatrix)
        if backend.__name__ == "firedrake":
            # Firedrake specifies assembled matrix type as part of the
            # solver parameters.
            mat_type = self.solver_parameters.get("mat_type")
            assemble = lambda x: backend.assemble(self.data, mat_type=mat_type)
        else:
            assemble = backend.assemble
        if not self.cache:
            if hasattr(self.data.arguments()[0], '_V_multi'):
                return backend.assemble_multimesh(self.data)
            else:
                return backend.assemble(self.data)

        else:
            if self.data in caching.assembled_adj_forms:
                if backend.parameters["adjoint"]["debug_cache"]:
                    backend.info_green("Got an assembly cache hit")
                return caching.assembled_adj_forms[self.data]
            else:
                if backend.parameters["adjoint"]["debug_cache"]:
                    backend.info_red("Got an assembly cache miss")

                if hasattr(self.data.arguments()[0], '_V_multi'):
                    M = backend.assemble_multimesh(self.data)
                else:
                    M = backend.assemble(self.data)

                caching.assembled_adj_forms[self.data] = M
                return M
示例#3
0
    def _ad_convert_type(self, value, options=None):
        options = {} if options is None else options
        riesz_representation = options.get("riesz_representation", "l2")

        if riesz_representation == "l2":
            return create_overloaded_object(
                compat.function_from_vector(self.function_space(),
                                            value,
                                            cls=backend.Function))
        elif riesz_representation == "L2":
            ret = compat.create_function(self.function_space())
            u = backend.TrialFunction(self.function_space())
            v = backend.TestFunction(self.function_space())
            M = backend.assemble(backend.inner(u, v) * backend.dx)
            compat.linalg_solve(M, ret.vector(), value)
            return ret
        elif riesz_representation == "H1":
            ret = compat.create_function(self.function_space())
            u = backend.TrialFunction(self.function_space())
            v = backend.TestFunction(self.function_space())
            M = backend.assemble(
                backend.inner(u, v) * backend.dx +
                backend.inner(backend.grad(u), backend.grad(v)) * backend.dx)
            compat.linalg_solve(M, ret.vector(), value)
            return ret
        elif callable(riesz_representation):
            return riesz_representation(value)
        else:
            raise NotImplementedError("Unknown Riesz representation %s" %
                                      riesz_representation)
示例#4
0
    def norm(self):

        if isinstance(self.data, backend.Function):
            return (abs(backend.assemble(backend.inner(self.data, self.data)*backend.dx)))**0.5
        elif isinstance(self.data, ufl.form.Form):
            return backend.assemble(self.data).norm("l2")
        elif isinstance(self.data, backend.MultiMeshFunction):
            raise NotImplementedError
示例#5
0
    def dot_product(self,y):

        if isinstance(self.data, ufl.form.Form):
            return backend.assemble(backend.inner(self.data, y.data)*backend.dx)
        elif isinstance(self.data, backend.Function):
            if isinstance(y.data, ufl.form.Form):
                other = backend.assemble(y.data)
            else:
                other = y.data.vector()
            return self.data.vector().inner(other)
        else:
            raise libadjoint.exceptions.LibadjointErrorNotImplemented("Don't know how to dot anything else.")
示例#6
0
    def _forward_solve(self, lhs, rhs, func, bcs, **kwargs):
        if self.assemble_system:
            A, b = backend.assemble_system(lhs, rhs, bcs)
        else:
            A = backend.assemble(lhs)
            b = backend.assemble(rhs)
            [bc.apply(A, b) for bc in bcs]

        if self.ident_zeros_tol is not None:
            A.ident_zeros(self.ident_zeros_tol)

        backend.solve(A, func.vector(), b, *self.forward_args,
                      **self.forward_kwargs)
        return func
示例#7
0
 def assemble_data(self):
     assert not isinstance(self.data, IdentityMatrix)
     if not self.cache:
         return backend.assemble(self.data)
     else:
         if self.data in caching.assembled_adj_forms:
             if backend.parameters["adjoint"]["debug_cache"]:
                 backend.info_green("Got an assembly cache hit")
             return caching.assembled_adj_forms[self.data]
         else:
             if backend.parameters["adjoint"]["debug_cache"]:
                 backend.info_red("Got an assembly cache miss")
             M = backend.assemble(self.data)
             caching.assembled_adj_forms[self.data] = M
             return M
示例#8
0
 def _ad_dot(self, other, options=None):
     options = {} if options is None else options
     riesz_representation = options.get("riesz_representation", "l2")
     if riesz_representation == "l2":
         return self.vector().inner(other.vector())
     elif riesz_representation == "L2":
         return backend.assemble(backend.inner(self, other) * backend.dx)
     elif riesz_representation == "H1":
         return backend.assemble(
             (backend.inner(self, other) +
              backend.inner(backend.grad(self), backend.grad(other))) *
             backend.dx)
     else:
         raise NotImplementedError("Unknown Riesz representation %s" %
                                   riesz_representation)
示例#9
0
    def action(self, x, y):
        assert isinstance(x.data, backend.Function)
        assert isinstance(y.data, backend.Function)

        action_form = backend.action(self.data, x.data)
        action_vec  = backend.assemble(action_form)
        y.data.vector()[:] = action_vec
示例#10
0
    def equation_partial_derivative(self, adjointer, adjoint, i, variable):
        form = adjresidual.get_residual(i)

        if form is None:
            return None
        else:
            form = -form

        fn_space = ufl.algorithms.extract_arguments(form)[0].function_space()
        dparam = backend.Function(
            backend.FunctionSpace(fn_space.mesh(), "R", 0))
        dparam.vector()[:] = 1.0

        dJdv = numpy.zeros(len(self.v))
        for (i, a) in enumerate(self.v):
            diff_form = ufl.algorithms.expand_derivatives(
                backend.derivative(form, a, dparam))

            dFdm = backend.assemble(diff_form)  # actually - dF/dm
            assert isinstance(dFdm, backend.GenericVector)

            out = dFdm.inner(adjoint.vector())
            dJdv[i] = out

        return dJdv
示例#11
0
    def equation_partial_second_derivative(self, adjointer, adjoint, i,
                                           variable, m_dot):
        form = adjresidual.get_residual(i)
        if form is not None:
            form = -form

            mesh = ufl.algorithms.extract_arguments(
                form)[0].function_space().mesh()
            fn_space = backend.FunctionSpace(mesh, "R", 0)
            dparam = backend.Function(fn_space)
            dparam.vector()[:] = 1.0 * float(self.coeff)
            d2param = backend.Function(fn_space)
            d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot

            diff_form = ufl.algorithms.expand_derivatives(
                backend.derivative(form, get_constant(self.a), dparam))
            if diff_form is None:
                return None

            diff_form = ufl.algorithms.expand_derivatives(
                backend.derivative(diff_form, get_constant(self.a), d2param))
            if diff_form is None:
                return None

            # Let's see if the form actually depends on the parameter m
            if len(diff_form.integrals()) != 0:
                dFdm = backend.assemble(diff_form)  # actually - dF/dm
                assert isinstance(dFdm, backend.GenericVector)

                out = dFdm.inner(adjoint.vector())
                return out
            else:
                return None  # dF/dm is zero, return None
示例#12
0
    def inner(self, vec):
        '''Compute the action of the gradient on the vector vec.'''
        def make_mdot(vec):
            if isinstance(self.m, FunctionControl):
                mdot = self.m.set_perturbation(
                    backend.Function(self.m.data().function_space(), vec))
            elif isinstance(self.m, ConstantControl):
                mdot = self.m.set_perturbation(backend.Constant(vec))

            return mdot

        mdot = make_mdot(vec)

        grad = 0.0
        last_timestep = -1

        for (tlm, tlm_var) in compute_tlm(mdot, forget=self.forget):
            self.cb(tlm_var, tlm)
            fwd_var = tlm_var.to_forward()
            dJdu = adjglobals.adjointer.evaluate_functional_derivative(
                self.J, fwd_var)
            if dJdu is not None and tlm is not None:
                dJdu_vec = backend.assemble(dJdu.data)
                grad = _add(grad, dJdu_vec.inner(tlm.vector()))

            if last_timestep < tlm_var.timestep:
                out = self.m.functional_partial_derivative(
                    adjglobals.adjointer, self.J, tlm_var.timestep)
                grad = _add(grad, out)

            last_timestep = tlm_var.timestep

        return grad
示例#13
0
    def functional_partial_derivative(self, adjointer, J, timestep):
        form = J.get_form(adjointer, timestep)

        if form is None:
            return None

        # OK. Now that we have the form for the functional at this timestep, let's differentiate it with respect to
        # my dear Constant, and be done.
        for coeff in ufl.algorithms.extract_coefficients(form):
            try:
                mesh = coeff.function_space().mesh()
                fn_space = backend.FunctionSpace(mesh, "R", 0)
                break
            except:
                pass

        dparam = backend.Function(fn_space)
        dparam.vector()[:] = 1.0 * float(self.coeff)

        d = backend.derivative(form, get_constant(self.a), dparam)
        d = ufl.algorithms.expand_derivatives(d)

        # Add the derivatives of Expressions wrt to the Constant
        d = self.expression_derivative(form, d)

        if len(d.integrals()) != 0:
            return backend.assemble(d)
        else:
            return None
示例#14
0
def assemble(*args, **kwargs):
    """When a form is assembled, the information about its nonlinear dependencies is lost,
    and it is no longer easy to manipulate. Therefore, fenics_adjoint overloads the :py:func:`dolfin.assemble`
    function to *attach the form to the assembled object*. This lets the automatic annotation work,
    even when the user calls the lower-level :py:data:`solve(A, x, b)`.
    """
    annotate = annotate_tape(kwargs)
    with stop_annotating():
        output = backend.assemble(*args, **kwargs)

    form = args[0]
    if isinstance(output, float):
        output = create_overloaded_object(output)

        if annotate:
            block = AssembleBlock(form)

            tape = get_working_tape()
            tape.add_block(block)

            block.add_output(output.block_variable)
    else:
        # Assembled a vector or matrix
        output.form = form

    return output
示例#15
0
    def inner(self, vec):
        '''Compute the action of the gradient on the vector vec.'''
        def make_mdot(vec):
            if isinstance(self.m, FunctionControl):
                mdot = self.m.set_perturbation(backend.Function(self.m.data().function_space(), vec))
            elif isinstance(self.m, ConstantControl):
                mdot = self.m.set_perturbation(backend.Constant(vec))

            return mdot

        mdot = make_mdot(vec)

        grad = 0.0
        last_timestep = -1

        for (tlm, tlm_var) in compute_tlm(mdot, forget=self.forget):
            self.cb(tlm_var, tlm)
            fwd_var = tlm_var.to_forward()
            dJdu = adjglobals.adjointer.evaluate_functional_derivative(self.J, fwd_var)
            if dJdu is not None and tlm is not None:
                dJdu_vec = backend.assemble(dJdu.data)
                grad = _add(grad, dJdu_vec.inner(tlm.vector()))

            if last_timestep < tlm_var.timestep:
                out = self.m.functional_partial_derivative(adjglobals.adjointer, self.J, tlm_var.timestep)
                grad = _add(grad, out)

            last_timestep = tlm_var.timestep

        return grad
示例#16
0
  def equation_partial_second_derivative(self, adjointer, adjoint, i, variable, m_dot):
    form = adjresidual.get_residual(i)
    if form is not None:
      form = -form

      mesh = ufl.algorithms.extract_arguments(form)[0].function_space().mesh()
      fn_space = backend.FunctionSpace(mesh, "R", 0)
      dparam = backend.Function(fn_space)
      dparam.vector()[:] = 1.0 * float(self.coeff)
      d2param = backend.Function(fn_space)
      d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot

      diff_form = ufl.algorithms.expand_derivatives(backend.derivative(form, get_constant(self.a), dparam))
      if diff_form is None:
        return None

      diff_form  = ufl.algorithms.expand_derivatives(backend.derivative(diff_form, get_constant(self.a), d2param))
      if diff_form is None:
        return None

      # Let's see if the form actually depends on the parameter m
      if len(diff_form.integrals()) != 0:
        dFdm = backend.assemble(diff_form) # actually - dF/dm
        assert isinstance(dFdm, backend.GenericVector)

        out = dFdm.inner(adjoint.vector())
        return out
      else:
        return None # dF/dm is zero, return None
示例#17
0
    def derivative_action(self, dependencies, values, variable, contraction_vector, hermitian):

        # If you want to apply boundary conditions symmetrically in the adjoint
        # -- and you often do --
        # then we need to have a UFL representation of all the terms in the adjoint equation.
        # However!
        # Since UFL cannot represent the identity map,
        # we need to find an f such that when
        # assemble(inner(f, v)*dx)
        # we get the contraction_vector.data back.
        # This involves inverting a mass matrix.

        if backend.parameters["adjoint"]["symmetric_bcs"] and backend.__version__ <= '1.2.0':
            backend.info_red("Warning: symmetric BC application requested but unavailable in dolfin <= 1.2.0.")

        if backend.parameters["adjoint"]["symmetric_bcs"] and backend.__version__ > '1.2.0':

            V = contraction_vector.data.function_space()
            v = backend.TestFunction(V)

            if str(V) not in adjglobals.fsp_lu:
                u = backend.TrialFunction(V)
                A = backend.assemble(backend.inner(u, v)*backend.dx)
                lusolver = backend.LUSolver(A, "mumps")
                lusolver.parameters["symmetric"] = True
                lusolver.parameters["reuse_factorization"] = True
                adjglobals.fsp_lu[str(V)] = lusolver
            else:
                lusolver = adjglobals.fsp_lu[str(V)]

            riesz = backend.Function(V)
            lusolver.solve(riesz.vector(), contraction_vector.data.vector())
            return adjlinalg.Vector(backend.inner(riesz, v)*backend.dx)
        else:
            return adjlinalg.Vector(contraction_vector.data)
示例#18
0
  def functional_partial_derivative(self, adjointer, J, timestep):
    form = J.get_form(adjointer, timestep)

    if form is None:
      return None

    # OK. Now that we have the form for the functional at this timestep, let's differentiate it with respect to
    # my dear Constant, and be done.
    for coeff in ufl.algorithms.extract_coefficients(form):
      try:
        mesh = coeff.function_space().mesh()
        fn_space = backend.FunctionSpace(mesh, "R", 0)
        break
      except:
        pass

    dparam = backend.Function(fn_space)
    dparam.vector()[:] = 1.0 * float(self.coeff)

    d = backend.derivative(form, get_constant(self.a), dparam)
    d = ufl.algorithms.expand_derivatives(d)

    # Add the derivatives of Expressions wrt to the Constant
    d = self.expression_derivative(form, d)

    if len(d.integrals()) != 0:
      return backend.assemble(d)
    else:
      return None
示例#19
0
    def functional_partial_second_derivative(self, adjointer, J, timestep,
                                             m_dot):
        form = J.get_form(adjointer, timestep)

        if form is None:
            return None

        for coeff in ufl.algorithms.extract_coefficients(form):
            try:
                mesh = coeff.function_space().mesh()
                fn_space = backend.FunctionSpace(mesh, "R", 0)
                break
            except:
                pass

        dparam = backend.Function(fn_space)
        dparam.vector()[:] = 1.0 * float(self.coeff)

        d = backend.derivative(form, get_constant(self.a), dparam)
        d = ufl.algorithms.expand_derivatives(d)

        d2param = backend.Function(fn_space)
        d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot

        d = backend.derivative(d, get_constant(self.a), d2param)
        d = ufl.algorithms.expand_derivatives(d)

        if len(d.integrals()) != 0:
            return backend.assemble(d)
        else:
            return None
示例#20
0
  def functional_partial_second_derivative(self, adjointer, J, timestep, m_dot):
    form = J.get_form(adjointer, timestep)

    if form is None:
      return None

    for coeff in ufl.algorithms.extract_coefficients(form):
      try:
        mesh = coeff.function_space().mesh()
        fn_space = backend.FunctionSpace(mesh, "R", 0)
        break
      except:
        pass

    dparam = backend.Function(fn_space)
    dparam.vector()[:] = 1.0 * float(self.coeff)

    d = backend.derivative(form, get_constant(self.a), dparam)
    d = ufl.algorithms.expand_derivatives(d)

    d2param = backend.Function(fn_space)
    d2param.vector()[:] = 1.0 * float(self.coeff) * m_dot

    d = backend.derivative(d, get_constant(self.a), d2param)
    d = ufl.algorithms.expand_derivatives(d)

    if len(d.integrals()) != 0:
      return backend.assemble(d)
    else:
      return None
示例#21
0
    def equation_partial_derivative(self, adjointer, adjoint, i, variable):
        form = adjresidual.get_residual(i)
        if form is not None:
            form = -form

            mesh = ufl.algorithms.extract_arguments(
                form)[0].function_space().mesh()
            fn_space = backend.FunctionSpace(mesh, "R", 0)
            dparam = backend.Function(fn_space)
            dparam.vector()[:] = 1.0 * float(self.coeff)

            diff_form = ufl.algorithms.expand_derivatives(
                backend.derivative(form, get_constant(self.a), dparam))

            # Add the derivatives of Expressions wrt to the Constant
            diff_form = self.expression_derivative(form, diff_form)

            # Let's see if the form actually depends on the parameter m
            if len(diff_form.integrals()) != 0:
                dFdm = backend.assemble(diff_form)  # actually - dF/dm
                out = adjoint.vector().inner(dFdm)
            else:
                out = None  # dF/dm is zero, return None

            return out
示例#22
0
    def _assemble_and_solve_adj_eq(self, dFdu_adj_form, dJdu, compute_bdy):
        dJdu_copy = dJdu.copy()
        bcs = self._homogenize_bcs()
        if self.assemble_system:
            rhs_bcs_form = backend.inner(
                backend.Function(self.function_space),
                dFdu_adj_form.arguments()[0]) * backend.dx
            A, _ = backend.assemble_system(dFdu_adj_form, rhs_bcs_form, bcs)
        else:
            A = backend.assemble(dFdu_adj_form)
            [bc.apply(A) for bc in bcs]

        [bc.apply(dJdu) for bc in bcs]

        adj_sol = compat.create_function(self.function_space)
        compat.linalg_solve(A, adj_sol.vector(), dJdu, *self.adj_args,
                            **self.adj_kwargs)

        adj_sol_bdy = None
        if compute_bdy:
            adj_sol_bdy = compat.function_from_vector(
                self.function_space, dJdu_copy - compat.assemble_adjoint_value(
                    backend.action(dFdu_adj_form, adj_sol)))

        return adj_sol, adj_sol_bdy
示例#23
0
  def solve(self, var, b):
    timer = backend.Timer("Matrix-free solver")
    solver = backend.PETScKrylovSolver(*self.solver_parameters)
    solver.parameters.update(self.parameters)

    x = backend.Function(self.fn_space)
    if b.data is None:
      backend.info_red("Warning: got zero RHS for the solve associated with variable %s" % var)
      return adjlinalg.Vector(x)

    if isinstance(b.data, backend.Function):
      rhs = b.data.vector().copy()
    else:
      rhs = backend.assemble(b.data)

    if var.type in ['ADJ_TLM', 'ADJ_ADJOINT']:
      self.bcs = [backend.homogenize(bc) for bc in self.bcs if isinstance(bc, backend.cpp.DirichletBC)] + [bc for bc in self.bcs if not isinstance(bc, backend.DirichletBC)]

    for bc in self.bcs:
      bc.apply(rhs)

    if self.operators[1] is not None: # we have a user-supplied preconditioner
      solver.set_operators(self.data, self.operators[1])
      solver.solve(backend.down_cast(x.vector()), backend.down_cast(rhs))
    else:
      solver.solve(self.data, backend.down_cast(x.vector()), backend.down_cast(rhs))

    timer.stop()
    return adjlinalg.Vector(x)
示例#24
0
    def jacobian(self, m):
        if isinstance(m, list):
            assert len(m) == 1
            m = m[0]

        self.update_control(m)
        out = [backend.assemble(self.dform)]
        return out
示例#25
0
  def mult(self, *args):
    shapes = self.shape(self.current_form)
    y = backend.PETScVector(shapes[0])

    action_fn = backend.Function(ufl.algorithms.extract_arguments(self.current_form)[-1].function_space())
    action_vec = action_fn.vector()
    for i in range(len(args[0])):
      action_vec[i] = args[0][i]

    action_form = backend.action(self.current_form, action_fn)
    backend.assemble(action_form, tensor=y)

    for bc in self.bcs:
      bcvals = bc.get_boundary_values()
      for idx in bcvals:
        y[idx] = action_vec[idx]

    args[1].set_local(y.array())
示例#26
0
def taylor_test_expression(exp, V, seed=None):
    """
    Performs a Taylor test of an Expression with dependencies.

    exp: The expression to test
    V: A suitable function space on which the expression will be projected.
    seed: The initial perturbation coefficient for the taylor test.

    Warning: This function resets the adjoint tape! """
    from . import drivers, functional, projection
    from .controls import Control

    adjglobals.adj_reset()

    # Annotate test model
    s = projection.project(exp, V, annotate=True)
    mesh = V.mesh()

    Jform = s**2*backend.dx + exp*backend.dx(domain=mesh)

    J = functional.Functional(Jform)
    J0 = backend.assemble(Jform)

    controls = [Control(c) for c in exp.dependencies]
    dJd0 = drivers.compute_gradient(J, controls, forget=False)

    for i in range(len(controls)):
        def Jfunc(new_val):
            dep = exp.dependencies[i]

            # Remember the old dependency value for later
            old_val = float(dep)

            # Compute the functional value
            dep.assign(new_val)
            s = projection.project(exp, V, annotate=False)
            out = backend.assemble(s**2*backend.dx + exp*backend.dx(domain=mesh))

            # Restore the old dependency value
            dep.assign(old_val)

            return out

        #HJ = hessian(J, controls[i], warn=False)
        #minconv = taylor_test(Jfunc, controls[i], J0, dJd0[i], HJm=HJ)
        minconv = taylor_test(Jfunc, controls[i], J0, dJd0[i], seed=seed)

        if math.isnan(minconv):
            warning("Convergence order is not a number. Assuming that you \
have a linear or constant constraint dependency (e.g. check that the Taylor \
remainder are all 0).")
        else:
            if not minconv > 1.9:
                raise Exception("The Taylor test failed when checking the \
derivative with respect to the %i'th dependency." % (i+1))

    adjglobals.adj_reset()
示例#27
0
    def basic_solve(self, var, b):
        if isinstance(self.data, IdentityMatrix):
            x=b.duplicate()
            x.axpy(1.0, b)
            if isinstance(x.data, ufl.Form):
                x = Vector(backend.Function(x.fn_space, backend.assemble(x.data)))
        else:
            if var.type in ['ADJ_TLM', 'ADJ_ADJOINT', 'ADJ_SOA']:
                dirichlet_bcs = [utils.homogenize(bc) for bc in self.bcs if isinstance(bc, backend.DirichletBC)]
                other_bcs  = [bc for bc in self.bcs if not isinstance(bc, backend.DirichletBC)]
                bcs = dirichlet_bcs + other_bcs
            else:
                bcs = self.bcs

            test = self.test_function()

            #FIXME: This is a hack, the test and trial function should carry
            # the MultiMeshFunctionSpace as an attribute
            if hasattr(test, '_V_multi'):
                x = Vector(backend.MultiMeshFunction(test._V_multi))
            else:
                x = Vector(backend.Function(test.function_space()))

            #print "b.data is a %s in the solution of %s" % (b.data.__class__, var)
            if b.data is None and not hasattr(b, 'nonlinear_form'):
                # This means we didn't get any contribution on the RHS of the adjoint system. This could be that the
                # simulation ran further ahead than when the functional was evaluated, or it could be that the
                # functional is set up incorrectly.
                backend.warning("Warning: got zero RHS for the solve associated with variable %s" % var)
            elif isinstance(b.data, backend.Function):

                assembled_lhs = self.assemble_data()
                [bc.apply(assembled_lhs) for bc in bcs]
                assembled_rhs = compatibility.assembled_rhs(b)
                [bc.apply(assembled_rhs) for bc in bcs]

                wrap_solve(assembled_lhs, x.data, assembled_rhs, self.solver_parameters)
            else:
                if hasattr(b, 'nonlinear_form'): # was a nonlinear solve
                    x = compatibility.assign_function_to_vector(x, b.nonlinear_u, function_space = test.function_space())
                    F = backend.replace(b.nonlinear_form, {b.nonlinear_u: x.data})
                    J = backend.replace(b.nonlinear_J, {b.nonlinear_u: x.data})
                    try:
                        compatibility.solve(F == 0, x.data, b.nonlinear_bcs, J=J, solver_parameters=self.solver_parameters)
                    except RuntimeError as rte:
                        x.data.vector()[:] = float("nan")

                else:
                    assembled_lhs = self.assemble_data()
                    [bc.apply(assembled_lhs) for bc in bcs]
                    assembled_rhs = wrap_assemble(b.data, test)
                    [bc.apply(assembled_rhs) for bc in bcs]

                    wrap_solve(assembled_lhs, x.data, assembled_rhs, self.solver_parameters)

        return x
示例#28
0
def transpose_operators(operators):
    out = [None, None]

    for i in range(2):
        op = operators[i]

        if op is None:
            out[i] = None
        elif isinstance(op, backend.cpp.GenericMatrix):
            out[i] = op.__class__()
            backend.assemble(backend.adjoint(op.form), tensor=out[i])

            if hasattr(op, 'bcs'):
                adjoint_bcs = [
                    utils.homogenize(bc)
                    for bc in op.bcs if isinstance(bc, backend.cpp.DirichletBC)
                ] + [
                    bc
                    for bc in op.bcs if not isinstance(bc, backend.DirichletBC)
                ]
                [bc.apply(out[i]) for bc in adjoint_bcs]

        elif isinstance(op, backend.Form) or isinstance(op, ufl.form.Form):
            out[i] = backend.adjoint(op)

            if hasattr(op, 'bcs'):
                out[i].bcs = [
                    utils.homogenize(bc)
                    for bc in op.bcs if isinstance(bc, backend.cpp.DirichletBC)
                ] + [
                    bc
                    for bc in op.bcs if not isinstance(bc, backend.DirichletBC)
                ]

        elif isinstance(op, AdjointKrylovMatrix):
            pass

        else:
            print("op.__class__: ", op.__class__)
            raise libadjoint.exceptions.LibadjointErrorNotImplemented(
                "Don't know how to transpose anything else!")

    return out
示例#29
0
    def jacobian_action(self, m, dm, result):
        """Computes the Jacobian action of c(m) in direction dm and stores the result in result. """

        if isinstance(m, list):
            assert len(m) == 1
            m = m[0]
        self.update_control(m)

        form = backend.action(self.dform, dm)
        result.assign(backend.assemble(form))
示例#30
0
def taylor_test_expression(exp, V):
    """
    Performs a Taylor test of an Expression with dependencies.

    exp: The expression to test
    V: A suitable function space on which the expression will be projected.

    Warning: This function resets the adjoint tape! """

    adjglobals.adj_reset()

    # Annotate test model
    s = projection.project(exp, V, annotate=True)
    mesh = V.mesh()

    Jform = s**2*backend.dx + exp*backend.dx(domain=mesh)

    J = functional.Functional(Jform)
    J0 = backend.assemble(Jform)

    deps = exp.dependencies()
    controls = [Control(c) for c in deps]
    dJd0 = drivers.compute_gradient(J, controls, forget=False)

    for i in range(len(controls)):
        def Jfunc(new_val):
            dep = exp.dependencies()[i]

            # Remember the old dependency value for later
            old_val = float(dep)

            # Compute the functional value
            dep.assign(new_val)
            s = projection.project(exp, V, annotate=False)
            out = backend.assemble(s**2*backend.dx + exp*backend.dx(domain=mesh))

            # Restore the old dependency value
            dep.assign(old_val)

            return out

        #HJ = hessian(J, controls[i], warn=False)
        #minconv = taylor_test(Jfunc, controls[i], J0, dJd0[i], HJm=HJ)
        minconv = taylor_test(Jfunc, controls[i], J0, dJd0[i])

        if math.isnan(minconv):
            warning("Convergence order is not a number. Assuming that you \
have a linear or constant constraint dependency (e.g. check that the Taylor \
remainder are all 0).")
        else:
            if not minconv > 1.9:
                raise Exception, "The Taylor test failed when checking the \
derivative with respect to the %i'th dependency." % (i+1)

    adjglobals.adj_reset()
示例#31
0
def project_test(func):
    if isinstance(func, backend.Function):
        V = func.function_space()
        u = backend.TrialFunction(V)
        v = backend.TestFunction(V)
        M = backend.assemble(backend.inner(u, v)*backend.dx)
        proj = backend.Function(V)
        backend.solve(M, proj.vector(), func.vector())
        return proj
    else:
        return func
示例#32
0
    def size(self):
        if hasattr(self, "fn_space") and self.data is None:
            self.data = backend.Function(self.fn_space)

        if isinstance(self.data, backend.Function):
            return self.data.vector().local_size()

        if isinstance(self.data, ufl.form.Form):
            return backend.assemble(self.data).local_size()

        raise libadjoint.exceptions.LibadjointErrorNotImplemented("Don't know how to get the size.")
示例#33
0
def project_test(func):
    if isinstance(func, backend.Function):
        V = func.function_space()
        u = backend.TrialFunction(V)
        v = backend.TestFunction(V)
        M = backend.assemble(backend.inner(u, v) * backend.dx)
        proj = backend.Function(V)
        backend.solve(M, proj.vector(), func.vector())
        return proj
    else:
        return func
示例#34
0
    def mult(self, *args):
        shapes = self.shape(self.current_form)
        y = backend.PETScVector(shapes[0])

        action_fn = backend.Function(
            ufl.algorithms.extract_arguments(
                self.current_form)[-1].function_space())
        action_vec = action_fn.vector()
        for i in range(len(args[0])):
            action_vec[i] = args[0][i]

        action_form = backend.action(self.current_form, action_fn)
        backend.assemble(action_form, tensor=y)

        for bc in self.bcs:
            bcvals = bc.get_boundary_values()
            for idx in bcvals:
                y[idx] = action_vec[idx]

        args[1].set_local(y.array())
示例#35
0
    def __call__(self, adjointer, timestep, dependencies, values):

        functional_value = self._substitute_form(adjointer, timestep, dependencies, values)

        if functional_value is not None:
            args = ufl.algorithms.extract_arguments(functional_value)
            if len(args) > 0:
                backend.info_red("The form passed into Functional must be rank-0 (a scalar)! You have passed in a rank-%s form." % len(args))
                raise libadjoint.exceptions.LibadjointErrorInvalidInputs

            return backend.assemble(functional_value)
        else:
            return 0.0
示例#36
0
    def hessian_action(self, m, dm, dp, result):
        """Computes the Hessian action of c(m) in direction dm and dp and stores the result in result. """

        if isinstance(m, list):
            assert len(m) == 1
            m = m[0]
        self.update_control(m)

        H = dm * ufl.replace(self.hess, {self.trial: dp})
        if isinstance(result, backend.Function):
            if backend.__name__ in ["dolfin", "fenics"]:
                if self.zero_hess:
                    result.vector().zero()
                else:
                    result.vector().zero()
                    result.vector().axpy(1.0, backend.assemble(H))
            else:
                if self.zero_hess:
                    result.assign(0)
                else:
                    result.assign(backend.assemble(H))
        else:
            raise NotImplementedError("Do I need to untangle all controls?")
示例#37
0
    def caching_solve(self, var, b):
        if isinstance(self.data, IdentityMatrix):
            output = b.duplicate()
            output.axpy(1.0, b)
            if isinstance(output.data, ufl.Form):
                output = Vector(backend.Function(output.fn_space, backend.assemble(output.data)))
        elif b.data is None:
            backend.warning("Warning: got zero RHS for the solve associated with variable %s" % var)
            output = Vector(backend.Function(self.test_function().function_space()))
        else:
            dirichlet_bcs = [utils.homogenize(bc) for bc in self.bcs if isinstance(bc, backend.DirichletBC)]
            other_bcs  = [bc for bc in self.bcs if not isinstance(bc, backend.DirichletBC)]
            bcs = dirichlet_bcs + other_bcs

            output = Vector(backend.Function(self.test_function().function_space()))
            #print "b.data is a %s in the solution of %s" % (b.data.__class__, var)
            if backend.parameters["adjoint"]["symmetric_bcs"] and backend.__version__ > '1.2.0':
                assembler = backend.SystemAssembler(self.data, b.data, bcs)
                assembled_rhs = backend.Vector()
                assembler.assemble(assembled_rhs)
            elif isinstance(b.data, ufl.Form):
                assembled_rhs = wrap_assemble(b.data, self.test_function())
            else:
                if backend.__name__ == "dolfin":
                    assembled_rhs = b.data.vector()
                else:
                    assembled_rhs = b.data
            [bc.apply(assembled_rhs) for bc in bcs]

            if not var in caching.lu_solvers:
                if backend.parameters["adjoint"]["debug_cache"]:
                    backend.info_red("Got a cache miss for %s" % var)

                if backend.parameters["adjoint"]["symmetric_bcs"] and backend.__version__ > '1.2.0':
                    assembled_lhs = backend.Matrix()
                    assembler.assemble(assembled_lhs)
                else:
                    assembled_lhs = self.assemble_data()
                    [bc.apply(assembled_lhs) for bc in bcs]

                caching.lu_solvers[var] = compatibility.LUSolver(assembled_lhs, "mumps")
                caching.lu_solvers[var].parameters["reuse_factorization"] = True
            else:
                if backend.parameters["adjoint"]["debug_cache"]:
                    backend.info_green("Got a cache hit for %s" % var)

            caching.lu_solvers[var].solve(output.data.vector(), assembled_rhs)

        return output
示例#38
0
        def Jfunc(new_val):
            dep = exp.dependencies()[i]

            # Remember the old dependency value for later
            old_val = float(dep)

            # Compute the functional value
            dep.assign(new_val)
            s = projection.project(exp, V, annotate=False)
            out = backend.assemble(s**2*backend.dx + exp*backend.dx(domain=mesh))

            # Restore the old dependency value
            dep.assign(old_val)

            return out
示例#39
0
        def Jfunc(new_val):
            dep = exp.dependencies[i]

            # Remember the old dependency value for later
            old_val = float(dep)

            # Compute the functional value
            dep.assign(new_val)
            s = projection.project(exp, V, annotate=False)
            out = backend.assemble(s**2*backend.dx + exp*backend.dx(domain=mesh))

            # Restore the old dependency value
            dep.assign(old_val)

            return out
示例#40
0
    def __call__(self, adjointer, timestep, dependencies, values):

        if self.verbose:
            print("eval ", len(values))
            print("timestep ", timestep)
            print("\r\n******************")

        toi = _time_levels(adjointer, timestep)[0]  # time of interest

        my = [0.0] * len(self.coords)
        for i in range(len(self.coords)):
            if not self.skip[i] and len(values) > 0:
                if timestep is adjointer.timestep_count - 1:

                    # add final contribution
                    if self.index[i] is None:
                        solu = values[0].data(self.coords[i])
                    else:
                        solu = values[0].data(self.coords[i])[self.index[i]]
                    ref = self.refs[i][self.times.index(self.times[-1])]
                    my[i] = (solu - float(ref)) * (solu - float(ref))

                    # if necessary, add one but last contribution
                    if toi in self.times and len(values) > 0:
                        if self.index[i] is None:
                            solu = values[-1].data(self.coords[i])
                        else:
                            solu = values[-1].data(
                                self.coords[i])[self.index[i]]
                        ref = self.refs[i][self.times.index(toi)]
                        my[i] += (solu - float(ref)) * (solu - float(ref))

                elif timestep is 0:
                    return backend.assemble(self.regform)
                else:  # normal situation
                    if self.index[i] is None:
                        solu = values[-1].data(self.coords[i])
                    else:
                        solu = values[-1].data(self.coords[i])[self.index[i]]
                    ref = self.refs[i][self.times.index(toi)]
                    my[i] = (solu - float(ref)) * (solu - float(ref))

            if self.verbose:
                print("my eval ", my[i])
                print("eval ", timestep, " times ",
                      _time_levels(adjointer, timestep))

        return self.alpha * sum(my)
示例#41
0
    def derivative_action(self, dependencies, values, variable,
                          contraction_vector, hermitian):
        idx = dependencies.index(variable)

        # If you want to apply boundary conditions symmetrically in the adjoint
        # -- and you often do --
        # then we need to have a UFL representation of all the terms in the adjoint equation.
        # However!
        # Since UFL cannot represent the identity map,
        # we need to find an f such that when
        # assemble(inner(f, v)*dx)
        # we get the contraction_vector.data back.
        # This involves inverting a mass matrix.

        if backend.parameters["adjoint"][
                "symmetric_bcs"] and backend.__version__ <= '1.2.0':
            backend.info_red(
                "Warning: symmetric BC application requested but unavailable in dolfin <= 1.2.0."
            )

        if backend.parameters["adjoint"][
                "symmetric_bcs"] and backend.__version__ > '1.2.0':

            V = contraction_vector.data.function_space()
            v = backend.TestFunction(V)

            if str(V) not in adjglobals.fsp_lu:
                u = backend.TrialFunction(V)
                A = backend.assemble(backend.inner(u, v) * backend.dx)
                solver = "mumps" if "mumps" in backend.lu_solver_methods(
                ).keys() else "default"
                lusolver = backend.LUSolver(A, solver)
                lusolver.parameters["symmetric"] = True
                lusolver.parameters["reuse_factorization"] = True
                adjglobals.fsp_lu[str(V)] = lusolver
            else:
                lusolver = adjglobals.fsp_lu[str(V)]

            riesz = backend.Function(V)
            lusolver.solve(
                riesz.vector(),
                self.weights[idx] * contraction_vector.data.vector())
            out = (backend.inner(riesz, v) * backend.dx)
        else:
            out = backend.Function(self.fn_space)
            out.assign(self.weights[idx] * contraction_vector.data)

        return adjlinalg.Vector(out)
示例#42
0
    def jacobian_adjoint_action(self, m, dp, result):
        """Computes the Jacobian adjoint action of c(m) in direction dp and stores the result in result. """

        if isinstance(m, list):
            assert len(m) == 1
            m = m[0]
        self.update_control(m)

        asm = backend.assemble(
            dp * ufl.replace(self.dform, {self.trial: self.test}))
        if isinstance(result, backend.Function):
            if backend.__name__ in ["dolfin", "fenics"]:
                result.vector().zero()
                result.vector().axpy(1.0, asm)
            else:
                result.assign(asm)
        else:
            raise NotImplementedError("Do I need to untangle all controls?")
示例#43
0
def wrap_assemble(form, test):
    '''If you do
       F = inner(grad(TrialFunction(V), grad(TestFunction(V))))
       a = lhs(F); L = rhs(F)
       solve(a == L, ...)

       it works, even though L is empty. But if you try to assemble(L) as we do here,
       you get a crash.

       This function wraps assemble to catch that crash and return an empty RHS instead.
    '''

    try:
        b = backend.assemble(form)
    except RuntimeError:
        assert len(form.integrals()) == 0
        b = backend.Function(test.function_space()).vector()

    return b
示例#44
0
    def __call__(self, adjointer, timestep, dependencies, values):

        functional_value = self._substitute_form(adjointer, timestep,
                                                 dependencies, values)

        if functional_value is not None:
            args = ufl.algorithms.extract_arguments(functional_value)
            if len(args) > 0:
                backend.info_red(
                    "The form passed into Functional must be rank-0 (a scalar)! You have passed in a rank-%s form."
                    % len(args))
                raise libadjoint.exceptions.LibadjointErrorInvalidInputs

            from .utils import _has_multimesh
            if _has_multimesh(functional_value):
                return backend.assemble_multimesh(functional_value)
            else:
                return backend.assemble(functional_value)
        else:
            return 0.0
示例#45
0
    def solve(self, var, b):
        timer = backend.Timer("Matrix-free solver")
        solver = backend.PETScKrylovSolver(*self.solver_parameters)
        solver.parameters.update(self.parameters)

        x = backend.Function(self.fn_space)
        if b.data is None:
            backend.info_red(
                "Warning: got zero RHS for the solve associated with variable %s"
                % var)
            return adjlinalg.Vector(x)

        if isinstance(b.data, backend.Function):
            rhs = b.data.vector().copy()
        else:
            rhs = backend.assemble(b.data)

        if var.type in ['ADJ_TLM', 'ADJ_ADJOINT']:
            self.bcs = [
                utils.homogenize(bc)
                for bc in self.bcs if isinstance(bc, backend.cpp.DirichletBC)
            ] + [
                bc
                for bc in self.bcs if not isinstance(bc, backend.DirichletBC)
            ]

        for bc in self.bcs:
            bc.apply(rhs)

        if self.operators[
                1] is not None:  # we have a user-supplied preconditioner
            solver.set_operators(self.data, self.operators[1])
            solver.solve(backend.down_cast(x.vector()), backend.down_cast(rhs))
        else:
            solver.solve(self.data, backend.down_cast(x.vector()),
                         backend.down_cast(rhs))

        timer.stop()
        return adjlinalg.Vector(x)
示例#46
0
  def equation_partial_derivative(self, adjointer, adjoint, i, variable):
    form = adjresidual.get_residual(i)

    if form is None:
      return None
    else:
      form = -form

    fn_space = ufl.algorithms.extract_arguments(form)[0].function_space()
    dparam = backend.Function(backend.FunctionSpace(fn_space.mesh(), "R", 0))
    dparam.vector()[:] = 1.0

    dJdv = numpy.zeros(len(self.v))
    for (i, a) in enumerate(self.v):
      diff_form = ufl.algorithms.expand_derivatives(backend.derivative(form, a, dparam))

      dFdm = backend.assemble(diff_form) # actually - dF/dm
      assert isinstance(dFdm, backend.GenericVector)

      out = dFdm.inner(adjoint.vector())
      dJdv[i] = out

    return dJdv
示例#47
0
    def assemble(self):
        u = TrialFunction(self.V)
        v = TestFunction(self.V)

        A = inner(u, v)*dx
        return assemble(A)
示例#48
0
    def assemble(self):
        u = TrialFunction(self.V)
        v = TestFunction(self.V)

        A = inner(u, v)*dx + alpha*inner(grad(u), grad(v))*dx
        return assemble(A)
示例#49
0
    def norm(self):

        if isinstance(self.data, backend.Function):
            return (abs(backend.assemble(backend.inner(self.data, self.data)*backend.dx)))**0.5
        elif isinstance(self.data, ufl.form.Form):
            return backend.assemble(self.data).norm("l2")
示例#50
0
def perturbed_replay(parameter, perturbation, perturbation_scale, observation, perturbation_norm="mass", observation_norm="mass", callback=None, forget=False):
  r"""Perturb the forward run and compute

  .. math::

    \frac{
    \left|\left| \delta \mathrm{observation} \right|\right|
    }{
    \left|\left| \delta \mathrm{input} \right| \right|
    }

  as a function of time.

  :py:data:`parameter` -- an FunctionControl to say what variable should be
                          perturbed (e.g. FunctionControl('InitialConcentration'))
  :py:data:`perturbation` -- a Function to give the perturbation direction (from a GST analysis, for example)
  :py:data:`perturbation_norm` -- a bilinear Form which induces a norm on the space of perturbation inputs
  :py:data:`perturbation_scale` -- how big the norm of the initial perturbation should be
  :py:data:`observation` -- the variable to observe (e.g. 'Concentration')
  :py:data:`observation_norm` -- a bilinear Form which induces a norm on the space of perturbation outputs
  :py:data:`callback` -- a function f(var, perturbed, unperturbed) that the user can supply (e.g. to dump out variables during the perturbed replay)
  """

  if not backend.parameters["adjoint"]["record_all"]:
    info_red("Warning: your replay test will be much more effective with backend.parameters['adjoint']['record_all'] = True.")

  assert isinstance(parameter, controls.FunctionControl)

  if perturbation_norm == "mass":
    p_fnsp = perturbation.function_space()
    u = backend.TrialFunction(p_fnsp)
    v = backend.TestFunction(p_fnsp)
    p_mass = backend.inner(u, v)*backend.dx
    perturbation_norm = p_mass

  if not isinstance(perturbation_norm, backend.GenericMatrix):
    perturbation_norm = backend.assemble(perturbation_norm)
  if not isinstance(observation_norm, backend.GenericMatrix) and observation_norm != "mass":
    observation_norm = backend.assemble(observation_norm)

  def compute_norm(perturbation, norm):
    # Need to compute <x, Ax> and then take its sqrt
    # where x is perturbation, A is norm
    try:
      vec = perturbation.vector()
    except:
      vec = perturbation

    Ax = vec.copy()
    norm.mult(vec, Ax)
    xAx = vec.inner(Ax)
    return math.sqrt(xAx)

  growths = []

  for i in range(adjglobals.adjointer.equation_count):
      (fwd_var, output) = adjglobals.adjointer.get_forward_solution(i)

      if fwd_var == parameter.var: # we've hit the initial condition we want to perturb
        current_norm = compute_norm(perturbation, perturbation_norm)
        output.data.vector()[:] += (perturbation_scale/current_norm) * perturbation.vector()

      unperturbed = adjglobals.adjointer.get_variable_value(fwd_var).data

      if fwd_var.name == observation: # we've hit something we want to observe
        # Fetch the unperturbed result from the record

        if observation_norm == "mass": # we can't do this earlier, because we don't have the observation function space yet
          o_fnsp = output.data.function_space()
          u = backend.TrialFunction(o_fnsp)
          v = backend.TestFunction(o_fnsp)
          o_mass = backend.inner(u, v)*backend.dx
          observation_norm = backend.assemble(o_mass)

        diff = output.data.vector() - unperturbed.vector()
        growths.append(compute_norm(diff, observation_norm)/perturbation_scale) # <--- the action line

      if callback is not None:
        callback(fwd_var, output.data, unperturbed)

      storage = libadjoint.MemoryStorage(output)
      storage.set_compare(tol=None)
      storage.set_overwrite(True)
      out = adjglobals.adjointer.record_variable(fwd_var, storage)

      if forget:
        adjglobals.adjointer.forget_forward_equation(i)

  # can happen if we initialised a nonlinear solve with a constant zero guess
  if growths[0] == 0.0:
    return growths[1:]
  else:
    return growths
示例#51
0
    def axpy(self, alpha, x):

        if hasattr(x, 'nonlinear_form'):
            self.nonlinear_form = x.nonlinear_form
            self.nonlinear_u = x.nonlinear_u
            self.nonlinear_bcs = x.nonlinear_bcs
            self.nonlinear_J = x.nonlinear_J

        if x.zero:
            return

        if (self.data is None):
            # self is an empty form.
            if isinstance(x.data, backend.Function):
                self.data = backend.Function(x.data)
                self.data.vector()._scale(alpha)
            else:
                self.data=alpha*x.data

        elif x.data is None:
            pass
        elif isinstance(self.data, backend.Coefficient):
            if isinstance(x.data, backend.Coefficient):
                self.data.vector().axpy(alpha, x.data.vector())
            else:
                # This occurs when adding a RHS derivative to an adjoint equation
                # corresponding to the initial conditions.
                self.data.vector().axpy(alpha, backend.assemble(x.data))
                self.data.form = alpha * x.data
        elif isinstance(x.data, ufl.form.Form) and isinstance(self.data, ufl.form.Form):

            # Let's do a bit of argument shuffling, shall we?
            xargs = ufl.algorithms.extract_arguments(x.data)
            sargs = ufl.algorithms.extract_arguments(self.data)

            if xargs != sargs:
                # OK, let's check that all of the function spaces are happy and so on.
                for i in range(len(xargs)):
                    assert xargs[i].element() == sargs[i].element()
                    assert xargs[i].function_space() == sargs[i].function_space()

                # Now that we are happy, let's replace the xargs with the sargs ones.
                x_form = backend.replace(x.data, dict(zip(xargs, sargs)))
            else:
                x_form = x.data

            self.data+=alpha*x_form
        elif isinstance(self.data, ufl.form.Form) and isinstance(x.data, backend.Function):
            #print "axpy assembling FormFunc. self.data is a %s; x.data is a %s" % (self.data.__class__, x.data.__class__)
            x_vec = x.data.vector().copy()
            self_vec = backend.assemble(self.data)
            self_vec.axpy(alpha, x_vec)
            new_fn = backend.Function(x.data.function_space())
            new_fn.vector()[:] = self_vec
            self.data = new_fn
            self.fn_space = self.data.function_space()

        else:
            print "self.data.__class__: ", self.data.__class__
            print "x.data.__class__: ", x.data.__class__
            assert False

        self.zero = False
示例#52
0
    def assemble(self):
        u = TrialFunction(self.V)
        v = TestFunction(self.V)

        A = inner(u, v) * dx
        return assemble(A)
示例#53
0
    def output_workspace(self):
        """Return an object like the output of c(m) for calculations."""

        return backend_types.Constant(backend.assemble(self.form))
示例#54
0
 def function(self, m):
     self.update_control(m)
     b = backend.assemble(self.form)
     return backend_types.Constant(b)
示例#55
0
def perturbed_replay(parameter,
                     perturbation,
                     perturbation_scale,
                     observation,
                     perturbation_norm="mass",
                     observation_norm="mass",
                     callback=None,
                     forget=False):
    r"""Perturb the forward run and compute

    .. math::

      \frac{
      \left|\left| \delta \mathrm{observation} \right|\right|
      }{
      \left|\left| \delta \mathrm{input} \right| \right|
      }

    as a function of time.

    :py:data:`parameter` -- an FunctionControl to say what variable should be
                            perturbed (e.g. FunctionControl('InitialConcentration'))
    :py:data:`perturbation` -- a Function to give the perturbation direction (from a GST analysis, for example)
    :py:data:`perturbation_norm` -- a bilinear Form which induces a norm on the space of perturbation inputs
    :py:data:`perturbation_scale` -- how big the norm of the initial perturbation should be
    :py:data:`observation` -- the variable to observe (e.g. 'Concentration')
    :py:data:`observation_norm` -- a bilinear Form which induces a norm on the space of perturbation outputs
    :py:data:`callback` -- a function f(var, perturbed, unperturbed) that the user can supply (e.g. to dump out variables during the perturbed replay)
    """

    if not backend.parameters["adjoint"]["record_all"]:
        info_red(
            "Warning: your replay test will be much more effective with backend.parameters['adjoint']['record_all'] = True."
        )

    assert isinstance(parameter, controls.FunctionControl)

    if perturbation_norm == "mass":
        p_fnsp = perturbation.function_space()
        u = backend.TrialFunction(p_fnsp)
        v = backend.TestFunction(p_fnsp)
        p_mass = backend.inner(u, v) * backend.dx
        perturbation_norm = p_mass

    if not isinstance(perturbation_norm, backend.GenericMatrix):
        perturbation_norm = backend.assemble(perturbation_norm)
    if not isinstance(observation_norm,
                      backend.GenericMatrix) and observation_norm != "mass":
        observation_norm = backend.assemble(observation_norm)

    def compute_norm(perturbation, norm):
        # Need to compute <x, Ax> and then take its sqrt
        # where x is perturbation, A is norm
        try:
            vec = perturbation.vector()
        except:
            vec = perturbation

        Ax = vec.copy()
        norm.mult(vec, Ax)
        xAx = vec.inner(Ax)
        return math.sqrt(xAx)

    growths = []

    for i in range(adjglobals.adjointer.equation_count):
        (fwd_var, output) = adjglobals.adjointer.get_forward_solution(i)

        if fwd_var == parameter.var:  # we've hit the initial condition we want to perturb
            current_norm = compute_norm(perturbation, perturbation_norm)
            output.data.vector()[:] += (perturbation_scale /
                                        current_norm) * perturbation.vector()

        unperturbed = adjglobals.adjointer.get_variable_value(fwd_var).data

        if fwd_var.name == observation:  # we've hit something we want to observe
            # Fetch the unperturbed result from the record

            if observation_norm == "mass":  # we can't do this earlier, because we don't have the observation function space yet
                o_fnsp = output.data.function_space()
                u = backend.TrialFunction(o_fnsp)
                v = backend.TestFunction(o_fnsp)
                o_mass = backend.inner(u, v) * backend.dx
                observation_norm = backend.assemble(o_mass)

            diff = output.data.vector() - unperturbed.vector()
            growths.append(
                compute_norm(diff, observation_norm) /
                perturbation_scale)  # <--- the action line

        if callback is not None:
            callback(fwd_var, output.data, unperturbed)

        storage = libadjoint.MemoryStorage(output)
        storage.set_compare(tol=None)
        storage.set_overwrite(True)
        out = adjglobals.adjointer.record_variable(fwd_var, storage)

        if forget:
            adjglobals.adjointer.forget_forward_equation(i)

    # can happen if we initialised a nonlinear solve with a constant zero guess
    if growths[0] == 0.0:
        return growths[1:]
    else:
        return growths
示例#56
0
 def recompute_component(self, inputs, block_variable, idx, prepared):
     form = prepared
     output = backend.assemble(form)
     output = create_overloaded_object(output)
     return output
示例#57
0
    def assemble(self):
        u = TrialFunction(self.V)
        v = TestFunction(self.V)

        A = inner(u, v) * dx + alpha * inner(grad(u), grad(v)) * dx
        return assemble(A)