def codeDG(self): code = self._code() u = self.trialFunction ubar = Coefficient(u.ufl_function_space()) penalty = self.penalty if penalty is None: penalty = 1 if isinstance(penalty, Expr): if penalty.ufl_shape == (): penalty = as_vector([penalty]) try: penalty = expand_indices( expand_derivatives(expand_compounds(penalty))) except: pass assert penalty.ufl_shape == u.ufl_shape dmPenalty = as_vector([ replace( expand_derivatives(diff(replace(penalty, {u: ubar}), ubar))[i, i], {ubar: u}) for i in range(u.ufl_shape[0]) ]) else: dmPenalty = None code.append(AccessModifier("public")) x = SpatialCoordinate(self.space.cell()) predefined = {} self.predefineCoefficients(predefined, x) spatial = Variable('const auto', 'y') predefined.update({ x: UnformattedExpression( 'auto', 'entity().geometry().global( Dune::Fem::coordinate( x ) )') }) generateMethod(code, penalty, 'RRangeType', 'penalty', args=['const Point &x', 'const DRangeType &u'], targs=['class Point', 'class DRangeType'], static=False, const=True, predefined=predefined) generateMethod(code, dmPenalty, 'RRangeType', 'linPenalty', args=['const Point &x', 'const DRangeType &u'], targs=['class Point', 'class DRangeType'], static=False, const=True, predefined=predefined) return code
def energy_norm(form, coefficient=None): """UFL form operator: Given a bilinear form *a* and a coefficient *f*, return the functional :math:`a(f,f)`.""" form = as_form(form) form = expand_derivatives(form) return compute_energy_norm(form, coefficient)
def block_replace_zero(block_form, index, block_function_space): assert len(index) in (1, 2) if len(index) == 2: I = index[0] # noqa J = index[1] assert (isinstance( block_form[I][J], Form ) # this function is always called after flattening, so it cannot be an array or list or (isinstance(block_form[I][J], (float, int)) and block_form[I][J] in zeros)) if block_form[I][J] in zeros: block_form_IJ = block_form[I][J] elif has_exact_type(block_form[I][J], CoefficientDerivative): block_form_IJ = expand_derivatives(block_form[I][J]) else: block_form_IJ = block_form[I][J] if block_form_IJ in zeros or block_form_IJ.empty(): block_form_IJ = _get_zero_form(block_function_space, (I, J)) else: assert not block_form_IJ.empty() return block_form_IJ else: I = index[0] # noqa assert (isinstance( block_form[I], Form ) # this function is always called after flattening, so it cannot be an array or list or (isinstance(block_form[I], (float, int)) and block_form[I] in zeros)) block_form_I = block_form[I] if block_form_I in zeros: block_form_I = _get_zero_form(block_function_space, (I, )) else: assert not block_form_I.empty() return block_form_I
def is_zero_ufl_expression(expr, return_val=False): """ Is the given expression always identically zero or not Returns a boolean by default, but will return the actual evaluated expression value if return_val=True This function is somewhat brittle. If the ufl library changes how forms are processed (additional steps or other complexity is added) then this function must be extended to be able to break the expressions down into the smallest possible parts. """ # Reduce the complexity of the expression as much as possible expr = expand_derivatives(expr) expr = expand_compounds(expr) expr = expand_indices(expr) expr = IndexSimplificator().visit(expr) # Perform the zero-form estimation val = EstimateZeroForms().visit(expr) val = int(val) # val > 0 if the form is (likely) non-Zero and 0 if it is # provably identically Zero() if return_val: return val else: return val == 0
def __init__(self, block_form, block_function_space, form_compiler_parameters=None): # Store UFL form self._block_form = block_form # Store block function space assert len(block_function_space) == 2 self._block_function_space = block_function_space # Replace UFL form by Dolfin form before passing it to the constructor # (note that we assume that block_form has been already preprocessed, # so we can assume that nested blocks have been unrolled and zero # placeholders have been replaced by zero forms) N = len(block_form) M = len(block_form[0]) assert all([len(block_form_I) == M for block_form_I in block_form]) replaced_block_form = empty((N, M), dtype=object) for I in range(N): for J in range(M): if isinstance(block_form[I, J], Form) and has_exact_type(block_form[I, J], CoefficientDerivative): block_form[I, J] = expand_derivatives(block_form[I, J]) replaced_block_form[I, J] = block_replace_zero(block_form, (I, J), block_function_space) assert isinstance(replaced_block_form[I, J], Form) or _is_zero(replaced_block_form[I, J]) if isinstance(replaced_block_form[I, J], Form): replaced_block_form[I, J] = _create_dolfin_form( form=replaced_block_form[I, J], form_compiler_parameters=form_compiler_parameters ) elif _is_zero(replaced_block_form[I, J]): assert isinstance(replaced_block_form[I, J], cpp_Form) else: raise TypeError("Invalid form") BlockForm2_Base.__init__(self, replaced_block_form.tolist(), [block_function_space_.cpp_object() for block_function_space_ in block_function_space]) # Store sizes for shape method self.N = N self.M = M
def __init__(self, form): # Preprocess form form = expand_derivatives(form) form = remove_complex_nodes( form) # TODO support forms in the complex field # Extract spaces from forms len_spaces = len(form.arguments()) assert len_spaces in (0, 1, 2) if len_spaces is 2: spaces = (form_argument_space(form, 0), form_argument_space(form, 1)) elif len_spaces is 1: spaces = (form_argument_space(form, 0), ) elif len_spaces is 0: spaces = () else: raise ValueError("Invalid arguments") # Create empty snapshot if len_spaces in (1, 2): def assemble_empty_snapshot(): empty_snapshot = assemble(form, keep_diagonal=True) empty_snapshot.zero() empty_snapshot.generator = self return empty_snapshot elif len_spaces is 0: def assemble_empty_snapshot(): return 0. else: raise ValueError("Invalid arguments") # Call Parent ParametrizedTensorFactory_Base.__init__(self, form, spaces, assemble_empty_snapshot)
def splitUFLForm(form): phi = form.arguments()[0] dphi = Grad(phi) source = ExprTensor(phi.ufl_shape) flux = ExprTensor(dphi.ufl_shape) boundarySource = ExprTensor(phi.ufl_shape) form = expand_indices(expand_derivatives(expand_compounds(form))) for integral in form.integrals(): if integral.integral_type() == 'cell': fluxExprs = splitMultiLinearExpr(integral.integrand(), [phi]) for op in fluxExprs: if op[0] == phi: source = source + fluxExprs[op] elif op[0] == dphi: flux = flux + fluxExprs[op] else: raise Exception('Invalid derivative encountered in bulk integral: ' + str(op[0])) elif integral.integral_type() == 'exterior_facet': fluxExprs = splitMultiLinearExpr(integral.integrand(), [phi]) for op in fluxExprs: if op[0] == phi: boundarySource = boundarySource + fluxExprs[op] else: raise Exception('Invalid derivative encountered in boundary integral: ' + str(op[0])) else: raise NotImplementedError('Integrals of type ' + integral.integral_type() + ' are not supported.') return source, flux, boundarySource
def replace(e, mapping): """Replace subexpressions in expression. @param e: An Expr or Form. @param mapping: A dict with from:to replacements to perform. """ mapping2 = dict((k, as_ufl(v)) for (k, v) in mapping.items()) # Workaround for problem with delayed derivative evaluation # The problem is that J = derivative(f(g, h), g) does not evaluate immediately # So if we subsequently do replace(J, {g: h}) we end up with an expression: # derivative(f(h, h), h) # rather than what were were probably thinking of: # replace(derivative(f(g, h), g), {g: h}) # # To fix this would require one to expand derivatives early (which # is not attractive), or make replace lazy too. if has_exact_type(e, CoefficientDerivative): # Hack to avoid circular dependencies from ufl.algorithms.ad import expand_derivatives e = expand_derivatives(e) return map_integrand_dags(MyReplacer(mapping2), e)
def __init__(self, form, strict=False): AbstractSeparatedParametrizedForm.__init__(self, form) form = expand_derivatives(form) form = expand_sum_product(form) form = rewrite_quotients(form) form = remove_complex_nodes( form) # TODO support forms in the complex field self._form = form self._coefficients = list() # of list of ParametrizedExpression self._placeholders = list() # of list of Constants self._placeholder_names = list() # of list of string self._form_with_placeholders = list() # of forms self._form_unchanged = list() # of forms # Internal usage self._NaN = float("NaN") # Strict mode when # * checking candidates to be added to coefficients which contain both parametrized and non parametrized leaves. # * checking for coefficients that are solution # If False (default) # * coefficient splitting is prevented, because separating the non parametrized part would result in more # than one coefficient, and the candidate is accepted as the coefficient which contain both parametrized and non parametrized leaves. # * solutions are considered as parametrized # If True # * coefficient is split in order to assure that all coefficients only contains parametrized terms, at the expense of # a larger number of coefficients # * solutions and geometric quantities (except normals) are prevented for being collected in coefficients self._strict = strict
def StokesTest(N, butcher_tableau, stage_type="deriv", splitting=AI): mesh = UnitSquareMesh(N, N) Ve = VectorElement("CG", mesh.ufl_cell(), 2) Pe = FiniteElement("CG", mesh.ufl_cell(), 1) Ze = MixedElement([Ve, Pe]) Z = FunctionSpace(mesh, Ze) t = Constant(0.0) dt = Constant(1.0/N) (x, y) = SpatialCoordinate(mesh) uexact = as_vector([x*t + y**2, -y*t+t*(x**2)]) pexact = Constant(0, domain=mesh) u_rhs = expand_derivatives(diff(uexact, t)) - div(grad(uexact)) + grad(pexact) p_rhs = -div(uexact) z = Function(Z) test_z = TestFunction(Z) (u, p) = split(z) (v, q) = split(test_z) F = (inner(Dt(u), v)*dx + inner(grad(u), grad(v))*dx - inner(p, div(v))*dx - inner(q, div(u))*dx - inner(u_rhs, v)*dx - inner(p_rhs, q)*dx) bcs = [DirichletBC(Z.sub(0), uexact, "on_boundary")] nsp = [(1, VectorSpaceBasis(constant=True))] u, p = z.split() u.interpolate(uexact) lu = {"mat_type": "aij", "snes_type": "newtonls", "snes_linesearch_type": "l2", "snes_linesearch_monitor": None, "snes_monitor": None, "snes_rtol": 1e-8, "snes_atol": 1e-8, "snes_force_iteration": 1, "ksp_type": "preonly", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps"} stepper = TimeStepper(F, butcher_tableau, t, dt, z, stage_type=stage_type, bcs=bcs, solver_parameters=lu, nullspace=nsp) while (float(t) < 1.0): if (float(t) + float(dt) > 1.0): dt.assign(1.0 - float(t)) stepper.advance() t.assign(float(t) + float(dt)) (u, p) = z.split() return errornorm(uexact, u) + errornorm(pexact, p)
def action(form, coefficient=None): """UFL form operator: Given a bilinear form, return a linear form with an additional coefficient, representing the action of the form on the coefficient. This can be used for matrix-free methods.""" form = as_form(form) form = expand_derivatives(form) return compute_form_action(form, coefficient)
def __compute_shape_derivative(self): """Computes the shape derivative. Returns ------- None Notes ----- This only works properly if differential operators only act on state and adjoint variables, else the results are incorrect. A corresponding warning whenever this could be the case is issued. """ # Shape derivative of Lagrangian w/o regularization and pull-backs self.shape_derivative = fenics.derivative( self.lagrangian.lagrangian_form, fenics.SpatialCoordinate(self.mesh), self.test_vector_field) # Add pull-backs if self.use_pull_back: self.state_adjoint_ids = [coeff.id() for coeff in self.states] + [ coeff.id() for coeff in self.adjoints ] self.material_derivative_coeffs = [] for coeff in self.lagrangian.lagrangian_form.coefficients(): if coeff.id() in self.state_adjoint_ids: pass else: if not (coeff.ufl_element().family() == 'Real'): self.material_derivative_coeffs.append(coeff) if len(self.material_derivative_coeffs) > 0: warning( 'Shape derivative might be wrong, if differential operators act on variables other than states and adjoints. \n' 'You can check for correctness of the shape derivative with cashocs.verification.shape_gradient_test\n' ) for coeff in self.material_derivative_coeffs: # temp_space = fenics.FunctionSpace(self.mesh, coeff.ufl_element()) # placeholder = fenics.Function(temp_space) # temp_form = fenics.derivative(self.lagrangian.lagrangian_form, coeff, placeholder) # material_derivative = replace(temp_form, {placeholder : fenics.dot(fenics.grad(coeff), self.test_vector_field)}) material_derivative = fenics.derivative( self.lagrangian.lagrangian_form, coeff, fenics.dot(fenics.grad(coeff), self.test_vector_field)) material_derivative = expand_derivatives(material_derivative) self.shape_derivative += material_derivative # Add regularization self.shape_derivative += self.regularization.compute_shape_derivative()
def ad_algorithm(expr): # alt = 1 # alt = 4 # alt = 6 alt = 0 if alt == 0: return expand_derivatives(expr) elif alt == 1: return expand_derivatives(expr, apply_expand_compounds_before=True, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=True) elif alt == 2: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=True, use_alternative_wrapper_algorithm=False) elif alt == 3: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=False) elif alt == 4: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=True) elif alt == 5: return expand_derivatives(expr, apply_expand_compounds_before=False, apply_expand_compounds_after=False, use_alternative_wrapper_algorithm=False)
def rhs(form): """UFL form operator: Given a combined bilinear and linear form, extract the right hand side (negated linear form part). Example:: a = u*v*dx + f*v*dx L = rhs(a) -> -f*v*dx """ form = as_form(form) form = expand_derivatives(form) return compute_form_rhs(form)
def lhs(form): """UFL form operator: Given a combined bilinear and linear form, extract the left hand side (bilinear form part). Example:: a = u*v*dx + f*v*dx a = lhs(a) -> u*v*dx """ form = as_form(form) form = expand_derivatives(form) return compute_form_lhs(form)
def test_expand_indices(): element = FiniteElement("Lagrange", triangle, 2) v = TestFunction(element) u = TrialFunction(element) def evaluate(form): return form.cell_integral()[0].integrand()((), {v: 3, u: 5}) # TODO: How to define values of derivatives? a = div(grad(v)) * u * dx # a1 = evaluate(a) a = expand_derivatives(a) # a2 = evaluate(a) a = expand_indices(a)
def _eval(self, coord, mapping=None, component=()): # Evaluate expression at this particular coordinate, with provided # values for other terminals in mapping # Evaluate derivatives first from ufl.algorithms import expand_derivatives f = expand_derivatives(self) # Evaluate recursively if mapping is None: mapping = {} index_values = StackDict() return f.evaluate(coord, mapping, component, index_values)
def adjoint(form, reordered_arguments=None): """UFL form operator: Given a combined bilinear form, compute the adjoint form by changing the ordering (count) of the test and trial functions. By default, new Argument objects will be created with opposite ordering. However, if the adjoint form is to be added to other forms later, their arguments must match. In that case, the user must provide a tuple reordered_arguments=(u2,v2). """ form = as_form(form) form = expand_derivatives(form) return compute_form_adjoint(form, reordered_arguments)
def adjoint(form, reordered_arguments=None): """UFL form operator: Given a combined bilinear form, compute the adjoint form by changing the ordering (count) of the test and trial functions. By default, new ``Argument`` objects will be created with opposite ordering. However, if the adjoint form is to be added to other forms later, their arguments must match. In that case, the user must provide a tuple *reordered_arguments*=(u2,v2). """ form = as_form(form) form = expand_derivatives(form) return compute_form_adjoint(form, reordered_arguments)
def cppcode(form,*args): import ufl.algorithms as alg # expand_derivatives calls expand_compounds for us g = form # g = alg.expand_compounds(g) g = alg.expand_derivatives(g) g = alg.expand_indices(g) s = g.__str__() for var in args: s = s.replace(var.__str__(), var.expression().cppcode) return s
def heat(n, deg, time_stages, stage_type="deriv", splitting=IA): N = 2**n msh = UnitIntervalMesh(N) params = { "snes_type": "ksponly", "ksp_type": "preonly", "mat_type": "aij", "pc_type": "lu" } V = FunctionSpace(msh, "CG", deg) x, = SpatialCoordinate(msh) t = Constant(0.0) dt = Constant(2.0 / N) uexact = exp(-t) * sin(pi * x) rhs = expand_derivatives(diff(uexact, t)) - div(grad(uexact)) butcher_tableau = GaussLegendre(time_stages) u = project(uexact, V) v = TestFunction(V) F = (inner(Dt(u), v) * dx + inner(grad(u), grad(v)) * dx - inner(rhs, v) * dx) bc = DirichletBC(V, Constant(0), "on_boundary") stepper = TimeStepper(F, butcher_tableau, t, dt, u, bcs=bc, solver_parameters=params, stage_type=stage_type, splitting=splitting) while (float(t) < 1.0): if (float(t) + float(dt) > 1.0): dt.assign(1.0 - float(t)) stepper.advance() t.assign(float(t) + float(dt)) return errornorm(uexact, u) / norm(uexact)
def test_expand_indices(): element = FiniteElement("Lagrange", triangle, 2) v = TestFunction(element) u = TrialFunction(element) def evaluate(form): return form.cell_integral()[0].integrand()((), { v: 3, u: 5 }) # TODO: How to define values of derivatives? a = div(grad(v)) * u * dx # a1 = evaluate(a) a = expand_derivatives(a) # a2 = evaluate(a) a = expand_indices(a)
def _find_linear_terms(rhs_exprs, u): """Help function that takes a list of rhs expressions and return a list of bools determining what component, rhs_exprs[i], is linear wrt u[i]. """ uu = [Constant(1.0) for _ in rhs_exprs] if len(rhs_exprs) > 1: repl = {u: ufl.as_vector(uu)} else: repl = {u: uu[0]} linear_terms = [] for i, ui in enumerate(uu): comp_i_s = expand_indices(ufl.replace(rhs_exprs[i], repl)) linear_terms.append(ui in extract_coefficients(comp_i_s) and ui not in extract_coefficients( expand_derivatives(ufl.diff(comp_i_s, ui)))) return linear_terms
def __init__(self, form): # Preprocess form form = expand_derivatives(form) # Extract spaces from forms len_spaces = len(form.arguments()) assert len_spaces in (1, 2) if len_spaces == 2: spaces = (form_argument_space(form, 0), form_argument_space(form, 1)) elif len_spaces == 1: spaces = (form_argument_space(form, 0), ) # Create empty snapshot def assemble_empty_snapshot(): empty_snapshot = assemble(form, keep_diagonal=True) empty_snapshot.zero() empty_snapshot.generator = self return empty_snapshot # Call Parent ParametrizedTensorFactory_Base.__init__(self, form, spaces, assemble_empty_snapshot)
def _eval(self, coord, mapping=None, component=()): # Evaluate expression at this particular coordinate, # with provided values for other terminals in mapping # Try to infer dimension from given x argument if coord is None: cell = self.cell() dim = None if cell is None else cell.geometric_dimension() elif isinstance(coord, (tuple, list)): dim = len(coord) else: # No type checking here, assuming a scalar x value... dim = 1 # Evaluate derivatives first from ufl.algorithms import expand_derivatives f = expand_derivatives(self, dim) # Evaluate recursively if mapping is None: mapping = {} index_values = StackDict() return f.evaluate(coord, mapping, component, index_values)
def functional(form): # TODO: Does this make sense for anything other than testing? "UFL form operator: Extract the functional part of form." form = as_form(form) form = expand_derivatives(form) return compute_form_functional(form)
def getForm(F, butch, t, dt, u0, bcs=None): """Given a time-dependent variational form and a :class:`ButcherTableau`, produce UFL for the s-stage RK method. :arg F: UFL form for the semidiscrete ODE/DAE :arg butch: the :class:`ButcherTableau` for the RK method being used to advance in time. :arg t: a :class:`Constant` referring to the current time level. Any explicit time-dependence in F is included :arg dt: a :class:`Constant` referring to the size of the current time step. :arg u0: a :class:`Function` referring to the state of the PDE system at time `t` :arg bcs: optionally, a :class:`DirichletBC` object (or iterable thereof) containing (possible time-dependent) boundary conditions imposed on the system. On output, we return a tuple consisting of four parts: - Fnew, the :class:`Form` - k, the :class:`firedrake.Function` holding all the stages. It lives in a :class:`firedrake.FunctionSpace` corresponding to the s-way tensor product of the space on which the semidiscrete form lives. - `bcnew`, a list of :class:`firedrake.DirichletBC` objects to be posed on the stages, - `gblah`, a list of pairs of the form (f, expr), where f is a :class:`firedrake.Function` and expr is a :class:`ufl.Expr`. at each time step, each expr needs to be re-interpolated/projected onto the corresponding f in order for Firedrake to pick up that time-dependent boundary conditions need to be re-applied. """ v = F.arguments()[0] V = v.function_space() assert V == u0.function_space() A = numpy.array([[Constant(aa) for aa in arow] for arow in butch.A]) c = numpy.array([Constant(ci) for ci in butch.c]) num_stages = len(c) num_fields = len(V) Vbig = numpy.prod([V for i in range(num_stages)]) # Silence a warning about transfer managers when we # coarsen coefficients in V push_parent(V.dm, Vbig.dm) vnew = TestFunction(Vbig) k = Function(Vbig) if len(V) == 1: u0bits = [u0] vbits = [v] if num_stages == 1: vbigbits = [vnew] kbits = [k] else: vbigbits = split(vnew) kbits = split(k) else: u0bits = split(u0) vbits = split(v) vbigbits = split(vnew) kbits = split(k) kbits_np = numpy.zeros((num_stages, num_fields), dtype="object") for i in range(num_stages): for j in range(num_fields): kbits_np[i, j] = kbits[i * num_fields + j] Ak = A @ kbits_np Fnew = Zero() for i in range(num_stages): repl = {t: t + c[i] * dt} for j, (ubit, vbit, kbit) in enumerate(zip(u0bits, vbits, kbits)): repl[ubit] = ubit + dt * Ak[i, j] repl[vbit] = vbigbits[num_fields * i + j] repl[TimeDerivative(ubit)] = kbits_np[i, j] if (len(ubit.ufl_shape) == 1): for kk, kbitbit in enumerate(kbits_np[i, j]): repl[TimeDerivative(ubit[kk])] = kbitbit repl[ubit[kk]] = repl[ubit][kk] repl[vbit[kk]] = repl[vbit][kk] Fnew += replace(F, repl) bcnew = [] gblah = [] if bcs is None: bcs = [] for bc in bcs: if isinstance(bc.domain_args[0], str): boundary = bc.domain_args[0] else: boundary = () try: for j in bc.sub_domain: boundary += j except TypeError: boundary = (bc.sub_domain, ) gfoo = expand_derivatives(diff(bc._original_arg, t)) if len(V) == 1: for i in range(num_stages): gcur = replace(gfoo, {t: t + c[i] * dt}) try: gdat = interpolate(gcur, V) except NotImplementedError: gdat = project(gcur, V) gblah.append((gdat, gcur)) bcnew.append(DirichletBC(Vbig[i], gdat, boundary)) else: sub = bc.function_space_index() for i in range(num_stages): gcur = replace(gfoo, {t: t + butch.c[i] * dt}) try: gdat = interpolate(gcur, V.sub(sub)) except NotImplementedError: gdat = project(gcur, V) gblah.append((gdat, gcur)) bcnew.append( DirichletBC(Vbig[sub + num_fields * i], gdat, boundary)) return Fnew, k, bcnew, gblah
def safe_action(x, y): x = expand_derivatives(x) if x.integrals() == (): return x # form is empty, return anyway else: return ufl_action(x, y)
def bc2gcur(bc, i): gorig = bc._original_arg gfoo = expand_derivatives(diff(gorig, t)) return replace(gfoo, {t: t + c[i] * dt}) + u0_mult[i] * gorig
def _rush_larsen_scheme_generator_adm(rhs_form, solution, time, order, generalized, perturbation): """Generates a list of forms and solutions for the adjoint linearisation of the Rush-Larsen scheme *Arguments* rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation solution (_Function_) The prognostic variable time (_Constant_) A Constant holding the time at the start of the time step order (int) The order of the scheme generalized (bool) If True generate a generalized Rush Larsen scheme, linearizing all components. perturbation (Function) The vector on which we compute the adjoint action. """ DX = _check_form(rhs_form) if DX != ufl.dP: raise TypeError("Expected a form with a Pointintegral.") # Create time step dt = Constant(0.1) # Get test function # arguments = rhs_form.arguments() # coefficients = rhs_form.coefficients() # Get time dependent expressions time_dep_expressions = _time_dependent_expressions(rhs_form, time) # Extract rhs expressions from form rhs_integrand = rhs_form.integrals()[0].integrand() rhs_exprs, v = extract_tested_expressions(rhs_integrand) vector_rhs = len(v.ufl_shape) > 0 and v.ufl_shape[0] > 1 system_size = v.ufl_shape[0] if vector_rhs else 1 # Fix for indexing of v for scalar expressions v = v if vector_rhs else [v] # Extract linear terms if not using generalized Rush Larsen if not generalized: linear_terms = _find_linear_terms(rhs_exprs, solution) else: linear_terms = [True for _ in range(system_size)] # Wrap the rhs expressions into a ufl vector type rhs_exprs = ufl.as_vector([rhs_exprs[i] for i in range(system_size)]) rhs_jac = ufl.diff(rhs_exprs, solution) # Takes time! if vector_rhs: diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind])) for ind in range(system_size)] soln = solution else: diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[0]))] solution = [solution] soln = solution[0] ufl_stage_forms = [] dolfin_stage_forms = [] dt_stage_offsets = [] trial = TrialFunction(soln.function_space()) # Stage solutions (3 per order rhs, linearized, and final step) # If 2nd order the final step for 1 step is a stage if order == 1: stage_solutions = [] # Fetch the original step rl_ufl_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, solution, None, dt, time, 1.0, 0.0, v, DX, time_dep_expressions) # If this is commented out, we don't get NaNs. Yhy is # solution a list of length zero anyway? rl_ufl_form = safe_action(safe_adjoint(derivative(rl_ufl_form, soln, trial)), perturbation) elif order == 2: # Stage solution for order 2 fn_space = soln.function_space() stage_solutions = [Function(fn_space, name="y_1/2"), Function(fn_space, name="y_1"), Function(fn_space, name="y_bar_1/2")] y_half_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, solution, None, dt, time, 0.5, 0.0, v, DX, time_dep_expressions) y_one_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, solution, stage_solutions[0], dt, time, 1.0, 0.5, v, DX, time_dep_expressions) y_bar_half_form = safe_action(safe_adjoint(derivative(y_one_form, stage_solutions[0], trial)), perturbation) rl_ufl_form = safe_action(safe_adjoint(derivative(y_one_form, soln, trial)), perturbation) + \ safe_action(safe_adjoint(derivative(y_half_form, soln, trial)), stage_solutions[2]) ufl_stage_forms.append([y_half_form]) ufl_stage_forms.append([y_one_form]) ufl_stage_forms.append([y_bar_half_form]) dolfin_stage_forms.append([Form(y_half_form)]) dolfin_stage_forms.append([Form(y_one_form)]) dolfin_stage_forms.append([Form(y_bar_half_form)]) # Get last stage form last_stage = Form(rl_ufl_form) human_form = "%srush larsen %s" % ("generalized " if generalized else "", str(order)) return rhs_form, linear_terms, ufl_stage_forms, dolfin_stage_forms, last_stage, \ stage_solutions, dt, dt_stage_offsets, human_form, perturbation
def _rush_larsen_scheme_generator(rhs_form, solution, time, order, generalized): """Generates a list of forms and solutions for a given Butcher tableau *Arguments* rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation solution (_Function_) The prognostic variable time (_Constant_) A Constant holding the time at the start of the time step order (int) The order of the scheme generalized (bool) If True generate a generalized Rush Larsen scheme, linearizing all components. """ DX = _check_form(rhs_form) if DX != ufl.dP: raise TypeError("Expected a form with a Pointintegral.") # Create time step dt = Constant(0.1) # Get test function # arguments = rhs_form.arguments() # coefficients = rhs_form.coefficients() # Get time dependent expressions time_dep_expressions = _time_dependent_expressions(rhs_form, time) # Extract rhs expressions from form rhs_integrand = rhs_form.integrals()[0].integrand() rhs_exprs, v = extract_tested_expressions(rhs_integrand) vector_rhs = len(v.ufl_shape) > 0 and v.ufl_shape[0] > 1 system_size = v.ufl_shape[0] if vector_rhs else 1 # Fix for indexing of v for scalar expressions v = v if vector_rhs else [v] # Extract linear terms if not using generalized Rush Larsen if not generalized: linear_terms = _find_linear_terms(rhs_exprs, solution) else: linear_terms = [True for _ in range(system_size)] # Wrap the rhs expressions into a ufl vector type rhs_exprs = ufl.as_vector([rhs_exprs[i] for i in range(system_size)]) rhs_jac = ufl.diff(rhs_exprs, solution) # Takes time! if vector_rhs: diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[ind, ind])) for ind in range(system_size)] else: diff_rhs_exprs = [expand_indices(expand_derivatives(rhs_jac[0]))] solution = [solution] ufl_stage_forms = [] dolfin_stage_forms = [] dt_stage_offsets = [] # Stage solutions (3 per order rhs, linearized, and final step) # If 2nd order the final step for 1 step is a stage if order == 1: stage_solutions = [] rl_ufl_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, solution, None, dt, time, 1.0, 0.0, v, DX, time_dep_expressions) elif order == 2: # Stage solution for order 2 if vector_rhs: stage_solutions = [Function(solution.function_space(), name="y_1/2")] else: stage_solutions = [Function(solution[0].function_space(), name="y_1/2")] stage_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, solution, None, dt, time, 0.5, 0.0, v, DX, time_dep_expressions) rl_ufl_form = _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, solution, stage_solutions[0], dt, time, 1.0, 0.5, v, DX, time_dep_expressions) ufl_stage_forms.append([stage_form]) dolfin_stage_forms.append([Form(stage_form)]) # Get last stage form last_stage = Form(rl_ufl_form) human_form = "%srush larsen %s" % ("generalized " if generalized else "", str(order)) return rhs_form, linear_terms, ufl_stage_forms, dolfin_stage_forms, last_stage, \ stage_solutions, dt, dt_stage_offsets, human_form, None
def testCoefficient(): coord_elem = VectorElement("P", triangle, 1, dim=3) mesh = Mesh(coord_elem) V = FunctionSpace(mesh, FiniteElement("P", triangle, 1)) v = Coefficient(V) assert round(expand_derivatives(diff(v, v)) - 1.0, 7) == 0
def testCoefficient(): v = Constant(triangle) assert round(expand_derivatives(diff(v, v))-1.0, 7) == 0