示例#1
0
    def scalar_product(self, a, b):
        """Computes the scalar product between control type functions a and b.

		Parameters
		----------
		a : list[dolfin.function.function.Function]
			The first argument.
		b : list[dolfin.function.function.Function]
			The second argument.

		Returns
		-------
		float
			The value of the scalar product.
		"""

        result = 0.0

        for i in range(self.control_dim):
            x = fenics.as_backend_type(a[i].vector()).vec()
            y = fenics.as_backend_type(b[i].vector()).vec()

            temp, _ = self.scalar_products_matrices[i].getVecs()
            self.scalar_products_matrices[i].mult(x, temp)
            result += temp.dot(y)

        return result
def _assemble_petsc_system(A_form, b_form, bcs=None):
	"""Assembles a system symmetrically and converts objects to PETSc format.

	Parameters
	----------
	A_form : ufl.form.Form
		The UFL form for the left-hand side of the linear equation.
	b_form : ufl.form.Form
		The UFL form for the right-hand side of the linear equation.
	bcs : None or dolfin.fem.dirichletbc.DirichletBC or list[dolfin.fem.dirichletbc.DirichletBC]
		A list of Dirichlet boundary conditions.

	Returns
	-------
	petsc4py.PETSc.Mat
		The petsc matrix for the left-hand side of the linear equation.
	petsc4py.PETSc.Vec
		The petsc vector for the right-hand side of the linear equation.

	Notes
	-----
	This function always uses the ident_zeros method of the matrix in order to add a one to the diagonal
	in case the corresponding row only consists of zeros. This allows for well-posed problems on the
	boundary etc.
	"""

	A, b = fenics.assemble_system(A_form, b_form, bcs, keep_diagonal=True)
	A.ident_zeros()

	A = fenics.as_backend_type(A).mat()
	b = fenics.as_backend_type(b).vec()

	return A, b
示例#3
0
    def solve(self):
        """Solves the Riesz projection problem to obtain the shape gradient of the cost functional.

		Returns
		-------
		gradient : dolfin.function.function.Function
			The function representing the shape gradient of the (reduced) cost functional.
		"""

        self.state_problem.solve()
        self.adjoint_problem.solve()

        if not self.has_solution:

            self.form_handler.regularization.update_geometric_quantities()
            self.form_handler.assembler.assemble(
                self.form_handler.fe_shape_derivative_vector)
            b = fenics.as_backend_type(
                self.form_handler.fe_shape_derivative_vector).vec()
            _solve_linear_problem(self.ksp,
                                  self.form_handler.scalar_product_matrix, b,
                                  self.gradient.vector().vec(),
                                  self.ksp_options)
            self.gradient.vector().apply('')

            self.has_solution = True

            self.gradient_norm_squared = self.form_handler.scalar_product(
                self.gradient, self.gradient)

        return self.gradient
示例#4
0
    def solve(self):
        """Solves the Riesz projection problem to obtain the gradient of the (reduced) cost functional.

		Returns
		-------
		gradients : list[dolfin.function.function.Function]
			The list of gradient of the cost functional.
		"""

        self.state_problem.solve()
        self.adjoint_problem.solve()

        if not self.has_solution:
            for i in range(self.form_handler.control_dim):
                b = fenics.as_backend_type(
                    fenics.assemble(
                        self.form_handler.gradient_forms_rhs[i])).vec()
                _solve_linear_problem(ksp=self.ksps[i],
                                      b=b,
                                      x=self.gradients[i].vector().vec())
                self.gradients[i].vector().apply('')

            self.has_solution = True

            self.gradient_norm_squared = self.form_handler.scalar_product(
                self.gradients, self.gradients)

        return self.gradients
示例#5
0
文件: ksdgts.py 项目: leonavery/KSDG
    def imExIF(self, ts, t, u, udot, f):
        """Implicit/Explicit IFunction for PETSc TS

        This is designed for use as the LHS in an implicit/explicit
        PETSc time-stepper. The DE to be solved is always A.u' =
        b(u). u corresponds to self.sol. A is a constant (i.e.,
        u-independent) matrix calculated by assembling the
        u'-dependent term of the weak form, and b a u-dependent vector
        calculated by assembling the remaining terms. This equation
        may be solved in any of three forms:

            Fully implicit: A.u' - b(u) = 0
            implicit/explicit: A.u' = b(u)
            Fully explicit: u' = A^(-1).b(u)

        Corresponding to these are seven functions that can be
        provided to PETSc.TS: imExIF, imExIJ, imExRHSF, and imExRHSJ
        calculate the LHS function and Jacobian and the RHS function
        and Jacobian for the implicit/explicit case. Arguments:

        t: the time.
        u: a petsc4py.PETSc.Vec containing the state vector (not used).
        udot: a petsc4py.PETSc.Vec containing the time derivative u'
        f: a petsc4py.PETSc.Vec in which A.u' will be left.
        """
        try:
            self.ksdg.setup_problem(t)
        except TypeError:
            self.ksdg.setup_problem()
        pA = fe.as_backend_type(self.ksdg.A).mat()
        pA.mult(udot, f)
        f.assemble()
def apply_time_boundary_conditions(domain, V, u0, A, b):
    """Apply the time slice boundary conditions by hand.

    Args:
        domain: Space-time domain.
        V: Function space.
        u0: Initial data.
        A: The stiffness matrix.
        b: The right-hand side.
    Outputs:
        A: The new stiffness matrix with the boundary conditions.
        b: The new right-hand side with the boundary conditions.
    """
    # Export matrices to PETSc
    A = as_backend_type(A).mat()
    b = as_backend_type(b).vec()

    # Apply terminal boundary condition on v by zeroing the corresponding
    # matrix rows. The dof indices are saved for later.
    def on_terminal_slice(x):
        return domain.get_terminal_slice().inside(x, True) \
            and (not domain.get_spatial_boundary().inside(x, True))

    (rows_to_zero, _) = get_dof_by_criterion(V, on_terminal_slice)
    A.zeroRows(rows_to_zero, diag=0)

    # Apply initial boundary condition on u
    def on_initial_slice(x):
        return domain.get_initial_slice().inside(x, True) \
            and (not domain.get_spatial_boundary().inside(x, True))

    (dof_no, dof_coor) = get_dof_by_criterion(V, on_initial_slice)
    # Update the matrices
    A.setOption(PETSc.Mat.Option.NEW_NONZERO_LOCATION_ERR, 0)
    for (i, k) in enumerate(dof_no):
        j = rows_to_zero[i]
        A[j, k] = 1.0
        b[j] = u0(dof_coor[i])
    A.assemble()
    b.assemble()

    # put petsc4py matrix back to fenics
    A = PETScMatrix(A)
    b = PETScVector(b)
    return (A, b)
示例#7
0
文件: ksdgts.py 项目: leonavery/KSDG
    def imExRHSF(self, ts, t, u, g, *args, **kwargs):
        """Implicit/Explicit RHSFunction for PETSc TS

        This is designed for use as the RHS in an implicit/explicit
        PETSc time-stepper. The DE to be solved is always A.u' =
        b(u). u corresponds to self.ksdg.sol. A is a constant (i.e.,
        u-independent) matrix calculated by assembling the
        u'-dependent term of the weak form, and b a u-dependent vector
        calculated by assembling the remaining terms. This equation
        may be solved in any of three forms:

            Fully implicit: A.u' - b(u) = 0
            implicit/explicit: A.u' = b(u)
            Fully explicit: u' = A^(-1).b(u)

        Corresponding to these are seven functions that can be
        provided to PETSc.TS: imExIF, imExIJ, imExRHSF, and imExRHSJ
        calculate the LHS function and Jacobian and the RHS function
        and Jacobian for the implicit/explicit case. Arguments:

        t: the time.
        u: a petsc4py.PETSc.Vec containing the state vector.
        g: a petsc4py.PETSc.Vec in which b will be left.
        """
        #
        # PETSc apparently calls RHSF when it shoud call RHSJ. We
        # detect this by the extra argument and reroute the call.
        #
        if (args):
            return self.imExRHSJ(t, u, g, *args, **kwargs)
        psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
        u.copy(psol)
        self.ksdg.sol.vector().apply('insert')
        try:
            self.ksdg.setup_problem(t)
        except TypeError:
            self.ksdg.setup_problem()
        self.ksdg.b = fe.assemble(self.ksdg.all_terms)
        if hasattr(self.ksdg, 'bcs'):
            for bc in self.ksdg.bcs:
                bc.apply(self.ksdg.b)
        pb = fe.as_backend_type(self.ksdg.b).vec()
        pb.copy(g)
        g.assemble()
示例#8
0
文件: ksdgts.py 项目: leonavery/KSDG
    def imExRHSJ(self, ts, t, u, J, B):
        """Implicit/Explicit RHSFunction for PETSc TS

        This is designed for use as the RHS in an implicit/explicit
        PETSc time-stepper. The DE to be solved is always A.u' =
        b(u). u corresponds to self.sol. A is a constant (i.e.,
        u-independent) matrix calculated by assembling the
        u'-dependent term of the weak form, and b a u-dependent vector
        calculated by assembling the remaining terms. This equation
        may be solved in any of three forms:

            Fully implicit: A.u' - b(u) = 0
            implicit/explicit: A.u' = b(u)
            Fully explicit: u' = A^(-1).b(u)

        Corresponding to these are seven functions that can be
        provided to PETSc.TS: imExIF, imExIJ, imExRHSF, and imExRHSJ
        calculate the LHS function and Jacobian and the RHS function
        and Jacobian for the implicit/explicit case. Arguments:

        t: the time.
        u: a petsc4py.PETSc.Vec containing the state vector
        J, B: matrices in which the Jacobian of b(u) is left. 
        """
        try:
            self.ksdg.setup_problem(t)
        except TypeError:
            self.ksdg.setup_problem()
        psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
        u.copy(psol)
        self.ksdg.sol.vector().apply('insert')
        Jsol = fe.assemble(self.ksdg.J_terms)
        pJsol = fe.as_backend_type(Jsol).mat()
        pA.copy(J)
        J.scale(shift)
        J.axpy(1.0, pJsol)
        pJsol.copy(J)
        J.assemble()
        if J != B:
            J.copy(B)
            B.assemble()
        return True
示例#9
0
	def compute_curvature(self):
		"""Computes the mean curvature vector of the geometry.
		
		Returns
		-------
		None
		"""
		
		if self.mu_curvature > 0.0:
			A = fenics.assemble(self.a_curvature, keep_diagonal=True)
			A.ident_zeros()
			A = fenics.as_backend_type(A).mat()
			
			b = fenics.assemble(self.L_curvature)
			b = fenics.as_backend_type(b).vec()
			
			_solve_linear_problem(A=A, b=b, x=self.kappa_curvature.vector().vec())
			
		else:
			pass
示例#10
0
文件: ksdgts.py 项目: leonavery/KSDG
    def explicitRHSF(self, ts, t, u, g):
        """Explicit RHSFunction for PETSc TS

        This is designed for use as the RHS in an explicit PETSc
        time-stepper. The DE to be solved is always A.u' = b(u). u
        corresponds to self.ksdg.sol. A is a constant (i.e., u-independent)
        matrix calculated by assembling the u'-dependent term of the
        weak form, and b a u-dependent vector calculated by assembling
        the remaining terms. This equation may be solved in any of
        three forms:

            Fully implicit: A.u' - b(u) = 0
            implicit/explicit: A.u' = b(u)
            Fully explicit: u' = A^(-1).b(u)

        Corresponding to these are seven functions that can be
        provided to PETSc.TS: explicitRHSF calculates
        the RHS function for the explicit case. Arguments:

        t: the time.
        u: a petsc4py.PETSc.Vec containing the state vector.
        g: a petsc4py.PETSc.Vec in which A^(-1).b(u) will be left.
        """
        psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
        u.copy(psol)
        self.ksdg.sol.vector().apply('insert')
        try:
            self.ksdg.setup_problem(t)
        except TypeError:
            self.ksdg.setup_problem()
        self.ksdg.b = fe.assemble(self.ksdg.all_terms)
        if hasattr(self.ksdg, 'bcs'):
            for bc in self.ksdg.bcs:
                bc.apply(self.ksdg.b)
        ret = self.ksdg.solver.solve(self.ksdg.dsol.vector(), self.ksdg.b)
        pdu = fe.as_backend_type(self.ksdg.dsol.vector()).vec()
        pdu.copy(g)
        g.assemble()
示例#11
0
文件: ksdgts.py 项目: leonavery/KSDG
 def historyMonitor(self, ts, k, t, u):
     """For use as TS monitor. Stores results in history"""
     #        logTS("historyMonitor", flush=True)
     h = ts.getTimeStep()
     if not hasattr(self, 'history'):
         self.history = []
     #
     # make a local copy of the dof vector
     #
     psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
     u.copy(psol)
     self.ksdg.sol.vector().apply('insert')
     lu = self.ksdg.sol.vector().gather_on_zero()
     self.history.append(dict(step=k, h=h, t=t, u=lu.array.copy()))
示例#12
0
    def scalar_product(self, a, b):
        """Computes the scalar product between two deformation functions.

		Parameters
		----------
		a : dolfin.function.function.Function
			The first argument.
		b : dolfin.function.function.Function
			The second argument.

		Returns
		-------
		float
			The value of the scalar product.
		"""

        x = fenics.as_backend_type(a.vector()).vec()
        y = fenics.as_backend_type(b.vector()).vec()

        temp, _ = self.scalar_product_matrix.getVecs()
        self.scalar_product_matrix.mult(x, temp)
        result = temp.dot(y)

        return result
示例#13
0
文件: ksdgts.py 项目: leonavery/KSDG
 def saveMonitor(ts, k, t, u):
     #
     # make a local copy of the dof vector
     #
     #            logTS('saveMonitor entered')
     psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
     u.copy(psol)
     self.ksdg.sol.vector().apply('insert')
     lu = self.ksdg.sol.vector().gather_on_zero()
     if ts.comm.rank == 0:
         lu = lu[self.remap]
         #
         # reopen and close every time, so file valid after abort
         #
         tsf = KSDGTimeSeries(tsname, 'r+')
         tsf.store(lu, t, k=k)
         tsf.close()
示例#14
0
    def update_scalar_product(self):
        """Updates the linear elasticity equations to the current geometry.

		Updates the left-hand-side of the linear elasticity equations
		(needed when the geometry changes).

		Returns
		-------
		None
		"""

        self.__compute_mu_elas()

        self.assembler.assemble(self.fe_scalar_product_matrix)
        self.fe_scalar_product_matrix.ident_zeros()
        self.scalar_product_matrix = fenics.as_backend_type(
            self.fe_scalar_product_matrix).mat()
示例#15
0
文件: ksdgts.py 项目: leonavery/KSDG
 def checkpointMonitor(self, ts, k, t, u, prefix):
     """For use as TS monitor. Checkpoints results"""
     h = ts.getTimeStep()
     #
     # make a local copy of the dof vector
     #
     if not hasattr(self, 'remap'):
         self.lmesh = gather_mesh(self.ksdg.mesh)
         logTS('checkpointMonitor: self.lmesh', self.lmesh)
         self.remap = dofremap(self.ksdg)
         logTS('checkpointMonitor: self.remap', self.remap)
     psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
     u.copy(psol)
     self.ksdg.sol.vector().apply('insert')
     lu = self.ksdg.sol.vector().gather_on_zero()
     cpname = prefix + '_' + str(k) + '.h5'
     if self.comm.rank == 0:
         lu = lu[self.remap]
         cpf = KSDGTimeSeries(cpname, 'w')
         cpf.store(lu, t, k=k)
         cpf.close()
def convert_fenics_csr_matrix_to_scipy_csr_matrix(A_fenics):
    ai, aj, av = fenics.as_backend_type(A_fenics).mat().getValuesCSR()
    A_scipy = sps.csr_matrix((av, aj, ai))
    return A_scipy
示例#17
0
    def hessian_application(self, h, out):
        r"""Computes the application of the Hessian to some element

		This is needed in the truncated Newton method where we solve the system

		.. math:: J''(u) [\Delta u] = - J'(u)

		via iterative methods (conjugate gradient or conjugate residual method)

		Parameters
		----------
		h : list[dolfin.function.function.Function]
			A function to which we want to apply the Hessian to.
		out : list[dolfin.function.function.Function]
			A list of functions into which the result is saved.

		Returns
		-------
		None
		"""

        for i in range(self.control_dim):
            self.test_directions[i].vector()[:] = h[i].vector()[:]

        self.states_prime = self.form_handler.states_prime
        self.adjoints_prime = self.form_handler.adjoints_prime
        self.bcs_list_ad = self.form_handler.bcs_list_ad

        if not self.form_handler.state_is_picard or self.form_handler.state_dim == 1:

            for i in range(self.state_dim):
                A, b = _assemble_petsc_system(
                    self.form_handler.sensitivity_eqs_lhs[i],
                    self.form_handler.sensitivity_eqs_rhs[i],
                    self.bcs_list_ad[i])
                _solve_linear_problem(self.state_ksps[i], A, b,
                                      self.states_prime[i].vector().vec(),
                                      self.form_handler.state_ksp_options[i])
                self.states_prime[i].vector().apply('')

            for i in range(self.state_dim):
                A, b = _assemble_petsc_system(
                    self.form_handler.adjoint_sensitivity_eqs_lhs[-1 - i],
                    self.form_handler.w_1[-1 - i], self.bcs_list_ad[-1 - i])
                _solve_linear_problem(
                    self.adjoint_ksps[-1 - i], A, b,
                    self.adjoints_prime[-1 - i].vector().vec(),
                    self.form_handler.adjoint_ksp_options[-1 - i])
                self.adjoints_prime[-1 - i].vector().apply('')

        else:
            for i in range(self.maxiter + 1):
                res = 0.0
                for j in range(self.form_handler.state_dim):
                    res_j = fenics.assemble(
                        self.form_handler.sensitivity_eqs_picard[j])
                    [
                        bc.apply(res_j)
                        for bc in self.form_handler.bcs_list_ad[j]
                    ]
                    res += pow(res_j.norm('l2'), 2)

                if res == 0:
                    break

                res = np.sqrt(res)

                if i == 0:
                    res_0 = res

                if self.picard_verbose:
                    print('Picard Sensitivity 1 Iteration ' + str(i) +
                          ': ||res|| (abs): ' + format(res, '.3e') +
                          '   ||res|| (rel): ' + format(res / res_0, '.3e'))

                if res / res_0 < self.rtol or res < self.atol:
                    break

                if i == self.maxiter:
                    raise NotConvergedError(
                        'Picard iteration for the computation of the state sensitivity',
                        'Maximum number of iterations were exceeded.')

                for j in range(self.form_handler.state_dim):
                    A, b = _assemble_petsc_system(
                        self.form_handler.sensitivity_eqs_lhs[j],
                        self.form_handler.sensitivity_eqs_rhs[j],
                        self.bcs_list_ad[j])
                    _solve_linear_problem(
                        self.state_ksps[j], A, b,
                        self.states_prime[j].vector().vec(),
                        self.form_handler.state_ksp_options[j])
                    self.states_prime[j].vector().apply('')

            if self.picard_verbose:
                print('')

            for i in range(self.maxiter + 1):
                res = 0.0
                for j in range(self.form_handler.state_dim):
                    res_j = fenics.assemble(
                        self.form_handler.adjoint_sensitivity_eqs_picard[j])
                    [
                        bc.apply(res_j)
                        for bc in self.form_handler.bcs_list_ad[j]
                    ]
                    res += pow(res_j.norm('l2'), 2)

                if res == 0:
                    break

                res = np.sqrt(res)

                if i == 0:
                    res_0 = res

                if self.picard_verbose:
                    print('Picard Sensitivity 2 Iteration ' + str(i) +
                          ': ||res|| (abs): ' + format(res, '.3e') +
                          '   ||res|| (rel): ' + format(res / res_0, '.3e'))

                if res / res_0 < self.rtol or res < self.atol:
                    break

                if i == self.maxiter:
                    raise NotConvergedError(
                        'Picard iteration for the computation of the adjoint sensitivity',
                        'Maximum number of iterations were exceeded.')

                for j in range(self.form_handler.state_dim):
                    A, b = _assemble_petsc_system(
                        self.form_handler.adjoint_sensitivity_eqs_lhs[-1 - j],
                        self.form_handler.w_1[-1 - j],
                        self.bcs_list_ad[-1 - j])
                    _solve_linear_problem(
                        self.adjoint_ksps[-1 - j], A, b,
                        self.adjoints_prime[-1 - j].vector().vec(),
                        self.form_handler.adjoint_ksp_options[-1 - j])
                    self.adjoints_prime[-1 - j].vector().apply('')

            if self.picard_verbose:
                print('')

        for i in range(self.control_dim):
            b = fenics.as_backend_type(
                fenics.assemble(self.form_handler.hessian_rhs[i])).vec()

            _solve_linear_problem(self.ksps[i],
                                  b=b,
                                  x=out[i].vector().vec(),
                                  ksp_options=self.riesz_ksp_options[i])
            out[i].vector().apply('')

        self.no_sensitivity_solves += 2
示例#18
0
文件: ksdgts.py 项目: leonavery/KSDG
    def implicitIJ(self, ts, t, u, udot, shift, J, B):
        """Fully implicit IJacobian for PETSc TS

        This is designed for use as the LHS in a fully implicit PETSc
        time-stepper. The DE to be solved is always A.u' = b(u). u
        corresponds to self.ksdg.sol. A is a constant (i.e., u-independent)
        matrix calculated by assembling the u'-dependent term of the
        weak form, and b a u-dependent vector calculated by assembling
        the remaining terms. This equation may be solved in any of
        three forms:

            Fully implicit: A.u' - b(u) = 0
            implicit/explicit: A.u' = b(u)
            Fully explicit: u' = A^(-1).b(u)

        Corresponding to these are seven functions that can be
        provided to PETSc.TS: implicitIF and implicitIJ calculate the
        LHS A.u' - b and its Jacobian for the fully implicit
        form. Arguments:

        t: the time.
        u: a petsc4py.PETSc.Vec containing the state vector
        udot: a petsc4py.PETSc.Vec containing the u' (not used)
        shift: a real number -- see PETSc Ts documentation.
        J, B: matrices in which the Jacobian shift*A - Jacobian(b(u)) are left. 
        """
        logTS('implicitIJ entered')
        try:
            try:
                self.ksdg.setup_problem(t)
            except TypeError:
                self.ksdg.setup_problem()
            pA = fe.as_backend_type(self.ksdg.A).mat()
            Adiag = pA.getDiagonal()
            logTS('Adiag.array', Adiag.array)
            psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
            u.copy(psol)
            self.ksdg.sol.vector().apply('insert')
            # if isinstance(self.ksdg.JU_terms, list):
            #     JUs = [fe.assemble(JU_term)
            #            for JU_term in self.ksdg.JU_terms]
            # else:
            #     JUs = [fe.assemble(self.ksdg.JU_terms)]
            # pJUs = [fe.as_backend_type(JU).mat()
            #         for JU in JUs]
            # if isinstance(self.ksdg.Jrho_terms, list):
            #     # Jrhos = [fe.assemble(Jrho_term,
            #     #          form_compiler_parameters = {"quadrature_degree": 3})
            #     #        for Jrho_term in self.ksdg.Jrho_terms]
            #     Jrhos = [fe.assemble(Jrho_term)
            #            for Jrho_term in self.ksdg.Jrho_terms]
            # else:
            #     Jrhos = [fe.assemble(self.ksdg.Jrho_terms)]
            # pJrhos = [fe.as_backend_type(Jrho).mat()
            #           for Jrho in Jrhos]
            Jsol = fe.assemble(self.ksdg.J_terms)
            #
            # Don't know if this is really what I should be doing:
            #
            for bc in self.ksdg.bcs:
                bc.apply(Jsol)
            pJsol = fe.as_backend_type(Jsol).mat()
            Jdiag = pJsol.getDiagonal()
            logTS('Jdiag.array', Jdiag.array)
            logTS('Adiag.array/Jdiag.array', Adiag.array / Jdiag.array)
            logTS('shift', shift)
            pA.copy(J)
            J.scale(shift)
            J.axpy(-1.0, pJsol)
            J.assemble()
            logTS('implicitIJ, J assembled')
            if J != B:
                J.copy(B)
                B.assemble()
        except FloatingPointError as e:
            logTS('implicitIF got FloatingPointError', str(type(e), str(e)))
            einfo = sys.exc_info()
            sys.excepthook(*einfo)
        except Exception as e:
            logTS('implicitIJ got exception', str(type(e)), str(e))
            tbstr = traceback.format_exc()
            logTS('Exception:', tbstr)
            logTS('str(ts)', str(self))
            einfo = sys.exc_info()
            raise einfo[1].with_traceback(einfo[2])
        return True
示例#19
0
    def __init__(self, lagrangian, bcs_list, states, controls, adjoints,
                 config, riesz_scalar_products, control_constraints,
                 ksp_options, adjoint_ksp_options,
                 require_control_constraints):
        """Initializes the ControlFormHandler class.

		Parameters
		----------
		lagrangian : cashocs._forms.Lagrangian
			The lagrangian corresponding to the optimization problem.
		bcs_list : list[list[dolfin.fem.dirichletbc.DirichletBC]]
			The list of DirichletBCs for the state equation.
		states : list[dolfin.function.function.Function]
			The function that acts as the state variable.
		controls : list[dolfin.function.function.Function]
			The function that acts as the control variable.
		adjoints : list[dolfin.function.function.Function]
			The function that acts as the adjoint variable.
		config : configparser.ConfigParser
			The configparser object of the config file.
		riesz_scalar_products : list[ufl.form.Form]
			The UFL forms of the scalar products for the control variables.
		control_constraints : list[list[dolfin.function.function.Function]]
			The control constraints of the problem.
		ksp_options : list[list[list[str]]]
			The list of command line options for the KSP for the
			state systems.
		adjoint_ksp_options : list[list[list[str]]]
			The list of command line options for the KSP for the
			adjoint systems.
		require_control_constraints : list[bool]
			A list of boolean flags that indicates, whether the i-th control
			has actual control constraints present.
		"""

        FormHandler.__init__(self, lagrangian, bcs_list, states, adjoints,
                             config, ksp_options, adjoint_ksp_options)

        # Initialize the attributes from the arguments
        self.controls = controls
        self.riesz_scalar_products = riesz_scalar_products
        self.control_constraints = control_constraints
        self.require_control_constraints = require_control_constraints

        self.control_dim = len(self.controls)
        self.control_spaces = [x.function_space() for x in self.controls]

        # Define the necessary functions
        self.states_prime = [fenics.Function(V) for V in self.state_spaces]
        self.adjoints_prime = [fenics.Function(V) for V in self.adjoint_spaces]

        self.test_directions = [
            fenics.Function(V) for V in self.control_spaces
        ]

        self.trial_functions_control = [
            fenics.TrialFunction(V) for V in self.control_spaces
        ]
        self.test_functions_control = [
            fenics.TestFunction(V) for V in self.control_spaces
        ]

        self.temp = [fenics.Function(V) for V in self.control_spaces]

        # Compute the necessary equations
        self.__compute_gradient_equations()

        if self.opt_algo == 'newton' or \
          (self.opt_algo == 'pdas' and self.inner_pdas == 'newton'):
            self.__compute_newton_forms()

        # Initialize the scalar products
        fenics_scalar_product_matrices = [
            fenics.assemble(self.riesz_scalar_products[i], keep_diagonal=True)
            for i in range(self.control_dim)
        ]
        self.scalar_products_matrices = [
            fenics.as_backend_type(fenics_scalar_product_matrices[i]).mat()
            for i in range(self.control_dim)
        ]

        copy_scalar_product_matrices = [
            fenics_scalar_product_matrices[i].copy()
            for i in range(self.control_dim)
        ]
        [
            copy_scalar_product_matrices[i].ident_zeros()
            for i in range(self.control_dim)
        ]
        self.riesz_projection_matrices = [
            fenics.as_backend_type(copy_scalar_product_matrices[i]).mat()
            for i in range(self.control_dim)
        ]

        # Test for symmetry of the scalar products
        for i in range(self.control_dim):
            if not self.riesz_projection_matrices[i].isSymmetric():
                if not self.riesz_projection_matrices[i].isSymmetric(1e-15):
                    if not (self.riesz_projection_matrices[i] -
                            self.riesz_projection_matrices[i].copy().transpose(
                            )).norm() / self.riesz_projection_matrices[i].norm(
                            ) < 1e-15:
                        raise InputError(
                            'cashocs._forms.ControlFormHandler',
                            'riesz_scalar_products',
                            'Supplied scalar product form is not symmetric.')
示例#20
0
文件: ksdgts.py 项目: leonavery/KSDG
    def __init__(self,
                 ksdg,
                 t0=0.0,
                 dt=0.001,
                 tmax=20,
                 maxsteps=100,
                 rtol=1e-5,
                 atol=1e-5,
                 restart=True,
                 tstype=PETSc.TS.Type.ROSW,
                 finaltime=PETSc.TS.ExactFinalTime.STEPOVER,
                 rollback_factor=None,
                 hmin=None,
                 comm=PETSc.COMM_WORLD):
        """
        Create a timestepper

        Required positional argument:
        ksdg: the KSDGSolver in which the problem has been set up.

        Keyword arguments:
        t0=0.0: the initial time.
        dt=0.001: the initial time step.
        maxdt = 0.5: the maximum time step
        tmax=20: the final time.
        maxsteps=100: maximum number of steps to take.
        rtol=1e-5: relative error tolerance.
        atol=1e-5: absolute error tolerance.
        restart=True: whether to set the initial condition to rho0, U0
        tstype=PETSc.TS.Type.ROSW: implicit solver to use.
        finaltime=PETSc.TS.ExactFinalTime.STEPOVER: how to handle
            final time step.
        fpe_factor=0.1: scale time step by this factor on floating
            point error.
        hmin=1e-20: minimum step size
        comm = PETSc.COMM_WORLD

        You can set other options by calling TS member functions
        directly. If you want to pay attention to PETSc command-line
        options, you should call ts.setFromOptions(). Call ts.solve()
        to run the timestepper, call ts.cleanup() to destroy its
        data when you're finished. 

        """
        #        print("KSDGTS __init__ entered", flush=True)
        super().__init__()
        self.create()
        #        self.create(comm=comm)
        #        self._comm = comm
        self.mpi_comm = self.comm
        if (not isinstance(self.comm, type(MPI.COMM_SELF))):
            self.mpi_comm = self.comm.tompi4py()
        self.ksdg = ksdg
        popts = PETSc.Options()
        if rollback_factor is None:
            try:
                self.rollback_factor = popts.getReal(
                    'ts_adapt_scale_solve_failed')
            except KeyError:
                self.rollback_factor = self.default_rollback_factor
        else:
            self.rollback_factor = rollback_factor
        if hmin:
            self.hmin = hmin
        else:
            self.hmin = self.default_hmin
        self.setProblemType(self.ProblemType.NONLINEAR)
        self.setMaxSNESFailures(1)
        self.setTolerances(atol=atol, rtol=rtol)
        self.setType(tstype)
        logTS('KSDGTS __init__, calling setup_problem')
        try:
            self.ksdg.setup_problem(t0)
        except TypeError:
            self.ksdg.setup_problem()
        logTS('KSDGTS __init__, setup_problem returned')
        self.history = []
        J = fe.as_backend_type(self.ksdg.A).mat().duplicate()
        J.setOption(PETSc.Mat.Option.NEW_NONZERO_ALLOCATION_ERR, False)
        J.setUp()
        if restart: self.ksdg.restart()
        u = fe.as_backend_type(self.ksdg.sol.vector()).vec().duplicate()
        u.setUp()
        f = u.duplicate()
        f.setUp()
        self.params = dict(t0=t0,
                           dt=dt,
                           tmax=tmax,
                           maxsteps=maxsteps,
                           rtol=rtol,
                           atol=atol,
                           tstype=tstype,
                           finaltime=finaltime,
                           restart=restart,
                           J=J,
                           u=u,
                           f=f)
        fe.as_backend_type(self.ksdg.sol.vector()).vec().copy(u)
        # logTS('init u.array', u.array)
        self.setSolution(u)
        self.setTime(t0)
        self.setTimeStep(dt)
        self.setMaxSteps(max_steps=maxsteps)
        self.setMaxTime(max_time=tmax)
        self.setExactFinalTime(finaltime)
        self.setMaxTime(tmax)
        self.setMaxSteps(maxsteps)
        fe.parameters['krylov_solver']['nonzero_initial_guess'] = True
示例#21
0
def damped_newton_solve(F, u, bcs, rtol=1e-10, atol=1e-10, max_iter=50, convergence_type='combined', norm_type='l2',
						damped=True, verbose=True, ksp=None, ksp_options=None):
	r"""A damped Newton method for solving nonlinear equations.

	The damped Newton method is based on the natural monotonicity test from
	`Deuflhard, Newton methods for nonlinear problems <https://doi.org/10.1007/978-3-642-23899-4>`_.
	It also allows fine tuning via a direct interface, and absolute, relative,
	and combined stopping criteria. Can also be used to specify the solver for
	the inner (linear) subproblems via petsc ksps.

	The method terminates after ``max_iter`` iterations, or if a termination criterion is
	satisfied. These criteria are given by

	- a relative one in case ``convergence_type = 'rel'``, i.e.,

	.. math:: \lvert\lvert F_{k} \rvert\rvert \leq \texttt{rtol} \lvert\lvert F_0 \rvert\rvert.

	- an absolute one in case ``convergence_type = 'abs'``, i.e.,

	.. math:: \lvert\lvert F_{k} \rvert\rvert \leq \texttt{atol}.

	- a combination of both in case ``convergence_type = 'combined'``, i.e.,

	.. math:: \lvert\lvert F_{k} \rvert\rvert \leq \texttt{atol} + \texttt{rtol} \lvert\lvert F_0 \rvert\rvert.

	The norm chosen for the termination criterion is specified via ``norm_type``.

	Parameters
	----------
	F : ufl.form.Form
		The variational form of the nonlinear problem to be solved by Newton's method.
	u : dolfin.function.function.Function
		The sought solution / initial guess. It is not assumed that the initial guess
		satisfies the Dirichlet boundary conditions, they are applied automatically.
		The method overwrites / updates this Function.
	bcs : list[dolfin.fem.dirichletbc.DirichletBC]
		A list of DirichletBCs for the nonlinear variational problem.
	rtol : float, optional
		Relative tolerance of the solver if convergence_type is either ``'combined'`` or ``'rel'``
		(default is ``rtol = 1e-10``).
	atol : float, optional
		Absolute tolerance of the solver if convergence_type is either ``'combined'`` or ``'abs'``
		(default is ``atol = 1e-10``).
	max_iter : int, optional
		Maximum number of iterations carried out by the method
		(default is ``max_iter = 50``).
	convergence_type : {'combined', 'rel', 'abs'}
		Determines the type of stopping criterion that is used.
	norm_type : {'l2', 'linf'}
		Determines which norm is used in the stopping criterion.
	damped : bool, optional
		If ``True``, then a damping strategy is used. If ``False``, the classical
		Newton-Raphson iteration (without damping) is used (default is ``True``).
	verbose : bool, optional
		If ``True``, prints status of the iteration to the console (default
		is ``True``).
	ksp : petsc4py.PETSc.KSP, optional
		The PETSc ksp object used to solve the inner (linear) problem
		if this is ``None`` it uses the direct solver MUMPS (default is
		``None``).
	ksp_options : list[list[str]]
		The list of options for the linear solver.


	Returns
	-------
	dolfin.function.function.Function
		The solution of the nonlinear variational problem, if converged.
		This overrides the input function u.


	Examples
	--------
	Consider the problem

	.. math::
		\begin{alignedat}{2}
		- \Delta u + u^3 &= 1 \quad &&\text{ in } \Omega=(0,1)^2 \\
		u &= 0 \quad &&\text{ on } \Gamma.
		\end{alignedat}

	This is solved with the code ::

		from fenics import *
		import cashocs

		mesh, _, boundaries, dx, _, _ = cashocs.regular_mesh(25)
		V = FunctionSpace(mesh, 'CG', 1)

		u = Function(V)
		v = TestFunction(V)
		F = inner(grad(u), grad(v))*dx + pow(u,3)*v*dx - Constant(1)*v*dx
		bcs = cashocs.create_bcs_list(V, Constant(0.0), boundaries, [1,2,3,4])
		cashocs.damped_newton_solve(F, u, bcs)
	"""

	if not convergence_type in ['rel', 'abs', 'combined']:
		raise InputError('cashocs.nonlinear_solvers.damped_newton_solve', 'convergence_type', 'Input convergence_type has to be one of \'rel\', \'abs\', or \'combined\'.')

	if not norm_type in ['l2', 'linf']:
		raise InputError('cashocs.nonlinear_solvers.damped_newton_solve', 'norm_type', 'Input norm_type has to be one of \'l2\' or \'linf\'.')

	# create the PETSc ksp
	if ksp is None:
		if ksp_options is None:
			ksp_options = [
				['ksp_type', 'preonly'],
				['pc_type', 'lu'],
				['pc_factor_mat_solver_type', 'mumps'],
				['mat_mumps_icntl_24', 1]
			]

		ksp = PETSc.KSP().create()
		_setup_petsc_options([ksp], [ksp_options])
		ksp.setFromOptions()

	# Calculate the Jacobian.
	dF = fenics.derivative(F, u)

	# Setup increment and function for monotonicity test
	V = u.function_space()
	du = fenics.Function(V)
	ddu = fenics.Function(V)
	u_save = fenics.Function(V)

	iterations = 0

	[bc.apply(u.vector()) for bc in bcs]
	# copy the boundary conditions and homogenize them for the increment
	bcs_hom = [fenics.DirichletBC(bc) for bc in bcs]
	[bc.homogenize() for bc in bcs_hom]

	assembler = fenics.SystemAssembler(dF, -F, bcs_hom)
	assembler.keep_diagonal = True
	A_fenics = fenics.PETScMatrix()
	residual = fenics.PETScVector()

	# Compute the initial residual
	assembler.assemble(A_fenics, residual)
	A_fenics.ident_zeros()
	A = fenics.as_backend_type(A_fenics).mat()
	b = fenics.as_backend_type(residual).vec()

	res_0 = residual.norm(norm_type)
	if res_0 == 0.0:
		if verbose:
			print('Residual vanishes, input is already a solution.')
		return u

	res = res_0
	if verbose:
		print('Newton Iteration ' + format(iterations, '2d') + ' - residual (abs):  '
			  + format(res, '.3e') + ' (tol = ' + format(atol, '.3e') + ')    residual (rel): '
			  + format(res/res_0, '.3e') + ' (tol = ' + format(rtol, '.3e') + ')')

	if convergence_type == 'abs':
		tol = atol
	elif convergence_type == 'rel':
		tol = rtol*res_0
	else:
		tol = rtol*res_0 + atol

	# While loop until termination
	while res > tol and iterations < max_iter:
		iterations += 1
		lmbd = 1.0
		breakdown = False
		u_save.vector()[:] = u.vector()[:]

		# Solve the inner problem
		_solve_linear_problem(ksp, A, b, du.vector().vec(), ksp_options)
		du.vector().apply('')

		# perform backtracking in case damping is used
		if damped:
			while True:
				u.vector()[:] += lmbd*du.vector()[:]
				assembler.assemble(residual)
				b = fenics.as_backend_type(residual).vec()
				_solve_linear_problem(ksp=ksp, b=b, x=ddu.vector().vec(), ksp_options=ksp_options)
				ddu.vector().apply('')

				if ddu.vector().norm(norm_type)/du.vector().norm(norm_type) <= 1:
					break
				else:
					u.vector()[:] = u_save.vector()[:]
					lmbd /= 2

				if lmbd < 1e-6:
					breakdown = True
					break

		else:
			u.vector()[:] += du.vector()[:]

		if breakdown:
			raise NotConvergedError('Newton solver (state system)', 'Stepsize for increment too low.')

		if iterations == max_iter:
			raise NotConvergedError('Newton solver (state system)', 'Maximum number of iterations were exceeded.')

		# compute the new residual
		assembler.assemble(A_fenics, residual)
		A_fenics.ident_zeros()
		A = fenics.as_backend_type(A_fenics).mat()
		b = fenics.as_backend_type(residual).vec()

		[bc.apply(residual) for bc in bcs_hom]

		res = residual.norm(norm_type)
		if verbose:
			print('Newton Iteration ' + format(iterations, '2d') + ' - residual (abs):  '
				  + format(res, '.3e') + ' (tol = ' + format(atol, '.3e') + ')    residual (rel): '
				  + format(res/res_0, '.3e') + ' (tol = ' + format(rtol, '.3e') + ')')

		if res < tol:
			if verbose:
				print('\nNewton Solver converged after ' + str(iterations) + ' iterations.\n')
			break

	return u
示例#22
0
文件: utils.py 项目: bdevl/PGMCPC
def ConvertFenicsBackendToScipyCSRSparse(A):

        A_mat = df.as_backend_type(A).mat()
        return csr_matrix(A_mat.getValuesCSR()[::-1], shape = A_mat.size)
示例#23
0
def fenics_matrix_to_csr_matrix(fenics_matrix):
    return csr_matrix(
        fenics.as_backend_type(fenics_matrix).mat().getValuesCSR()[::-1],
        shape=(fenics_matrix.size(0), fenics_matrix.size(1)),
    )
示例#24
0
文件: ksdgts.py 项目: leonavery/KSDG
    def implicitIF(self, ts, t, u, udot, f):
        """Fully implicit IFunction for PETSc TS

        This is designed for use as the LHS in a fully implicit PETSc
        time-stepper. The DE to be solved is always A.u' = b(u). u
        corresponds to self.ksdg.sol. A is a constant (i.e., u-independent)
        matrix calculated by assembling the u'-dependent term of the
        weak form, and b a u-dependent vector calculated by assembling
        the remaining terms. This equation may be solved in any of
        three forms:

            Fully implicit: A.u' - b(u) = 0
            implicit/explicit: A.u' = b(u)
            Fully explicit: u' = A^(-1).b(u)

        Corresponding to these are seven functions that can be
        provided to PETSc.TS: implicitIF and implicitIJ calculate the
        LHS A.u' - b and its Jacobian for the fully implicit
        form. Arguments: 

        t: the time
        u: a petsc4py.PETSc.Vec containing the state vector
        udot: a petsc4py.PETSc.Vec containing the time derivative u'
        f: a petsc4py.PETSc.Vec in which A.u' - b will be left.
        """
        try:
            #            if self.rollback:
            #                raise ValueError('rolling back')
            logTS('implicitIF entered')
            try:
                self.ksdg.setup_problem(t)
            except TypeError:
                self.ksdg.setup_problem()
            # logTS('self.ksdg.A.array()', self.ksdg.A.array())
            pA = fe.as_backend_type(self.ksdg.A).mat()
            # logTS('u.array', u.array)
            psol = fe.as_backend_type(self.ksdg.sol.vector()).vec()
            u.copy(psol)
            # logTS('psol.array', psol.array)
            self.ksdg.sol.vector().apply('insert')
            self.ksdg.b = fe.assemble(self.ksdg.all_terms)
            # logTS('self.ksdg.b[:]', self.ksdg.b[:])
            if hasattr(self.ksdg, 'bcs'):
                for bc in self.ksdg.bcs:
                    bc.apply(self.ksdg.b)
                self.ksdg.b.apply('insert')
            pb = fe.as_backend_type(self.ksdg.b).vec()
            # logTS('pb.array', pb.array)
            pb.copy(f)
            f.scale(-1.0)
            pA.multAdd(udot, f, f)
            f.assemble()
            logTS('implicitIF, f assembled')
            #            if not np.min(u.array) > 0:
            #                logTS('np.min(u.array)', np.min(u.array))
            #                raise(ValueError('nonpositive function values'))
            # logTS('udot.array', udot.array)
            if np.isnan(np.min(udot.array)):
                logTS('np.min(udot.array)', np.min(udot.array))
#                raise(ValueError('nans in udot'))
# logTS('f.array', f.array)
            if np.isnan(np.min(f.array)):
                logTS('np.min(f.array)', np.min(f.array))


#                raise(ValueError('nans in computed f'))
        except (ValueError, FloatingPointError) as e:
            logTS('implicitIF got FloatingPointError', str(type(e)), str(e))
            einfo = sys.exc_info()
            tbstr = traceback.format_exc()
            logTS('traceback', tbstr)
            #            self.rollback = True          # signal trouble upstairs
            f.set(0.0)
            f.assemble()
        except Exception as e:
            logTS('implicitIF got exception', str(type(e)), str(e))
            tbstr = traceback.format_exc()
            logTS('Exception:', tbstr)
            logTS('str(ts)', str(self))
            einfo = sys.exc_info()
            raise einfo[1].with_traceback(einfo[2])
示例#25
0
文件: ksdgvar.py 项目: leonavery/KSDG
 def setup_problem(self, t, debug=False):
     self.set_time(t)
     #
     # assemble the matrix, if necessary (once for all time points)
     #
     if not hasattr(self, 'A'):
         logVARIABLE('making matrix A')
         self.drho_integral = sum([
             tdrho * wrho * self.dx
             for tdrho, wrho in zip(self.tdrhos, self.wrhos)
         ])
         self.dU_integral = sum(
             [tdU * wU * self.dx for tdU, wU in zip(self.tdUs, self.wUs)])
         logVARIABLE('assembling A')
         self.A = fe.PETScMatrix()
         logVARIABLE('self.A', self.A)
         fe.assemble(self.drho_integral + self.dU_integral, tensor=self.A)
         logVARIABLE('A assembled. Applying BCs')
         pA = fe.as_backend_type(self.A).mat()
         Adiag = pA.getDiagonal()
         logVARIABLE('Adiag.array', Adiag.array)
         # self.A = fe.assemble(self.drho_integral + self.dU_integral +
         #                      self.dP_integral)
         for bc in self.bcs:
             bc.apply(self.A)
         Adiag = pA.getDiagonal()
         logVARIABLE('Adiag.array', Adiag.array)
         self.dsol = Function(self.VS)
         dsolsplit = self.dsol.split()
         self.drhos, self.dUs = (dsolsplit[:2**self.dim],
                                 dsolsplit[2**self.dim:])
     #
     # assemble RHS (for each time point, but compile only once)
     #
     #
     # These are the values of rho and U themselves (not their
     # symmetrized versions) on all subdomains of the original
     # domain.
     #
     if not hasattr(self, 'rhosds'):
         self.rhosds = matmul(self.eomat, self.irhos)
     # self.Usds is a list of nligands lists. Sublist i is of
     # length 2**dim and lists the value of ligand i on each of the
     # 2**dim subdomains.
     #
     if not hasattr(self, 'Usds'):
         self.Usds = [
             matmul(self.eomat,
                    self.iUs[i * 2**self.dim:(i + 1) * 2**self.dim])
             for i in range(self.nligands)
         ]
     if not hasattr(self, 'rho_terms'):
         logVARIABLE('making rho_terms')
         self.sigma = self.iparams['sigma']
         self.s2 = self.sigma * self.sigma / 2
         self.rhomin = self.iparams['rhomin']
         self.rhopen = self.iparams['rhopen']
         self.grhopen = self.iparams['grhopen']
         #
         # Compute fluxes on subdomains.
         # Vsds is a list of length 2**dim, the value of V on each
         # subdomain.
         #
         self.Vsds = []
         for Usd, rhosd in zip(zip(*self.Usds), self.rhosds):
             self.Vsds.append(self.V(Usd, ufl.max_value(rhosd,
                                                        self.rhomin)))
         self.vsds = [
             -ufl.grad(Vsd) -
             (self.s2 * ufl.grad(rhosd) / ufl.max_value(rhosd, self.rhomin))
             for Vsd, rhosd in zip(self.Vsds, self.rhosds)
         ]
         self.fluxsds = [
             vsd * rhosd for vsd, rhosd in zip(self.vsds, self.rhosds)
         ]
         self.vnsds = [
             ufl.max_value(ufl.dot(vsd, self.n), 0) for vsd in self.vsds
         ]
         self.facet_fluxsds = [
             (vnsd('+') * ufl.max_value(rhosd('+'), 0.0) -
              vnsd('-') * ufl.max_value(rhosd('-'), 0.0))
             for vnsd, rhosd in zip(self.vnsds, self.rhosds)
         ]
         #
         # Now combine the subdomain fluxes to get the fluxes for
         # the symmetrized functions
         #
         self.fluxs = matmul((2.0**-self.dim) * self.eomat, self.fluxsds)
         self.facet_fluxs = matmul((2.0**-self.dim) * self.eomat,
                                   self.facet_fluxsds)
         self.rho_flux_jump = sum([
             -facet_flux * ufl.jump(wrho) * self.dS
             for facet_flux, wrho in zip(self.facet_fluxs, self.wrhos)
         ])
         self.rho_grad_move = sum([
             ufl.dot(flux, ufl.grad(wrho)) * self.dx
             for flux, wrho in zip(self.fluxs, self.wrhos)
         ])
         self.rho_penalty = sum([
             -(self.degree**2 / self.havg) *
             ufl.dot(ufl.jump(rho, self.n),
                     ufl.jump(self.rhopen * wrho, self.n)) * self.dS
             for rho, wrho in zip(self.irhos, self.wrhos)
         ])
         self.grho_penalty = sum([
             self.degree**2 *
             (ufl.jump(ufl.grad(rho), self.n) *
              ufl.jump(ufl.grad(-self.grhopen * wrho), self.n)) * self.dS
             for rho, wrho in zip(self.irhos, self.wrhos)
         ])
         self.rho_terms = (self.rho_flux_jump + self.rho_grad_move +
                           self.rho_penalty + self.grho_penalty)
         logVARIABLE('rho_terms made')
     if not hasattr(self, 'U_terms'):
         logVARIABLE('making U_terms')
         self.Umin = self.iparams['Umin']
         self.Upen = self.iparams['Upen']
         self.gUpen = self.iparams['gUpen']
         self.U_decay = 0.0
         self.U_secretion = 0.0
         self.jump_gUw = 0.0
         self.U_diffusion = 0.0
         self.U_penalty = 0.0
         self.gU_penalty = 0.0
         for j, lig in enumerate(self.iligands.ligands()):
             sl = slice(j * 2**self.dim, (j + 1) * 2**self.dim)
             self.U_decay += sum([
                 -lig.gamma * iUi * wUi * self.dx
                 for iUi, wUi in zip(self.iUs[sl], self.wUs[sl])
             ])
             self.U_secretion += sum([
                 lig.s * rho * wU * self.dx
                 for rho, wU in zip(self.irhos, self.wUs[sl])
             ])
             self.jump_gUw += sum([
                 ufl.jump(lig.D * wU * ufl.grad(U), self.n) * self.dS
                 for wU, U in zip(self.wUs[sl], self.iUs[sl])
             ])
             self.U_diffusion += sum([
                 -lig.D * ufl.dot(ufl.grad(U), ufl.grad(wU)) * self.dx
                 for U, wU in zip(self.iUs[sl], self.wUs[sl])
             ])
             self.U_penalty += sum([
                 (-self.degree**2 / self.havg) *
                 ufl.dot(ufl.jump(U, self.n),
                         ufl.jump(self.Upen * wU, self.n)) * self.dS
                 for U, wU in zip(self.iUs[sl], self.wUs[sl])
             ])
             self.gU_penalty += sum([
                 -self.degree**2 * ufl.jump(ufl.grad(U), self.n) *
                 ufl.jump(ufl.grad(self.gUpen * wU), self.n) * self.dS
                 for U, wU in zip(self.iUs[sl], self.wUs[sl])
             ])
         self.U_terms = (
             # decay and secretion
             self.U_decay + self.U_secretion +
             # diffusion
             self.jump_gUw + self.U_diffusion +
             # penalties (to enforce continuity)
             self.U_penalty + self.gU_penalty)
         logVARIABLE('U_terms made')
     if not hasattr(self, 'all_terms'):
         logVARIABLE('making all_terms')
         self.all_terms = self.rho_terms + self.U_terms
     if not hasattr(self, 'J_terms'):
         logVARIABLE('making J_terms')
         self.J_terms = fe.derivative(self.all_terms, self.sol)