def set_solver_alpha_snes2(self): V = self.alpha.function_space() denergy = derivative(self.energy, self.alpha, TestFunction(V)) ddenergy = derivative(denergy, self.alpha, TrialFunction(V)) self.lb = self.alpha_init # interpolate(Constant("0."), V) ub = interpolate(Constant("1."), V) self.problem_alpha = NonlinearVariationalProblem(denergy, self.alpha, self.bcs_alpha, J=ddenergy) self.problem_alpha.set_bounds(self.lb, ub) self.problem_alpha.lb = self.lb # set up the solver solver = NonlinearVariationalSolver(self.problem_alpha) snes_solver_parameters_bounds = { "nonlinear_solver": "snes", "snes_solver": { "linear_solver": "mumps", "maximum_iterations": 300, "report": True, "line_search": "basic", "method": "vinewtonrsls", "absolute_tolerance": 1e-5, "relative_tolerance": 1e-5, "solution_tolerance": 1e-5 } } solver.parameters.update(snes_solver_parameters_bounds) #solver.solve() self.solver = solver
def __init__(self, energy, u, bcs, nullspace=None): NonlinearProblem.__init__(self) self.z = u self.bcs = bcs self.nullspace = nullspace self.residual = derivative(energy, self.z, TestFunction(self.z.function_space())) self.jacobian = derivative(self.residual, self.z, TrialFunction(self.z.function_space()))
def tangent(problem, solution, v, w, oldmu, newmu, k, params, task, vi, hint=None): if k == 1: return (hint, 0, 0) info_green("Attempting tangent linearisation") coldmu = backend.Constant(float(oldmu)) chgmu = backend.Constant(float(newmu)-float(oldmu)) rho = solution.split(deepcopy=True)[0] Z = solution.function_space() (lb, ub) = problem.bounds(Z, newmu, params) vi_du = None # FIXME: cache the symbolic calculation once, it can be expensive sometimes du = backend.Function(Z) #du = solution.copy(deepcopy=True) F = problem.residual(solution, v, lb, ub, coldmu, params) G = derivative(F, solution, du) + derivative(F, coldmu, chgmu) J = problem.jacobian(G, du, params, v, w) # FIXME: figure out if the boundary conditions depend on # the parameters, and set the boundary conditions on the update dubcs = problem.boundary_conditions(Z, newmu) [dubc.homogenize() for dubc in dubcs] # dm = problem._dm # FIXME: make this optional # teamno = 0 # FIXME: there's probably a more elegant way to do this. # Or should we use one semismooth Newton step? After all # we already have a Newton linearisation. newproblem = BensonMunson(problem) sp = problem.solver_parameters(float(oldmu), 0, task, params) (success, iters, liters) = newton(G, J, du, dubcs, newproblem.solver, params, sp, None, None, problem._dm, vi_du) hint = [None, float(oldmu)] if not success: #do nothing return (hint, iters, liters) else: solution.assign(solution + du) infeasibility = assemble(problem.infeasibility(solution, lb, ub, newmu, params)) info_green("Tangent linearisation success, infeasibility of guess %.5e" % (infeasibility)) return (hint, iters, liters)
def problem(): mesh = dolfin.UnitCubeMesh(MPI.comm_world, 10, 10, 10) cell = mesh.ufl_cell() vec_element = dolfin.VectorElement("Lagrange", cell, 1) # scl_element = dolfin.FiniteElement("Lagrange", cell, 1) Q = dolfin.FunctionSpace(mesh, vec_element) # Qs = dolfin.FunctionSpace(mesh, scl_element) # Coefficients v = dolfin.function.argument.TestFunction(Q) # Test function du = dolfin.function.argument.TrialFunction(Q) # Incremental displacement u = dolfin.Function(Q) # Displacement from previous iteration B = dolfin.Constant((0.0, -0.5, 0.0), cell) # Body force per unit volume T = dolfin.Constant((0.1, 0.0, 0.0), cell) # Traction force on the boundary # B, T = dolfin.Function(Q), dolfin.Function(Q) # Kinematics d = u.geometric_dimension() F = ufl.Identity(d) + grad(u) # Deformation gradient C = F.T * F # Right Cauchy-Green tensor # Invariants of deformation tensors Ic = tr(C) J = det(F) # Elasticity parameters E, nu = 10.0, 0.3 mu = dolfin.Constant(E / (2 * (1 + nu)), cell) lmbda = dolfin.Constant(E * nu / ((1 + nu) * (1 - 2 * nu)), cell) # mu, lmbda = dolfin.Function(Qs), dolfin.Function(Qs) # Stored strain energy density (compressible neo-Hookean model) psi = (mu / 2) * (Ic - 3) - mu * ln(J) + (lmbda / 2) * (ln(J))**2 # Total potential energy Pi = psi * dx - dot(B, u) * dx - dot(T, u) * ds # Compute first variation of Pi (directional derivative about u in the direction of v) F = ufl.derivative(Pi, u, v) # Compute Jacobian of F J = ufl.derivative(F, u, du) return J, F
def hyperelasticity(domain, q, p, nf=0): # Based on https://github.com/firedrakeproject/firedrake-bench/blob/experiments/forms/firedrake_forms.py V = ufl.FunctionSpace(domain, ufl.VectorElement('P', domain.ufl_cell(), q)) P = ufl.FunctionSpace(domain, ufl.VectorElement('P', domain.ufl_cell(), p)) v = ufl.TestFunction(V) du = ufl.TrialFunction(V) # Incremental displacement u = ufl.Coefficient(V) # Displacement from previous iteration B = ufl.Coefficient(V) # Body force per unit mass # Kinematics I = ufl.Identity(domain.ufl_cell().topological_dimension()) F = I + ufl.grad(u) # Deformation gradient C = F.T*F # Right Cauchy-Green tensor E = (C - I)/2 # Euler-Lagrange strain tensor E = ufl.variable(E) # Material constants mu = ufl.Constant(domain) # Lame's constants lmbda = ufl.Constant(domain) # Strain energy function (material model) psi = lmbda/2*(ufl.tr(E)**2) + mu*ufl.tr(E*E) S = ufl.diff(psi, E) # Second Piola-Kirchhoff stress tensor PK = F*S # First Piola-Kirchoff stress tensor # Variational problem it = ufl.inner(PK, ufl.grad(v)) - ufl.inner(B, v) f = [ufl.Coefficient(P) for _ in range(nf)] return ufl.derivative(reduce(ufl.inner, list(map(ufl.div, f)) + [it])*ufl.dx, u, du)
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one with the next argument number from ufl.algorithms import extract_arguments form_arguments = extract_arguments(form) number = max([-1] + [arg.number() for arg in form_arguments]) + 1 if any(arg.part() is not None for arg in form_arguments): cpp.dolfin_error( "formmanipulation.py", "compute derivative of form", "Cannot automatically create third argument using parts, please supply one" ) part = None if isinstance(u, Function): V = u.function_space() du = Argument(V, number, part) elif isinstance(u, (list, tuple)) and all( isinstance(w, Function) for w in u): V = MixedFunctionSpace([w.function_space() for w in u]) du = ufl.split(Argument(V, number, part)) else: cpp.dolfin_error( "formmanipulation.py", "compute derivative of form", "Cannot automatically create third argument, please supply one" ) return ufl.derivative(form, u, du, coefficient_derivatives)
def test_assemble_derivatives(): """This test checks the original_coefficient_positions, which may change under differentiation (some coefficients and constants are eliminated)""" mesh = UnitSquareMesh(MPI.COMM_WORLD, 12, 12) Q = dolfinx.FunctionSpace(mesh, ("Lagrange", 1)) u = dolfinx.Function(Q) v = ufl.TestFunction(Q) du = ufl.TrialFunction(Q) b = dolfinx.Function(Q) c1 = fem.Constant(mesh, [[1.0, 0.0], [3.0, 4.0]]) c2 = fem.Constant(mesh, 2.0) with b.vector.localForm() as b_local: b_local.set(2.0) # derivative eliminates 'u' and 'c1' L = ufl.inner(c1, c1) * v * dx + c2 * b * inner(u, v) * dx a = derivative(L, u, du) A1 = dolfinx.fem.assemble_matrix(a) A1.assemble() a = c2 * b * inner(du, v) * dx A2 = dolfinx.fem.assemble_matrix(a) A2.assemble() assert (A1 - A2).norm() == pytest.approx(0.0, rel=1e-12, abs=1e-12)
def split_arity(form, x, argument): if x not in form.coefficients(): # No dependence on x return ufl.classes.Form([]), form form_derivative = ufl.derivative(form, x, argument=argument) form_derivative = ufl.algorithms.expand_derivatives(form_derivative) if x in form_derivative.coefficients(): # Non-linear return ufl.classes.Form([]), form arity = len(form.arguments()) try: eq_form = ufl.algorithms.expand_derivatives( ufl.replace(form, {x: argument})) A = ufl.algorithms.formtransformations.compute_form_with_arity( eq_form, arity + 1) b = ufl.algorithms.formtransformations.compute_form_with_arity( eq_form, arity) except ufl.UFLException: # UFL error encountered return ufl.classes.Form([]), form if not is_cached(A): # Non-cached higher arity form return ufl.classes.Form([]), form # Success return A, b
def evaluate_adj_component(self, inputs, adj_inputs, block_variable, idx, prepared=None): if not self.lincom: if isinstance(block_variable.output, (AdjFloat, self.backend.Constant)): return adj_inputs[0].sum() else: adj_output = self.backend.Function( block_variable.output.function_space()) adj_output.assign(prepared) return adj_output.vector() else: # Linear combination expr, adj_input_func = prepared adj_output = self.backend.Function( block_variable.output.function_space()) diff_expr = ufl.algorithms.expand_derivatives( ufl.derivative(expr, block_variable.saved_output, adj_input_func)) adj_output.assign(diff_expr) return adj_output.vector()
def derivative(form, u, du=None, coefficient_derivatives=None): """Compute the derivative of a form. Given a form, this computes its linearization with respect to the provided :class:`.Function`. The resulting form has one additional :class:`Argument` in the same finite element space as the Function. :arg form: a :class:`~ufl.classes.Form` to compute the derivative of. :arg u: a :class:`.Function` to compute the derivative with respect to. :arg du: an optional :class:`Argument` to use as the replacement in the new form (constructed automatically if not provided). :arg coefficient_derivatives: an optional :class:`dict` to provide the derivative of a coefficient function. :raises ValueError: If any of the coefficients in ``form`` were obtained from ``u.split()``. UFL doesn't notice that these are related to ``u`` and so therefore the derivative is wrong (instead one should have written ``split(u)``). See also :func:`ufl.derivative`. """ #EVENTUALLY ADD THIS CHECKING BACK IN! # if set(extract_coefficients(form)) & set(u.split()): # raise ValueError("Taking derivative of form wrt u, but form contains coefficients from u.split()." # "\nYou probably meant to write split(u) when defining your form.") if du is None: if isinstance(u, Function): V = u.function_space() args = form.arguments() number = max(a.number() for a in args) if args else -1 du = Argument(V, number + 1) else: raise RuntimeError("Can't compute derivative for form") return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one # with the next argument number form_arguments = form.arguments() number = max([-1] + [arg.number() for arg in form_arguments]) + 1 if any(arg.part() is not None for arg in form_arguments): cpp.dolfin_error("formmanipulation.py", "compute derivative of form", "Cannot automatically create new Argument using " "parts, please supply one") part = None if isinstance(u, Function): V = u.function_space() du = Argument(V, number, part) elif isinstance(u, (list, tuple)) and all(isinstance(w, Function) for w in u): cpp.dolfin_error("formmanipulation.py", "take derivative of form w.r.t. a tuple of Coefficients", "Take derivative w.r.t. a single Coefficient on " "a mixed space instead.") else: cpp.dolfin_error("formmanipulation.py", "compute derivative of form w.r.t. '%s'" % u, "Supply Function as a Coefficient") return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one # with the next argument number form_arguments = form.arguments() number = max([-1] + [arg.number() for arg in form_arguments]) + 1 if any(arg.part() is not None for arg in form_arguments): raise RuntimeError( "Cannot automatically create new Argument using parts, please supply one" ) part = None if isinstance(u, Function): V = u.function_space() du = Argument(V, number, part) elif isinstance(u, (list, tuple)) and all( isinstance(w, Function) for w in u): raise RuntimeError( "Take derivative w.r.t. a single Coefficient on a mixed space instead." ) else: raise RuntimeError( "Computing derivative of form w.r.t. '{}'. Supply Function as a Coefficient" .format(u)) return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one with the next argument number from ufl.algorithms import extract_arguments form_arguments = extract_arguments(form) number = max([-1] + [arg.number() for arg in form_arguments]) + 1 if any(arg.part() is not None for arg in form_arguments): cpp.dolfin_error("formmanipulation.py", "compute derivative of form", "Cannot automatically create third argument using parts, please supply one") part = None if isinstance(u, Function): V = u.function_space() du = Argument(V, number, part) elif isinstance(u, (list,tuple)) and all(isinstance(w, Function) for w in u): V = MixedFunctionSpace([w.function_space() for w in u]) du = ufl.split(Argument(V, number, part)) else: cpp.dolfin_error("formmanipulation.py", "compute derivative of form", "Cannot automatically create third argument, please supply one") return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None, coefficient_derivatives=None): """Compute the derivative of a form. Given a form, this computes its linearization with respect to the provided :class:`.Function`. The resulting form has one additional :class:`Argument` in the same finite element space as the Function. :arg form: a :class:`~ufl.classes.Form` to compute the derivative of. :arg u: a :class:`.Function` to compute the derivative with respect to. :arg du: an optional :class:`Argument` to use as the replacement in the new form (constructed automatically if not provided). :arg coefficient_derivatives: an optional :class:`dict` to provide the derivative of a coefficient function. See also :func:`ufl.derivative`. """ if du is None: if isinstance(u, function.Function): V = u.function_space() args = form.arguments() number = max(a.number() for a in args) if args else -1 du = Argument(V, number + 1) else: raise RuntimeError("Can't compute derivative for form") return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one # with the next argument number form_arguments = form.arguments() number = max([-1] + [arg.number() for arg in form_arguments]) + 1 # NOTE : Mixed-domains problems need to have arg.part() != None # if any(arg.part() is not None for arg in form_arguments): # raise RuntimeError("Compute derivative of form, cannot automatically create new Argument using parts, please supply one") part = None if isinstance(u, Function): # u.part() is None except with mixed-domains part = u.part() V = u.function_space() du = Argument(V, number, part) elif isinstance(u, SpatialCoordinate): mesh = u.ufl_domain().ufl_cargo() element = u.ufl_domain().ufl_coordinate_element() V = FunctionSpace(mesh, element) du = Argument(V, number, part) elif isinstance(u, (list, tuple)) and all( isinstance(w, Function) for w in u): raise RuntimeError( "Taking derivative of form w.r.t. a tuple of Coefficients. Take derivative w.r.t. a single Coefficient on a mixed space instead." ) else: raise RuntimeError( "Computing derivative of form w.r.t. '{}'. Supply Function as a Coefficient" .format(u)) return ufl.derivative(form, u, du, coefficient_derivatives)
def test_assemble_derivatives(): """This test checks the original_coefficient_positions, which may change under differentiation (some coefficients and constants are eliminated)""" mesh = create_unit_square(MPI.COMM_WORLD, 12, 12) Q = FunctionSpace(mesh, ("Lagrange", 1)) u = Function(Q) v = ufl.TestFunction(Q) du = ufl.TrialFunction(Q) b = Function(Q) c1 = Constant(mesh, np.array([[1.0, 0.0], [3.0, 4.0]], PETSc.ScalarType)) c2 = Constant(mesh, PETSc.ScalarType(2.0)) b.x.array[:] = 2.0 # derivative eliminates 'u' and 'c1' L = ufl.inner(c1, c1) * v * dx + c2 * b * inner(u, v) * dx a = form(derivative(L, u, du)) A1 = assemble_matrix(a) A1.assemble() a = form(c2 * b * inner(du, v) * dx) A2 = assemble_matrix(a) A2.assemble() assert (A1 - A2).norm() == pytest.approx(0.0, rel=1e-12, abs=1e-12)
def derivative(form: ufl.Form, u, du, coefficient_derivatives=None) -> ufl.Form: """Compute derivative of from about u (coefficient) in the direction of du (Argument) """ return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one # with the next argument number form_arguments = form.arguments() number = max([-1] + [arg.number() for arg in form_arguments]) + 1 if any(arg.part() is not None for arg in form_arguments): raise RuntimeError("Compute derivative of form, cannot automatically create new Argument using parts, please supply one") part = None if isinstance(u, Function): V = u.function_space() du = Argument(V, number, part) elif isinstance(u, SpatialCoordinate): mesh = u.ufl_domain().ufl_cargo() element = u.ufl_domain().ufl_coordinate_element() V = FunctionSpace(mesh, element) du = Argument(V, number, part) elif isinstance(u, (list, tuple)) and all(isinstance(w, Function) for w in u): raise RuntimeError("Taking derivative of form w.r.t. a tuple of Coefficients. Take derivative w.r.t. a single Coefficient on a mixed space instead.") else: raise RuntimeError("Computing derivative of form w.r.t. '{}'. Supply Function as a Coefficient".format(u)) return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None, coefficient_derivatives=None): """Compute the derivative of a form. Given a form, this computes its linearization with respect to the provided :class:`.Function`. The resulting form has one additional :class:`Argument` in the same finite element space as the Function. :arg form: a :class:`ufl.Form` to compute the derivative of. :arg u: a :class:`.Function` to compute the derivative with respect to. :arg du: an optional :class:`Argument` to use as the replacement in the new form (constructed automatically if not provided). :arg coefficient_derivatives: an optional :class:`dict` to provide the derivative of a coefficient function. See also :func:`ufl.derivative`. """ if du is None: if isinstance(u, function.Function): V = u.function_space() args = form.arguments() number = max(a.number() for a in args) if args else -1 du = Argument(V, number + 1) else: raise RuntimeError("Can't compute derivative for form") return ufl.derivative(form, u, du, coefficient_derivatives)
def differentiate_expr(expr, u, expand = True): """ Wrapper for the UFL derivative function. This chooses an argument equal to Constant(1.0). Form s should be differentiated using the derivative function. """ if not isinstance(expr, ufl.expr.Expr): raise InvalidArgumentException("expr must be an Expr") if isinstance(u, ufl.indexed.Indexed): op = u.operands() assert(len(op) == 2) if not isinstance(op[0], (dolfin.Constant, dolfin.Function)): raise InvalidArgumentException("Invalid Indexed") elif not isinstance(u, (dolfin.Constant, dolfin.Function)): raise InvalidArgumentException("u must be an Indexed, Constant, or Function") if expr is u: der = ufl.constantvalue.IntValue(1) else: unity = dolfin.Constant(1.0) der = dolfin.replace(ufl.derivative(expr, u, argument = unity), {unity:ufl.constantvalue.IntValue(1)}) if expand: # Based on code from expand_derivatives1 in UFL file ad.py, (see e.g. bzr # 1.1.x branch revision 1484) cell = der.cell() if cell is None: dim = 0 else: dim = der.cell().geometric_dimension() der = ufl.algorithms.expand_derivatives(der, dim = dim) return der
def differentiate_expr(expr, u, expand = True): """ Wrapper for the UFL derivative function. This chooses an argument equal to Constant(1.0). Form s should be differentiated using the derivative function. """ if not isinstance(expr, ufl.core.expr.Expr): raise InvalidArgumentException("expr must be an Expr") if isinstance(u, ufl.indexed.Indexed): op = u.operands() assert(len(op) == 2) if not isinstance(op[0], (dolfin.Constant, dolfin.Function)): raise InvalidArgumentException("Invalid Indexed") elif not isinstance(u, (dolfin.Constant, dolfin.Function)): raise InvalidArgumentException("u must be an Indexed, Constant, or Function") if expr is u: der = ufl.constantvalue.IntValue(1) else: unity = dolfin.Constant(1.0) der = dolfin.replace(ufl.derivative(expr, u, argument = unity), {unity:ufl.constantvalue.IntValue(1)}) if expand: # Based on code from expand_derivatives1 in UFL file ad.py, (see e.g. bzr # 1.1.x branch revision 1484) cell = der.cell() if cell is None: dim = 0 else: dim = der.cell().geometric_dimension() der = ufl.algorithms.expand_derivatives(der, dim = dim) return der
def test_pack_coefficients(): """Test packing of form coefficients ahead of main assembly call""" mesh = create_unit_square(MPI.COMM_WORLD, 12, 15) V = FunctionSpace(mesh, ("Lagrange", 1)) # Non-blocked u = Function(V) v = ufl.TestFunction(V) c = Constant(mesh, PETSc.ScalarType(12.0)) F = ufl.inner(c, v) * dx - c * ufl.sqrt(u * u) * ufl.inner(u, v) * dx u.x.array[:] = 10.0 _F = form(F) # -- Test vector b0 = assemble_vector(_F) b0.assemble() constants = pack_constants(_F) coeffs = pack_coefficients(_F) with b0.localForm() as _b0: for c in [(None, None), (None, coeffs), (constants, None), (constants, coeffs)]: b = assemble_vector(_F, coeffs=c) b.assemble() with b.localForm() as _b: assert (_b0.array_r == _b.array_r).all() # Change coefficients constants *= 5.0 for coeff in coeffs.values(): coeff *= 5.0 with b0.localForm() as _b0: for c in [(None, coeffs), (constants, None), (constants, coeffs)]: b = assemble_vector(_F, coeffs=c) b.assemble() with b.localForm() as _b: assert (_b0 - _b).norm() > 1.0e-5 # -- Test matrix du = ufl.TrialFunction(V) J = ufl.derivative(F, u, du) J = form(J) A0 = assemble_matrix(J) A0.assemble() constants = pack_constants(J) coeffs = pack_coefficients(J) for c in [(None, None), (None, coeffs), (constants, None), (constants, coeffs)]: A = assemble_matrix(J, coeffs=c) A.assemble() assert pytest.approx((A - A0).norm(), 1.0e-12) == 0.0 # Change coefficients constants *= 5.0 for coeff in coeffs.values(): coeff *= 5.0 for c in [(None, coeffs), (constants, None), (constants, coeffs)]: A = assemble_matrix(J, coeffs=c) A.assemble() assert (A - A0).norm() > 1.0e-5
def hyperelasticity_action_forms(mesh, vec_el): cell = mesh.ufl_cell() Q = dolfin.FunctionSpace(mesh, vec_el) # Coefficients v = dolfin.function.argument.TestFunction(Q) # Test function du = dolfin.function.argument.TrialFunction(Q) # Incremental displacement u = dolfin.Function(Q) # Displacement from previous iteration u.vector().set(0.5) B = dolfin.Constant((0.0, -0.5, 0.0), cell) # Body force per unit volume T = dolfin.Constant((0.1, 0.0, 0.0), cell) # Traction force on the boundary # Kinematics d = u.geometric_dimension() F = ufl.Identity(d) + grad(u) # Deformation gradient C = F.T * F # Right Cauchy-Green tensor # Invariants of deformation tensors Ic = tr(C) J = det(F) # Elasticity parameters E, nu = 10.0, 0.3 mu = dolfin.Constant(E / (2 * (1 + nu)), cell) lmbda = dolfin.Constant(E * nu / ((1 + nu) * (1 - 2 * nu)), cell) # Stored strain energy density (compressible neo-Hookean model) psi = (mu / 2) * (Ic - 3) - mu * ln(J) + (lmbda / 2) * (ln(J)) ** 2 # Total potential energy Pi = psi * dx - dot(B, u) * dx - dot(T, u) * ds # Compute first variation of Pi (directional derivative about u in the direction of v) F = ufl.derivative(Pi, u, v) # Compute Jacobian of F J = ufl.derivative(F, u, du) w = dolfin.Function(Q) w.vector().set(1.2) L = ufl.action(J, w) return None, L, None
def __init__(self, F, u, bc): V = u.function_space du = TrialFunction(V) self.L = form(F) self.a = form(derivative(F, u, du)) self.bc = bc self._F, self._J = None, None self.u = u
def derivative(form, x, argument=None): if argument is None: space = derivative_space(x) rank = len(form.arguments()) Argument = {0: TestFunction, 1: TrialFunction}[rank] argument = Argument(space) return ufl.derivative(form, x, argument=argument)
def variation(self, v): """ Directional derivative in direction v """ # return ufl.algorithms.expand_derivatives(ufl.derivative(self.expression, self.variable, v)) deriv = sum([ ufl.derivative(self.expression, var, v_) for (var, v_) in zip(split(self.variable), split(v)) ]) return ufl.algorithms.expand_derivatives(deriv)
def __init__(self, F, u, bc): super().__init__() V = u.function_space du = TrialFunction(V) self.L = F self.a = derivative(F, u, du) self.bc = bc self._F, self._J = None, None
def derivative(e, u, du, u0=None): """Generate the functional derivative of UFL expression (or list of expressions) for the given function(s) u in the direction of function(s) du and at u0. Example (first variation): δe = dolfiny.expression.derivative(e, m, δm) Parameters ---------- e: UFL Expr or list of UFL expressions/forms u: UFL Function or list of UFL functions du: UFL Function or list of UFL functions u0: UFL Function or list of UFL functions, defaults to u Returns ------- Expression (or list of expressions) with functional derivative. """ if u0 is None: u0 = u e0 = evaluate(e, u, u0) if isinstance(e0, list): de0 = [] for e0_ in e0: if isinstance(u0, list) and isinstance(du, list): de0_ = sum( ufl.derivative(e0_, v0, dv) for v0, dv in zip(u0, du)) else: de0_ = ufl.derivative(e0_, u0, du) de0_ = ufl.algorithms.apply_algebra_lowering.apply_algebra_lowering( de0_) de0_ = ufl.algorithms.apply_derivatives.apply_derivatives(de0_) de0.append(de0_) else: if isinstance(u0, list) and isinstance(du, list): de0 = sum(ufl.derivative(e0, v0, dv) for v0, dv in zip(u0, du)) else: de0 = ufl.derivative(e0, u0, du) de0 = ufl.algorithms.apply_algebra_lowering.apply_algebra_lowering(de0) de0 = ufl.algorithms.apply_derivatives.apply_derivatives(de0) return de0
def __init__(self, energy, alpha, bcs, lb=None, ub=None): OptimisationProblem.__init__(self) self.energy = energy self.alpha = alpha self.V = self.alpha.function_space() self.denergy = derivative(self.energy, self.alpha, TestFunction(self.V)) self.ddenergy = derivative(self.denergy, self.alpha, TrialFunction(self.V)) if lb == None: lb = interpolate(Constant("0."), self.V) if ub == None: ub = interpolate(Constant("2."), self.V) self.lb = lb self.ub = ub self.bcs = bcs self.update_lb()
def __init__(self, F, u, bc): V = u.function_space du = TrialFunction(V) self.L = F self.a = derivative(F, u, du) self.a_comp = dolfinx.fem.Form(self.a) self.bc = bc self._F, self._J = None, None self.u = u
def evaluate_adj_component(self, inputs, adj_inputs, block_variable, idx, prepared=None): if self.expr is None: if isinstance(block_variable.output, AdjFloat): return adj_inputs[0].sum() elif isinstance(block_variable.output, self.backend.Constant): R = block_variable.output._ad_function_space( prepared.function_space().mesh()) return self._adj_assign_constant(prepared, R) else: adj_output = self.backend.Function( block_variable.output.function_space()) adj_output.assign(prepared) return adj_output.vector() else: # Linear combination expr, adj_input_func = prepared adj_output = self.backend.Function(adj_input_func.function_space()) if block_variable.saved_output.ufl_shape == adj_input_func.ufl_shape: diff_expr = ufl.algorithms.expand_derivatives( ufl.derivative(expr, block_variable.saved_output, adj_input_func)) adj_output.assign(diff_expr) else: # Assume current variable is scalar constant. # This assumption might be wrong for firedrake. assert isinstance(block_variable.output, self.backend.Constant) diff_expr = ufl.algorithms.expand_derivatives( ufl.derivative(expr, block_variable.saved_output, self.backend.Constant(1.))) adj_output.assign(diff_expr) return adj_output.vector().inner(adj_input_func.vector()) if isinstance(block_variable.output, self.backend.Constant): R = block_variable.output._ad_function_space( adj_output.function_space().mesh()) return self._adj_assign_constant(adj_output, R) else: return adj_output.vector()
def __init__(self, energy, alpha, bcs, lb=None, ub=None): NonlinearProblem.__init__(self) self.energy = energy self.alpha = alpha self.V = self.alpha.function_space() self.denergy = derivative(self.energy, self.alpha, TestFunction(self.V)) self.ddenergy = derivative(self.denergy, self.alpha, TrialFunction(self.V)) if lb == None: lb = interpolate(Constant("0."), self.V) if ub == None: ub = interpolate(Constant("1."), self.V) self.lb = lb self.ub = ub self.bcs = bcs self.b = PETScVector() self.A = PETScMatrix()
def test_block(V1, V2, squaremesh_5, nest): mesh = squaremesh_5 u0 = dolfinx.Function(V1, name="u0") u1 = dolfinx.Function(V2, name="u1") v0 = ufl.TestFunction(V1) v1 = ufl.TestFunction(V2) Phi = (ufl.sin(u0) - 0.5)**2 * ufl.dx(mesh) + (4.0 * u0 - u1)**2 * ufl.dx(mesh) F0 = ufl.derivative(Phi, u0, v0) F1 = ufl.derivative(Phi, u1, v1) F = [F0, F1] u = [u0, u1] opts = PETSc.Options("block") opts.setValue('snes_type', 'newtontr') opts.setValue('snes_rtol', 1.0e-08) opts.setValue('snes_max_it', 12) if nest: opts.setValue('ksp_type', 'cg') opts.setValue('pc_type', 'fieldsplit') opts.setValue('fieldsplit_pc_type', 'lu') opts.setValue('ksp_rtol', 1.0e-10) else: opts.setValue('ksp_type', 'preonly') opts.setValue('pc_type', 'lu') opts.setValue('pc_factor_mat_solver_type', 'mumps') problem = dolfiny.snesblockproblem.SNESBlockProblem(F, u, nest=nest, prefix="block") sol = problem.solve() assert problem.snes.getConvergedReason() > 0 assert np.isclose((sol[0].vector - np.arcsin(0.5)).norm(), 0.0) assert np.isclose((sol[1].vector - 4.0 * np.arcsin(0.5)).norm(), 0.0)
def __init__(self, F, u, bc): super().__init__() V = u.function_space() du = function.TrialFunction(V) self.L = F self.a = derivative(F, u, du) self.a_comp = dolfin.fem.Form(self.a) self.bc = bc self._F, self._J = None, None self.u = u
def derivative(form, u, du=None): if du is None: if isinstance(u, Function): V = u.function_space() du = Argument(V) elif isinstance(u, (list,tuple)) and all(isinstance(w, Function) for w in u): V = MixedFunctionSpace([w.function_space() for w in u]) du = ufl.split(Argument(V)) else: cpp.dolfin_error("formmanipulation.py", "compute derivative of form", "Cannot automatically create third argument, please supply one") return ufl.derivative(form, u, du)
def derivative(form, u, du=None, coefficient_derivatives=None): if du is None: # Get existing arguments from form and position the new one with the next argument number form_arguments = form.arguments() number = max([-1] + [arg.number() for arg in form_arguments]) + 1 if any(arg.part() is not None for arg in form_arguments): cpp.dolfin_error("formmanipulation.py", "compute derivative of form", "Cannot automatically create new Argument using " "parts, please supply one") part = None if isinstance(u, Function): V = u.function_space() du = Argument(V, number, part) elif isinstance(u, (list,tuple)) and all(isinstance(w, Function) for w in u): cpp.deprecation("derivative of form w.r.t. a tuple of Coefficients", "1.7.0", "2.0.0", "Take derivative w.r.t. a single Coefficient on " "a mixed space instead.") # Get mesh mesh = u[0].function_space().mesh() if not all(mesh.id() == v.function_space().mesh().id() for v in u): cpp.dolfin_error("formmanipulation.py", "compute derivative of form", "Don't know how to compute derivative with " "respect to Coefficients on different meshes yet") # Build mixed element, space and argument element = ufl.MixedElement([v.function_space().ufl_element() for v in u]) V = FunctionSpace(mesh, element) du = ufl.split(Argument(V, number, part)) else: cpp.dolfin_error("formmanipulation.py", "compute derivative of form w.r.t. '%s'" % u, "Supply Function as a Coefficient") return ufl.derivative(form, u, du, coefficient_derivatives)
def derivative(form, u, du=None): """Compute the derivative of a form. Given a form, this computes its linearization with respect to the provided :class:`.Function`. The resulting form has one additional :class:`Argument` in the same finite element space as the Function. :arg form: a :class:`ufl.Form` to compute the derivative of. :arg u: a :class:`.Function` to compute the derivative with respect to. :arg du: an optional :class:`Argument` to use as the replacement in the new form (constructed automatically if not provided). See also :func:`ufl.derivative`. """ if du is None: if isinstance(u, function.Function): V = u.function_space() du = TrialFunction(V) else: raise RuntimeError("Can't compute derivative for form") return ufl.derivative(form, u, du)
def test_conditional_nan(): # Test case courtesy of Marco Morandini: # https://github.com/firedrakeproject/tsfc/issues/183 def crossTensor(V): return as_tensor([ [0.0, V[2], -V[1]], [-V[2], 0.0, V[0]], [V[1], -V[0], 0.0]]) def coefa(phi2): return conditional(le(phi2, 1.e-6), phi2, sin(phi2)/phi2) def RotDiffTensor(phi): PhiCross = crossTensor(phi) phi2 = dot(phi, phi) a = coefa(phi2) return PhiCross * a mesh = UnitIntervalMesh(8) V = VectorFunctionSpace(mesh, "CG", 1, dim=3) u_phi = Function(V) v_phi = TestFunction(V) Gamma = RotDiffTensor(u_phi) beta = dot(Gamma, grad(u_phi)) delta_beta = ufl.derivative(beta, u_phi, v_phi) L = inner(delta_beta, grad(u_phi)) * dx result = assemble(L).dat.data # Check whether there are any NaNs in the result assert not np.isnan(result).any()
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('solve', 'Running nonlinear solver', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') mesh = self.problem.mesh # Define function spaces (P2-P1) self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.W = MixedFunctionSpace([self.V, self.Q]) self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space # to assign solution in space W.sub(0) to Function(V) we need FunctionAssigner (cannot be assigned directly) fa = FunctionAssigner(self.V, self.W.sub(0)) velSp = Function(self.V) problem.initialize(self.V, self.Q, self.PS, self.D) # Define unknown and test function(s) NS v, q = TestFunctions(self.W) w = Function(self.W) dw = TrialFunction(self.W) u, p = split(w) # Define fields n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) theta = 0.5 # Crank-Nicholson k = Constant(self.metadata['dt']) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) if doSave: problem.save_vel(False, u0, 0.0) # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.W.sub(0), self.W.sub(1)) # NT bcp is not used # Define steady part of the equation def T(u): return -p * I + 2.0 * nu * sym(grad(u)) def F(u, v, q): return (inner(T(u), grad(v)) - q * div(u)) * dx + inner(grad(u) * u, v) * dx # Define variational forms F_ns = (inner((u - u0), v) / k) * dx + (1.0 - theta) * F(u0, v, q) + theta * F(u, v, q) J_ns = derivative(F_ns, w, dw) # J_ns = derivative(F_ns, w) # did not work # NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns, form_compiler_parameters=ffc_options) NS_problem = NonlinearVariationalProblem(F_ns, w, bcu, J_ns) # (var. formulation, unknown, Dir. BC, jacobian, optional) NS_solver = NonlinearVariationalSolver(NS_problem) prm = NS_solver.parameters prm['newton_solver']['absolute_tolerance'] = 1E-08 prm['newton_solver']['relative_tolerance'] = 1E-08 # prm['newton_solver']['maximum_iterations'] = 45 # prm['newton_solver']['relaxation_parameter'] = 1.0 prm['newton_solver']['linear_solver'] = 'mumps' info(NS_solver.parameters, True) self.tc.end('init') # Time-stepping info("Running of direct method") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt/2.0): info("t = %f" % t) self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # Compute begin("Solving NS ....") try: self.tc.start('solve') NS_solver.solve() self.tc.end('solve') except RuntimeError as inst: problem.report_fail(t) return 1 end() # Extract solutions: (u, p) = w.split() fa.assign(velSp, u) # we are assigning twice (now and inside save_vel), but it works with one method save_vel for direct and # projection (we could split save_vel to save one assign) if save_this_step: self.tc.start('saveVel') problem.save_vel(False, velSp, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u) problem.compute_err(False, u, t) problem.compute_div(False, u) # foo = Function(self.Q) # foo.assign(p) # problem.averaging_pressure(foo) # if save_this_step and not onlyVel: # problem.save_pressure(False, foo) if save_this_step and not onlyVel: problem.save_pressure(False, p) # compute functionals (e. g. forces) problem.compute_functionals(u, p, t) # Move to next time step self.tc.start('next') u0.assign(velSp) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: direct method") problem.report() return 0
def derivative(form, u, du=None, coefficient_derivatives=None): """Compute the derivative of a form. Given a form, this computes its linearization with respect to the provided :class:`.Function`. The resulting form has one additional :class:`Argument` in the same finite element space as the Function. :arg form: a :class:`~ufl.classes.Form` to compute the derivative of. :arg u: a :class:`.Function` to compute the derivative with respect to. :arg du: an optional :class:`Argument` to use as the replacement in the new form (constructed automatically if not provided). :arg coefficient_derivatives: an optional :class:`dict` to provide the derivative of a coefficient function. :raises ValueError: If any of the coefficients in ``form`` were obtained from ``u.split()``. UFL doesn't notice that these are related to ``u`` and so therefore the derivative is wrong (instead one should have written ``split(u)``). See also :func:`ufl.derivative`. """ # TODO: What about Constant? u_is_x = isinstance(u, ufl.SpatialCoordinate) if not u_is_x and len(u.split()) > 1 and set(extract_coefficients(form)) & set(u.split()): raise ValueError("Taking derivative of form wrt u, but form contains coefficients from u.split()." "\nYou probably meant to write split(u) when defining your form.") mesh = form.ufl_domain() is_dX = u_is_x or u is mesh.coordinates args = form.arguments() def argument(V): if du is None: n = max(a.number() for a in args) if args else -1 return Argument(V, n + 1) else: return du if is_dX: coords = mesh.coordinates u = ufl.SpatialCoordinate(mesh) V = coords.function_space() du = argument(V) cds = {coords: du} if coefficient_derivatives is not None: cds.update(coefficient_derivatives) coefficient_derivatives = cds elif isinstance(u, firedrake.Function): V = u.function_space() du = argument(V) elif isinstance(u, firedrake.Constant): if u.ufl_shape != (): raise ValueError("Real function space of vector elements not supported") V = firedrake.FunctionSpace(mesh, "Real", 0) du = argument(V) else: raise RuntimeError("Can't compute derivative for form") return ufl.derivative(form, u, du, coefficient_derivatives)