Newton method failed to converge for non-affine geometry when evaluating the function

I met this error when I try to evaluate the funcion V:

V = FunctionSpace(mesh, v_cg2)
u_ = Function(V)
u_.eval(inlet_cylinder_coord, inlet_cylinder_cell)

and inlet_cylinder_coord and inlet_cylinder_cell are defined by:

obstacle_cell = locate_dofs_topological(V, fdim, ft.find(self.obstacle_marker))
inlet_cell = locate_dofs_topological(V, fdim, ft.find(self.inlet_marker))
inlet_cylinder_cell = np.concatenate([inlet_cell, obstacle_cell], axis=0)
inlet_cylinder_coord = mesh.geometry.x[inlet_cylinder_cell]

Here are the full error info:

Traceback (most recent call last):
  File "/home/wulong/pycharm-community-2021.3.3/plugins/python-ce/helpers/pydev/_pydevd_bundle/pydevd_exec2.py", line 3, in Exec
    exec(exp, global_vars, local_vars)
  File "<input>", line 1, in <module>
  File "/home/wulong/anaconda3/envs/drl/lib/python3.10/site-packages/dolfinx/fem/function.py", line 328, in eval
    self._cpp_object.eval(_x, _cells, u)
RuntimeError: Newton method failed to converge for non-affine geometry

And here are the full code:

import gmsh
import numpy as np
import sys
from mpi4py import MPI
from fluid_mechanics.area import *
from petsc4py import PETSc
import dolfinx
from dolfinx import geometry
from dolfinx.fem import (Constant, Function, FunctionSpace,
                         assemble_scalar, dirichletbc, form, locate_dofs_topological, set_bc)
from dolfinx.fem.petsc import (apply_lifting, assemble_matrix, assemble_vector,
                               create_vector, create_matrix, set_bc)
from dolfinx.geometry import BoundingBoxTree, compute_collisions, compute_colliding_cells
from dolfinx.io import (XDMFFile, distribute_entity_data, gmshio)
from utils.encoder import normalization
from ufl import (FacetNormal, FiniteElement, Identity, Measure, TestFunction, TrialFunction, VectorElement,
                 as_vector, div, dot, ds, dx, inner, lhs, grad, nabla_grad, rhs, sym)


class simulation:
    def __init__(self, c_x, c_y, o_x, o_y, r, r2,
                 obstacle_type='cylinder',
                 save_mesh='false',
                 save='false',
                 visualize='false'):

        self.L = 2.2
        self.H = 0.41
        self.c_x = c_x
        self.c_y = c_y
        self.r = r
        self.gdim = 2
        self.model_rank = 0
        self.mesh_comm = MPI.COMM_WORLD
        self.save = save
        self.o_x = o_x
        self.o_y = o_y
        self.r2 = r2
        self.obstacle_type = obstacle_type
        self.save_mesh = save_mesh
        self.visualize = visualize

    def generate_mesh(self):
        # L*H:computational domain's size
        # (c_x,c_y):the coordinates of big cylinder
        # r:radius of the big cylinder
        # r2:radius of the small cylinder
        gmsh.initialize()
        gmsh.option.setNumber('General.Verbosity', 1)
        rectangle = gmsh.model.occ.addRectangle(0, 0, 0, self.L, self.H, tag=1)
        obstacle1 = gmsh.model.occ.addDisk(self.c_x, self.c_y, 0, self.r, self.r)
        pre_fluid = gmsh.model.occ.cut([(self.gdim, rectangle)], [(self.gdim, obstacle1)])
        if self.r2 == 0:
            fluid = pre_fluid
        else:
            if self.obstacle_type == 'cylinder':
                obstacle2 = gmsh.model.occ.addDisk(self.o_x, self.o_y, 0, self.r2, self.r2)
            elif self.obstacle_type == 'rectangular':
                obstacle2 = gmsh.model.occ.addRectangle(self.o_x, self.o_y, 0, self.r2, self.r2)
            fluid = gmsh.model.occ.cut([(self.gdim, rectangle)], [(self.gdim, obstacle2)])

        gmsh.model.occ.synchronize()
        fluid_marker = 1

        volumes = gmsh.model.getEntities(dim=self.gdim)
        assert (len(volumes) == 1)
        gmsh.model.addPhysicalGroup(volumes[0][0], [volumes[0][1]], fluid_marker)
        gmsh.model.setPhysicalName(volumes[0][0], fluid_marker, "Fluid")

        self.inlet_marker, self.outlet_marker, self.wall_marker, self.obstacle_marker = 2, 3, 4, 5
        inflow, outflow, walls, obstacle = [], [], [], []

        boundaries = gmsh.model.getBoundary(volumes, oriented=False)
        for boundary in boundaries:
            center_of_mass = gmsh.model.occ.getCenterOfMass(boundary[0], boundary[1])
            if np.allclose(center_of_mass, [0, self.H / 2, 0]):
                inflow.append(boundary[1])
            elif np.allclose(center_of_mass, [self.L, self.H / 2, 0]):
                outflow.append(boundary[1])
            elif np.allclose(center_of_mass, [self.L / 2, self.H, 0]) or np.allclose(center_of_mass, [self.L / 2, 0, 0]):
                walls.append(boundary[1])
            else:
                obstacle.append(boundary[1])
        gmsh.model.addPhysicalGroup(1, walls, self.wall_marker)
        gmsh.model.setPhysicalName(1, self.wall_marker, "Walls")
        gmsh.model.addPhysicalGroup(1, inflow, self.inlet_marker)
        gmsh.model.setPhysicalName(1, self.inlet_marker, "Inlet")
        gmsh.model.addPhysicalGroup(1, outflow, self.outlet_marker)
        gmsh.model.setPhysicalName(1, self.outlet_marker, "Outlet")
        gmsh.model.addPhysicalGroup(1, obstacle, self.obstacle_marker)
        gmsh.model.setPhysicalName(1, self.obstacle_marker, "Obstacle")

        res_min = self.r / 3
        distance_field = gmsh.model.mesh.field.add("Distance")
        gmsh.model.mesh.field.setNumbers(distance_field, "EdgesList", obstacle)
        threshold_field = gmsh.model.mesh.field.add("Threshold")
        gmsh.model.mesh.field.setNumber(threshold_field, "IField", distance_field)
        gmsh.model.mesh.field.setNumber(threshold_field, "LcMin", res_min)
        gmsh.model.mesh.field.setNumber(threshold_field, "LcMax", 0.25 * self.H)
        gmsh.model.mesh.field.setNumber(threshold_field, "DistMin", self.r)
        gmsh.model.mesh.field.setNumber(threshold_field, "DistMax", 2 * self.H)
        min_field = gmsh.model.mesh.field.add("Min")
        gmsh.model.mesh.field.setNumbers(min_field, "FieldsList", [threshold_field])
        gmsh.model.mesh.field.setAsBackgroundMesh(min_field)

        gmsh.option.setNumber("Mesh.Algorithm", 8)
        gmsh.option.setNumber("Mesh.RecombinationAlgorithm", 2)
        gmsh.option.setNumber("Mesh.RecombineAll", 1)
        gmsh.option.setNumber("Mesh.SubdivisionAlgorithm", 1)
        gmsh.model.mesh.generate(self.gdim)
        gmsh.model.mesh.setOrder(2)
        gmsh.model.mesh.optimize("Netgen")
        mesh, _, ft = gmshio.model_to_mesh(gmsh.model, self.mesh_comm, self.model_rank, gdim=self.gdim)
        ft.name = "facet markers"

        if self.visualize == 'true':
            if '-nopopup' not in sys.argv:
                gmsh.fltk.run()
        if self.save_mesh == 'true':
            gmsh.write("cylinders.msh")

        return mesh, ft

    def compute(self, mesh, ft, probes, jet):
        jet_position = jet.get("position")
        jet_x = jet.get("jet_x")
        jet_y = jet.get("jet_y")
        inlet_cylinder = probes.get("inlet_cylinder")
        outlet = probes.get("outlet")
        probes = probes.get("probes")
        bb_tree = geometry.BoundingBoxTree(mesh, mesh.topology.dim)
        domain = mesh.geometry.x  # The coordinates of all mesh
        jet_cells = []
        jet_points = []
        probes_cells = []
        domain_cells = []
        probes_points_on_pro = []
        domain_points_on_pro = []
        domain_cell_candidates = geometry.compute_collisions(bb_tree, np.array(domain))
        probes_cell_candidates = geometry.compute_collisions(bb_tree, probes.T)
        jet_cell_candidates = geometry.compute_collisions(bb_tree, jet_position.T)
        domain_colliding_cells = geometry.compute_colliding_cells(mesh, domain_cell_candidates, np.array(domain))
        probes_colliding_cells = geometry.compute_colliding_cells(mesh, probes_cell_candidates, probes.T)
        jet_colliding_cells = geometry.compute_colliding_cells(mesh, jet_cell_candidates, jet_position.T)

        for i, point in enumerate(np.array(domain)):
            if len(domain_colliding_cells.links(i)) > 0:
                #print(len(domain_colliding_cells.links(i)))
                domain_points_on_pro.append(point)
                domain_cells.append(domain_colliding_cells.links(i)[0])

        for i, point in enumerate(probes.T):
            if len(probes_colliding_cells.links(i)) > 0:
                probes_points_on_pro.append(point)
                probes_cells.append(probes_colliding_cells.links(i)[0])

        for i, jet_point in enumerate(jet_position.T):
            if len(jet_colliding_cells.links(i)) > 0:
                jet_points.append(jet_point)
                jet_cells.append(jet_colliding_cells.links(i)[0])
        t = 0
        T = 7  # Final time
        dt = 1 / 1600  # Time step size
        self.num_steps = 50
        k = Constant(mesh, PETSc.ScalarType(dt))
        mu = Constant(mesh, PETSc.ScalarType(0.001))  # Dynamic viscosity
        rho = Constant(mesh, PETSc.ScalarType(1))  # Density

        v_cg2 = VectorElement("CG", mesh.ufl_cell(), 2)
        s_cg1 = FiniteElement("CG", mesh.ufl_cell(), 1)
        V = FunctionSpace(mesh, v_cg2)
        Q = FunctionSpace(mesh, s_cg1)

        fdim = mesh.topology.dim - 1

        # Define boundary conditions
        class InletVelocity():
            def __init__(self, t):
                self.t = t

            def __call__(self, x):
                values = np.zeros((2, x.shape[1]), dtype=PETSc.ScalarType)
                values[0] = 4 * 1.5 * x[1] * (0.41 - x[1]) / (0.41 ** 2)
                return values

        class JetVelocity():
            def __init__(self, t, jet_x, jet_y):
                self.t_jet = t
                self.jet_x = jet_x
                self.jet_y = jet_y

            def __call__(self, x):
                values = np.zeros((2, x.shape[1]), dtype=PETSc.ScalarType)
                values[1] = self.jet_y
                index = np.where(x[1, :] < 0.2)
                values[1, index] = -self.jet_y
                values[0] = self.jet_x
                return values
        # Inlet
        u_inlet = Function(V)
        u_jet = Function(V)
        inlet_velocity = InletVelocity(t)
        jet_velocity = JetVelocity(t, jet_x, jet_y)
        u_inlet.interpolate(inlet_velocity)
        u_jet.interpolate(jet_velocity)
        bcu_inflow = dirichletbc(u_inlet, locate_dofs_topological(V, fdim, ft.find(self.inlet_marker)))
        # Jets
        bcu_jets = dirichletbc(u_jet, locate_dofs_topological(V, fdim + 1, jet_cells))
        # Walls
        u_nonslip = np.array((0,) * mesh.geometry.dim, dtype=PETSc.ScalarType)
        bcu_walls = dirichletbc(u_nonslip, locate_dofs_topological(V, fdim, ft.find(self.wall_marker)), V)
        # Obstacle
        bcu_obstacle = dirichletbc(u_nonslip, locate_dofs_topological(V, fdim, ft.find(self.obstacle_marker)), V)
        bcu = [bcu_inflow, bcu_obstacle, bcu_walls, bcu_jets]
        # Outlet
        bcp_outlet = dirichletbc(PETSc.ScalarType(0), locate_dofs_topological(Q, fdim, ft.find(self.outlet_marker)), Q)
        bcp = [bcp_outlet]
        obstacle_cell = locate_dofs_topological(V, fdim, ft.find(self.obstacle_marker))
        inlet_cell = locate_dofs_topological(V, fdim, ft.find(self.inlet_marker))
        inlet_cylinder_cell = np.concatenate([inlet_cell, obstacle_cell], axis=0)
        inlet_cylinder_coord = mesh.geometry.x[inlet_cylinder_cell]
        outlet_cells = locate_dofs_topological(V, fdim, ft.find(self.outlet_marker))
        outlet_coord = mesh.geometry.x[outlet_cells]


        u = TrialFunction(V)
        v = TestFunction(V)
        u_ = Function(V)
        u_.name = "u"
        u_s = Function(V)
        u_n = Function(V)
        u_n1 = Function(V)
        p = TrialFunction(Q)
        q = TestFunction(Q)
        p_ = Function(Q)
        p_.name = "p"
        phi = Function(Q)

        # the first step

        f = Constant(mesh, PETSc.ScalarType((0, 0)))
        F1 = rho / k * dot(u - u_n, v) * dx
        F1 += inner(dot(1.5 * u_n - 0.5 * u_n1, 0.5 * nabla_grad(u + u_n)), v) * dx
        F1 += 0.5 * mu * inner(grad(u + u_n), grad(v)) * dx - dot(p_, div(v)) * dx
        F1 += dot(f, v) * dx
        a1 = form(lhs(F1))
        L1 = form(rhs(F1))
        A1 = create_matrix(a1)
        b1 = create_vector(L1)

        # the second step

        a2 = form(dot(grad(p), grad(q)) * dx)
        L2 = form(-rho / k * dot(div(u_s), q) * dx)
        A2 = assemble_matrix(a2, bcs=bcp)
        A2.assemble()
        b2 = create_vector(L2)

        # the last step

        a3 = form(rho * dot(u, v) * dx)
        L3 = form(rho * dot(u_s, v) * dx - k * dot(nabla_grad(phi), v) * dx)
        A3 = assemble_matrix(a3)
        A3.assemble()
        b3 = create_vector(L3)

        # Solver for step 1
        solver1 = PETSc.KSP().create(mesh.comm)
        solver1.setOperators(A1)
        solver1.setType(PETSc.KSP.Type.BCGS)
        pc1 = solver1.getPC()
        pc1.setType(PETSc.PC.Type.JACOBI)

        # Solver for step 2
        solver2 = PETSc.KSP().create(mesh.comm)
        solver2.setOperators(A2)
        solver2.setType(PETSc.KSP.Type.MINRES)
        pc2 = solver2.getPC()
        pc2.setType(PETSc.PC.Type.HYPRE)
        pc2.setHYPREType("boomeramg")

        # Solver for step 3
        solver3 = PETSc.KSP().create(mesh.comm)
        solver3.setOperators(A3)
        solver3.setType(PETSc.KSP.Type.CG)
        pc3 = solver3.getPC()
        pc3.setType(PETSc.PC.Type.SOR)
        # -

        u_probes = []
        p_probes = []
        inlet_cylinder_probes = []
        outlet_probes = []
        time_list = []
        eval_time = []
        init_domain_v_probes = []
        init_domain_p_probes = []
        xdmf = XDMFFile(mesh.comm, f"active_{jet_position}_{jet_x}_{jet_y}.xdmf", "w")
        xdmf.write_mesh(mesh)
        for i in range(self.num_steps):
            # Update current time step
            t += dt
            # Update inlet velocity
            inlet_velocity.t = t
            u_inlet.interpolate(inlet_velocity)

            # Step 1: Tentative velocity step
            A1.zeroEntries()
            assemble_matrix(A1, a1, bcs=bcu)
            A1.assemble()
            with b1.localForm() as loc:
                loc.set(0)
            assemble_vector(b1, L1)
            apply_lifting(b1, [a1], [bcu])
            b1.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
            set_bc(b1, bcu)
            solver1.solve(b1, u_s.vector)
            u_s.x.scatter_forward()

            # Step 2: Pressure corrrection step
            with b2.localForm() as loc:
                loc.set(0)
            assemble_vector(b2, L2)
            apply_lifting(b2, [a2], [bcp])
            b2.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
            set_bc(b2, bcp)
            solver2.solve(b2, phi.vector)
            phi.x.scatter_forward()

            p_.vector.axpy(1, phi.vector)
            p_.x.scatter_forward()

            # Step 3: Velocity correction step
            with b3.localForm() as loc:
                loc.set(0)
            assemble_vector(b3, L3)
            b3.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
            solver3.solve(b3, u_.vector)
            u_.x.scatter_forward()

            # write the data of probes
            if i / 256 == 0:
                u_values = u_.eval(probes_points_on_pro, probes_cells)
                p_values = p_.eval(probes_points_on_pro, probes_cells)
                u_probes.append(u_values)
                p_probes.append(p_values)
                xdmf.write_function(u_, t)
                xdmf.write_function(p_, t)

            if i == 0:
                #print(V.tabulate_dof_coordinates())
                init_v_values = u_.eval(domain_points_on_pro, domain_cells)
                init_p_values = p_.eval(domain_points_on_pro, domain_cells)
                init_domain_v_probes.append(init_v_values)
                init_domain_p_probes.append(init_p_values)

            if i == self.num_steps - 1:
                inlet_cylinder_values = u_.eval(inlet_cylinder_coord, inlet_cylinder_cell)
                outlet_values = p_.eval(outlet_coord, outlet_cells)
                inlet_cylinder_probes.append(inlet_cylinder_values)
                outlet_probes.append(outlet_values)
            time_list.append(i)
            eval_time.append(i)

        gmsh.clear()
        xdmf.close()
        # Update variable with solution form this time step
        with u_.vector.localForm() as loc_, u_n.vector.localForm() as loc_n, u_n1.vector.localForm() as loc_n1:
            loc_n.copy(loc_n1)
            loc_.copy(loc_n)
        time = np.repeat(np.array(time_list), 4)
        eval_time = np.repeat(np.array(eval_time), len(V.tabulate_dof_coordinates()))
        domain_eval = np.tile(np.array(domain), (50, 1))
        u0 = np.array(u_probes)[:, :, 0]
        u1 = np.array(u_probes)[:, :, 1]
        inlet_u0 = np.array(inlet_cylinder_probes)[:, :, 0]
        inlet_u1 = np.array(inlet_cylinder_probes)[:, :, 1]
        init_u0 = np.array(init_domain_v_probes)[:, :, 0]
        init_u1 = np.array(init_domain_v_probes)[:, :, 1]
        probes_points_on_pro = np.tile(np.transpose(probes_points_on_pro), 50)
        obstacle_cell = locate_dofs_topological(V, fdim, ft.find(self.obstacle_marker))
        inlet_cell = locate_dofs_topological(V, fdim, ft.find(self.inlet_marker))
        inlet_cylinder_cell = np.concatenate([inlet_cell, obstacle_cell], axis=0)
        inlet_cylinder_coord = mesh.geometry.x[inlet_cylinder_cell]


        probes = {
            "p": np.array(p_probes).reshape(-1, 1).flatten(),
            "u0": u0.reshape(-1, 1).flatten(),
            "u1": u1.reshape(-1, 1).flatten(),
            "t": time,
            "x": probes_points_on_pro[0],
            "y": probes_points_on_pro[1]
        }

        jet_config = {
            "jet_x": jet.get("jet_x"),
            "jet_y": jet.get("jet_y")
        }

        domain_train = {
            "x": domain[:, 0],
            "y": domain[:, 1],
            "z": domain[:, 2]
        }

        cylinder_inlet = {
            "x": inlet_cylinder_coord[:, 0],
            "y": inlet_cylinder_coord[:, 1],
            "u0": inlet_u0.reshape(-1, 1).flatten(),
            "u1": inlet_u1.reshape(-1, 1).flatten()
        }
        outlet = {
            "x": outlet_coord[:, 0],
            "y": outlet_coord[:, 1],
            "p": np.array(outlet_probes).reshape(-1, 1).flatten()
        }

        IC_0_1 = {
            "x": domain[:, 0],
            "y": domain[:, 1],
            "z": domain[:, 2],
            "p": np.zeros((len(domain))),
            "u0": init_u0.reshape(-1, 1).flatten(),
            "u1": init_u1.reshape(-1, 1).flatten(),
        }
        eval = {
            "x": domain_eval[:, 0],
            "y": domain_eval[:, 1],
            "t": eval_time
        }
        return cylinder_inlet, outlet, domain_train, IC_0_1, probes, jet_config, eval

if __name__ == "__main__":
    from fluid_mechanics.area import *
    from math import sin, cos, pi
    import os
    import pandas as pd

    dataset_dir = "datasets"
    ic_dir = "datasets/initial"
    probes_dir = "datasets/probe"

    if not os.path.exists(dataset_dir):
        os.makedirs(dataset_dir)

    if not os.path.exists(probes_dir):
        os.makedirs(probes_dir)

    if not os.path.exists(ic_dir):
        os.makedirs(ic_dir)

    r = 0.05
    x1 = r * cos(pi / 2) + 0.3
    y1 = r * sin(pi / 2) + 0.2
    x2 = r * cos(3 / 2 * pi) + 0.3
    y2 = r * sin(3 * pi / 2) + 0.2
    jet_positions = [(x1, y1, 0), (x2, y2, 0)]
    jet_coordinates = positions(jet_positions)
    jet = {
        "jet_x": 0,
        "jet_y": 0,
        "position": jet_coordinates
    }
    input_points = pinn_inlet()
    outlet_points = pinn_outlet()
    cylinder_points = pinn_cylinder()
    inlet_cylinder_points = add_points(input_points, cylinder_points)
    probes_points = circle_probes(0.05, 4)
    points = {
        "inlet_cylinder": inlet_cylinder_points,
        "outlet": outlet_points,
        "probes": probes_points
    }

    jet_x = jet.get("jet_x")
    jet_y = jet.get("jet_y")
    simu = simulation(c_x=0.3, c_y=0.2, o_x=0.3, o_y=0.2, r=0.05, r2=0.02)
    mesh, ft = simu.generate_mesh()
    cylinder_inlet, outlet, domain_train, IC_0_1, probes, jet_config, eval = simu.compute(mesh=mesh, ft=ft,
                                                                                                 probes=points,
                                                                                                 jet=jet)



This is not correct, as locate_dofs_topological returns a degree of freedom not a cell.
If you want the cells connected to ft.find(self.inlet_marker) you should use

        inlet_cells = dolfinx.mesh.compute_incident_entities(domain, ft.find(self.inlet_marker), domain.topology.dim-1, domain.topology.dim)

which would give you a list of all cells connected to inlet.

As your example is way to long (and not executable), I cant give you any further guidance.

maybe you could explain why you want to use a point evaluation at some point at the inlet?

Hi dokken,I want to log the value of cells which are marked with facets during computation by u_.eval().
And now I met some problem: The output of
inlet_index = dolfinx.mesh.compute_incident_entities(domain, ft.find(self.inlet_marker),domain.topology.dim-1, domain.topology.dim), print(domain[inlet_index]) is unexpected,which is far from my inlet boundary.

domain[inlet_index] is not the correct usage, as inlet_index is a list of cells, not mesh vertices.

Again, for me to give further comments, you would have to drastically simplify your example.

Here are the easy example:


import gmsh
import numpy as np
import dolfinx
from mpi4py import MPI
from petsc4py import PETSc
from dolfinx.cpp.mesh import to_type, cell_entity_type
from dolfinx import geometry
from dolfinx.fem import (Constant, Function, FunctionSpace,
                         assemble_scalar, dirichletbc, form, locate_dofs_topological, set_bc)
from dolfinx.fem.petsc import (apply_lifting, assemble_matrix, assemble_vector,
                               create_vector, create_matrix, set_bc)
from dolfinx.graph import create_adjacencylist
from dolfinx.geometry import BoundingBoxTree, compute_collisions, compute_colliding_cells
from dolfinx.io import (XDMFFile, distribute_entity_data, gmshio)
from dolfinx.mesh import create_mesh, meshtags_from_entities

from ufl import (FacetNormal, FiniteElement, Identity, Measure, TestFunction, TrialFunction, VectorElement,
                 as_vector, div, dot, ds, dx, inner, lhs, grad, nabla_grad, rhs, sym)

gmsh.initialize()

L = 2.2
H = 0.41
c1_x = c1_y = 0.2
r1 = 0.05
gdim = 2
mesh_comm = MPI.COMM_WORLD
model_rank = 0
fluid_marker = 1
if mesh_comm.rank == model_rank:
    rectangle = gmsh.model.occ.addRectangle(0, 0, 0, L, H, tag=1)
    obstacle = gmsh.model.occ.addDisk(c1_x, c1_y, 0, r1, r1)
    fluid = gmsh.model.occ.cut([(gdim, rectangle)], [(gdim, obstacle)])
    gmsh.model.occ.synchronize()
    volumes = gmsh.model.getEntities(dim=gdim)
    assert (len(volumes) == 1)
    gmsh.model.addPhysicalGroup(volumes[0][0], [volumes[0][1]], fluid_marker)
    gmsh.model.setPhysicalName(volumes[0][0], fluid_marker, "Fluid")
inlet_marker, outlet_marker, wall_marker, obstacle_marker = 2, 3, 4, 5
inflow, outflow, walls, obstacle = [], [], [], []
if mesh_comm.rank == model_rank:
    boundaries = gmsh.model.getBoundary(volumes, oriented=False)
    for boundary in boundaries:
        center_of_mass = gmsh.model.occ.getCenterOfMass(boundary[0], boundary[1])
        if np.allclose(center_of_mass, [0, H / 2, 0]):
            inflow.append(boundary[1])
        elif np.allclose(center_of_mass, [L, H / 2, 0]):
            outflow.append(boundary[1])
        elif np.allclose(center_of_mass, [L / 2, H, 0]) or np.allclose(center_of_mass, [L / 2, 0, 0]):
            walls.append(boundary[1])
        else:
            obstacle.append(boundary[1])
    gmsh.model.addPhysicalGroup(1, walls, wall_marker)
    gmsh.model.setPhysicalName(1, wall_marker, "Walls")
    gmsh.model.addPhysicalGroup(1, inflow, inlet_marker)
    gmsh.model.setPhysicalName(1, inlet_marker, "Inlet")
    gmsh.model.addPhysicalGroup(1, outflow, outlet_marker)
    gmsh.model.setPhysicalName(1, outlet_marker, "Outlet")
    gmsh.model.addPhysicalGroup(1, obstacle, obstacle_marker)
    gmsh.model.setPhysicalName(1, obstacle_marker, "Obstacle")

res_min = r1 / 3
if mesh_comm.rank == model_rank:
    distance_field = gmsh.model.mesh.field.add("Distance")
    gmsh.model.mesh.field.setNumbers(distance_field, "EdgesList", obstacle)
    threshold_field = gmsh.model.mesh.field.add("Threshold")
    gmsh.model.mesh.field.setNumber(threshold_field, "IField", distance_field)
    gmsh.model.mesh.field.setNumber(threshold_field, "LcMin", res_min)
    gmsh.model.mesh.field.setNumber(threshold_field, "LcMax", 0.25 * H)
    gmsh.model.mesh.field.setNumber(threshold_field, "DistMin", r1)
    gmsh.model.mesh.field.setNumber(threshold_field, "DistMax", 2 * H)
    min_field = gmsh.model.mesh.field.add("Min")
    gmsh.model.mesh.field.setNumbers(min_field, "FieldsList", [threshold_field])
    gmsh.model.mesh.field.setAsBackgroundMesh(min_field)

if mesh_comm.rank == model_rank:
    gmsh.option.setNumber("Mesh.Algorithm", 8)
    gmsh.option.setNumber("Mesh.RecombinationAlgorithm", 2)
    gmsh.option.setNumber("Mesh.RecombineAll", 1)
    gmsh.option.setNumber("Mesh.SubdivisionAlgorithm", 1)
    gmsh.model.mesh.generate(gdim)
    gmsh.model.mesh.setOrder(2)
    gmsh.model.mesh.optimize("Netgen")

mesh, _, ft = gmshio.model_to_mesh(gmsh.model, mesh_comm, model_rank, gdim=gdim)

bb_tree = geometry.BoundingBoxTree(mesh, mesh.topology.dim)
ft.name = "Facet markers"

domain = mesh.geometry.x
inlet_index = dolfinx.mesh.compute_incident_entities(mesh, ft.find(inlet_marker), mesh.topology.dim-1, mesh.topology.dim)
inlet_coord = domain[inlet_index]

t = 0
T = 8  # Final time
dt = 1 / 1600  # Time step size
num_steps = int(T / dt)
k = Constant(mesh, PETSc.ScalarType(dt))
mu = Constant(mesh, PETSc.ScalarType(0.001))  # Dynamic viscosity
rho = Constant(mesh, PETSc.ScalarType(1))  # Density

v_cg2 = VectorElement("CG", mesh.ufl_cell(), 2)
s_cg1 = FiniteElement("CG", mesh.ufl_cell(), 1)
V = FunctionSpace(mesh, v_cg2)
Q = FunctionSpace(mesh, s_cg1)

fdim = mesh.topology.dim - 1


# Define boundary conditions
class InletVelocity():
    def __init__(self, t):
        self.t = t

    def __call__(self, x):
        values = np.zeros((gdim, x.shape[1]), dtype=PETSc.ScalarType)
        values[0] = 4 * 1.5 * np.sin(self.t * np.pi / 8) * x[1] * (0.41 - x[1]) / (0.41 ** 2)
        return values


# Inlet
u_inlet = Function(V)
inlet_velocity = InletVelocity(t)
u_inlet.interpolate(inlet_velocity)
bcu_inflow = dirichletbc(u_inlet, locate_dofs_topological(V, fdim, ft.find(inlet_marker)))
# Walls
u_nonslip = np.array((0,) * mesh.geometry.dim, dtype=PETSc.ScalarType)
bcu_walls = dirichletbc(u_nonslip, locate_dofs_topological(V, fdim, ft.find(wall_marker)), V)
# Obstacle
bcu_obstacle = dirichletbc(u_nonslip, locate_dofs_topological(V, fdim, ft.find(obstacle_marker)), V)
bcu = [bcu_inflow, bcu_obstacle, bcu_walls]
# Outlet
bcp_outlet = dirichletbc(PETSc.ScalarType(0), locate_dofs_topological(Q, fdim, ft.find(outlet_marker)), Q)
bcp = [bcp_outlet]


u = TrialFunction(V)
v = TestFunction(V)
u_ = Function(V)
u_.name = "u"
u_s = Function(V)
u_n = Function(V)
u_n1 = Function(V)
p = TrialFunction(Q)
q = TestFunction(Q)
p_ = Function(Q)
p_.name = "p"
phi = Function(Q)


f = Constant(mesh, PETSc.ScalarType((0, 0)))
F1 = rho / k * dot(u - u_n, v) * dx
F1 += inner(dot(1.5 * u_n - 0.5 * u_n1, 0.5 * nabla_grad(u + u_n)), v) * dx
F1 += 0.5 * mu * inner(grad(u + u_n), grad(v)) * dx - dot(p_, div(v)) * dx
F1 += dot(f, v) * dx
a1 = form(lhs(F1))
L1 = form(rhs(F1))
A1 = create_matrix(a1)
b1 = create_vector(L1)

# Next we define the second step

a2 = form(dot(grad(p), grad(q)) * dx)
L2 = form(-rho / k * dot(div(u_s), q) * dx)
A2 = assemble_matrix(a2, bcs=bcp)
A2.assemble()
b2 = create_vector(L2)

# We finally create the last step

a3 = form(rho * dot(u, v) * dx)
L3 = form(rho * dot(u_s, v) * dx - k * dot(nabla_grad(phi), v) * dx)
A3 = assemble_matrix(a3)
A3.assemble()
b3 = create_vector(L3)

# +
# Solver for step 1
solver1 = PETSc.KSP().create(mesh.comm)
solver1.setOperators(A1)
solver1.setType(PETSc.KSP.Type.BCGS)
pc1 = solver1.getPC()
pc1.setType(PETSc.PC.Type.JACOBI)

# Solver for step 2
solver2 = PETSc.KSP().create(mesh.comm)
solver2.setOperators(A2)
solver2.setType(PETSc.KSP.Type.MINRES)
pc2 = solver2.getPC()
pc2.setType(PETSc.PC.Type.HYPRE)
pc2.setHYPREType("boomeramg")

# Solver for step 3
solver3 = PETSc.KSP().create(mesh.comm)
solver3.setOperators(A3)
solver3.setType(PETSc.KSP.Type.CG)
pc3 = solver3.getPC()
pc3.setType(PETSc.PC.Type.SOR)


for i in range(50):
    # Update current time step
    t += dt
    # Update inlet velocity
    inlet_velocity.t = t
    u_inlet.interpolate(inlet_velocity)

    # Step 1: Tentative velocity step
    A1.zeroEntries()
    assemble_matrix(A1, a1, bcs=bcu)
    A1.assemble()
    with b1.localForm() as loc:
        loc.set(0)
    assemble_vector(b1, L1)
    apply_lifting(b1, [a1], [bcu])
    b1.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
    set_bc(b1, bcu)
    solver1.solve(b1, u_s.vector)
    u_s.x.scatter_forward()

    # Step 2: Pressure corrrection step
    with b2.localForm() as loc:
        loc.set(0)
    assemble_vector(b2, L2)
    apply_lifting(b2, [a2], [bcp])
    b2.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
    set_bc(b2, bcp)
    solver2.solve(b2, phi.vector)
    phi.x.scatter_forward()

    p_.vector.axpy(1, phi.vector)
    p_.x.scatter_forward()

    # Step 3: Velocity correction step
    with b3.localForm() as loc:
        loc.set(0)
    assemble_vector(b3, L3)
    b3.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
    solver3.solve(b3, u_.vector)
    u_.x.scatter_forward()

    inlet_values = u_.eval(inlet_coord, inlet_index)



I met the same non-affine geometry problem when I interpolate a function from a mesh onto another one in 2nd order 3D meshes. However, it cannot be reproduced in a pair of simpilified meshes. How can we find the problematic cells to cause the problem in the meshes? Or any other recommend methods to locate the problem?

@Chris_Richardson made some changes to the gjk algorithm to support coplanar cells (affine second order cells) in: Use boost multiprecision for GJK by chrisrichardson · Pull Request #3770 · FEniCS/dolfinx · GitHub

The other option, which hasn’t been fixed, would be to expose the nokaffine pullback tolerance in eval:

I have been using v0.9.0 for a while. Indeed, switching to v0.10.0 without changing of API resolves my non-affine geometry in my test case. But the speed of nonmatching meshes interpolation in v0.10.0 seems to be slower. Is my feeling correct?

The collision routines uses higher precision floats to be more accurate, so it might be a bit slower.
I’ve also made a PR for exposing the pull-back parameters in: