Examples and tutorials » Mesh geometry examples » Constructing a distance approximation from a Poisson field

Mesh three implicit disks, solve a signed auxiliary problem and inspect its normalization error.

The geometric reference

Let $D=[0,1]^2$ contain three disks with centers and radii

CenterRadius
$(0.75,0.25)$ $0.05$
$(0.25,0.25)$ $0.25$
$(0.75,0.75)$ $0.10$

The largest disk touches the square at two points. The disks have disjoint interiors. Their union has signed distance

\[ d(x)=\min_{i=1,2,3}\bigl(\lVert x-c_i\rVert-R_i\bigr), \]

negative inside and positive outside. This analytic field supplies both the implicit geometry and a reference for the normalized output.

    RealFunction exact([](const Point& point) {
      return std::min(
        {std::hypot(point.x() - Real(0.75), point.y() - Real(0.25)) - Real(0.05),
          std::hypot(point.x() - Real(0.25), point.y() - Real(0.25)) - Real(0.25),
          std::hypot(point.x() - Real(0.75), point.y() - Real(0.75)) - Real(0.10)});
    });

Resolve and label the interface

The background has 32 vertices per axis. Its nodal $P_1$ interpolation approximates the signed-distance field. MMG cuts the zero level set of that interpolant and remeshes with maximum requested edge size $0.02$ .

    {
      P1 initialSpace(mesh);
      MMG::RealGridFunction levelSet(initialSpace);
      levelSet = exact;
      mesh = MMG::LevelSetDiscretizer()
               .split(BaseMaterial, {Inside, Outside})
               .setBoundaryReference(Interface)
               .setAngleDetection()
               .setHMax(MaximumSize)
               .discretize(levelSet);
    }
    mesh.getConnectivity().compute(1, 2);

The material split explicitly assigns inside cells attribute 3, outside cells attribute 2 and interface edges attribute 5. The field used to drive remeshing is scoped to that operation. The following solve uses a new space on the returned mesh.

In the checked run, the result has 3,852 vertices and 7,458 triangles. Its inside area is $0.2339284$ , compared with the analytic disk area $\pi\sum_iR_i^2=0.2356194$ . Refining the background interpolation is part of controlling this geometric discrepancy; remeshing a fixed interpolant retains its zero-set approximation.

Solve the auxiliary problem

With $\Omega_-$ the mesh's inside region and $\Gamma_h$ its marked interface, the scalar potential satisfies

\[ -\Delta u=1-2\chi_{\Omega_-}\quad\text{in }D, \qquad u=0\quad\text{on }\Gamma_h. \]

The exterior boundary portions have the homogeneous Neumann condition of the weak formulation. The force is negative inside and positive outside. The fitted interface provides the essential condition that anchors each region. The corresponding constrained stiffness matrix is symmetric positive definite.

The program constructs this problem explicitly so that CG convergence and the assembled residual can be checked before normalization. The same attributed forcing is used by SignedPoisson.

    P1 space(mesh);
    TrialFunction u(space);
    TestFunction v(space);
    Problem potential(u, v);
    potential = Integral(Grad(u), Grad(v)) + Integral(2 * v).over(Inside) - Integral(v) +
      DirichletBC(u, Zero()).on(Interface);
    Solver::CG solver(potential);
    constexpr Real LinearTolerance = Real(1e-10);
    solver.setTolerance(LinearTolerance).solve();
    if (!solver.success())
      return EXIT_FAILURE;
    const auto& system = potential.getLinearSystem();
    const Real relativeResidual =
      (system.getOperator() * system.getSolution() - system.getVector()).norm() /
      std::max(Real(1), system.getVector().norm());
    constexpr Real ResidualTolerance = Real(1e-9);
    if (!std::isfinite(relativeResidual) || relativeResidual > ResidualTolerance)
      return EXIT_FAILURE;
    const auto& poisson = u.getSolution();
    auto normalized = Distance::SpaldingTucker()(poisson);
    GridFunction reference(space);
    reference = exact;

SpaldingTucker evaluates

\[ d_{ST}=\frac{2u}{\lVert\nabla u\rVert+ \sqrt{\lVert\nabla u\rVert^2+2|u|}} \]

and interpolates it into the input space. A positive denominator preserves the sign of the potential pointwise. The expression requires a defined gradient and becomes undefined when the potential and its gradient vanish together. The executable checks the resulting nodal values for finiteness.

Inspect the approximation

Image
Analytic nodal distance on the fitted mesh.
Image
Normalized signed Poisson field.
Image
Absolute difference of the two nodal fields.

The two signed fields use the same color range, $[-0.55,0.55]$ . The error range is $[0,0.12]$ . Dark contours show the meshed interface. Dense triangle edges are omitted to keep the scalar fields readable.

The checked local run produced:

QuantityValue
Maximum absolute nodal error $0.110375$
Arithmetic mean absolute nodal error $0.0191554$
Maximum normalized value on interface vertices $0$
$\lVert Au-b\rVert_2/\max(1,\lVert b\rVert_2)$ $1.13\times10^{-12}$

The arithmetic mean is over mesh vertices and differs from an area-weighted error norm. The maximum occurs on the upper boundary near $(0.2000,1)$ , where the normalized value is $0.391290$ and the reference value is $0.501665$ .

The normalization has an intrinsic multidimensional approximation error. For a single disk with the exact radial interior potential $u(r)=(r^2-R^2)/4$ , the center value becomes $d_{ST}(0)=-R/\sqrt2$ , whereas the signed distance is $-R$ . For $R=0.25$ , this gives an absolute center discrepancy of $0.0732233$ before any discretization. The three-disk computation also includes interface approximation, finite element gradients and the outer Neumann boundary. Distance accuracy is assessed through the reference comparison in addition to the linear residual.

Build and inspect the output

Build the SpaldingTucker target and run it in a scratch directory. It generates its input mesh and writes DistanceNormalization.xdmf with its HDF5 companions. The arrays are potential, normalized and reference. DistanceNormalization.csv records mesh counts, inside area, nodal errors, interface values and the relative algebraic residual.

The program requires CG success, a residual below $10^{-9}$ , finite nodal fields, nonempty inside/outside regions and interface values within $10^{-12}$ of zero. The recorded distance errors describe this approximation and geometry.

Complete program

The complete program is available in SpaldingTucker.cpp.

Continue