How to set multiple BCs using general boundary condition class in fenicsx?

Hello,

I have a problem with applying multiple constant b.c. in Fenicsx (fenics-dolfinx 0.7.0.dev0). I have inspired from this example: Setting multiple Dirichlet, Neumann, and Robin conditions — FEniCSx tutorial

I am trying to simulate a 2D plate that is being subjected to tension. The bottom and top ends have Neumman boundary conditions, and I have also added a node in the top right corner of the plate with a Dirichlet boundary condition to ensure stability but the uh is obtained as ([inf, inf, inf, inf, …]).

I think, I made an error in assigning the Dirichlet boundary condition to the node. Could you please let me know if you have any suggestions?

I used to work with fenics and mshr and I was able to apply such BC as:
class BoundaryC1(SubDomain):
def inside(self, x, on_boundary):
return on_boundary and near(x[0], 1, 0.01) and near(x[1], 1, 0.01)

Thanks!

Here is the code for the example that I am attempting to solve:

import numpy as np

from dolfinx.fem import (Constant,  Function, FunctionSpace, assemble_scalar, 
                         dirichletbc, form, locate_dofs_topological)
from dolfinx.fem.petsc import LinearProblem
from dolfinx.mesh import create_unit_square, locate_entities, meshtags
from mpi4py import MPI
from petsc4py.PETSc import ScalarType
from ufl import (FacetNormal, Measure, SpatialCoordinate, TestFunction, TrialFunction,  div, dot, dx, grad, inner, lhs, rhs)



mesh = create_unit_square(MPI.COMM_WORLD, 10, 10)


x = SpatialCoordinate(mesh)

# Define physical parameters 
f=Constant(mesh, ScalarType(0))
kappa = Constant(mesh, ScalarType(1))

# Define function space and standard part of variational form
V = FunctionSpace(mesh, ("CG", 1))
u, v = TrialFunction(V), TestFunction(V)
F = kappa * inner(grad(u), grad(v)) * dx - inner(f, v) * dx



boundaries = [(1, lambda x: np.isclose(x[1], 0)),
              (2, lambda x: np.isclose(x[1], 1)),
              (3, lambda x: np.isclose(x[0],1) & np.isclose(x[1],1).all())]


g1=Constant(mesh, ScalarType(-1))
g2=Constant(mesh, ScalarType(-1))
u1= lambda x: x[1]+x[0]-2



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_like(facets, marker))
facet_indices = np.hstack(facet_indices).astype(np.int32)
facet_markers = np.hstack(facet_markers).astype(np.int32)
sorted_facets = np.argsort(facet_indices)
facet_tag = meshtags(mesh, fdim, facet_indices[sorted_facets], facet_markers[sorted_facets])


    
    
    
ds = Measure("ds", domain=mesh, 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)
        else:
            raise TypeError("Unknown boundary condition: {0:s}".format(type))
    @property
    def bc(self):
        return self._bc

    @property
    def type(self):
        return self._type
    
    
b1 = Constant(mesh, ScalarType(0))    

# Define the BC

boundary_conditions = [BoundaryCondition("Neumann", 1, g1),
                        BoundaryCondition("Neumann", 2, g2),
                        BoundaryCondition("Dirichlet", 3, u1)]

bcs = []
for condition in boundary_conditions:
    if condition.type == "Dirichlet":
        bcs.append(condition.bc)
    else:
        F += condition.bc
        
# Solve linear variational problem
a = lhs(F)
L = rhs(F)
problem = LinearProblem(a, L, bcs=bcs, petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
uh = problem.solve()

The problem is that a node is not a facet, and thus you find no facet (line segment) whose two endpoints satisfies

You would need to replace fdim (1) with 0 for single node constraints

Thanks Dokken for your explanation. I could apply the constrain on the single node.

Can you please also help me with the method to apply a constraint in only one direction? For instance, I would like to constrain the node located at the top right corner of the plate in the x direction only.

Can the obtained displacement in magnitude (uh) be broken down into separate x and y components for each node?
I have problem with computing strain since “ufl.nabla_grad(uh).T” gives error as ValueError: Transposed is only defined for rank 2 tensors.

See
https://jsdokken.com/dolfinx-tutorial/chapter3/component_bc.html

Without an example code reproducing the issue, Im not sure how to interpret this.

Thanks Dokken for the example.

I used your example to simulate uniaxial tension. Please see below the code.

When the values of L and H are equal, I obtain the expected symmetric results. However, when L and H are not equal, the results are asymmetrical and appear unusual. Could you please check my code to see where is the problem?

Here are examples for H=L=1 and H=2, L=1

from dolfinx.fem import (Constant, dirichletbc, Function, FunctionSpace, locate_dofs_geometrical,
                         locate_dofs_topological)
from dolfinx.fem.petsc import LinearProblem
from dolfinx.mesh import CellType, create_rectangle, locate_entities_boundary
from ufl import Identity, Measure, TestFunction, TrialFunction, VectorElement, dot, dx, inner, grad, nabla_div, sym
from mpi4py import MPI
from petsc4py.PETSc import ScalarType

import numpy as np
import pyvista


L = 1
H = 2
lambda_ = 1.25 
mu = 1
rho = 1
g = 1

mesh = create_rectangle(MPI.COMM_WORLD, np.array([[0,0],[L, H]]), [30,30], cell_type=CellType.triangle)
element = VectorElement("CG", mesh.ufl_cell(), 1)
V = FunctionSpace(mesh, element)


bcs=[]


tol=0.000000001
boundaries_n = [(1, lambda x: np.isclose(x[1], 0,tol)),
              (2, lambda x: np.isclose(x[1], H,tol)),
              (3, lambda x: np.isclose(x[0], 0,tol)),
              (4, lambda x: np.isclose(x[0], L,tol))]

g1=Constant(mesh, ScalarType((0,-1)))
g2=Constant(mesh, ScalarType((0,1)))
g3=Constant(mesh, ScalarType((0,0)))
g4=Constant(mesh, ScalarType((0,0)))


from dolfinx.mesh import locate_entities, meshtags

facet_indices, facet_markers = [], []
fdim = mesh.topology.dim - 1
for (marker, locator) in boundaries_n:
    facets = locate_entities(mesh, fdim, locator)
    print(facets)
    facet_indices.append(facets)
    facet_markers.append(np.full_like(facets, marker))
facet_indices = np.hstack(facet_indices).astype(np.int32)
facet_markers = np.hstack(facet_markers).astype(np.int32)
sorted_facets = np.argsort(facet_indices)
facet_tag = meshtags(mesh, fdim, facet_indices[sorted_facets], facet_markers[sorted_facets])


from ufl import Measure
ds = Measure("ds", domain=mesh, subdomain_data=facet_tag)

def epsilon(u):
    return sym(grad(u)) 
def sigma(u):
    return lambda_ * nabla_div(u) * Identity(len(u)) + 2*mu*epsilon(u)

u = TrialFunction(V)
v = TestFunction(V)
f = Constant(mesh, ScalarType((0, 0)))
a = inner(sigma(u), epsilon(v)) * dx
L = dot(f, v) * dx + inner(g1, v) * ds(1)+inner(g2, v) * ds(2)+inner(g3, v) * ds(3)+inner(g4, v) * ds(4)

problem = LinearProblem(a, L, bcs=bcs, petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
uh = problem.solve()

Without any strong enforcement of boundary conditions for linear elasticity, the solution space includes all rigid motions. You should add this nullspace to your matrix, or constrain a sufficient amount of degrees of freedom (2) to avoid rigid motions. The nullspace can be found at: https://github.com/FEniCS/dolfinx/blob/3cf9b8b9b20f3b02e02aa6af57d85a0f225f40a9/python/test/unit/la/test_krylov_solver.py#L65-L84
and should be set to the linear operator A, which you can get as problem.a.setNullSpace(ns) prior to solving.

1 Like

Thanks for the example.
I will try to add this null space to the matrix.

I was thinking to apply a symmetry B.C. by simulating only half of the plate, applying traction on one side, and using symmetry boundary conditions on the other side.

Do you think this would solve the problem? Is there any example of apply symmetry boundary conditions in Fenicsx?

Symmetry usually is enforced by only enforcing a zero deformation in the normal direction, which usually aligns with a sub space direction, meaning that you can enforce it in the sub space Define Symmetry Boundary Conditions - #9 by kamilova-maths

1 Like