def create_assembly_callable(expr, tensor=None, bcs=None, form_compiler_parameters=None, mat_type=None, sub_mat_type=None, diagonal=False): r"""Create a callable object than be used to assemble expr into a tensor. This is really only designed to be used inside residual and jacobian callbacks, since it always assembles back into the initially provided tensor. See also :func:`allocate_matrix`. .. warning:: Really do not use this function unless you know what you're doing. """ if tensor is None: raise ValueError("Have to provide tensor to write to") if mat_type == "matfree": return tensor.assemble loops = _assemble(expr, tensor=tensor, bcs=solving._extract_bcs(bcs), form_compiler_parameters=form_compiler_parameters, mat_type=mat_type, sub_mat_type=sub_mat_type, diagonal=diagonal, assemble_now=False) loops = tuple(loops) def thunk(): for kernel in loops: kernel() return thunk
def __init__(self, F, u, bcs=None, J=None, Jp=None, form_compiler_parameters=None): r""" :param F: the nonlinear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param J: the Jacobian J = dF/du (optional) :param Jp: a form used for preconditioning the linear system, optional, if not supplied then the Jacobian itself will be used. :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) """ from firedrake import solving from firedrake import function # Store input UFL forms and solution Function self.F = F self.Jp = Jp self.u = u self.bcs = solving._extract_bcs(bcs) # Argument checking if not isinstance(self.F, (ufl.Form, slate.slate.TensorBase)): raise TypeError( "Provided residual is a '%s', not a Form or Slate Tensor" % type(self.F).__name__) if len(self.F.arguments()) != 1: raise ValueError("Provided residual is not a linear form") if not isinstance(self.u, function.Function): raise TypeError("Provided solution is a '%s', not a Function" % type(self.u).__name__) # Use the user-provided Jacobian. If none is provided, derive # the Jacobian from the residual. self.J = J or ufl_expr.derivative(F, u) if not isinstance(self.J, (ufl.Form, slate.slate.TensorBase)): raise TypeError( "Provided Jacobian is a '%s', not a Form or Slate Tensor" % type(self.J).__name__) if len(self.J.arguments()) != 2: raise ValueError("Provided Jacobian is not a bilinear form") if self.Jp is not None and not isinstance( self.Jp, (ufl.Form, slate.slate.TensorBase)): raise TypeError( "Provided preconditioner is a '%s', not a Form or Slate Tensor" % type(self.Jp).__name__) if self.Jp is not None and len(self.Jp.arguments()) != 2: raise ValueError("Provided preconditioner is not a bilinear form") # Store form compiler parameters self.form_compiler_parameters = form_compiler_parameters self._constant_jacobian = False
def assemble(f, tensor=None, bcs=None, form_compiler_parameters=None, inverse=False, nest=None): """Evaluate f. :arg f: a :class:`ufl.Form` or :class:`ufl.core.expr.Expr`. :arg tensor: an existing tensor object to place the result in (optional). :arg bcs: a list of boundary conditions to apply (optional). :arg form_compiler_parameters: (optional) dict of parameters to pass to the form compiler. Ignored if not assembling a :class:`ufl.Form`. Any parameters provided here will be overridden by parameters set on the :class;`ufl.Measure` in the form. For example, if a :data:`quadrature_degree` of 4 is specified in this argument, but a degree of 3 is requested in the measure, the latter will be used. :arg inverse: (optional) if f is a 2-form, then assemble the inverse of the local matrices. :arg nest: (optional) flag indicating if a 2-form (matrix) on a mixed space should be assembled as a block matrix (if :data:`nest` is :data:`True`) or not. The default value is taken from the parameters dict :data:`parameters["matnest"]`. If f is a :class:`ufl.Form` then this evaluates the corresponding integral(s) and returns a :class:`float` for 0-forms, a :class:`.Function` for 1-forms and a :class:`.Matrix` for 2-forms. If f is an expression other than a form, it will be evaluated pointwise on the :class:`.Function`\s in the expression. This will only succeed if all the Functions are on the same :class:`.FunctionSpace` If ``tensor`` is supplied, the assembled result will be placed there, otherwise a new object of the appropriate type will be returned. If ``bcs`` is supplied and ``f`` is a 2-form, the rows and columns of the resulting :class:`.Matrix` corresponding to boundary nodes will be set to 0 and the diagonal entries to 1. If ``f`` is a 1-form, the vector entries at boundary nodes are set to the boundary condition values. """ if isinstance(f, ufl.form.Form): return _assemble(f, tensor=tensor, bcs=solving._extract_bcs(bcs), form_compiler_parameters=form_compiler_parameters, inverse=inverse, nest=nest) elif isinstance(f, ufl.core.expr.Expr): return assemble_expressions.assemble_expression(f) else: raise TypeError("Unable to assemble: %r" % f)
def __init__(self, F, u, bcs=None, J=None, Jp=None, form_compiler_parameters=None, nest=None): """ :param F: the nonlinear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param J: the Jacobian J = dF/du (optional) :param Jp: a form used for preconditioning the linear system, optional, if not supplied then the Jacobian itself will be used. :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :param nest: indicate if matrices on mixed spaces should be built as monolithic operators (suitable for direct solves), or as nested blocks (suitable for fieldsplit preconditioning). If not provided, uses the default given by ``parameters["matnest"]``. """ from firedrake import solving from firedrake import function # Store input UFL forms and solution Function self.F = F self.Jp = Jp self.u = u self.bcs = solving._extract_bcs(bcs) # Argument checking if not isinstance(self.F, ufl.Form): raise TypeError("Provided residual is a '%s', not a Form" % type(self.F).__name__) if len(self.F.arguments()) != 1: raise ValueError("Provided residual is not a linear form") if not isinstance(self.u, function.Function): raise TypeError("Provided solution is a '%s', not a Function" % type(self.u).__name__) # Use the user-provided Jacobian. If none is provided, derive # the Jacobian from the residual. self.J = J or ufl_expr.derivative(F, u) if not isinstance(self.J, ufl.Form): raise TypeError("Provided Jacobian is a '%s', not a Form" % type(self.J).__name__) if len(self.J.arguments()) != 2: raise ValueError("Provided Jacobian is not a bilinear form") if self.Jp is not None and not isinstance(self.Jp, ufl.Form): raise TypeError("Provided preconditioner is a '%s', not a Form" % type(self.Jp).__name__) if self.Jp is not None and len(self.Jp.arguments()) != 2: raise ValueError("Provided preconditioner is not a bilinear form") self._nest = nest # Store form compiler parameters self.form_compiler_parameters = form_compiler_parameters self._constant_jacobian = False
def __init__(self, *args, bcs=None, J=None, Jp=None, method="topological", V=None, is_linear=False, Jp_eq_J=False): from firedrake.variational_solver import check_pde_args, is_form_consistent if isinstance(args[0], ufl.classes.Equation): # initial construction from equation eq = args[0] u = args[1] sub_domain = args[2] if V is None: V = eq.lhs.arguments()[0].function_space() bcs = solving._extract_bcs(bcs) # Jp_eq_J is progressively evaluated as the tree is constructed self.Jp_eq_J = Jp is None and all([bc.Jp_eq_J for bc in bcs]) # linear if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form): J = eq.lhs Jp = Jp or J if eq.rhs == 0: F = ufl_expr.action(J, u) else: if not isinstance(eq.rhs, (ufl.Form, slate.slate.TensorBase)): raise TypeError("Provided BC RHS is a '%s', not a Form or Slate Tensor" % type(eq.rhs).__name__) if len(eq.rhs.arguments()) != 1: raise ValueError("Provided BC RHS is not a linear form") F = ufl_expr.action(J, u) - eq.rhs self.is_linear = True # nonlinear else: if eq.rhs != 0: raise TypeError("RHS of a nonlinear form equation has to be 0") F = eq.lhs J = J or ufl_expr.derivative(F, u) Jp = Jp or J self.is_linear = False # Check form style consistency is_form_consistent(self.is_linear, bcs) # Argument checking check_pde_args(F, J, Jp) # EquationBCSplit objects for `F`, `J`, and `Jp` self._F = EquationBCSplit(F, u, sub_domain, bcs=[bc if isinstance(bc, DirichletBC) else bc._F for bc in bcs], method=method, V=V) self._J = EquationBCSplit(J, u, sub_domain, bcs=[bc if isinstance(bc, DirichletBC) else bc._J for bc in bcs], method=method, V=V) self._Jp = EquationBCSplit(Jp, u, sub_domain, bcs=[bc if isinstance(bc, DirichletBC) else bc._Jp for bc in bcs], method=method, V=V) elif all(isinstance(args[i], EquationBCSplit) for i in range(3)): # reconstruction for splitting `solving_utils.split` self.Jp_eq_J = Jp_eq_J self.is_linear = is_linear self._F = args[0] self._J = args[1] self._Jp = args[2] else: raise TypeError("Wrong EquationBC arguments")
def assemble(f, tensor=None, bcs=None, form_compiler_parameters=None, inverse=False, nest=None): """Evaluate f. :arg f: a :class:`~ufl.classes.Form` or :class:`~ufl.classes.Expr`. :arg tensor: an existing tensor object to place the result in (optional). :arg bcs: a list of boundary conditions to apply (optional). :arg form_compiler_parameters: (optional) dict of parameters to pass to the form compiler. Ignored if not assembling a :class:`~ufl.classes.Form`. Any parameters provided here will be overridden by parameters set on the :class:`~ufl.classes.Measure` in the form. For example, if a ``quadrature_degree`` of 4 is specified in this argument, but a degree of 3 is requested in the measure, the latter will be used. :arg inverse: (optional) if f is a 2-form, then assemble the inverse of the local matrices. :arg nest: (optional) flag indicating if a 2-form (matrix) on a mixed space should be assembled as a block matrix (if ``nest`` is ``True``) or not. The default value is taken from the parameters dict, ``parameters["matnest"]``. If f is a :class:`~ufl.classes.Form` then this evaluates the corresponding integral(s) and returns a :class:`float` for 0-forms, a :class:`.Function` for 1-forms and a :class:`.Matrix` for 2-forms. If f is an expression other than a form, it will be evaluated pointwise on the :class:`.Function`\s in the expression. This will only succeed if all the Functions are on the same :class:`.FunctionSpace`. If ``tensor`` is supplied, the assembled result will be placed there, otherwise a new object of the appropriate type will be returned. If ``bcs`` is supplied and ``f`` is a 2-form, the rows and columns of the resulting :class:`.Matrix` corresponding to boundary nodes will be set to 0 and the diagonal entries to 1. If ``f`` is a 1-form, the vector entries at boundary nodes are set to the boundary condition values. """ if isinstance(f, ufl.form.Form): return _assemble(f, tensor=tensor, bcs=solving._extract_bcs(bcs), form_compiler_parameters=form_compiler_parameters, inverse=inverse, nest=nest) elif isinstance(f, ufl.core.expr.Expr): return assemble_expressions.assemble_expression(f) else: raise TypeError("Unable to assemble: %r" % f)
def __init__(self, F, u, bcs=None, J=None, Jp=None, form_compiler_parameters=None): """ :param F: the nonlinear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param J: the Jacobian J = dF/du (optional) :param Jp: a form used for preconditioning the linear system, optional, if not supplied then the Jacobian itself will be used. :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) """ from firedrake import solving from firedrake import function # Store input UFL forms and solution Function self.F = F self.Jp = Jp self.u = u self.bcs = solving._extract_bcs(bcs) # Argument checking if not isinstance(self.F, ufl.Form): raise TypeError("Provided residual is a '%s', not a Form" % type(self.F).__name__) if len(self.F.arguments()) != 1: raise ValueError("Provided residual is not a linear form") if not isinstance(self.u, function.Function): raise TypeError("Provided solution is a '%s', not a Function" % type(self.u).__name__) # Use the user-provided Jacobian. If none is provided, derive # the Jacobian from the residual. self.J = J or ufl_expr.derivative(F, u) if not isinstance(self.J, ufl.Form): raise TypeError("Provided Jacobian is a '%s', not a Form" % type(self.J).__name__) if len(self.J.arguments()) != 2: raise ValueError("Provided Jacobian is not a bilinear form") if self.Jp is not None and not isinstance(self.Jp, ufl.Form): raise TypeError("Provided preconditioner is a '%s', not a Form" % type(self.Jp).__name__) if self.Jp is not None and len(self.Jp.arguments()) != 2: raise ValueError("Provided preconditioner is not a bilinear form") # Store form compiler parameters self.form_compiler_parameters = form_compiler_parameters self._constant_jacobian = False
def __init__(self, F, u, bcs=None, J=None, Jp=None, form_compiler_parameters=None, is_linear=False): r""" :param F: the nonlinear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param J: the Jacobian J = dF/du (optional) :param Jp: a form used for preconditioning the linear system, optional, if not supplied then the Jacobian itself will be used. :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :is_linear: internally used to check if all domain/bc forms are given either in 'A == b' style or in 'F == 0' style. """ from firedrake import solving from firedrake import function self.bcs = solving._extract_bcs(bcs) # Check form style consistency self.is_linear = is_linear is_form_consistent(self.is_linear, self.bcs) self.Jp_eq_J = Jp is None self.u = u self.F = F self.Jp = Jp if not isinstance(self.u, function.Function): raise TypeError("Provided solution is a '%s', not a Function" % type(self.u).__name__) # Use the user-provided Jacobian. If none is provided, derive # the Jacobian from the residual. self.J = J or ufl_expr.derivative(F, u) # Argument checking check_pde_args(self.F, self.J, self.Jp) # Store form compiler parameters self.form_compiler_parameters = form_compiler_parameters self._constant_jacobian = False
def __init__(self, F, u, bcs=None, J=None, Jp=None, form_compiler_parameters=None, nest=None): """ :param F: the nonlinear form :param u: the :class:`.Function` to solve for :param bcs: the boundary conditions (optional) :param J: the Jacobian J = dF/du (optional) :param Jp: a form used for preconditioning the linear system, optional, if not supplied then the Jacobian itself will be used. :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :param nest: indicate if matrices on mixed spaces should be built as monolithic operators (suitable for direct solves), or as nested blocks (suitable for fieldsplit preconditioning). If not provided, uses the default given by ``parameters["matnest"]``. """ from firedrake import solving # Extract and check arguments u = solving._extract_u(u) bcs = solving._extract_bcs(bcs) # Store input UFL forms and solution Function self.F = F # Use the user-provided Jacobian. If none is provided, derive # the Jacobian from the residual. self.J = J or ufl_expr.derivative(F, u) self.Jp = Jp self.u = u self.bcs = bcs self._nest = nest # Store form compiler parameters self.form_compiler_parameters = form_compiler_parameters self._constant_jacobian = False
def assemble_form(expr, tensor, bcs, diagonal, assembly_type, form_compiler_parameters, mat_type, sub_mat_type, appctx, options_prefix): """Assemble an expression. :arg expr: a :class:`~ufl.classes.Form` or a :class:`~slate.TensorBase` expression. See :func:`assemble` for a description of the possible additional arguments and return values. """ # Do some setup of the arguments and wrap them in a namedtuple. bcs = solving._extract_bcs(bcs) if assembly_type == "solution": assembly_type = _AssemblyType.SOLUTION elif assembly_type == "residual": assembly_type = _AssemblyType.RESIDUAL else: raise ValueError( "assembly_type must be either 'solution' or 'residual'") mat_type, sub_mat_type = _get_mat_type(mat_type, sub_mat_type, expr.arguments()) opts = _AssemblyOpts(diagonal, assembly_type, form_compiler_parameters, mat_type, sub_mat_type, appctx, options_prefix) assembly_rank = _get_assembly_rank(expr, diagonal) if assembly_rank == _AssemblyRank.SCALAR: if tensor: raise ValueError("Can't assemble 0-form into existing tensor") return _assemble_scalar(expr, bcs, opts) elif assembly_rank == _AssemblyRank.VECTOR: return _assemble_vector(expr, tensor, bcs, opts) elif assembly_rank == _AssemblyRank.MATRIX: return _assemble_matrix(expr, tensor, bcs, opts) else: raise AssertionError
def assemble(f, tensor=None, bcs=None, form_compiler_parameters=None, inverse=False, mat_type=None, sub_mat_type=None, appctx={}, options_prefix=None, **kwargs): r"""Evaluate f. :arg f: a :class:`~ufl.classes.Form`, :class:`~ufl.classes.Expr` or a :class:`~slate.TensorBase` expression. :arg tensor: an existing tensor object to place the result in (optional). :arg bcs: a list of boundary conditions to apply (optional). :arg form_compiler_parameters: (optional) dict of parameters to pass to the form compiler. Ignored if not assembling a :class:`~ufl.classes.Form`. Any parameters provided here will be overridden by parameters set on the :class:`~ufl.classes.Measure` in the form. For example, if a ``quadrature_degree`` of 4 is specified in this argument, but a degree of 3 is requested in the measure, the latter will be used. :arg inverse: (optional) if f is a 2-form, then assemble the inverse of the local matrices. :arg mat_type: (optional) string indicating how a 2-form (matrix) should be assembled -- either as a monolithic matrix ('aij' or 'baij'), a block matrix ('nest'), or left as a :class:`.ImplicitMatrix` giving matrix-free actions ('matfree'). If not supplied, the default value in ``parameters["default_matrix_type"]`` is used. BAIJ differs from AIJ in that only the block sparsity rather than the dof sparsity is constructed. This can result in some memory savings, but does not work with all PETSc preconditioners. BAIJ matrices only make sense for non-mixed matrices. :arg sub_mat_type: (optional) string indicating the matrix type to use *inside* a nested block matrix. Only makes sense if ``mat_type`` is ``nest``. May be one of 'aij' or 'baij'. If not supplied, defaults to ``parameters["default_sub_matrix_type"]``. :arg appctx: Additional information to hang on the assembled matrix if an implicit matrix is requested (mat_type "matfree"). :arg options_prefix: PETSc options prefix to apply to matrices. If f is a :class:`~ufl.classes.Form` then this evaluates the corresponding integral(s) and returns a :class:`float` for 0-forms, a :class:`.Function` for 1-forms and a :class:`.Matrix` or :class:`.ImplicitMatrix` for 2-forms. If f is an expression other than a form, it will be evaluated pointwise on the :class:`.Function`\s in the expression. This will only succeed if all the Functions are on the same :class:`.FunctionSpace`. If f is a Slate tensor expression, then it will be compiled using Slate's linear algebra compiler. If ``tensor`` is supplied, the assembled result will be placed there, otherwise a new object of the appropriate type will be returned. If ``bcs`` is supplied and ``f`` is a 2-form, the rows and columns of the resulting :class:`.Matrix` corresponding to boundary nodes will be set to 0 and the diagonal entries to 1. If ``f`` is a 1-form, the vector entries at boundary nodes are set to the boundary condition values. """ if "nest" in kwargs: nest = kwargs.pop("nest") from firedrake.logging import warning, RED warning(RED % "The 'nest' argument is deprecated, please set 'mat_type' instead") if nest is not None: mat_type = "nest" if nest else "aij" collect_loops = kwargs.pop("collect_loops", False) allocate_only = kwargs.pop("allocate_only", False) if len(kwargs) > 0: raise TypeError("Unknown keyword arguments '%s'" % ', '.join(kwargs.keys())) if isinstance(f, (ufl.form.Form, slate.TensorBase)): loops = _assemble(f, tensor=tensor, bcs=solving._extract_bcs(bcs), form_compiler_parameters=form_compiler_parameters, inverse=inverse, mat_type=mat_type, sub_mat_type=sub_mat_type, appctx=appctx, assemble_now=not collect_loops, allocate_only=allocate_only, options_prefix=options_prefix) loops = tuple(loops) if collect_loops and not allocate_only: # Will this be useful? return loops else: for l in loops: m = l() return m elif isinstance(f, ufl.core.expr.Expr): return assemble_expressions.assemble_expression(f) else: raise TypeError("Unable to assemble: %r" % f)
def assemble(f, tensor=None, bcs=None, form_compiler_parameters=None, inverse=False, mat_type=None, sub_mat_type=None, appctx={}, options_prefix=None, **kwargs): """Evaluate f. :arg f: a :class:`~ufl.classes.Form`, :class:`~ufl.classes.Expr` or a :class:`~slate.TensorBase` expression. :arg tensor: an existing tensor object to place the result in (optional). :arg bcs: a list of boundary conditions to apply (optional). :arg form_compiler_parameters: (optional) dict of parameters to pass to the form compiler. Ignored if not assembling a :class:`~ufl.classes.Form`. Any parameters provided here will be overridden by parameters set on the :class:`~ufl.classes.Measure` in the form. For example, if a ``quadrature_degree`` of 4 is specified in this argument, but a degree of 3 is requested in the measure, the latter will be used. :arg inverse: (optional) if f is a 2-form, then assemble the inverse of the local matrices. :arg mat_type: (optional) string indicating how a 2-form (matrix) should be assembled -- either as a monolithic matrix ('aij' or 'baij'), a block matrix ('nest'), or left as a :class:`.ImplicitMatrix` giving matrix-free actions ('matfree'). If not supplied, the default value in ``parameters["default_matrix_type"]`` is used. BAIJ differs from AIJ in that only the block sparsity rather than the dof sparsity is constructed. This can result in some memory savings, but does not work with all PETSc preconditioners. BAIJ matrices only make sense for non-mixed matrices. :arg sub_mat_type: (optional) string indicating the matrix type to use *inside* a nested block matrix. Only makes sense if ``mat_type`` is ``nest``. May be one of 'aij' or 'baij'. If not supplied, defaults to ``parameters["default_sub_matrix_type"]``. :arg appctx: Additional information to hang on the assembled matrix if an implicit matrix is requested (mat_type "matfree"). :arg options_prefix: PETSc options prefix to apply to matrices. If f is a :class:`~ufl.classes.Form` then this evaluates the corresponding integral(s) and returns a :class:`float` for 0-forms, a :class:`.Function` for 1-forms and a :class:`.Matrix` or :class:`.ImplicitMatrix` for 2-forms. If f is an expression other than a form, it will be evaluated pointwise on the :class:`.Function`\s in the expression. This will only succeed if all the Functions are on the same :class:`.FunctionSpace`. If f is a Slate tensor expression, then it will be compiled using Slate's linear algebra compiler. If ``tensor`` is supplied, the assembled result will be placed there, otherwise a new object of the appropriate type will be returned. If ``bcs`` is supplied and ``f`` is a 2-form, the rows and columns of the resulting :class:`.Matrix` corresponding to boundary nodes will be set to 0 and the diagonal entries to 1. If ``f`` is a 1-form, the vector entries at boundary nodes are set to the boundary condition values. """ if "nest" in kwargs: nest = kwargs.pop("nest") from firedrake.logging import warning, RED warning( RED % "The 'nest' argument is deprecated, please set 'mat_type' instead") if nest is not None: mat_type = "nest" if nest else "aij" collect_loops = kwargs.pop("collect_loops", False) allocate_only = kwargs.pop("allocate_only", False) if len(kwargs) > 0: raise TypeError("Unknown keyword arguments '%s'" % ', '.join(kwargs.keys())) if isinstance(f, (ufl.form.Form, slate.TensorBase)): return _assemble(f, tensor=tensor, bcs=solving._extract_bcs(bcs), form_compiler_parameters=form_compiler_parameters, inverse=inverse, mat_type=mat_type, sub_mat_type=sub_mat_type, appctx=appctx, collect_loops=collect_loops, allocate_only=allocate_only, options_prefix=options_prefix) elif isinstance(f, ufl.core.expr.Expr): return assemble_expressions.assemble_expression(f) else: raise TypeError("Unable to assemble: %r" % f)
def __init__( self, F, u, udot, tspan, time=None, bcs=None, J=None, Jp=None, form_compiler_parameters=None, is_linear=False, ): r""" :param F: the nonlinear form :param u: the :class:`.Function` to solve for :param udot: the :class:`.Function` for time derivative :param tspan: the tuple for start time and end time :param time: the :class:`.Constant` for time-dependent weak forms :param bcs: the boundary conditions (optional) :param J: the Jacobian J = sigma*dF/dudot + dF/du (optional) :param Jp: a form used for preconditioning the linear system, optional, if not supplied then the Jacobian itself will be used. :param dict form_compiler_parameters: parameters to pass to the form compiler (optional) :is_linear: internally used to check if all domain/bc forms are given either in 'A == b' style or in 'F == 0' style. """ from firedrake import solving from firedrake import function, Constant self.bcs = solving._extract_bcs(bcs) # Check form style consistency self.is_linear = is_linear is_form_consistent(self.is_linear, self.bcs) self.Jp_eq_J = Jp is None self.u = u self.udot = udot self.tspan = tspan self.F = F self.Jp = Jp if not isinstance(self.u, function.Function): raise TypeError( "Provided solution is a '%s', not a Function" % type(self.u).__name__ ) if not isinstance(self.udot, function.Function): raise TypeError( "Provided time derivative is a '%s', not a Function" % type(self.udot).__name__ ) # current value of time that may be used in weak form self.time = time or Constant(0.0) # timeshift value provided by the solver self.shift = Constant(1.0) # Use the user-provided Jacobian. If none is provided, derive # the Jacobian from the residual. self.J = J or self.shift * ufl_expr.derivative(F, udot) + ufl_expr.derivative( F, u ) # Argument checking check_pde_args(self.F, self.J, self.Jp) # Store form compiler parameters self.form_compiler_parameters = form_compiler_parameters self._constant_jacobian = False