Boundary conditions (all direction or only x)

HI ALL,

im setting a comined boundary conditions on a rode(direchlet and neumann)
for some reason i only set direchlet condition on x direction via para view it seems to be moving from the initial location and not staying in the same location , any idea why?
im using the code of the documantion of the hyperelastic material

when im setting the direclet condition on all on the directions its being clamped and not moving
attaching also screenshots of what i mean

import fenics as fe
from dolfin import *
import matplotlib.pyplot as plt
import os


# Optimization options for the form compiler
parameters["form_compiler"]["cpp_optimize"] = True
parameters["form_compiler"]["representation"] = "uflacs"

E1, E2, nu = 1e5, 6e5, 0.35

# Create mesh and define function space
length, width, height = 1.0, 0.2, 0.2  # Dimensions of the rod
num_elements = 10  # Number of elements along each dimension
mesh = BoxMesh(Point(0, 0, 0), Point(length, width, height), num_elements, 4, 4)


#mesh = UnitCubeMesh(24, 16, 16)


V = VectorFunctionSpace(mesh, "Lagrange", 1)


class Omega_0(SubDomain):
    def inside(self, x, on_boundary):
        return x[0] <= 1/2 + DOLFIN_EPS


class Omega_1(SubDomain):
    def inside(self, x, on_boundary):
        return x[0] > 1/2 - DOLFIN_EPS


subdomain_0 = Omega_0()
subdomain_1 = Omega_1()
materials = MeshFunction('size_t', mesh, mesh.topology().dim())
materials.set_all(0)
subdomain_0.mark(materials, 1)
subdomain_1.mark(materials, 2)


# Boundary conditions

boundary_parts = MeshFunction('size_t', mesh, mesh.topology().dim()-1)
left = AutoSubDomain(lambda x: near(x[0], 0))
right = AutoSubDomain(lambda x: near(x[0], 1))

left.mark(boundary_parts, 1)
right.mark(boundary_parts, 2)

bc1 = DirichletBC(V.sub(0), Constant(0), boundary_parts, 1)
#bc2 = DirichletBC(V.sub(1), Constant(0), boundary_parts, 1)
#bc3 = DirichletBC(V.sub(2), Constant(0), boundary_parts, 1)
bcs = [bc1]

neumann = Expression(("0.1*x[0]"), degree=1)
ds = Measure('ds', domain=mesh, subdomain_data=boundary_parts)

class K(fe.UserExpression):
    def __init__(self, materials, k_0, k_1, **kwargs):
        super().__init__(**kwargs)
        self.materials = materials
        self.k_0 = k_0
        self.k_1 = k_1

    def eval_cell(self, values, x, cell):
        if self.materials[cell.index] == 1:
            values[0] = self.k_0
        else:
            values[0] = self.k_1

    def value_shape(self):
        return ()


E_global = K(materials, E1, E2, degree=0)


DG = FunctionSpace(mesh, "DG", 2)
materials_function = Function(DG)
E_values = [E1, E2]
materials_function = project(E_global, DG)

MU1, LAMBDA1 = Constant(E1/(2*(1 + nu))), Constant(E1*nu/((1 + nu)*(1 - 2*nu)))
MU2, LAMBDA2 = Constant(E2/(2*(1 + nu))), Constant(E2*nu/((1 + nu)*(1 - 2*nu)))

class MUMU(fe.UserExpression):
    def __init__(self, materials, MU_0, MU_1, **kwargs):
        super().__init__(**kwargs)
        self.materials = materials
        self.MU_0 = MU_0
        self.MU_1 = MU_1

    def eval_cell(self, values, x, cell):
        if self.materials[cell.index] == 1:
            values[0] = self.MU_0
        else:
            values[0] = self.MU_1

    def value_shape(self):
        return ()


MU = MUMU(materials, MU1, MU2, degree=0)


class lamabada(fe.UserExpression):
    def __init__(self, materials, lam_0, lam_1, **kwargs):
        super().__init__(**kwargs)
        self.materials = materials
        self.lam_0 = lam_0
        self.lam_1 = lam_1

    def eval_cell(self, values, x, cell):
        if self.materials[cell.index] == 1:
            values[0] = self.lam_0
        else:
            values[0] = self.lam_1

    def value_shape(self):
        return ()


LAMBDA = lamabada(materials, LAMBDA1, LAMBDA2, degree=0)

# Define functions
du = TrialFunction(V)            # Incremental displacement
v = TestFunction(V)             # Test function
u = Function(V)                 # Displacement from previous iteration
B = Constant((0.0, 0.0, 0.0))  # Body force per unit volume
T = Constant((0.0, 0.0, 0.0))  # Traction force on the boundary

#u.vector()[:] = 0.0
# Define an initial guess as an Expression
initial_guess = Expression(("x[0]", "0", "0"), degree=1)

# Project or interpolate the initial guess onto u
u.interpolate(initial_guess)


# Kinematics
d = len(u)
I = Identity(d)             # Identity tensor
F = I + grad(u)             # Deformation gradient
C = F.T*F                   # Right Cauchy-Green tensor
CL = F*F.T

# Invariants of deformation tensors
Ic = tr(C)
J = det(F)
# CS=  MU*(C-I)         # cauchy stress
# S= J*CS*inv(F).T

# Stored strain energy density (compressible neo-Hookean model)
psi = (MU/2)*(Ic - 3) - MU*ln(J) + (LAMBDA/2)*(ln(J))**2

n = FacetNormal(mesh)
# Total potential energy
Pi = psi*dx - dot(B, u)*dx - neumann * dot(n, u)*ds(2)


# Compute first variation of Pi (directional derivative about u in the direction of v)
F = derivative(Pi, u, v)

# Compute Jacobian of F
Je = derivative(F, u, du)

# Solve variational problem
solver_parameters = {'newton_solver': {'maximum_iterations': 100,
                                       'absolute_tolerance': 1E-8,
                                       'relative_tolerance': 1E-7}}

solve(F == 0, u, bcs, J=Je, solver_parameters=solver_parameters)
# Deviatoric part
dev_stress = MU * (CL - I)

# Volumetric part
vol_stress = LAMBDA * ln(J) * I
sigma = (1/J) * (dev_stress + vol_stress)
S = FunctionSpace(mesh, "P", 1)  # Example function space
sigma_xx = project(sigma[0, 0], S)

points_of_interest = [(0.1, 0.1, 0.1), (0.5, 0.1, 0.1), (0.9, 0.1, 0.1)]  # Example points
for point in points_of_interest:
    try:
        sigma_xx_value = sigma_xx(point)
        print(f"sigma_xx at {point}: {sigma_xx_value}")
    except Exception as e:
        print(f"Could not evaluate sigma_xx at {point}: {e}")


# Save solution in VTK format
file = File("displacement.pvd");
file << u;

File("Materials.pvd")<< materials
vtkfile_subdomains = File('subdomains.pvd')
vtkfile_subdomains << materials


# Time-stepping loop
q = Constant(0.5)
dt = 0.25
dt = Constant(dt)
bF_magnitude = Constant(0.0)
t = 0
while t <= 5:
    t += float(dt)
    info("Time: {}".format(t))

    # Increase traction
    bF_magnitude.assign(100.0 * t)

    # Prepare to solve and solve
    solve(F == 0, u, bcs, J=Je, solver_parameters=solver_parameters)



# Close files



materials_function.rename("materials", "")
u.rename("Displacement Vector", "")
beam_deflection_file = fe.XDMFFile("beam_deflection.xdmf")
beam_deflection_file.parameters["flush_output"] = True
beam_deflection_file.parameters["functions_share_mesh"] = True
beam_deflection_file.write(u, 0.0)
beam_deflection_file.write(materials_function, 0.0)

# Plot solution
plot(u)
plt.show()


# Close the XDMF files
beam_deflection_file.close()

clamped
withonlyxdirection

Honestly, this sentence is a mystery to me. Is there a typo “i only set direchlet” → “if I only set direchlet”?
What does it mean that you set the Dirichlet conditions via paraview? BCs are set in FEniCS, not in Paraview.
If you ask someone for help, It wouldn’t hurt to have the post written in a somewhat legible language.

What I understood from your question is that you only set constrain certain components (the x one, in particular) of the displacement field on the Dirichlet boundary. If that is indeed the case, then in principle the remaining components are free to move. If your plot is showing the mesh deformed by the displacement field, then the solution you see is compatible with the BCs you prescribed as long as the deformation on the Dirichlet boundary only occurs in the y and/or z direction.

hi,

what i meant to say is when i set direchlet conditions on the 3 component of x,y,z and then create and xdmf file and view it on paraview the rode seems to be clamped and the deformation is indeed showing on the y,z components only.

bc1 = DirichletBC(V.sub(0), Constant(0), boundary_parts, 1)
bc2 = DirichletBC(V.sub(1), Constant(0), boundary_parts, 1)
bc3 = DirichletBC(V.sub(2), Constant(0), boundary_parts, 1)
bcs = [bc1, bc2,bc3]

but if i change the direchlet condition to only on the x component and then export it to an xdmf file it seems to be moving completely from its original place like in the pic i attached

bc1 = DirichletBC(V.sub(0), Constant(0), boundary_parts, 1)
bcs = [bc1]

so what i wanted to ask is there is something wrong with how i define my boundary condition or it seems to be an expected behavior ?

btw, sorry for my “legible language”… i think there is a nicer way to say that my question is not clear enough

If you only constrain displacement in one component, you allow for several rigid motions, such as rotation around an axis, translation etc to be in your solution. You would then have to remove the nullspace from your solution.

where can i find some exp how to implement it ? how to remove the nullspace from the solution

See for instance Bitbucket

2 Likes

hi thanks for the reply
im trying to apply it on a nonlinear system(hyperplastic material), is it possible?

I think so, in the sense that you would need to apply it to the linear solver at every nonlinear iteration. However, I don’t have any example in mind that does that.

Maybe have a look at Welcome to Numerical tours of Computational Mechanics using FEniCS — Numerical tours of continuum mechanics using FEniCS master documentation if there is anything helpful.

thanks, went through it and also found an old doc of someone who was kind off trying to do the same as I.
im able to run my code with the addition of the nullspace but it does keep failing after few iterations , any idea what else can i change?

import fenics as fe
from dolfin import *
import matplotlib.pyplot as plt
import os


def build_nullspace(V):
    x = Function(V).vector()
    nullspace_basis = [x.copy() for i in range(6)]
    V.sub(0).dofmap().set(nullspace_basis[0], 1.0)
    V.sub(1).dofmap().set(nullspace_basis[1], 1.0)
    V.sub(2).dofmap().set(nullspace_basis[2], 1.0)
    V.sub(0).set_x(nullspace_basis[3], -1.0, 1)
    V.sub(1).set_x(nullspace_basis[3],  1.0, 0)
    V.sub(0).set_x(nullspace_basis[4],  1.0, 2)
    V.sub(2).set_x(nullspace_basis[4], -1.0, 0)
    V.sub(1).set_x(nullspace_basis[5], -1.0, 2)
    V.sub(2).set_x(nullspace_basis[5],  1.0, 1)
    for x in nullspace_basis:
    	x.apply("insert")

    basis = VectorSpaceBasis(nullspace_basis)
    basis.orthonormalize()
    return basis


class Problem(NonlinearProblem):
    def __init__(self, J, J_pc, F, bcs):
        self.bilinear_form = J
        self.preconditioner_form = J_pc
        self.linear_form = F
        self.bcs = bcs
        NonlinearProblem.__init__(self)


    def F(self, b, x):
        pass


    def J(self, A, x):
        pass

    def form(self, A, P, b, x):
        assemble(self.linear_form, tensor=b)
        assemble(self.bilinear_form, tensor=A)
        assemble(self.preconditioner_form, tensor=P)
        for bc in self.bcs:
            bc.apply(b, x)
            bc.apply(A)
            bc.apply(P)
        NS = build_nullspace(V)
        NS.orthogonalize(b)


class CustomSolver(NewtonSolver):
    def __init__(self):
        NewtonSolver.__init__(self, mesh.mpi_comm(),
                                  PETScKrylovSolver(), PETScFactory.instance())

    def solver_setup(self, A, P, problem, iteration):
        self.linear_solver().set_operators(A, P)
        NS = build_nullspace(V)
        as_backend_type(A).set_nullspace(NS)
        PETScOptions.set("ksp_type", "minres")
        PETScOptions.set("pc_type", "hypre")
        PETScOptions.set("ksp_view")

        self.linear_solver().set_from_options()


# Optimization options for the form compiler
parameters["form_compiler"]["cpp_optimize"] = True
parameters["form_compiler"]["representation"] = "uflacs"

E1, E2, nu = 1, 10, 0.2

# Create mesh and define function space
length, width, height = 1.0, 0.2, 0.2  # Dimensions of the rod
num_elements = 10  # Number of elements along each dimension
mesh = BoxMesh(Point(0, 0, 0), Point(length, width, height), num_elements, 5, 5)


#mesh = UnitCubeMesh(24, 16, 16)


V = VectorFunctionSpace(mesh, "Lagrange", 1)


class Omega_0(SubDomain):
    def inside(self, x, on_boundary):
        return x[0] <= 1/2 + DOLFIN_EPS


class Omega_1(SubDomain):
    def inside(self, x, on_boundary):
        return x[0] > 1/2 - DOLFIN_EPS


subdomain_0 = Omega_0()
subdomain_1 = Omega_1()
materials = MeshFunction('size_t', mesh, mesh.topology().dim())
materials.set_all(0)
subdomain_0.mark(materials, 1)
subdomain_1.mark(materials, 2)


# Boundary conditions

boundary_parts = MeshFunction('size_t', mesh, mesh.topology().dim()-1)
left = AutoSubDomain(lambda x: near(x[0], 0))
right = AutoSubDomain(lambda x: near(x[0], 1))

left.mark(boundary_parts, 1)
right.mark(boundary_parts, 2)

bc1 = DirichletBC(V.sub(0), Constant(0), boundary_parts, 1)
#bc2 = DirichletBC(V.sub(1), Constant(0), boundary_parts, 1)
#bc3 = DirichletBC(V.sub(2), Constant(0), boundary_parts, 1)
#bcs = [bc1, bc2,bc3]
bcs=[bc1]

neumann = Expression(("0.1"), degree=0)
ds = Measure('ds', domain=mesh, subdomain_data=boundary_parts)

class K(fe.UserExpression):
    def __init__(self, materials, k_0, k_1, **kwargs):
        super().__init__(**kwargs)
        self.materials = materials
        self.k_0 = k_0
        self.k_1 = k_1

    def eval_cell(self, values, x, cell):
        if self.materials[cell.index] == 1:
            values[0] = self.k_0
        else:
            values[0] = self.k_1

    def value_shape(self):
        return ()


E_global = K(materials, E1, E2, degree=0)


DG = FunctionSpace(mesh, "DG", 2)
materials_function = Function(DG)
E_values = [E1, E2]
materials_function = project(E_global, DG)

MU1, LAMBDA1 = Constant(E1/(2*(1 + nu))), Constant(E1*nu/((1 + nu)*(1 - 2*nu)))
MU2, LAMBDA2 = Constant(E2/(2*(1 + nu))), Constant(E2*nu/((1 + nu)*(1 - 2*nu)))

class MUMU(fe.UserExpression):
    def __init__(self, materials, MU_0, MU_1, **kwargs):
        super().__init__(**kwargs)
        self.materials = materials
        self.MU_0 = MU_0
        self.MU_1 = MU_1

    def eval_cell(self, values, x, cell):
        if self.materials[cell.index] == 1:
            values[0] = self.MU_0
        else:
            values[0] = self.MU_1

    def value_shape(self):
        return ()


MU = MUMU(materials, MU1, MU2, degree=0)


class lamabada(fe.UserExpression):
    def __init__(self, materials, lam_0, lam_1, **kwargs):
        super().__init__(**kwargs)
        self.materials = materials
        self.lam_0 = lam_0
        self.lam_1 = lam_1

    def eval_cell(self, values, x, cell):
        if self.materials[cell.index] == 1:
            values[0] = self.lam_0
        else:
            values[0] = self.lam_1

    def value_shape(self):
        return ()


LAMBDA = lamabada(materials, LAMBDA1, LAMBDA2, degree=0)

# Define functions
du = TrialFunction(V)            # Incremental displacement
v = TestFunction(V)             # Test function
u = Function(V)                 # Displacement from previous iteration
B = Constant((0.0, 0.0, 0.0))  # Body force per unit volume
T = Constant((0.0, 0.0, 0.0))  # Traction force on the boundary
#D=5e5*length**3/12(1-nu**2)
#u.vector()[:] = 0.0
# Define an initial guess as an Expression
#initial_guess = Expression(("x[0]", "x[0]", "x[0]"), degree=2)
initial_guess = Expression(("amplitude*sin(pi*x[0]/length)", "0", "0"), degree=2, amplitude=0.1, length=length)
#initial_guess = Expression(("0.2/1.234", "x[0]", "x[0]"), degree=1)
# Project or interpolate the initial guess onto u
u.interpolate(initial_guess)


# Kinematics
d = len(u)
I = Identity(d)             # Identity tensor
F = I + grad(u)             # Deformation gradient
C = F.T*F                   # Right Cauchy-Green tensor
CL = F*F.T                  # left cauchy-green tensor



# Invariants of deformation tensors
Ic = tr(C)
J = det(F)
# CS=  MU*(C-I)         # cauchy stress
# S= J*CS*inv(F).T

# Stored strain energy density (compressible neo-Hookean model)
psi = (MU/2)*(Ic - 3) - MU*ln(J) + (LAMBDA/2)*(ln(J))**2

n = FacetNormal(mesh)
# Total potential energy
Pi = psi*dx - dot(B, u)*dx - neumann * dot(n, u)*ds(2)


# Compute first variation of Pi (directional derivative about u in the direction of v)
F = derivative(Pi, u, v)
J_pc = inner(grad(du), grad(v))*dx

# Compute Jacobian of F
Je = derivative(F, u, du)
problem=Problem(Je,J_pc, F, bcs)

custom_solver = CustomSolver()
custom_solver.solve(problem, u.vector())

'''
# Solve variational problem
solver_parameters = {'newton_solver': {'maximum_iterations': 100,
                                       'absolute_tolerance': 1E-8,
                                       'relative_tolerance': 1E-7}}

solve(F == 0, u, bcs, J=Je, solver_parameters=solver_parameters)
'''
# Deviatoric part
dev_stress = MU * (CL - I)

# Volumetric part
vol_stress = LAMBDA * ln(J) * I
sigma = (1/J) * (dev_stress + vol_stress)
S = FunctionSpace(mesh, "P", 1)  # Example function space
sigma_xx = project(sigma[0, 0], S)

points_of_interest = [(0.1, 0.1, 0.1), (0.5, 0.1, 0.1), (0.9, 0.1, 0.1)]  # Example points
for point in points_of_interest:
    try:
        sigma_xx_value = sigma_xx(point)
        print(f"sigma_xx at {point}: {sigma_xx_value}")
    except Exception as e:
        print(f"Could not evaluate sigma_xx at {point}: {e}")


# Save solution in VTK format
file = File("displacement.pvd");
file << u;

File("Materials.pvd")<< materials
vtkfile_subdomains = File('subdomains.pvd')
vtkfile_subdomains << materials

'''
# Time-stepping loop
q = Constant(0.5)
dt = 0.25
dt = Constant(dt)
bF_magnitude = Constant(0.0)
t = 0
while t <= 5:
    t += float(dt)
    info("Time: {}".format(t))

    # Increase traction
    bF_magnitude.assign(100.0 * t)

    # Prepare to solve and solve
    solve(F == 0, u, bcs, J=Je, solver_parameters=solver_parameters)
'''


# Close files



materials_function.rename("materials", "")
u.rename("Displacement Vector", "")
beam_deflection_file = fe.XDMFFile("beam_deflection.xdmf")
beam_deflection_file.parameters["flush_output"] = True
beam_deflection_file.parameters["functions_share_mesh"] = True
beam_deflection_file.write(u, 0.0)
beam_deflection_file.write(materials_function, 0.0)

# Plot solution
plot(u)
plt.show()


# Close the XDMF files
beam_deflection_file.close()

this is the error i get
Traceback (most recent call last):
File “/home/mirialex/PycharmProjects/hyperelastic_model/20_02_1.py”, line 240, in
custom_solver.solve(problem, u.vector())
RuntimeError:

*** -------------------------------------------------------------------------
*** DOLFIN encountered an error. If you are not able to resolve this issue
*** using the information listed below, you can ask for help at


*** fenics-support@googlegroups.com


*** Remember to include the error message listed below and, if possible,
*** include a minimal running example to reproduce the error.


*** -------------------------------------------------------------------------
*** Error: Unable to solve nonlinear system with NewtonSolver.
*** Reason: Newton solver did not converge because maximum number of iterations reached.
*** Where: This error was encountered inside NewtonSolver.cpp.
*** Process: 0


*** DOLFIN version: 2019.2.0.dev0
*** Git changeset: ubuntu
*** -------------------------------------------------------------------------