Hello,
I’m trying to understand the best way to identify subdomains defined from a level set function on dolfinx. Briefly speaking, I have a reference domain D and the domain of interest Omega is defined as x in D such that phi(x) < 0.
When an analytical expression of phi is know, i’m using “locate_entities” from dolfinx.mesh (“strategy1” in the example bellow). However, assuming that the expression of phi is not known, the only approach I was able to figure out was using ufl conditional (“strategy2” in the example). Although error with conditional is smaller, the behavior with mesh refinement seems a bit strange.
Is there any problems with this use of “conditional”? I wonder if there is a better approach when no analytical expression of phi is know.
I appreciate any help!
import numpy as np
from dolfinx import fem
from dolfinx.mesh import (locate_entities, create_rectangle, CellType, meshtags)
from mpi4py import MPI
from ufl import Measure, conditional, ge,dx
from matplotlib import pyplot as plt
import dolfinx.cpp as _cpp
def tag_subdomains(msh, subdomains): # Identifies and marks subdomains accoring to locator function
cell_indices, cell_markers = [], [] # List for facet indices and respective markers
fdim = msh.topology.dim
for (marker, locator) in subdomains:
cells = locate_entities(msh, fdim, locator)
cell_indices.append(cells)
cell_markers.append(np.full_like(cells, marker))
cell_indices = np.hstack(cell_indices).astype(np.int32)
cell_markers = np.hstack(cell_markers).astype(np.int32)
sorted_cells = np.argsort(cell_indices)
cell_tag = meshtags(msh, fdim, cell_indices[sorted_cells], cell_markers[sorted_cells])
return cell_tag
tol = 1e-8
# simple example with circle inside a square
def phi(x):
return np.sqrt((x[0] - 0.5)**2 + (x[1]-0.5)**2) - radius
subdomains = [(1, lambda x: phi(x) >= tol),
(0, lambda x: phi(x) <= tol)]
N_list = np.array([5,10,20,40,80,160,320])
err = []; err2 = []
for N in N_list:
comm = MPI.COMM_WORLD
msh = create_rectangle(comm=MPI.COMM_WORLD,
points=((0.0, 0.0), (1.0, 1.0)), n=(N, N),
cell_type=CellType.triangle, diagonal = _cpp.mesh.DiagonalType.crossed)
# Strategy 1: analytical expression
cell_tag = tag_subdomains(msh, subdomains)
dx = Measure("dx", domain=msh, subdomain_data=cell_tag)
one = fem.Constant(msh, 1.0)
area = 1 - np.pi*radius**2
err.append(np.abs(area - fem.assemble_scalar(fem.form(one*dx(1)))))
# Strategy 2: if no analytical expression is known
dx = Measure("dx", domain=msh)
V = fem.FunctionSpace(msh, ("CG", 1))
phi_ = fem.Function(V)
phi_.interpolate(phi)
err2.append(np.abs(area - fem.assemble_scalar(fem.form(conditional(ge(phi_, tol), 1, 0)*dx))))
plt.plot(1/N_list,err, label = 'strategy 1'); plt.plot(1/N_list,err2, label = 'strategy 2'); plt.grid()
plt.yscale("log"); plt.xscale("log")
plt.xlabel('h'); plt.ylabel('error'); plt.legend()