Interface problem with nitsche method

Hello everyone,
I am solving an interface problem with Nitsche’s method where I want to impose stress continuity, and displacement continuity at the interface of two elastic bodies. I’ve found an excellent example here (Stress continuity using mixed-dimensional branch) that works very well with Lagrange multipliers. Only I’ve chosen Nitsche’s method because I don’t want to add extra variables to penalise the problem.
I’m not familiar with Nitsche’s method and how it works. I can’t always understand how the interface is managed. Should I create the interface before making the mesh or should I split the mesh a bit like in the attached code. Is the best solution to solve for a global displacement u and therefore use the jump and avg functions in FEniCS?
As mentioned above, I started from an example already shared here which I tried to adapt using the Nitsche method. The code is below. I’m getting an error that I can’t fix.
I need your expertise to understand this method and how to implement it with FEniCS. Sorry, I’m new to FEniCSx and as I have a deadline, I wanted to test it with FEniCS first.

Thanks in advance.

from dolfin import *

class Dirichlet(SubDomain):
  def inside(self, x, on_boundary):
    return near(x[0], 2) \
        or near(x[1], 0) \
        or near(x[1], 1)

class Neumann(SubDomain):
  def inside(self, x, on_boundary):
    return near(x[0], 0)

class Interface(SubDomain):
  def inside(self, x, on_boundary):
    return near(x[0], 1)

# Mesh generation
n = 20
mesh = RectangleMesh(Point(0,0,0),Point(2,1,0),2*n,n)

# Domain markers
domain = MeshFunction("size_t", mesh, mesh.geometry().dim(), 0)
for c in cells(mesh):
  domain[c] = c.midpoint().x() > 1.0

# Interface markers
facets = MeshFunction("size_t", mesh, mesh.geometry().dim()-1, 0)
Interface().mark(facets,1)

# Meshes
mesh_L = MeshView.create(domain, 0)
mesh_R = MeshView.create(domain, 1)
mesh_I = MeshView.create(facets, 1)

# Function spaces
FE = VectorElement("CG", mesh_I.ufl_cell(), 1, dim=2) # Lagrange multiplier
VE = VectorElement("CG", mesh.ufl_cell(), 1, dim=2)   # Displacement
V_L  = FunctionSpace(mesh_L,VE)    # Displacement on left domain
V_R  = FunctionSpace(mesh_R,VE)    # Displacement on right domain
V_I_disp  = FunctionSpace(mesh_I,FE)   # Lagrange multiplier - displacement continuity
V_I_stress  = FunctionSpace(mesh_I,FE)   # Lagrange multiplier -stress continuity
V_I  = FunctionSpace(mesh_I,FE)
W = MixedFunctionSpace(V_L, V_R)

# Function and test functions
(ul, ur) = TrialFunctions(W)
(wl, wr) = TestFunctions(W)

# Markers for Dirichlet and Neuman bcs
ff_L = MeshFunction("size_t", mesh_L, mesh_L.geometry().dim()-1, 0)
Dirichlet().mark(ff_L, 1)
Neumann().mark(ff_L, 2)
Interface().mark(ff_L, 3)
ff_R = MeshFunction("size_t", mesh_R, mesh_R.geometry().dim()-1, 0)
Dirichlet().mark(ff_R, 1)
Interface().mark(ff_R, 3)

# Boundary conditions
bc_L = DirichletBC(W.sub_space(0), Constant((0.0,0.0)), ff_L, 1)
bc_R = DirichletBC(W.sub_space(1), Constant((0,0)), ff_R, 1)
bcs = [bc_L, bc_R]

# Elasticity parameters
rho   = Constant(2200)
lmbda = Constant(7.428e+04)
mu    = Constant(7.428e+04)

# Neumann condition
g = Constant((1e+5, 0))

# Measures
dx_L  = Measure("dx", domain=W.sub_space(0).mesh())
ds_L  = Measure("ds", domain=W.sub_space(0).mesh(), subdomain_data=ff_L)
dx_R = Measure("dx", domain=W.sub_space(1).mesh())
ds_R  = Measure("ds", domain=W.sub_space(1).mesh(), subdomain_data=ff_R)
#ds_I   = Measure("dx", domain=W.sub_space(2).mesh())

# Définition de l'interface
class Interface(SubDomain):
    def inside(self, x, on_boundary):
        return near(x[0], 1)

# Marquage de l'interface sur le maillage
facet_marker = MeshFunction("size_t", mesh_L, mesh_L.topology().dim() - 1)
facet_marker.set_all(0)
interface = Interface()
interface.mark(facet_marker, 4)

#ds = Measure('ds', domain=mesh, subdomain_data=boundaries)
# Stress tensor
def sigma(r):
    return 2.0*mu*sym(grad(r)) + lmbda*tr(sym(grad(r)))*Identity(len(r))

# Variational problem
a_left = inner(sigma(ul),sym(grad(wl)))*dx_L + inner(sigma(ul)*FacetNormal(mesh_L), wl)*ds_L(2)
L_left = inner(g, wl)*ds_L(2)

a_right = inner(sigma(ur),sym(grad(wr)))*dx_R  #- inner(sigma(ul)*FacetNormal(mesh_R), wr)*ds_R(3)
L_right = inner(Constant((0,0)), wr)*dx_R

nL = Constant((1,0))
nR = Constant((-1,0))


# definition fo the Nitsche
Thet = -1; 
gamma = 10
lam = 0.5
h= CellDiameter(mesh_L)
avg_u = lam*sigma(ul)+ (1-lam)*sigma(ur) 
avg_w = lam*sigma(wl)+ (1-lam)*sigma(wr)
jum_u = ul-ur
jum_w = wl-wr
a = a_left + a_right - inner(avg_u*nL, jum_w)*ds_L(3) + Thet*inner(avg_w*nL, jum_u)*ds_L(3) + 100* inner(jum_u,jum_w)*ds_L(3) 
L = L_left + L_right
    
# Output files
pvda_file = File("output1/ul.pvd")
pvdb_file = File("output1/ur.pvd")
#pvdc_file = File("output/uc.pvd")

# Solve
u_mixed = Function(W)
solve(a == L, u_mixed, bcs, solver_parameters={"linear_solver":"direct"})
ul, ur= u_mixed.sub(0), u_mixed.sub(1)

# Save solution
pvda_file << ul
pvdb_file << ur
#pvdc_file << uc

I have the following error,

*** -------------------------------------------------------------------------
*** Error:   Unable to successfully call PETSc function 'MatSetValuesLocal'.
*** Reason:  PETSc error code is: 63 (Argument out of range).
*** Where:   This error was encountered inside /build/dolfin-pnfrym/dolfin-2019.2.0~git20201207.b495043/dolfin/la/PETScMatrix.cpp.
*** Process: 0
*** 
*** DOLFIN version: 2019.2.0.dev0
*** Git changeset:  unknown
*** -------------------------------------------------------------------------

Thanks in Advance

Hi Rene,
I am also new to FEniCS and FEniCSx. Two months ago I found lots of tutorial based on FEniCS, which is difficult to implement nowdays(though I spent lots of time). Then I focus on the tutorial for FEniCSx, mainly based on The FEniCSx tutorial and Numerical Tours of Computational Mechanics with FEniCSx. Maybe the second link has some good examples relevant to you. :slight_smile:

Hi Yanjun,

You’re right, it would probably be a good idea to switch to FEniCSx now.
The resources you’ve shared are a very good starting point.
Thanks

1 Like