Locate_entities() yields unexpected results

I am trying to solve a simple diffusion problem on a 1D IntervalMesh with different diffusion coefficients in two different regions. For this, I am following the tutorial Defining subdomains for different materials using locate_entities(). However, for some reason, one of my cells is not marked by the following code:

# Define tolerance
tol = 1E-14

# Define mesh quantities
mesh_n = 101
mesh_min = -2E-4
mesh_max = 1E-3

# Create mesh and define function space
mesh = create_interval(MPI.COMM_WORLD, mesh_n, (mesh_min, mesh_max))

# Define subdomains for c-region and a-region
def subdomain_c(x):
    return x[0] <= tol

def subdomain_a(x):
    return x[0] >= -tol

# Mark cells based on their correspondence to a region
cells_subdomain_c = locate_entities(mesh, mesh.topology.dim, subdomain_c)
cells_subdomain_a = locate_entities(mesh, mesh.topology.dim, subdomain_a)
print(cells_subdomain_c)
print(cells_subdomain_a)

The output of this script is:

[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]
[ 17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34
  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52
  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70
  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88
  89  90  91  92  93  94  95  96  97  98  99 100]

So obviously, the dof #16 does not correspond to any of the two regions, even though its x-coordinate should return true for one of the two functions subdomain_c/a(). What did I miss here and how do I code this in a more robust way?

What happens if you increase tol?

I tried that already, unfortunately it did not work. If that helps, I can also output the dof coordinates using

V = FunctionSpace(mesh, ('CG', 1))
print(V.tabulate_dof_coordinates())

which shows that the dofs shouldn’t be “on the edge” of any of the subdomains, at least as far as I understand.

[[-2.00000000e-04  0.00000000e+00  0.00000000e+00]
 [-1.88118812e-04  0.00000000e+00  0.00000000e+00]
 [-1.76237624e-04  0.00000000e+00  0.00000000e+00]
 [-1.64356436e-04  0.00000000e+00  0.00000000e+00]
 [-1.52475248e-04  0.00000000e+00  0.00000000e+00]
 [-1.40594059e-04  0.00000000e+00  0.00000000e+00]
 [-1.28712871e-04  0.00000000e+00  0.00000000e+00]
 [-1.16831683e-04  0.00000000e+00  0.00000000e+00]
 [-1.04950495e-04  0.00000000e+00  0.00000000e+00]
 [-9.30693069e-05  0.00000000e+00  0.00000000e+00]
 [-8.11881188e-05  0.00000000e+00  0.00000000e+00]
 [-6.93069307e-05  0.00000000e+00  0.00000000e+00]
 [-5.74257426e-05  0.00000000e+00  0.00000000e+00]
 [-4.55445545e-05  0.00000000e+00  0.00000000e+00]
 [-3.36633663e-05  0.00000000e+00  0.00000000e+00]
 [-2.17821782e-05  0.00000000e+00  0.00000000e+00]
 [-9.90099010e-06  0.00000000e+00  0.00000000e+00]
 [ 1.98019802e-06  0.00000000e+00  0.00000000e+00]
 [ 1.38613861e-05  0.00000000e+00  0.00000000e+00]
 [ 2.57425743e-05  0.00000000e+00  0.00000000e+00]
 [ 3.76237624e-05  0.00000000e+00  0.00000000e+00]
 [ 4.95049505e-05  0.00000000e+00  0.00000000e+00]
 [ 6.13861386e-05  0.00000000e+00  0.00000000e+00]
 [ 7.32673267e-05  0.00000000e+00  0.00000000e+00]
 [ 8.51485149e-05  0.00000000e+00  0.00000000e+00]
 [ 9.70297030e-05  0.00000000e+00  0.00000000e+00]
 [ 1.08910891e-04  0.00000000e+00  0.00000000e+00]
 [ 1.20792079e-04  0.00000000e+00  0.00000000e+00]
 [ 1.32673267e-04  0.00000000e+00  0.00000000e+00]
 [ 1.44554455e-04  0.00000000e+00  0.00000000e+00]
 [ 1.56435644e-04  0.00000000e+00  0.00000000e+00]
 [ 1.68316832e-04  0.00000000e+00  0.00000000e+00]
 [ 1.80198020e-04  0.00000000e+00  0.00000000e+00]
 [ 1.92079208e-04  0.00000000e+00  0.00000000e+00]
 [ 2.03960396e-04  0.00000000e+00  0.00000000e+00]
 [ 2.15841584e-04  0.00000000e+00  0.00000000e+00]
 [ 2.27722772e-04  0.00000000e+00  0.00000000e+00]
 [ 2.39603960e-04  0.00000000e+00  0.00000000e+00]
 [ 2.51485149e-04  0.00000000e+00  0.00000000e+00]
 [ 2.63366337e-04  0.00000000e+00  0.00000000e+00]
 [ 2.75247525e-04  0.00000000e+00  0.00000000e+00]
 [ 2.87128713e-04  0.00000000e+00  0.00000000e+00]
 [ 2.99009901e-04  0.00000000e+00  0.00000000e+00]
 [ 3.10891089e-04  0.00000000e+00  0.00000000e+00]
 [ 3.22772277e-04  0.00000000e+00  0.00000000e+00]
 [ 3.34653465e-04  0.00000000e+00  0.00000000e+00]
 [ 3.46534653e-04  0.00000000e+00  0.00000000e+00]
 [ 3.58415842e-04  0.00000000e+00  0.00000000e+00]
 [ 3.70297030e-04  0.00000000e+00  0.00000000e+00]
 [ 3.82178218e-04  0.00000000e+00  0.00000000e+00]
 [ 3.94059406e-04  0.00000000e+00  0.00000000e+00]
 [ 4.05940594e-04  0.00000000e+00  0.00000000e+00]
 [ 4.17821782e-04  0.00000000e+00  0.00000000e+00]
 [ 4.29702970e-04  0.00000000e+00  0.00000000e+00]
 [ 4.41584158e-04  0.00000000e+00  0.00000000e+00]
 [ 4.53465347e-04  0.00000000e+00  0.00000000e+00]
 [ 4.65346535e-04  0.00000000e+00  0.00000000e+00]
 [ 4.77227723e-04  0.00000000e+00  0.00000000e+00]
 [ 4.89108911e-04  0.00000000e+00  0.00000000e+00]
 [ 5.00990099e-04  0.00000000e+00  0.00000000e+00]
 [ 5.12871287e-04  0.00000000e+00  0.00000000e+00]
 [ 5.24752475e-04  0.00000000e+00  0.00000000e+00]
 [ 5.36633663e-04  0.00000000e+00  0.00000000e+00]
 [ 5.48514851e-04  0.00000000e+00  0.00000000e+00]
 [ 5.60396040e-04  0.00000000e+00  0.00000000e+00]
 [ 5.72277228e-04  0.00000000e+00  0.00000000e+00]
 [ 5.84158416e-04  0.00000000e+00  0.00000000e+00]
 [ 5.96039604e-04  0.00000000e+00  0.00000000e+00]
 [ 6.07920792e-04  0.00000000e+00  0.00000000e+00]
 [ 6.19801980e-04  0.00000000e+00  0.00000000e+00]
 [ 6.31683168e-04  0.00000000e+00  0.00000000e+00]
 [ 6.43564356e-04  0.00000000e+00  0.00000000e+00]
 [ 6.55445545e-04  0.00000000e+00  0.00000000e+00]
 [ 6.67326733e-04  0.00000000e+00  0.00000000e+00]
 [ 6.79207921e-04  0.00000000e+00  0.00000000e+00]
 [ 6.91089109e-04  0.00000000e+00  0.00000000e+00]
 [ 7.02970297e-04  0.00000000e+00  0.00000000e+00]
 [ 7.14851485e-04  0.00000000e+00  0.00000000e+00]
 [ 7.26732673e-04  0.00000000e+00  0.00000000e+00]
 [ 7.38613861e-04  0.00000000e+00  0.00000000e+00]
 [ 7.50495050e-04  0.00000000e+00  0.00000000e+00]
 [ 7.62376238e-04  0.00000000e+00  0.00000000e+00]
 [ 7.74257426e-04  0.00000000e+00  0.00000000e+00]
 [ 7.86138614e-04  0.00000000e+00  0.00000000e+00]
 [ 7.98019802e-04  0.00000000e+00  0.00000000e+00]
 [ 8.09900990e-04  0.00000000e+00  0.00000000e+00]
 [ 8.21782178e-04  0.00000000e+00  0.00000000e+00]
 [ 8.33663366e-04  0.00000000e+00  0.00000000e+00]
 [ 8.45544554e-04  0.00000000e+00  0.00000000e+00]
 [ 8.57425743e-04  0.00000000e+00  0.00000000e+00]
 [ 8.69306931e-04  0.00000000e+00  0.00000000e+00]
 [ 8.81188119e-04  0.00000000e+00  0.00000000e+00]
 [ 8.93069307e-04  0.00000000e+00  0.00000000e+00]
 [ 9.04950495e-04  0.00000000e+00  0.00000000e+00]
 [ 9.16831683e-04  0.00000000e+00  0.00000000e+00]
 [ 9.28712871e-04  0.00000000e+00  0.00000000e+00]
 [ 9.40594059e-04  0.00000000e+00  0.00000000e+00]
 [ 9.52475248e-04  0.00000000e+00  0.00000000e+00]
 [ 9.64356436e-04  0.00000000e+00  0.00000000e+00]
 [ 9.76237624e-04  0.00000000e+00  0.00000000e+00]
 [ 9.88118812e-04  0.00000000e+00  0.00000000e+00]
 [ 1.00000000e-03  0.00000000e+00  0.00000000e+00]]

This is because you have a cell that cuts the middle of your condition.
This can be visualized with:


from dolfinx.mesh import create_interval, locate_entities
from dolfinx.cpp.mesh import entities_to_geometry
from mpi4py import MPI

# Define tolerance
tol = 1E-14

# Define mesh quantities
mesh_n = 101
mesh_min = -2E-4
mesh_max = 1E-3

mesh = create_interval(MPI.COMM_WORLD, mesh_n, (mesh_min, mesh_max))

def subdomain_c(x):
    return x[0] <= tol


def subdomain_a(x):
    return x[0] >= -tol

x = mesh.geometry.x
g_nodes = entities_to_geometry(mesh, mesh.topology.dim,  [16], False)
print(g_nodes[0], x[g_nodes[0]][:, 0])
print(subdomain_c(x[g_nodes[0]].T))
print(subdomain_a(x[g_nodes[0]].T))

which shows you that the 16th cell consist of the vertices with coordinate -9.90099010e-06 and 1.98019802e-06
which means that the cell only satisfies subdomain_a for the 1.98019802e-06, while subdomain_c only holds for -9.90099010e-06.

To be marked with locate_entities, the condition has to hold for all vertices.

1 Like

Thank you for clearing this up @dokken! I’ve finally had some time to work on this a bit, however, I am still not 100% happy with how I am handling this. Basically, I see two solutions for my problem.

A) I can assign a magic number to mesh_n, e.g. 102, to make sure that there are vertices at exactly x = 0, in which case my code works as expected. However, magic numbers are usually to be avoided and the code shouldn’t just work for certain combinations of mesh_min, mesh_max and mesh_n.

B) I can set my tolerance to half the cell size, i.e. tol = (mesh_max - mesh_min) / (2 * mesh_n) to capture the vertices properly. However, there is an edge case for a cell with vertices at +tol and -tol, which would return true for both subdomains. Also, I do not necessarily have a mesh with constant cell size at all times, in which case this approach would fail.

I feel like there should be a more elegant way to do what I want, but I am not experienced enough to see it. My question, essentially, is how to implement a robust solution to my problem that doesn’t depend on magic numbers and is free of edge cases.

Since you just want to split the cells in two distinct groups (i.e. you want to mark all cells with a value), you can do as follows:



from dolfinx.mesh import create_interval, locate_entities, meshtags
from dolfinx.io import XDMFFile
from mpi4py import MPI
import numpy as np

# Define tolerance
tol = 1E-14

# Define mesh quantities
mesh_n = 101
mesh_min = -2E-4
mesh_max = 1E-3

mesh = create_interval(MPI.COMM_WORLD, mesh_n, (mesh_min, mesh_max))
num_cells = mesh.topology.index_map(
    mesh.topology.dim).size_local + mesh.topology.index_map(mesh.topology.dim).num_ghosts

cells = np.arange(num_cells, dtype=np.int32)


def subdomain_c(x):
    return x[0] <= tol


A = 5
C = 7
# Mark all cells with A
cell_values = np.full_like(cells, A, dtype=np.int32)
cells_subdomain_c = locate_entities(mesh, mesh.topology.dim, subdomain_c)
cell_values[cells_subdomain_c] = C
cell_tags = meshtags(mesh, mesh.topology.dim, cells, cell_values)

with XDMFFile(mesh.comm, "cell_markers.xdmf", "w") as xdmf:
    xdmf.write_mesh(mesh)
    xdmf.write_meshtags(cell_tags)