def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None): """ Create linear variational problem a(u, v) = L(v). An optional argument bcs may be passed to specify boundary conditions. Another optional argument form_compiler_parameters may be specified to pass parameters to the form compiler. """ # Extract and check arguments u = _extract_u(u) bcs = _extract_bcs(bcs) # Store input UFL forms and solution Function self.a_ufl = a self.L_ufl = L self.u_ufl = u # Store form compiler parameters form_compiler_parameters = form_compiler_parameters or {} self.form_compiler_parameters = form_compiler_parameters # Wrap forms (and check if linear form L is empty) if L.empty(): L = cpp.Form(1, 0) else: L = Form(L, form_compiler_parameters=form_compiler_parameters) a = Form(a, form_compiler_parameters=form_compiler_parameters) # Initialize C++ base class cpp.LinearVariationalProblem.__init__(self, a, L, u, bcs)
def _solve_varproblem(*args, **kwargs): "Solve variational problem a == L or F == 0" # Extract arguments eq, u, bcs, J, tol, M, form_compiler_parameters, petsc_options \ = _extract_args(*args, **kwargs) # Solve linear variational problem if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form): a = Form(eq.lhs, form_compiler_parameters=form_compiler_parameters) L = Form(eq.rhs, form_compiler_parameters=form_compiler_parameters) A = PETScMatrix() b = PETScVector() assembler = SystemAssembler(a, L, bcs) assembler.assemble(A, b) comm = L.mesh().mpi_comm() solver = PETScKrylovSolver(comm) solver.set_options_prefix("dolfin_solve_") for k, v in petsc_options.items(): PETScOptions.set("dolfin_solve_" + k, v) solver.set_from_options() solver.set_operator(A) solver.solve(u.vector(), b) # Solve nonlinear variational problem else: raise RuntimeError("Not implemented")
def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None): """Create linear variational problem a(u, v) = L(v). An optional argument bcs may be passed to specify boundary conditions. Another optional argument form_compiler_parameters may be specified to pass parameters to the form compiler. """ if bcs is None: bcs = [] elif not isinstance(bcs, (list, tuple)): bcs = [bcs] # Store input UFL forms and solution Function self.a_ufl = a self.L_ufl = L self.u_ufl = u # Store form compiler parameters form_compiler_parameters = form_compiler_parameters or {} self.form_compiler_parameters = form_compiler_parameters # Wrap forms (and check if linear form L is empty) if L.empty(): L = cpp.fem.Form(1, 0) else: L = Form(L, form_compiler_parameters=form_compiler_parameters) a = Form(a, form_compiler_parameters=form_compiler_parameters) # Initialize C++ base class cpp.fem.LinearVariationalProblem.__init__(self, a, L, u._cpp_object, bcs)
def __init__(self, F, u, bcs=None, J=None, form_compiler_parameters=None): """Create nonlinear variational problem F(u; v) = 0. Optional arguments bcs and J may be passed to specify boundary conditions and the Jacobian J = dF/du. Another optional argument form_compiler_parameters may be specified to pass parameters to the form compiler. """ if bcs is None: bcs = [] elif not isinstance(bcs, (list, tuple)): bcs = [bcs] # Store input UFL forms and solution Function self.F_ufl = F self.J_ufl = J self.u_ufl = u # Store form compiler parameters form_compiler_parameters = form_compiler_parameters or {} self.form_compiler_parameters = form_compiler_parameters # Wrap forms F = Form(F, form_compiler_parameters=form_compiler_parameters) if J is not None: J = Form(J, form_compiler_parameters=form_compiler_parameters) # Initialize C++ base class cpp.fem.NonlinearVariationalProblem.__init__(self, F, u._cpp_object, bcs, J)
def __init__(self, a, L=None, solver_type=cpp.fem.LocalSolver.SolverType.LU): """Create a local (cell-wise) solver for a linear variational problem a(u, v) = L(v). """ # Store input UFL forms and solution Function self.a_ufl = a self.L_ufl = L # Wrap as DOLFIN forms a = Form(a) if L is None: # Initialize C++ base class cpp.fem.LocalSolver.__init__(self, a, solver_type) else: if L.empty(): L = cpp.fem.Form(1, 0) else: L = Form(L) # Initialize C++ base class cpp.fem.LocalSolver.__init__(self, a, L, solver_type)
def __init__(self, F, u, bcs=None, J=None, form_compiler_parameters=None): """ Create nonlinear variational problem F(u; v) = 0. Optional arguments bcs and J may be passed to specify boundary conditions and the Jacobian J = dF/du. Another optional argument form_compiler_parameters may be specified to pass parameters to the form compiler. """ # Extract and check arguments u = _extract_u(u) bcs = _extract_bcs(bcs) # Store input UFL forms and solution Function self.F_ufl = F self.J_ufl = J self.u_ufl = u # Store form compiler parameters form_compiler_parameters = form_compiler_parameters or {} self.form_compiler_parameters = form_compiler_parameters # Wrap forms F = Form(F, form_compiler_parameters=form_compiler_parameters) if J is not None: J = Form(J, form_compiler_parameters=form_compiler_parameters) # Initialize C++ base class cpp.NonlinearVariationalProblem.__init__(self, F, u, bcs, J)
def __init__(self, a, L, u, bcs=None, form_compiler_parameters=None): """Create mixed linear variational problem a(u, v) = L(v). An optional argument bcs may be passed to specify boundary conditions. Another optional argument form_compiler_parameters may be specified to pass parameters to the form compiler. """ # Extract and check arguments (u is a list of Function) u_comps = [u[i]._cpp_object for i in range(len(u))] bcs = dolfin.fem.solving._extract_bcs(bcs) # Store form compiler parameters form_compiler_parameters = form_compiler_parameters or {} self.form_compiler_parameters = form_compiler_parameters # Update rhs if we don't have a consistent number of blocks if len(L) != len(u): L_tmp = [None for i in range(len(u))] for Li in L: L_tmp[Li.arguments()[0].part()] = Li L = L_tmp # Check number of blocks in lhs, rhs are consistent assert(len(a) == len(u) * len(u)) assert(len(L) == len(u)) # Create list of forms/blocks a_list = list() L_list = list() for Li in L: if Li is None: L_list.append([cpp.fem.Form(1, 0)]) # single-elt list elif Li.empty(): L_list.append([cpp.fem.Form(1, 0)]) # single-elt list else: Ls = [] # List of Li subforms for Lsub in sub_forms_by_domain(Li): if Lsub is None: Ls.append(cpp.fem.Form(1, 0)) elif Lsub.empty(): Ls.append(cpp.fem.Form(1, 0)) else: Ls.append(Form(Lsub, form_compiler_parameters=form_compiler_parameters)) L_list.append(Ls) for ai in a: if ai is None: a_list.append([cpp.fem.Form(2, 0)]) else: As = [] for Asub in sub_forms_by_domain(ai): As.append(Form(Asub, form_compiler_parameters=form_compiler_parameters)) a_list.append(As) # Initialize C++ base class cpp.fem.MixedLinearVariationalProblem.__init__(self, a_list, L_list, u_comps, bcs)
def generate_error_control(problem, goal): """ Create suitable ErrorControl object from problem and the goal *Arguments* problem (:py:class:`LinearVariationalProblem <dolfin.fem.solving.LinearVariationalProblem>` or :py:class:`NonlinearVariationalProblem <dolfin.fem.solving.NonlinearVariationalProblem>`) The (primal) problem goal (:py:class:`Form <dolfin.fem.form.Form>`) The goal functional *Returns* :py:class:`ErrorControl <dolfin.cpp.ErrorControl>` """ # Generate UFL forms to be used for error control (ufl_forms, is_linear) = generate_error_control_forms(problem, goal) # Compile generated forms p = problem.form_compiler_parameters forms = [Form(form, form_compiler_parameters=p) for form in ufl_forms] # Create cpp.ErrorControl object forms += [is_linear] # NOTE: Lingering design inconsistency. ec = cpp.adaptivity.ErrorControl(*forms) # Return generated ErrorControl return ec
def _create_cpp_form(form: typing.Union[Form, cpp.fem.Form]) -> cpp.fem.Form: """Create a compiled Form from a UFL form""" if form is None: return None elif isinstance(form, cpp.fem.Form): return form else: # FIXME: Attach cpp Form to UFL Form to avoid re-processing form = Form(form) return form._cpp_object
def _create_cpp_form(form, form_compiler_parameters=None): """Create a C++ Form from a UFL form""" if isinstance(form, cpp.fem.Form): if form_compiler_parameters is not None: cpp.warning( "Ignoring form_compiler_parameters when passed a dolfin Form!") return form elif isinstance(form, ufl.Form): form = Form(form, form_compiler_parameters=form_compiler_parameters) return form._cpp_object elif form is None: return None else: raise TypeError("Invalid form type: {}".format(type(form)))
def _create_cpp_form(form, form_compiler_parameters=None): # First check if we got a cpp.Form if isinstance(form, cpp.fem.Form): # Check that jit compilation has already happened if not hasattr(form, "_compiled_form"): raise TypeError( "Expected a dolfin form to have a _compiled_form attribute.") # Warn that we don't use the parameters if we get any if form_compiler_parameters is not None: cpp.warning( "Ignoring form_compiler_parameters when passed a dolfin Form!") return form elif isinstance(form, ufl.Form): form = Form(form, form_compiler_parameters=form_compiler_parameters) return form._cpp_object else: raise TypeError("Invalid form type %s" % (type(form), ))
def __init__(self, problem, goal): """ Create AdaptiveNonlinearVariationalSolver *Arguments* problem (:py:class:`NonlinearVariationalProblem <dolfin.fem.solving.NonlinearVariationalProblem>`) """ # Store problem self.problem = problem # Generate error control object ec = generate_error_control(self.problem, goal) # Compile goal functional separately p = self.problem.form_compiler_parameters M = Form(goal, form_compiler_parameters=p) # Initialize C++ base class cpp.AdaptiveNonlinearVariationalSolver.__init__(self, problem, M, ec)
def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. time (_Constant_) A Constant holding the time at the start of the time step solution (_Function_) The prognostic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation adj (_Function_) The derivative of the functional with respect to y_n+1 """ a = _check_abc(a, b, c) size = a.shape[0] DX = _check_form(rhs_form) # Get test function arguments, coefficients = ufl.algorithms.\ extract_arguments_and_coefficients(rhs_form) v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [ Function(solution.function_space(), name="k_%d" % i) for i in range(size) ] kbar = [Function(solution.function_space(), name="kbar_%d"%i) \ for i in range(size)] # Create the stage forms y_ = solution ydot = solution.copy() time_ = time time_dep_expressions = _time_dependent_expressions(rhs_form, time) zero_ = ufl.zero(*y_.shape()) forward_forms = [] stage_solutions = [] jacobian_indices = [] # The recomputation of the forward run: for i, ki in enumerate(k): # Check whether the stage is explicit explicit = a[i, i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], zero_) time = time_ + dt * c[i] replace_dict = _replace_dict_time_dependent_expression(\ time_dep_expressions, time_, dt, c[i]) replace_dict[y_] = evalargs replace_dict[time_] = time stage_form = ufl.replace(rhs_form, replace_dict) forward_forms.append(stage_form) if explicit: stage_forms = [stage_form] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_implicit = stage_form - ufl.inner(ki, v) * DX stage_forms = [stage_form_implicit, derivative(\ stage_form_implicit, ki)] jacobian_indices.append(0) ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) stage_solutions.append(ki) for i, kbari in reversed(list(enumerate(kbar))): # Check whether the stage is explicit explicit = a[i, i] == 0 # And now the adjoint linearisation: stage_form_adm = ufl.inner(dt * b[i] * adj, v)*DX + sum(\ [dt * float(a[j,i]) * safe_action(safe_adjoint(derivative(\ forward_forms[j], y_)), kbar[j]) for j in range(i, size)]) if explicit: stage_forms_adm = [stage_form_adm] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_adm -= ufl.inner(kbar[i], v) * DX stage_forms_adm = [ stage_form_adm, derivative(stage_form_adm, kbari) ] jacobian_indices.append(1) ufl_stage_forms.append(stage_forms_adm) dolfin_stage_forms.append([Form(form) for form in stage_forms_adm]) stage_solutions.append(kbari) # Only one last stage if len(b.shape) == 1: last_stage = Form(ufl.inner(adj, v)*DX + sum(\ [safe_action(safe_adjoint(derivative(forward_forms[i], y_)), kbar[i]) \ for i in range(size)])) else: raise Exception("Not sure what to do here") human_form = "unimplemented" return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage,\ stage_solutions, dt, human_form, adj
def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form, perturbation): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. time (_Constant_) A Constant holding the time at the start of the time step solution (_Function_) The prognostic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation perturbation (_Function_) The perturbation in the initial condition of the solution """ a = _check_abc(a, b, c) size = a.shape[0] DX = _check_form(rhs_form) # Get test function arguments, coefficients = ufl.algorithms.\ extract_arguments_and_coefficients(rhs_form) v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [ Function(solution.function_space(), name="k_%d" % i) for i in range(size) ] kdot = [Function(solution.function_space(), name="kdot_%d"%i) \ for i in range(size)] # Create the stage forms y_ = solution ydot = solution.copy() time_ = time time_dep_expressions = _time_dependent_expressions(rhs_form, time) zero_ = ufl.zero(*y_.shape()) forward_forms = [] stage_solutions = [] jacobian_indices = [] for i, ki in enumerate(k): # Check whether the stage is explicit explicit = a[i, i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], zero_) time = time_ + dt * c[i] replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions, \ time_, dt, c[i]) replace_dict[y_] = evalargs replace_dict[time_] = time stage_form = ufl.replace(rhs_form, replace_dict) forward_forms.append(stage_form) # The recomputation of the forward run: if explicit: stage_forms = [stage_form] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_implicit = stage_form - ufl.inner(ki, v) * DX stage_forms = [ stage_form_implicit, derivative(stage_form_implicit, ki) ] jacobian_indices.append(0) ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) stage_solutions.append(ki) # And now the tangent linearisation: stage_form_tlm = safe_action(derivative(stage_form, y_), perturbation) + \ sum([dt*float(a[i,j]) * safe_action(derivative(\ forward_forms[j], y_), kdot[j]) for j in range(i+1)]) if explicit: stage_forms_tlm = [stage_form_tlm] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_tlm -= ufl.inner(kdot[i], v) * DX stage_forms_tlm = [ stage_form_tlm, derivative(stage_form_tlm, kdot[i]) ] jacobian_indices.append(1) ufl_stage_forms.append(stage_forms_tlm) dolfin_stage_forms.append([Form(form) for form in stage_forms_tlm]) stage_solutions.append(kdot[i]) # Only one last stage if len(b.shape) == 1: last_stage = Form(ufl.inner(perturbation + sum(\ [dt*float(bi)*kdoti for bi, kdoti in zip(b, kdot)], zero_), v)*DX) else: raise Exception("Not sure what to do here") human_form = [] for i in range(size): kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \ "%s*"% a[i,j], j) \ for j in range(size) if a[i,j] != 0) if c[i] in [0.0, 1.0]: cih = " + h" if c[i] == 1.0 else "" else: cih = " + %s*h" % c[i] kdotterm = " + ".join("%(a)sh*action(derivative(f(t_n%(cih)s, y_n + "\ "%(kterm)s), kdot_%(i)s" % \ {"a": ("" if a[i,j] == 1.0 else "%s*"% a[i,j], j), "i": i, "cih": cih, "kterm": kterm} \ for j in range(size) if a[i,j] != 0) if len(kterm) == 0: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % { "i": i, "cih": cih }) human_form.append("kdot_%(i)s = action(derivative("\ "f(t_n%(cih)s, y_n), y_n), ydot_n)" % \ {"i": i, "cih": cih}) else: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \ {"i": i, "cih": cih, "kterm": kterm}) human_form.append("kdot_%(i)s = action(derivative(f(t_n%(cih)s, "\ "y_n + %(kterm)s), y_n) + %(kdotterm)s" % \ {"i": i, "cih": cih, "kterm": kterm, "kdotterm": kdotterm}) parentheses = "(%s)" if np.sum(b > 0) > 1 else "%s" human_form.append("ydot_{n+1} = ydot_n + h*" + parentheses % (" + ".join(\ "%skdot_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \ for i in range(size) if b[i] > 0))) human_form = "\n".join(human_form) return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \ stage_solutions, dt, human_form, perturbation
def _butcher_scheme_generator(a, b, c, time, solution, rhs_form): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. time (_Constant_) A Constant holding the time at the start of the time step solution (_Function_) The prognostic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation """ a = _check_abc(a, b, c) size = a.shape[0] DX = _check_form(rhs_form) # Get test function arguments, coefficients = ufl.algorithms.\ extract_arguments_and_coefficients(rhs_form) v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [ Function(solution.function_space(), name="k_%d" % i) for i in range(size) ] jacobian_indices = [] # Create the stage forms y_ = solution time_ = time time_dep_expressions = _time_dependent_expressions(rhs_form, time) zero_ = ufl.zero(*y_.shape()) for i, ki in enumerate(k): # Check whether the stage is explicit explicit = a[i, i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], zero_) time = time_ + dt * c[i] replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions, \ time_, dt, c[i]) replace_dict[y_] = evalargs replace_dict[time_] = time stage_form = ufl.replace(rhs_form, replace_dict) if explicit: stage_forms = [stage_form] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form -= ufl.inner(ki, v) * DX stage_forms = [stage_form, derivative(stage_form, ki)] jacobian_indices.append(0) ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) # Only one last stage if len(b.shape) == 1: last_stage = Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \ zip(b, k)], zero_), v)*DX) else: # FIXME: Add support for addaptivity in RKSolver and MultiStageScheme last_stage = [Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \ zip(b[0,:], k)], zero_), v)*DX), Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \ zip(b[1,:], k)], zero_), v)*DX)] # Create the Function holding the solution at end of time step #k.append(solution.copy()) # Generate human form of MultiStageScheme human_form = [] for i in range(size): kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \ "%s*"% a[i,j], j) \ for j in range(size) if a[i,j] != 0) if c[i] in [0.0, 1.0]: cih = " + h" if c[i] == 1.0 else "" else: cih = " + %s*h" % c[i] if len(kterm) == 0: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % { "i": i, "cih": cih }) else: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \ {"i": i, "cih": cih, "kterm": kterm}) parentheses = "(%s)" if np.sum(b > 0) > 1 else "%s" human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(\ "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \ for i in range(size) if b[i] > 0))) human_form = "\n".join(human_form) return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \ k, dt, human_form, None
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 _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 __init__(self, F, u, bcs=None, J=None, form_compiler_parameters=None): """Create nonlinear variational problem F(u; v) = 0. An optional argument bcs may be passed to specify boundary conditions. Another optional argument form_compiler_parameters may be specified to pass parameters to the form compiler. """ # Extract and check arguments (u is a list of Function) u_comps = [u[i]._cpp_object for i in range(len(u))] bcs = dolfin.fem.solving._extract_bcs(bcs) # Store form compiler parameters form_compiler_parameters = form_compiler_parameters or {} self.form_compiler_parameters = form_compiler_parameters # Store input UFL forms and solution Function self.F_ufl = F self.J_ufl = J self.u_ufl = u # Update rhs if we don't have a consistent number of blocks if len(F) != len(u): F_tmp = [None for i in range(len(u))] for Fi in F: F_tmp[Fi.arguments()[0].part()] = Fi F = F_tmp # Check number of blocks in the residual and solution are coherent assert(len(J) == len(u) * len(u)) assert(len(F) == len(u)) # Create list of forms/blocks F_list = list() for Fi in F: if Fi is None: F_list.append([cpp.fem.Form(1, 0)]) elif Fi.empty(): F_list.append([cpp.fem.Form(1, 0)]) # single-elt list else: Fs = [] for Fsub in sub_forms_by_domain(Fi): if Fsub is None: Fs.append(cpp.fem.Form(1, 0)) elif Fsub.empty(): Fs.append(cpp.fem.Form(1, 0)) else: Fs.append(Form(Fsub, form_compiler_parameters=form_compiler_parameters)) F_list.append(Fs) print("[problem] create list of residual forms OK") J_list = None if J is not None: J_list = list() for Ji in J: if Ji is None: J_list.append([cpp.fem.Form(2, 0)]) elif Ji.empty(): J_list.append([cpp.fem.Form(2, 0)]) else: Js = [] for Jsub in sub_forms_by_domain(Ji): Js.append(Form(Jsub, form_compiler_parameters=form_compiler_parameters)) J_list.append(Js) print("[problem] create list of jacobian forms OK, J_list size = ", len(J_list)) # Initialize C++ base class cpp.fem.MixedNonlinearVariationalProblem.__init__(self, F_list, u_comps, bcs, J_list)
def _butcher_scheme_generator(a, b, c, solution, rhs_form): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. solution (_Function_) The prognastic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation """ if not (isinstance(a, np.ndarray) and (len(a) == 1 or \ (len(a.shape)==2 and a.shape[0] == a.shape[1]))): raise TypeError("Expected an m x m numpy array as the first argument") if not (isinstance(b, np.ndarray) and len(b.shape) in [1,2]): raise TypeError("Expected a 1 or 2 dimensional numpy array as the second argument") if not (isinstance(c, np.ndarray) and len(c.shape) == 1): raise TypeError("Expected a 1 dimensional numpy array as the third argument") # Make sure a is a "matrix" if len(a) == 1: a.shape = (1, 1) # Get size of system size = a.shape[0] # If b is a matrix we expect it to have two rows if len(b.shape) == 2: if not (b.shape[0] == 2 and b.shape[1] == size): raise ValueError("Expected a 2 row matrix with the same number "\ "of collumns as the first dimension of the a matrix.") elif len(b) != size: raise ValueError("Expected the length of the b vector to have the "\ "same size as the first dimension of the a matrix.") if len(c) != size: raise ValueError("Expected the length of the c vector to have the "\ "same size as the first dimension of the a matrix.") # Check if tableau is fully implicit for i in range(size): for j in range(i): if a[j, i] != 0: raise ValueError("Does not support fully implicit Butcher tableau.") if not isinstance(rhs_form, ufl.Form): raise TypeError("Expected a ufl.Form as the 5th argument.") # Check if form contains a cell or point integral if "cell" in rhs_form.integral_groups(): DX = ufl.dx elif "point" in rhs_form.integral_groups(): DX = ufl.dP else: raise ValueError("Expected either a cell or point integral in the form.") # Get test function arguments, coefficients = ufl.algorithms.extract_arguments_and_coefficients(rhs_form) if len(arguments) != 1: raise ValueError("Expected the form to have rank 1") v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [solution.copy(deepcopy=True) for i in range(size)] # Create the stage forms y_ = solution for i, ki in enumerate(k): # Check wether the stage is explicit explicit = a[i,i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], ufl.zero(*y_.shape())) stage_form = ufl.replace(rhs_form, {y_:evalargs}) if explicit: stage_forms = [stage_form] else: # Create a F=0 form and differentiate it stage_form -= ufl.inner(ki, v)*DX stage_forms = [stage_form, derivative(stage_form, ki)] ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) # Only one last stage if len(b.shape) == 1: last_stage = cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b, k)]) else: # FIXME: Add support for addaptivity in RKSolver and MultiStageScheme last_stage = [cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b[0,:], k)]), cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b[1,:], k)])] # Create the Function holding the solution at end of time step #k.append(solution.copy()) # Generate human form of MultiStageScheme human_form = [] for i in range(size): kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \ "%s*"% a[i,j], j) \ for j in range(size) if a[i,j] != 0) if c[i] in [0.0, 1.0]: cih = " + h" if c[i] == 1.0 else "" else: cih = " + %s*h" % c[i] if len(kterm) == 0: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {"i": i, "cih": cih}) else: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \ {"i": i, "cih": cih, "kterm": kterm}) parentheses = "(%s)" if np.sum(b>0) > 1 else "%s" human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(\ "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \ for i in range(size) if b[i] > 0))) human_form = "\n".join(human_form) return ufl_stage_forms, dolfin_stage_forms, last_stage, k, dt, human_form