Simple periodic boundary condition without dolfinx_mpc

Hello,
I’m new to DolfinX (using the 0.6.0 version), and I wanted to implement a very simple case of Poisson equation with Dirichlet BC on the vertical walls (x=0 and x=1), and with periodic BC on horizontal walls (y=0 and y=1).

Unfortunately I didn’t find anything in the documentation to solve this kind of problem. I saw the dolfinx_mpc library, however it seems to be overpowered in my case.
So I wanted to know if there is an alternative simple and straightforward way to just map my top boundary to the bottom one, as I have a very simple grid.

Here is my code so far (if needed) working for the Dirichlet BC on vertical walls :

import numpy as np
import ufl
from dolfinx import fem, io, mesh, plot
from ufl import ds, dx, grad, inner

from mpi4py import MPI
from petsc4py import PETSc
from petsc4py.PETSc import ScalarType

## Create a simple rectangle mesh
# define geometric and mesh parameters
Lx = 1.0
Ly = 1.0
n_x = 64
n_y = 64
# create the mesh
msh = mesh.create_rectangle(comm=MPI.COMM_WORLD,
                            points=((0.0, 0.0), (Lx, Ly)), n=(n_x, n_y),
                            cell_type=mesh.CellType.triangle,)
V = fem.FunctionSpace(msh, ("Lagrange", 1))

## Define boundaries
v_facets = mesh.locate_entities_boundary(msh, dim=1,
                                                marker=lambda x: np.logical_or(np.isclose(x[0], 0.0),
                                                                               np.isclose(x[0], Lx)))
dofs_v = fem.locate_dofs_topological(V=V, entity_dim=1, entities=v_facets)
Dbc = fem.dirichletbc(value=ScalarType(0), dofs=dofs_v, V=V)

bc = [Dbc]

## Define the variational problem
u = ufl.TrialFunction(V)
v = ufl.TestFunction(V)
x = ufl.SpatialCoordinate(msh)
f = 10 * ufl.exp(-((x[0] - 0.5) ** 2 + (x[1] - 0.5) ** 2) / 0.02)
g = ufl.sin(5 * x[0])
a = inner(grad(u), grad(v)) * dx
L = inner(f, v) * dx + inner(g, v) * ds


## Assemble matrix and vector, applying the boundary conditions
A = fem.petsc.assemble_matrix(fem.form(a), bcs=bc) 
A.assemble()
b = fem.petsc.assemble_vector(fem.form(L))
fem.petsc.apply_lifting(b, [fem.form(a)], bcs=[bc])
b.assemble()
fem.petsc.set_bc(b, bcs=bc)

## Setup solver
u_sol = fem.Function(V) 
solver = PETSc.KSP().create(msh.comm)  
solver.setOperators(A) 
solver.setType(PETSc.KSP.Type.PREONLY) 
solver.getPC().setType(PETSc.PC.Type.LU) 
solver.solve(b, u_sol.vector) 
u_sol.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)

Consider the following workaround (works in serial):