Hello all,
I have found an interesting detail when evaluating function at certain points as shown in this tutorial by @dokken.
I think this is what is happening (or at least this is one trigger of this):
If a point lays on a facet of two cells the collision mechanism finds two links. When these two cells are own by different processes the point is evaluated twice.
This line cells.append(colliding_cells.links(i)[0])
returns only one cell (the first it found) if the two cells are found on the same process, but cannot handle the case where the two cells are own by different processes.
This scenario is not likely but it happens.
I was getting bunch of unexpected behavior before I have found out.
Consider this MWE:
from dolfinx.mesh import CellType, create_rectangle
from mpi4py import MPI
from dolfinx import fem
from dolfinx import geometry
import numpy as np
mesh = create_rectangle(MPI.COMM_WORLD, [[0, 0], [1, 1]], [64, 64], CellType.triangle)
V = fem.FunctionSpace(mesh, ("CG", 1))
u = fem.Function(V)
def probe_function(p_coords, f, make_safe=False):
domain = f.function_space.mesh
points = np.array(p_coords)
bb_tree = geometry.bb_tree(domain, domain.topology.dim)
cell_candidates = geometry.compute_collisions_points(bb_tree, points)
colliding_cells = geometry.compute_colliding_cells(domain, cell_candidates, points)
cells = []
points_on_proc = []
idx = []
for i, point in enumerate(points):
if len(colliding_cells.links(i)) > 0:
points_on_proc.append(point)
cells.append(colliding_cells.links(i)[0])
idx.append(i)
points_on_proc = np.array(points_on_proc, dtype=np.float64)
idx = np.array(idx, dtype=int)
fv = f.eval(points_on_proc, cells)
fv = domain.comm.allgather(fv)
idx = domain.comm.allgather(idx)
fv = np.vstack(fv)
idx = np.concatenate(idx)
if make_safe:
sorted_idx_arg = np.argsort(idx) # sort ascending (might contain duplicates)
fv = fv[sorted_idx_arg] # reorder values to original order of the p_coords
idx = idx[sorted_idx_arg] # indexes to original order (duplicates still in)
_, u_idx = np.unique(idx, return_index=True) # create idexing for unique values
fv = fv[u_idx] # remove duplicates from values
return fv.flatten()
points = [[0.0, 0.0, 0.0], [0.5, 0.5, 0.0]]
vals_unsafe = probe_function(points, u)
vals_safe = probe_function(points, u, make_safe=True)
if MPI.COMM_WORLD.rank == 0:
print(vals_unsafe)
print(vals_safe)
with mpirun -n 1
[0. 0.]
[0. 0.]
with mpirun -n 2
[0. 0. 0.] # three values for two points!!
[0. 0.]
Also the “safe” version respects the order in points
.
Perhaps this should be reflected in the tutorial @dokken ?