How generate polygonal meshes in DOLFIN-X? And how generate triangle submesh on each polygonal elements

Well, that is because your mesh is disjoint. You havent actually combined the different polygons into a single mesh (there are duplicate facets).
You can verify this by picking one of the “interior” surfaces, for instance with:

from mpi4py import MPI

import numpy as np
from ufl import dS
from dolfinx.fem import (
    assemble_scalar,
    dirichletbc,
    form,
)
from dolfinx.mesh import exterior_facet_indices, meshtags
from dolfinx.io import gmshio
import gmsh

# import functions1
import dolfinx

currentmesh = "mesh9"  # Change  N and the filename for different refinements
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
gmsh.open(currentmesh + ".msh")
model = gmsh.model
model.setCurrent(currentmesh)


msh, cell_markers, facet_markers = gmshio.model_to_mesh(
    gmsh.model, MPI.COMM_WORLD, 0, gdim=2
)

with dolfinx.io.XDMFFile(MPI.COMM_WORLD, "output.xdmf", "w") as xdmf:
    xdmf.write_mesh(msh)
    xdmf.write_meshtags(facet_markers, msh.geometry)
dS_c = dS(domain=msh, subdomain_data=facet_markers, subdomain_id=60)
print(assemble_scalar(form(1 * dS_c)))

num_facets_in_mesh = (
    msh.topology.index_map(msh.topology.dim - 1).size_local
    + msh.topology.index_map(msh.topology.dim - 1).num_ghosts
)
facets = np.arange(num_facets_in_mesh, dtype=np.int32)
values = np.zeros_like(facets)
values[exterior_facet_indices(msh.topology)] = 1
ext = meshtags(msh, msh.topology.dim - 1, facets, values)
with dolfinx.io.XDMFFile(MPI.COMM_WORLD, "exterior_facets.xdmf", "w") as xdmf:
    xdmf.write_mesh(msh)
    xdmf.write_meshtags(ext, msh.geometry)

giving


For further posts, please remove all unused code.

Hi Dokken,

Thank you for your answer. Your answers are always helpful.
I tried this code, but I always both “facets” and “ext” remain identical. This is due to the using of distinct meshtags for neighbouring facets. So for this reason, the code distinguishes between facets in adjacent subdomains. The rationale behind using two separate meshtags is to utilize a Discontinuous Galerkin (DG) method on polygons while applying a continuous Galerkin (CG) method on the submesh. If I unify the meshtags on neighbouring subdomains, then I am always using CG even over the polygonals. I think what I need to do is to unify the facets with identical coordinates. Which I don’t know how to do that. I am not sure that I can generate a mesh with different meshtags on gmsh or even be able to unify the facets with identical coordinates in FeniCS.

The mesh itself needs to be continuous, you cannot have disconnected regions in the mesh and expect them to magically connect through the dS measure, as that is an integral measure over the facets of the mesh which is connected to two cells. For your problem, it is likely that you want to use sub-meshes for each of the polygonal domains, and then have a mixed assembly routine coupling these. @jpdean might have some demos that illustrate this.

Thank you for your answer. I think in this case it is possible to add the jumps manually to the assembled matrix. Do you think it is possible to generate a mesh in gmsh which has different meshtags for two neighbouring subdomains but have a unique index for the facets? Then is it compatible with FeniCS?

Yes, that is possible.
Mark each subdomain with a given physical surface, then the interfaces can either be marked by gmsh, or by comparing neighbouring subdomain surface cell tags in dolfinx to build the interior facet markers.

Thank you always your comments are helpful. How can I restrict dS on a set of facets not all the internal facets, I add the boundary of the subdomains to the internal facets. But I only want to integral over the boundary of the subdomains (which I have their index). In fact I have

tdim = msh.topology.dim
fdim = tdim - 1
boundary_facets = mesh.exterior_facet_indices(msh.topology)

which If I subtract the facet_markers :

msh, cell_markers, facet_markers = gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=2)

gives the facets of the boundary of the subdomains except the global boundary \Omega=[0,1]\times[0,1].

To me it is a bit unclear what information you have at the moment.
Do you have a mesh where each subdomain is marked with unique integer, and some markers over the (interior) boundary?

This is my mesh:

In fact using this mesh the facet_markers only include the global boundary:

msh, cell_markers, facet_markers = gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=2)

But still I can access the internal subdomain boundaries by

tdim = msh.topology.dim
fdim = tdim - 1
boundary_facets = mesh.exterior_facet_indices(msh.topology)

This is an example of what I am doing:



import sys
import pyvista
import math
from dolfinx.io import XDMFFile, gmshio
from dolfinx.io.gmshio import model_to_mesh
from dolfinx.plot import vtk_mesh
sys.path.insert(0, 'meshes')
#import gmsh
import matplotlib.pyplot as plt
import numpy as np
import numpy.linalg as npla
from scipy.sparse.linalg import spsolve
import scipy.sparse as sps

import ufl
from dolfinx import fem
from dolfinx import default_real_type, mesh
from ufl import FacetNormal, SpatialCoordinate, ds, dx, grad, CellDiameter, inner, dot, as_ufl #Unified form language pieces
from ufl import (FacetNormal, FiniteElement, Identity, Measure, TestFunction, TrialFunction, VectorElement, TensorElement,
                 as_vector, div, dot, dS,ds, dx, inner, outer, lhs, grad, nabla_grad, rhs, sym, jump, avg)
from dolfinx.fem import (Expression, Constant, Function, FunctionSpace, 
                         assemble_scalar, dirichletbc, form, locate_dofs_geometrical, locate_dofs_topological)
from dolfinx.fem.petsc import assemble_matrix, assemble_vector, LinearProblem
from dolfinx.mesh import create_unit_square, locate_entities, meshtags

import numpy
import gmsh
from mpi4py import MPI
from petsc4py import PETSc
#import functions1
import dolfinx
from dolfinx.io import XDMFFile
#from dolfinx.plot import create_vtk_mesh
import dolfinx.io  
from dolfinx import io  

currentmesh = "mesh10"    # Change  N and the filename for different refinements
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
gmsh.open(currentmesh + ".msh")
model = gmsh.model
model.setCurrent(currentmesh)

print('Model ' + model.getCurrent() + ' (' + str(model.getDimension()) + 'D)')

# Define mesh

msh, cell_markers, facet_markers = io.gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=2)
n = FacetNormal(msh)
degree=1
V = FunctionSpace(msh, ("CG", degree)) 

tdim = msh.topology.dim
fdim = tdim - 1
u, v = TrialFunction(V), TestFunction(V)
msh.topology.create_connectivity(fdim, tdim)

boundaries = [(1, lambda x: np.isclose(x[0], 0)),
              (2, lambda x: np.isclose(x[0], 1)),
              (3, lambda x: np.isclose(x[1], 0)),
              (4, lambda x: np.isclose(x[1], 1))]
facet_indices1, facet_markers1 = [], []
fdim = msh.topology.dim - 1

for (marker, locator) in boundaries:
    facets = locate_entities(msh, fdim, locator)
    facet_indices1.append(facets)
    facet_markers1.append(np.full_like(facets, marker))
facet_indices1 = np.hstack(facet_indices1).astype(np.int32)
facet_markers1 = np.hstack(facet_markers1).astype(np.int32)
sorted_facets = np.argsort(facet_indices1)
facet_tag = meshtags(msh, fdim, facet_indices1[sorted_facets], facet_markers1[sorted_facets])
msh.topology.create_connectivity(msh.topology.dim-1, msh.topology.dim)
ds = Measure("ds", domain=msh, subdomain_data=facet_tag)   
class BoundaryCondition():
    def __init__(self, type, marker, values):
        self._type = type
        if type == "Dirichlet":
            u_D = Function(V)
            u_D.interpolate(values)
            facets = facet_tag.find(marker)
            dofs = locate_dofs_topological(V, fdim, facets)
            self._bc = dirichletbc(u_D, dofs)
        elif type == "Neumann":
                self._bc = inner(values, v) * ds(marker)
        elif type == "Robin":
            self._bc = values[0] * inner(u-values[1], v)* ds(marker)
        else:
            raise TypeError("Unknown boundary condition: {0:s}".format(type))
    @property
    def bc(self):
        return self._bc

    @property
    def type(self):
        return self._type
u_1 = Function(V)
u_1.interpolate(lambda x: 0*x[1]**0)
boundary_conditions = [BoundaryCondition("Dirichlet", 1, u_1),
                       BoundaryCondition("Dirichlet", 2, u_1),
                       BoundaryCondition("Dirichlet", 3, u_1),
                       BoundaryCondition("Dirichlet", 4, u_1),
                       ]
#####################################################################################################################
jump_grad_u = dot(grad(u)('+'),n('+'))-dot(grad(u)('-'),n('-'))
jump_grad_v = dot(grad(v)('+'),n('+'))-dot(grad(v)('-'),n('-'))


jump_u = u('+')*n('+')+u('-')*n('-')
jump_v = v('+')*n('+')+v('-')*n('-')

epsilon=10.0**(-2)
alpha = dolfinx.fem.Constant(msh, 1.0)
h = CellDiameter(msh)
dS= Measure("dS", domain=msh, subdomain_data=facet_markers)
###################################################################################

a = epsilon*dot(grad(u), grad(v))*dx+ u * v * dx- epsilon*inner(avg(grad(v)), jump_u) * dS + epsilon*inner(jump_v, avg(grad(u))) * dS+epsilon*(alpha / avg(h))* epsilon* inner(jump_u, jump_v) * dS#+epsilon*(alpha / avg(h))* epsilon* inner(jump_u, jump_v) * dS  

LL= dolfinx.fem.Constant(msh, 1.0) * v * dx
F=a-LL
bcs = []
for condition in boundary_conditions:
    if condition.type == "Dirichlet":
        bcs.append(condition.bc)
    else:
        F += condition.bc
a = lhs(F)
L = rhs(F)
problem = LinearProblem(a, L,  bcs=bcs, petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
    
uh = problem.solve()

I want only integrate over internal subdomain boundaries.

Please start by inspecting your cell and facet markers.
Neither represent the information you would require:


import dolfinx
import gmsh
from mpi4py import MPI
currentmesh = "mesh10"    # Change  N and the filename for different refinements
gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
gmsh.open(currentmesh + ".msh")
model = gmsh.model
model.setCurrent(currentmesh)

print('Model ' + model.getCurrent() + ' (' + str(model.getDimension()) + 'D)')

# Define mesh

msh, cell_markers, facet_markers = dolfinx.io.gmshio.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0, gdim=2)

with dolfinx.io.XDMFFile(msh.comm, "mesh.xdmf", "w") as xdmf:
    xdmf.write_mesh(msh)
    xdmf.write_meshtags(cell_markers, msh.geometry)
    xdmf.write_meshtags(facet_markers, msh.geometry)


What would be preferable is to mark each of the polygonal sub-domains with a unique value.

In this mesh I marked each of the polygonal sub-domains with a unique value.

I found it is wrong to add the subdomain boundary facets in gmsh to the internal facets. Because then the test function is not computed correctly. Since the test function which is define on a subdomain is not zero on the neighbouring subdomain. Then I don’t know is there anyway to compute the jump over two facets with two different indices to compute

- epsilon*inner(avg(grad(v)), jump_u) * dS + epsilon*inner(jump_v, avg(grad(u))) * dS+epsilon*(alpha / avg(h))* epsilon* inner(jump_u, jump_v) * dS

I am using this mesh with the above FeniCS code which I sent before.

If you want something with discontinuous patches, then I would suggest that you do the following:

  1. Add subdomain boundaries in GMSH to make a single, conforming mesh
  2. Use dolfinx.mesh.create_submesh to create the polygonal patches of interest with appropriate function spaces.
  3. Use the mixed dimensional assembler for codim-0 to couple the different equations, see for instance: Add mixed-domain vector assembly by jpdean · Pull Request #3087 · FEniCS/dolfinx · GitHub