for example, this should theoretically work
md.add_macro("W(gap)", "1/2*c*sqr(neg_part(gap))")
md.add_nonlinear_term(mim_slave, "Interpolate_filter(raytrace, W(gap), 1)", SLAVE_RG)
but it does not because neg_part is not twice differentiable, but the W function is actually twice differentiable.
For this reason we need to provide the 1st order expression instead:
md.add_nonlinear_term(mim_slave,
"Interpolate_filter(raytrace, -c*neg_part(gap)*(Interpolate(Test_u,raytrace)-Test_u2).normal2, 1)",
SLAVE_RG)
you can see how it works in practice in this example
#!/usr/bin/env python
# -*- coding: UTF8 -*-
#
############################################################################
import sys
import getfem as gf
import numpy as np
gf.util_trace_level(1)
gf.util_warning_level(1)
π = np.pi
# Input data
R = 50. # ring radius
r = 5. # cross section radius
NC = 26 # Number of elements along the ring circumference
Nr1 = 2 # Number of elements in the cross section core
Nr2 = 2 # Number of elements in the cross section peel
E = 1e5 # Young's modulus
nu = 0.45 # Poisson's ratio
mult_fem_order = 1 # contact multipliers finite element order
integration_degree = 5 # 27 gauss points per hexa
integration_contact_degree = 7 # 16 gauss points per quad
c_penal = 1e-2*E # penalty parameter
release_dist = r
# auxiliary constants
EXTERIOR_RG = 1
HANDLE1_RG = 2
HANDLE2_RG = 3
# Mesh
m_ = gf.Mesh("import", "structured_ball",
"GT='GT_QK(2,2)';ORG=[0,0];SIZES=[%g];NSUBDIV=[%i,%i]"
% (r, Nr1, Nr2))
m0 = gf.Mesh("prismatic", m_, NC, 2) # second order
pts = m0.pts()
for i in range(pts.shape[1]):
x = pts[0,i]
z = pts[2,i]
pts[0,i] = (R+x)*np.cos(2*π*z)
pts[2,i] = (R+x)*np.sin(2*π*z)
m0.set_pts(pts)
m1 = gf.Mesh("empty", 3)
m1.merge(m0, tol=1e-3) # this trick is used just for gluing the open ends of the ring in m0
m1.set_region(EXTERIOR_RG, m1.outer_faces())
m1.set_region(HANDLE1_RG, m1.outer_faces_in_box([-R-1.1*r,-1.1*r,-0.3*R],
[-R+1.1*r, 1.1*r, 0.3*R]))
m1.set_region(HANDLE2_RG, m1.outer_faces_in_box([R-1.1*r,-1.1*r,-0.3*R],
[R+1.1*r, 1.1*r, 0.3*R]))
m1.optimize_structure()
m1.export_to_vtu("mesh1.vtu")
m2 = gf.Mesh("clone", m1)
m2.translate([-R,2.5*r,0])
m2.export_to_vtu("mesh2.vtu")
# FEM
mf1 = gf.MeshFem(m1, 3)
mf1.set_fem(gf.Fem("FEM_Q2_INCOMPLETE(3)"))
mf2 = gf.MeshFem(m2, 3)
mf2.set_fem(gf.Fem("FEM_Q2_INCOMPLETE(3)"))
mfmult1 = gf.MeshFem(m1, 3)
mfmult1.set_classical_fem(mult_fem_order)
mfmult2 = gf.MeshFem(m2, 3)
mfmult2.set_classical_fem(mult_fem_order)
mim1 = gf.MeshIm(m1, integration_degree)
mim2 = gf.MeshIm(m2, integration_degree)
mim_contact1 = gf.MeshIm(m1, integration_contact_degree)
mim_contact2 = gf.MeshIm(m2, integration_contact_degree)
# Model
md = gf.Model("real")
md.add_fem_variable("u1", mf1)
md.add_fem_variable("u2", mf2)
# Elasticity
md.add_initialized_data("K", E/(3*(1-2*nu)))
md.add_initialized_data("G", E/(2*(1+nu)))
md.add_macro("F(u)", "Id(3)+Grad_u")
md.add_macro("tau(u)", "K*log(Det(F(u)))*Id(3)+G*pow(Det(F(u)),-2/3)*Deviator(F(u)*F(u)')")
md.add_nonlinear_term(mim1, "(tau(u1)*Inv(F(u1)')):Grad_Test_u1")
md.add_nonlinear_term(mim2, "(tau(u2)*Inv(F(u2)')):Grad_Test_u2")
# Lagrange multipliers for handles
md.add_variable("q11",3) # ring 1 handle 1
md.add_variable("q12",3) # ring 1 handle 2
md.add_variable("q21",3) # ring 2 handle 1
md.add_variable("q22",3) # ring 2 handle 2
md.add_initialized_data("p11", [0,0,0]) # we will keep this fixed
md.add_initialized_data("p12", [0,0,0]) # we will keep this fixed only at the beginning
md.add_initialized_data("p21", [0,0,0]) # we will drive this only at the beginning
md.add_initialized_data("p22", [0,0,0]) # we will drive this till the end
md.add_linear_term(mim1, "(u1-p11).q11", HANDLE1_RG)
md.add_linear_term(mim1, "(u1-p12).q12", HANDLE2_RG)
md.add_linear_term(mim2, "(u2-p21).q21", HANDLE1_RG)
md.add_linear_term(mim2, "(u2-p22).q22", HANDLE2_RG)
md.add_initialized_data("penal", 0.)
md.add_linear_term(mim1, "1e4*(u1-p11).Test_u1", HANDLE1_RG) # add some elastic support to handle 1 of ring 1 to avoid twisting
md.add_linear_term(mim1, "penal*(u1-p12).Test_u1", HANDLE2_RG) # add some elastic support to handle 2 of ring 1 to avoid twisting
md.add_linear_term(mim2, "penal*(u2-p21).Test_u2", HANDLE1_RG) # add some elastic support to handle 1 of ring 2 to avoid twisting
md.add_linear_term(mim2, "1e4*(u2-p22).Test_u2", HANDLE2_RG) # add some elastic support to handle 2 of ring 2 to avoid twisting
# Contact condition
md.add_initialized_data("c", c_penal)
md.add_raytracing_transformation("raytrace", release_dist)
md.define_variable_group("u", "u1", "u2")
md.add_master_contact_boundary_to_raytracing_transformation("raytrace", m1, "u", EXTERIOR_RG)
md.add_slave_contact_boundary_to_raytracing_transformation("raytrace", m2, "u", EXTERIOR_RG)
md.add_macro("normal2", "Normalized(Inv(F(u2)').Normal)")
md.add_macro("gap2", "(Interpolate(X,raytrace)+Interpolate(u,raytrace)-X-u2).normal2")
#md.add_macro("W(gap)", "1/2*c*sqr(neg_part(gap))") # dW/dgap = -c*neg_part(gap)
md.add_nonlinear_term(mim_contact2,
"Interpolate_filter(raytrace, -c*neg_part(gap2)*(Interpolate(Test_u,raytrace)-Test_u2).normal2, 1)",
EXTERIOR_RG)
print("Model dofs: %i" % md.nbdof())
print("Displacement fem dofs: %i" % (mf1.nbdof()+mf2.nbdof()))
for step in range(90):
if step <= 30: # initial phase
t = step/30
md.set_variable("p21", [1.8*R*(max(t,min(4*t,0.2))),
-5*r*min(t/0.4,1), #-5*r*(np.cos(π*t)-1)/2,
0])
md.set_variable("p22", [1.8*R*max(0,(t-0.4)/0.6)**2,
-5*r*min(t/0.4,1),
0])
elif step <= 38: # second phase, releasing handles 21 and 22
if step == 31:
md.disable_variable("q12")
md.disable_variable("q21")
md.set_variable("q12", [0,0,0])
md.set_variable("q21", [0,0,0])
md.set_variable("penal", (1e2,3e1,1e1,3e0,1e0,1e-1,1e-3,0.)[step-31])
else: # final phase
t = 1+(step-38)/100
if t > 1.25: # slow down
t = 1.25 + 0.2*(t-1.25)
md.set_variable("p22", [1.8*R*max(0,(t-0.4)/0.6)**2,
-5*r,
0])
print("Step", step, "t =", t)
md.solve("noisy", "max_iter", 20, "max_res", 1e-8,
"lsearch", "simplest", "alpha max ratio", 3., "alpha min", 0.02, "alpha mult", 0.6)
mf1.export_to_vtu(f"ring1_{step}.vtu", mf1, md.variable("u1"), "Displacements")
mf2.export_to_vtu(f"ring2_{step}.vtu", mf2, md.variable("u2"), "Displacements")
This example implements the standard penalty approach which is the blue curve in the graph below. Your task is to replace the blue curve with one that looks like the red one: