def homogenize(bc): """ Return a homogeneous version of the given boundary condition. *Arguments* bc a :py:class:`DirichletBC <dolfin.fem.bcs.DirichletBC>` instance, or a list/tuple of :py:class:`DirichletBC <dolfin.fem.bcs.DirichletBC>` instances. Other types of boundary conditions are ignored. If the given boundary condition is a list of boundary conditions, then a list of homogeneous boundary conditions is returned. """ # Handle case when boundary condition is a list if isinstance(bc, (list, tuple)): bcs = bc return [homogenize(bc) for bc in bcs] # Only consider Dirichlet boundary conditions if not isinstance(bc, cpp.DirichletBC): cpp.dolfin_error("bcs.py", "homogenize boundary condition", "Can only homogenize DirichletBCs") # Create zero function V = bc.function_space() if V.element().value_rank() == 0: zero = Constant(0) elif V.element().value_rank() == 1: zero = Constant([0] * V.element().value_dimension(0)) else: cpp.dolfin_error("bcs.py", "homogenize boundary condition", "Unhandled value rank %d for homogenization of boundary conditions" % \ V.element().value_rank()) # Create homogeneous boundary condition if len(bc.domain_args) == 1: new_bc = cpp.DirichletBC(V, zero, bc.domain_args[0]) elif len(bc.domain_args) == 2: new_bc = cpp.DirichletBC(V, zero, bc.domain_args[0], bc.domain_args[1]) else: cpp.dolfin_error("bcs.py", "homogenize boundary condition", "Unknown type of boundary specification") new_bc.domain_args = bc.domain_args return new_bc
def __init__(self, rhs_form, solution, t, bcs, a, b, c, order, \ generator=_butcher_scheme_generator): bcs = bcs or [] t = t or Constant(0.0) ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \ k, dt, human_form, contraction = generator(\ a, b, c, t, solution, rhs_form) # Store data self._rhs_form = rhs_form self._ufl_stage_forms = ufl_stage_forms self._dolfin_stage_forms = dolfin_stage_forms self._t = t self._bcs = bcs self._dt = dt self._last_stage = last_stage self._solution = solution self._k = k self.a = a self.b = b self.c = c self._order = order self.contraction = contraction self.jacobian_indices = jacobian_indices cpp.MultiStageScheme.__init__(self, dolfin_stage_forms, last_stage, k, \ solution, t, dt, c, jacobian_indices, order,\ self.__class__.__name__, human_form, bcs)
def __init__(self, *args, **kwargs): "Create Dirichlet boundary condition" # Copy constructor if len(args) == 1: if not isinstance(args[0], cpp.DirichletBC): cpp.dolfin_error("bcs.py", "create DirichletBC", "Expecting a DirichleBC as only argument"\ " for copy constructor") # Initialize base class cpp.DirichletBC.__init__(self, args[0]) self.domain_args = args[0].domain_args return # Special case for value specified as float, tuple or similar if len(args) >= 2 and not isinstance(args[1], cpp.GenericFunction): if isinstance(args[1], ufl.classes.Expr): expr = project(args[1], args[0]) else: expr = Constant(args[1]) # let Constant handle all problems args = args[:1] + (expr, ) + args[2:] # Special case for sub domain specified as a function if len(args) >= 3 and isinstance(args[2], types.FunctionType): sub_domain = AutoSubDomain(args[2]) args = args[:2] + (sub_domain, ) + args[3:] # Special case for sub domain specified as a string if len(args) >= 3 and isinstance(args[2], str): sub_domain = CompiledSubDomain(args[2]) args = args[:2] + (sub_domain, ) + args[3:] # Store Expression to avoid scoping issue with SWIG directors if isinstance(args[1], cpp.Expression): self.function_arg = args[1] # Store SubDomain to avoid scoping issue with SWIG directors self.domain_args = args[2:] # FIXME: Handling of multiple default arguments does not # really work between Python and C++ when C++ requires them to # be ordered and cannot accept the latter without the # former... # Add keyword arguments (in correct order...) allowed_kwargs = ["method", "check_midpoint"] for key in allowed_kwargs: if key in kwargs: args = tuple(list(args) + [kwargs[key]]) # Check for other keyword arguments for key in kwargs: if not key in allowed_kwargs: cpp.dolfin_error("bcs.py", "create boundary condition", "Unknown keyword argument \"%s\"" % key) # Initialize base class cpp.DirichletBC.__init__(self, *args)
def __init__(self, rhs_form, solution, time, bcs, a, b, c, order, generator=_butcher_scheme_generator): bcs = bcs or [] time = time or Constant(0.0) ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \ stage_solutions, dt, human_form, contraction = \ generator(a, b, c, time, solution, rhs_form) # Store data self.a = a self.b = b self.c = c MultiStageScheme.__init__(self, rhs_form, ufl_stage_forms, dolfin_stage_forms, last_stage, stage_solutions, solution, time, dt, c, jacobian_indices, order,\ self.__class__.__name__, human_form, bcs, contraction)
def __init__(self, rhs_form, solution, time, order, generalized, generator=_rush_larsen_scheme_generator): # FIXME: What with bcs? bcs = [] time = time or Constant(0.0) if order not in [1, 2]: raise ValueError("Expected order to be either 1 or 2") rhs_form, ufl_stage_forms, linear_terms, dofin_stage_forms, last_stage, \ stage_solutions, dt, dt_stage_offsets, human_form, contraction = \ generator(rhs_form, solution, time, order, generalized) self.linear_terms = linear_terms # All stages are explicit jacobian_indices = [-1 for _ in stage_solutions] # Highjack a and b to hold parameters for RushLarsen scheme generation MultiStageScheme.__init__(self, rhs_form, ufl_stage_forms, dofin_stage_forms, last_stage, stage_solutions, solution, time, dt, dt_stage_offsets, jacobian_indices, order, self.__class__.__name__, human_form, bcs, contraction)
def _rush_larsen_step(rhs_exprs, diff_rhs_exprs, linear_terms, system_size, \ y0, stage_solution, dt, time, a, c, v, DX, time_dep_expressions): # If we need to replace the original solution with stage solution repl = None if stage_solution is not None: if system_size > 1: repl = {y0: stage_solution} else: repl = {y0[0]: stage_solution} # If we have time dependent expressions if time_dep_expressions and abs(float(c)) > DOLFIN_EPS: time_ = time time = time + dt * float(c) repl.update(_replace_dict_time_dependent_expression(time_dep_expressions, \ time_, dt, float(c))) repl[time_] = time # If all terms are linear (using generalized=True) we add a safe # guard to the linearized term. See below safe_guard = sum(linear_terms) == system_size # Add componentwise contribution to rl form rl_ufl_form = ufl.zero() num_rl_steps = 0 for ind in range(system_size): # forward euler step fe_du_i = rhs_exprs[ind] * dt * float(a) # If exact integration if linear_terms[ind]: num_rl_steps += 1 # Rush Larsen step # Safeguard the divisor: let's hope diff_rhs_exprs[ind] is never 1.0e-16! # Let's get rid of this when the conditional fixes land properly in UFL. eps = Constant(1.0e-16) rl_du_i = rhs_exprs[ind]/(diff_rhs_exprs[ind] + eps)*(\ ufl.exp(diff_rhs_exprs[ind]*dt) - 1.0) # If safe guard if safe_guard: du_i = ufl.conditional(ufl.lt(abs(diff_rhs_exprs[ind]), 1e-8), fe_du_i, rl_du_i) else: du_i = rl_du_i else: du_i = fe_du_i # If we should replace solution in form with stage solution if repl: du_i = ufl.replace(du_i, repl) rl_ufl_form += (y0[ind] + du_i) * v[ind] return rl_ufl_form * DX
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, rhs_form, solution, t, bcs, a, b, c, order): bcs = bcs or [] t = t or Constant(0.0) ufl_stage_forms, dolfin_stage_forms, last_stage, k, dt, human_form = \ _butcher_scheme_generator(a, b, c, solution, rhs_form) # Store data self._rhs_form = rhs_form self._ufl_stage_forms = ufl_stage_forms self._dolfin_stage_forms = dolfin_stage_forms self._t = t self._dt = dt self._last_stage = last_stage self._solution = solution self._k = k self.a = a self.b = b self.c = c cpp.MultiStageScheme.__init__(self, dolfin_stage_forms, last_stage, k, \ solution, t, dt, c, order, self.__class__.__name__, human_form, bcs)
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 _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
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