Imposition boundary condition problem

Hi,

I am experiencing an unexpected issue in implementing a solution for a benchmark problem in the context of Stokes Equations (flow in a driven cavity, slipping condition on the upper boundary with u=1).

I built the weak formulation and the solver and I imposed the value of pressure on (0,0) in order to make the problem well-posed. Unfortunately, the results we are having is still nan for both pressure and velocities.

Could you help me detect the issue in our code?
I think the problem is in the imposition of the Boundary conditions or in the implementation of the direct solver.
Unfortunately there is no enough documentation to solve this kind of issue
Thank you very much!

#importing the packages 

import numpy as np

# import ufl
from dolfinx import fem, io, mesh, plot
from dolfinx.fem import (Function, FunctionSpace, dirichletbc, form,
                         locate_dofs_geometrical, locate_dofs_topological)
from dolfinx.mesh import (CellType, GhostMode, create_rectangle,
                          locate_entities_boundary)
from ufl import ds, dx, SpatialCoordinate, \
                sin, cos, dot, \
                grad, inner, div, FacetNormal, \
                TrialFunctions, TestFunctions, as_vector, as_tensor,\
                VectorElement, FiniteElement, CellDiameter
                
import matplotlib.pyplot as plt

from mpi4py import MPI
from petsc4py import PETSc

import gmsh
from dolfinx.io import gmshio

I think you send a minimal working example, as the code you have supplied only has the import statements.

You are right, sorry, here is the full code

#importing the packages 

import numpy as np
# import ufl
from dolfinx import fem, io, mesh, plot
from dolfinx.fem import (Function, FunctionSpace, dirichletbc, form,
                         locate_dofs_geometrical, locate_dofs_topological)
from dolfinx.mesh import (CellType, GhostMode, create_rectangle,
                          locate_entities_boundary)
from ufl import ds, dx, SpatialCoordinate, \
                sin, cos, dot, \
                grad, inner, div, FacetNormal, \
                TrialFunctions, TestFunctions, as_vector, as_tensor,\
                VectorElement, FiniteElement, CellDiameter
                
import matplotlib.pyplot as plt

from mpi4py import MPI
from petsc4py import PETSc

import gmsh
from dolfinx.io import gmshio

######################################
# Lid-Driven Cavity Scenario:
#                             ------>>>>> u_top
#                                  border 3
#           1 +-------------------------------------------------+
#             |                                                 |
#             |             *                      *            |
#             |          *           *    *    *                |
#         0.8 |                                                 |
#             |                                 *               |
#             |     *       *                                   |
#             |                      *     *                    |
#         0.6 |                                            *    |
# u = 0       |      *                             *            |   u = 0
# v = 0       |                             *                   |   v = 0
# border 4    |                     *                           |  border 2
#             |           *                *         *          |
#         0.4 |                                                 |
#             |                                                 |
#             |      *            *             *               |
#             |           *                             *       |
#         0.2 |                       *           *             |
#             |                               *                 |
#             |  *          *      *                 *       *  |
#             |                            *                    |
#           0 +-------------------------------------------------+
#             0        0.2       0.4       0.6       0.8        1
#                                     u = 0
#                                     v = 0
#                                   border 1

###########################

########### Creation of the mesh  ##################

# DEFINITION OF THE MESH - type 1

meshsize= 0.1
gmsh.initialize()
gmsh.option.setNumber("Mesh.Algorithm", 1)
gmsh.option.setNumber("Mesh.MeshSizeMin", meshsize)
gmsh.option.setNumber("Mesh.MeshSizeMax", meshsize)
gmsh.model.occ.addRectangle(0, 0, 0, 1, 1, tag=1)
gmsh.model.occ.synchronize()
gmsh.model.addPhysicalGroup(2, [1], tag=2)
gmsh.model.mesh.generate(2)

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

#####################################

########### Definition of the functional spaces ###############

P2 = VectorElement("CG", msh.ufl_cell(), 2)
P1 = FiniteElement("CG",msh.ufl_cell(), 1)

V0, Q0 = FunctionSpace(msh, P2), FunctionSpace(msh, P1)

MIXED_SPACE = fem.FunctionSpace(msh, P2*P1)

V, _ = MIXED_SPACE.sub(0).collapse()
Q, _ = MIXED_SPACE.sub(1).collapse()


# Definition of fixed boundaries
def fixed_boundary(x):
    return np.logical_or(np.logical_or(np.isclose(x[0], 0.0),
                                       np.isclose(x[0], 1.0)),
                         np.isclose(x[1], 0.0))

# Definition of the lid
def lid(x):
    return np.isclose(x[1], 1.0)
    
####### Definition of the boundary conditions #########

# Lid boundary condition

class LidVelocity():
    def __call__(self, x):
        values = np.zeros((2, x.shape[1]),dtype=PETSc.ScalarType)
        values[0] = np.ones((1,x.shape[1]), dtype=PETSc.ScalarType)
        values[1]= np.zeros((1,x.shape[1]), dtype=PETSc.ScalarType)
        return values

lid_velocity= LidVelocity()


# interpolation of the given velocity in the FEM space 
lid_u= fem.Function(V)
lid_u.interpolate(lid_velocity)
facets_lid = mesh.locate_entities_boundary(msh, 1, lid)

# set the non-homogeneous dirichlet boundary condition
dofs_lid = fem.locate_dofs_topological((MIXED_SPACE.sub(0), V0), 1, facets_lid)
bc1 = fem.dirichletbc(lid_u, dofs_lid, MIXED_SPACE.sub(0))


# No slip boundary condition
fixed = fem.Function(V) # QUESTO IN AUTOMATICO TI METTE NOSLIP = 0? 
facets = mesh.locate_entities_boundary(msh, 1, fixed_boundary)
dofs = fem.locate_dofs_topological((MIXED_SPACE.sub(0), V0), 1, facets)
bc0 = fem.dirichletbc(fixed, dofs, MIXED_SPACE.sub(0))


# Since for this problem the pressure is only determined up to a
# constant, we pin the pressure at the point (0, 0)
zero = Function(Q)
with zero.vector.localForm() as zero_local:
    zero_local.set(0.0)
dofs = locate_dofs_geometrical((MIXED_SPACE.sub(1), Q),
                               lambda x: np.isclose(x.T, [0, 0, 0]).all(axis=1))
bc2 = dirichletbc(zero, dofs, MIXED_SPACE.sub(1))

#define the final boundary conditions
bcs = [bc0, bc1, bc2]

################ Definition of the variational problem #####################
(u, p) = TrialFunctions(MIXED_SPACE)
(v, q) = TestFunctions(MIXED_SPACE)
zero = fem.Constant(msh, 0.0)
f = fem.Function(V)
# f.x.array[:] = 0.0
f= fem.Constant(msh, (0.0, 0.0))


a = fem.form((inner(grad(u), grad(v)) - div(v)*p - q*div(u)) * dx)                
L = fem.form((inner(f, v))*dx)


# Assemble LHS matrix and RHS vector
A = fem.petsc.assemble_matrix(a, bcs=bcs)
A.assemble()
b = fem.petsc.assemble_vector(L)

# Implement boundary conditions in the right hand side
fem.petsc.apply_lifting(b, [a], bcs=[bcs]) # inhomgeneous dirichlet boundary
b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) #update right hand side
fem.petsc.set_bc(b, bcs) # Set Dirichlet boundary condition values in the RHS

# Create and configure solver
ksp = PETSc.KSP().create(msh.comm)
ksp.setOperators(A)
ksp.setType("preonly")
ksp.getPC().setType("lu")
ksp.getPC().setFactorSolverType("superlu_dist")

# compute the solution and split it
U = fem.Function(MIXED_SPACE)
ksp.solve(b, U.vector)

# Split the mixed solution and collapse
u_h = U.sub(0).collapse()
p_h = U.sub(1).collapse()
uh_x, uh_y = u_h.split()

# Compute norms
norm_u_3 = u_h.vector.norm()
norm_p_3 = p_h.vector.norm()
if MPI.COMM_WORLD.rank == 0:
    print("(D) Norm of velocity coefficient vector (monolithic, direct): {}".format(norm_u_3))
    print("(D) Norm of pressure coefficient vector (monolithic, direct): {}".format(norm_p_3))
assert np.isclose(norm_u_3, norm_p_3)

################ PLOTTING THE PRESSURE ###################################################
import pyvista
cells, types, geometry = plot.create_vtk_mesh(Q0)
grid = pyvista.UnstructuredGrid(cells, types, geometry)
grid.point_data["p"] = p_h.x.array.real
grid.set_active_scalars("p")
plotter = pyvista.Plotter()
plotter.add_mesh(grid, show_edges=False)
plotter.view_xy()
plotter.show(screenshot='pressure.png')
###########################################################################################

##### SAVING THE DATA
# Plotting the mesh and solution, copy+paste for the exercises!
with io.XDMFFile(msh.comm, "pressure_out.xdmf", "w") as file:
    file.write_mesh(msh)
    file.write_function(p_h)

with io.XDMFFile(msh.comm, "velocity_out.xdmf", "w") as file:
    file.write_mesh(msh)
    file.write_function(u_h)
    
print('Computation is over! Success!')

I cannot reproduce this with dolfinx/dolfinx:nightly docker image.
Also consider comparing the matrix and vector norms with the dolfinx Stokes tutorial:

You can either replace your gmsh with the built in mesh that is used there, or try using your mesh in that code.

Hi, thanks for your answer.

We used that piece of code to define our problem again and the solver eventually reached a solution, though, weirdly, the demo gives an error for the monolithic direct solver and the non blocked matrix solver.
Specifically, it return NaN again and inf as norm of both pressure and velocity. Thus, the demo has some problems for those specific solvers and yields results only for the iterative and nested one. Would you guess why that happens?

Nonetheless, we happen to have quite different results when it comes to considering different definition of the meshes. Find attached three possibilities and hereafter the code snippets. The more we try to refine the mesh on the boundary the more we find ourselves with huge pressures that seem to be non-physical. How would you address this issue, provided that the demo itself has some problems?

refined mesh in gmesh was defined as follows (plotted size for fine mesh size 0.1 and 0.01)


gmsh.initialize()
gmsh.model.add("refined")
fine_dim = 0.01 #pictures include 0.1
coarse_dim= 0.1
gmsh.model.geo.addPoint(0, 0, 0, coarse_dim, 1)
gmsh.model.geo.addPoint(1, 0, 0, coarse_dim, 2)
gmsh.model.geo.addPoint(0, 1, 0, fine_dim, 3)
gmsh.model.geo.addPoint(1, 1, 0, fine_dim, 4)

gmsh.model.geo.addLine(1, 2, 1)
gmsh.model.geo.addLine(2, 4, 2)
gmsh.model.geo.addLine(4, 3, 3)
gmsh.model.geo.addLine(3, 1, 4)

gmsh.model.geo.addCurveLoop([1, 2, 3, 4], 1)
gmsh.model.geo.addPlaneSurface([1], 1)
gmsh.model.geo.synchronize()

gmsh.model.addPhysicalGroup(1, [1, 2, 4], 5)
gmsh.model.addPhysicalGroup(2, [1], name = "My surface")
gmsh.model.mesh.generate(2)

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

I was not able to reproduce that in docker. How did you install dolfinx, what version are you using?
What version of petsc are you using?

We have installed the last release of fenicsx on VirtualBox. The mesh is defined with

from dolfinx.io import gmshio
import gmsh

but, apart from that, the code is exactly the same as the one you sent
What are your results for the demo code?
Where could be the bug in our dolfinx version?

Regarding the difference in the pressure solution with mesh refinement, this is entirely expected. The lid driven cavity problem as you’ve defined it is ilposed since you have a discontinuity in the boundary conditions at the top left and top right corners. The pressure is thereby singular at these points. As you refine the mesh closer to these singularities you will resolve them at a rate \backsim\mathcal{O}(h). A more realistic x-component for the lid boundary condition would be some form of parabola.

1 Like

Did you use conda/docker or any other method to install it?
What is the output of

import dolfinx
print(dolfinx.__version__, dolfinx.git_commit_hash)
import petsc4py
print(petsc4py.__version__)

The output of the demo (using docker, command: docker run -ti --network=host -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v $(pwd):/fenics/shared -w /fenics/shared --rm --shm-size=512m dolfinx/dolfinx:v0.5.2) is:

(B) Norm of velocity coefficient vector (blocked, iterative): 17.4783701591756
(B) Norm of pressure coefficient vector (blocked, iterative): 311.44253798166875
(C) Norm of velocity coefficient vector (blocked, direct): 17.478370157081297
(C) Norm of pressure coefficient vector (blocked, direct): 311.44253797757824
(D) Norm of velocity coefficient vector (monolithic, direct): 17.4783701570813
(D) Norm of pressure coefficient vector (monolithic, direct): 311.59141819977174

Immagine WhatsApp 2022-12-18 ore 10.28.03
Thank you for your answer. We did not use nor Docker nor Conda, but installed an Ubuntu Virtual Machine using Virtual Box and then ran the sudo apt get install commands in Ubuntu terminal as available on GitHub - FEniCS/dolfinx: Next generation FEniCS problem solving environment

I updated the ubuntu repository so to be able to have dolfinx 0.5.2 as you have shown, the PETSc version remains the one shown in the previous screen. The results are still infinite for direct solvers. Could you guess why that happens? Is there an issue with the PETSc version?

It is probably an issue with PETSc, as I cannot reproduce it.