Source code for dolfinx_adjoint.interpolation

import warnings

import dolfinx
import numpy as np
import ufl
from pyadjoint.overloaded_type import create_overloaded_object
from pyadjoint.tape import annotate_tape, get_working_tape, stop_annotating

from .blocks.interpolation import ExprInterpolationBlock, InterpolationBlock
from .blocks.nonmatching_interpolation import NonmatchingInterpolationBlock
from .compat import get_interpolation_points


[docs] def interpolate(u_or_expr, V: dolfinx.fem.FunctionSpace, **kwargs): """Interpolate a Function or UFL Expression into a different function space.""" ad_block_tag = kwargs.pop("ad_block_tag", None) petsc_mat = kwargs.pop("petsc_mat", False) annotate = annotate_tape(kwargs) # Evaluate the forward interpolation without recording to tape yet with stop_annotating(): v = dolfinx.fem.Function(V) if isinstance(u_or_expr, dolfinx.fem.Function): v.interpolate(u_or_expr) elif isinstance(u_or_expr, ufl.core.expr.Expr): ip = get_interpolation_points(V) compiled_expr = dolfinx.fem.Expression(u_or_expr, ip) v.interpolate(compiled_expr) else: raise TypeError("Input must be a dolfinx.fem.Function or ufl.core.expr.Expr") v.x.scatter_forward() output = create_overloaded_object(v) if annotate: tape = get_working_tape() if isinstance(u_or_expr, dolfinx.fem.Function): # Assume InterpolationBlock is imported block = InterpolationBlock(u_or_expr, output, ad_block_tag=ad_block_tag, petsc_mat=petsc_mat) elif isinstance(u_or_expr, ufl.core.expr.Expr): block = ExprInterpolationBlock(u_or_expr, output, ad_block_tag=ad_block_tag, petsc_mat=petsc_mat) tape.add_block(block) block.add_output(output.block_variable) return output
[docs] def interpolate_nonmatching( u_from: dolfinx.fem.Function, V_to: dolfinx.fem.FunctionSpace, cells=None, interpolation_data=None, tol: float = 1e-6, maxit: int = 15, **kwargs, ): """Interpolate a Function into a different function space on a non-matching mesh.""" ad_block_tag = kwargs.pop("ad_block_tag", None) petsc_mat = kwargs.pop("petsc_mat", False) red_op = kwargs.pop("red_op", None) if red_op is not None and (cells is not None or interpolation_data is not None): warnings.warn( "A custom `red_op` was supplied together with explicit `cells`/`interpolation_data`. " "The transfer matrix used for the adjoint, TLM, Hessian, and all recomputes with a " "custom `red_op` is built by `fenicsx_ii.create_interpolation_matrix`, which does not " "accept `cells`/`interpolation_data` — those are only honored by the initial, " "tape-external forward evaluation done here.", stacklevel=2, ) annotate = annotate_tape(kwargs) with stop_annotating(): v = dolfinx.fem.Function(V_to) # 1. Provide defaults for cells and interpolation_data if not supplied if cells is None: mesh_to = V_to.mesh cells = np.arange(mesh_to.topology.index_map(mesh_to.topology.dim).size_local, dtype=np.int32) if interpolation_data is None: # Note: create_interpolation_data takes C++ objects for the function spaces interpolation_data = dolfinx.fem.create_interpolation_data(V_to, u_from.function_space, cells, padding=tol) # 2. Evaluate the forward non-matching interpolation natively v.interpolate_nonmatching(u_from, cells, interpolation_data, tol=tol, maxit=maxit) v.x.scatter_forward() # 3. Create the PyAdjoint wrapper output = create_overloaded_object(v) if annotate: tape = get_working_tape() # 4. Construct the block with all non-matching metadata block = NonmatchingInterpolationBlock( u_from, output, cells=cells, interpolation_data=interpolation_data, tol=tol, maxit=maxit, red_op=red_op, ad_block_tag=ad_block_tag, use_petsc=petsc_mat, ) tape.add_block(block) block.add_output(output.block_variable) return output