Hello all,
Have used legacy fenics for a long time and am finally getting around to porting my utilities over. The team has done a great job!
Particularly, I’m trying to build an operator (matrix) which acts on an element of the finite element space and extracts its values at a certain set of locations. Note, this is different from the evaluation at those locations. I need the operator as later I’ll need its transpose.
Following the excellent tutorial written here:
I’ve figured out how to obtain the action of this operator. Then, knowing what a tabulation is, I’ve written the following (serial) MWE to back out the operator corresponding to what I want.
from itertools import product
import dolfinx
from dolfinx import fem, mesh
from dolfinx.fem import functionspace
from mpi4py import MPI
import numpy as np
from scipy.sparse import csr_matrix
domain = mesh.create_unit_square(MPI.COMM_WORLD, 16, 16)
V = functionspace(domain, ("Lagrange", 1))
def u_py(x):
return 1 + x[0] ** 2 + 2 * x[1] ** 2
u = fem.Function(V)
u.interpolate(u_py)
sensor_locations = list(product(np.linspace(0.1, 0.9, 4), np.linspace(0.1, 0.9, 4)))
true_vals = [u_py(x) for x in sensor_locations]
# Goal, obtain operator B such that B(u) = [u(p) for p in sensor_locations]
# Alternatively, obtain ufl form that, when assembled, produced B(u)
pts_3d = np.array([[p[0], p[1], 0] for p in sensor_locations])
bb_tree = dolfinx.geometry.bb_tree(domain, domain.topology.dim)
potential_colliding_cells = dolfinx.geometry.compute_collisions_points(bb_tree, pts_3d)
colliding_cells = dolfinx.geometry.compute_colliding_cells(
domain, potential_colliding_cells, pts_3d
)
cells = []
for i, point in enumerate(pts_3d):
if len(colliding_cells.links(i)) > 0:
cells.append(colliding_cells.links(i)[0])
cells = np.array(cells, dtype=np.int32)
cell_global_dofs = [V.dofmap.cell_dofs(idx) for idx in cells]
tab = V.element.basix_element.tabulate(0, np.array(sensor_locations))
row_idx = []
col_idx = []
data = []
for i, target in enumerate(pts_3d):
for local_dof, global_dof in enumerate(cell_global_dofs[i]):
row_idx.append(i)
col_idx.append(global_dof)
data.append(tab[0][i][local_dof][0])
Q = csr_matrix(
(data, (row_idx, col_idx)),
shape=(pts_3d.shape[0], V.dofmap.index_map.size_global),
)
u_pts = Q @ u.x.array
# u_pts \not\approx true_vals !!! But it's close?
Basically,
- Obtain the cells which contain the points
- Back out the corresponding global degrees of freedom
- Compute a tabulation
- Build the operator corresponding to what I understand the interpolation operator should be doing
However, this does not coincide with what I expect the true value of interpolation to be! I expect a small amount of error (~1e-2) due to the discretization, but this error is much larger! Moreover, u.eval(pts_3d, cells)
obtains what I would expect, hence I assume that there’s an issue with my implementation.
Perhaps an assumption I’ve made regarding the support of the Lagrangian elements? Or is there an ordering issue? Anyone who knows how eval works would care to help me out?
Anything is appreciated!
Best,
Abhi