Periodic boundary condition with DG and MPC

So at the moment I don’t have such a MWE (but I would like to ^^!).

However I modified the script of the post https://fenicsproject.discourse.group/t/discontinuous-1d-eigenvector-using-dg-elements/10491 to use the function assemble_matrix provided by dolfinx_mpc together with an empty MultiPointConstraint instance:

# From https://fenicsproject.discourse.group/t/discontinuous-1d-eigenvector-using-dg-elements/10491
from dolfinx.fem import FunctionSpace, form, Function,dirichletbc,locate_dofs_topological
from dolfinx.mesh import meshtags,locate_entities, create_interval
from ufl import TestFunction,TrialFunction,Measure,inner,grad,jump,avg,Circumradius, FacetNormal
from dolfinx_mpc import MultiPointConstraint, assemble_matrix
import numpy as np
from slepc4py import SLEPc
from mpi4py import MPI

def OneDimensionalSetup(n_elem, x_MP = 0.2, r_outer = 0.25):
    # 1 ------------------------------------ 2
    #                 x=r_outer
    """ This function builds one dimensional setup.
        For boundaries, Tag 1 specifies left end and Tag 2 specifies right end. 
    Returns:
        mesh, subdomains, facet_tags
    """
    mesh = create_interval(MPI.COMM_WORLD, n_elem, [0.0, r_outer])
    def subdomain_func(x, x_MP=x_MP, eps=1e-16):
        x = x[0]
        return np.logical_and(x_MP - eps <= x, x <= x_MP + eps)
    tdim = mesh.topology.dim
    marked_cells = locate_entities(mesh, tdim, subdomain_func)
    fl = 0
    subdomains = meshtags(mesh, tdim, marked_cells, np.full(len(marked_cells), fl, dtype=np.int32))
    boundaries = [  (0, lambda x: np.isclose(x[0], x_MP)),
                    (1, lambda x: np.isclose(x[0], 0)),
                    (2, lambda x: np.isclose(x[0], r_outer))]
    facet_indices, facet_markers = [], []
    fdim = mesh.topology.dim - 1
    for (marker, locator) in boundaries:
        facets = locate_entities(mesh, fdim, locator)
        facet_indices.append(facets)
        facet_markers.append(np.full(len(facets), marker))
    facet_indices = np.array(np.hstack(facet_indices), dtype=np.int32)
    facet_markers = np.array(np.hstack(facet_markers), dtype=np.int32)
    sorted_facets = np.argsort(facet_indices)
    facet_tags = meshtags(mesh, fdim, facet_indices[sorted_facets], facet_markers[sorted_facets])
    return mesh, subdomains, facet_tags

n_elem = 50
mesh, subdomains, facet_tags = OneDimensionalSetup(n_elem)
dx = Measure("dx", domain=mesh, subdomain_data=subdomains)
ds = Measure("ds", domain=mesh, subdomain_data=facet_tags)
dS = Measure("dS", domain=mesh, subdomain_data=facet_tags)
V = FunctionSpace(mesh, ("DG", 1))

# Dirichlet boundary conditions for left and right ends (equals to zero)
fdim = mesh.topology.dim-1
u_bc_left = Function(V)
u_bc_right = Function(V)
facets_left = np.array(facet_tags.indices[facet_tags.values == 1])
facets_right = np.array(facet_tags.indices[facet_tags.values == 2])
dofs_left = locate_dofs_topological(V, fdim, facets_left)
dofs_right = locate_dofs_topological(V, fdim, facets_right)
bc_left = dirichletbc(u_bc_left, dofs_left)
bc_right = dirichletbc(u_bc_right, dofs_right)
bcs=[bc_left,bc_right]

u = TrialFunction(V)
v = TestFunction(V)
n = FacetNormal(mesh)
h = 2 * Circumradius(mesh)
c = 347
alpha = 10
gamma = 10
h_avg = avg(h)

mass  = -c**2 * (inner(grad(u), grad(v))*dx + inner(inner(grad(u), n), v) * ds)
# Add DG/IP terms
mass +=  c**2 * (inner(jump(u, n), avg(grad(v)) )*dS + inner(avg(grad(u)), jump(v, n))*dS)
mass += -c**2*gamma/h_avg*inner(jump(u, n), jump(v, n))*dS
# Add Nitsche terms
mass +=  c**2*(inner(u, inner(grad(v), n)) * ds - alpha / h * inner(u, v) * ds)

mass_form = form(mass)

mpc = MultiPointConstraint(V)
mpc.finalize()

A = assemble_matrix(mass_form, mpc, bcs=bcs)
A.assemble()

stiffness = inner(u , v) * dx
stiffnes_form = form(stiffness)

C = assemble_matrix(stiffnes_form, mpc)
C.assemble()

target = 694*2*np.pi

nev=6

# This function prints the results of the eps_solver1 function below
def results(E):
    print("******************************")
    print("*** SLEPc Solution Results ***")
    print("******************************")
    print("Number of iterations of the method: %d" % E.getIterationNumber())
    print("Solution method: %s" % E.getType())
    nev, ncv, mpd = E.getDimensions()
    print("Number of requested eigenvalues: %d" % nev)
    print("Stopping condition: tol=%.4g, maxit=%d" % (E.getTolerances()))
    print("Number of converged eigenpairs %d" % E.getConverged())
    if E.getConverged() > 0:
        print()
    for i in range(E.getConverged()):
        k = E.getEigenvalue(i)
        w = np.sqrt(k)
        f = w/np.pi/2
        print("%6f, %6f" % (f.real, f.imag))
    print()

# This function solves eigensystem
def eps_solver1(A, C, target, nev, two_sided=False, print_results=False):
    E = SLEPc.EPS().create(MPI.COMM_WORLD)
    C = - C
    E.setOperators(A, C)
    st = E.getST()
    st.setType('sinvert')
    E.setTarget(target)
    E.setWhichEigenpairs(SLEPc.EPS.Which.TARGET_MAGNITUDE)  # TARGET_REAL or TARGET_IMAGINARY
    E.setDimensions(nev, SLEPc.DECIDE)
    E.setTolerances(1e-15)
    E.setFromOptions()
    E.solve()
    if print_results:
        results(E)
    return E

E = eps_solver1(A, C, target**2, nev=nev, print_results= True)

vr = Function(V)
vi = Function(V)

ind = 0
E.getEigenvector(ind, vr.vector, vi.vector)

import matplotlib.pyplot as plt
fig, ax = plt.subplots(2, figsize=(12, 6))
ax[0].plot(V.tabulate_dof_coordinates()[:, 0], vr.x.array.real)
ax[1].plot(V.tabulate_dof_coordinates()[:, 0], vr.x.array.imag)
plt.show()

Since mpc is empty, I expect to get the same eigenvalues and eigenvectors as in the original post. However, the computed eigenvector looks to be quite spurious. However if do the same thing with a “CG” FunctionSpace I obtain the same eigenvectors with and without an empty mpc.

PS: I use doflinx in complex mode.