207 : Nonlinear Elasticity Bimetal 2D
This example computes the displacement field $u$ of the nonlinear elasticity problem
\[\begin{aligned} -\mathrm{div}(\mathbb{C} (\epsilon(u)-\epsilon_T)) & = 0 \quad \text{in } \Omega \end{aligned}\]
where an isotropic stress tensor $\mathbb{C}$ is applied to the nonlinear strain $\epsilon(u) := \frac{1}{2}(\nabla u + (\nabla u)^T + (\nabla u)^T \nabla u)$ and a misfit strain $\epsilon_T := \Delta T \alpha$ due to thermal load caused by temperature(s) $\Delta T$ and thermal expansion coefficients $\alpha$ (that may be different) in the two regions of the bimetal.
This example demonstrates how to setup a (parameter- and region-dependent) nonlinear expression and how to assign it to the problem description.
module Example207_NonlinearElasticityBimetal2D
using GradientRobustMultiPhysics
using ExtendableGrids
using GridVisualize
# parameter-dependent nonlinear operator uses a callable struct to reduce allocations
mutable struct nonlinear_operator{T}
λ::Vector{T}
μ::Vector{T}
ϵT::Vector{T}
end
function strain!(result, input)
result[1] = input[1]
result[2] = input[4]
result[3] = input[2] + input[3]
# add nonlinear part of the strain 1/2 * (grad(u)'*grad(u))
result[1] += 1//2 * (input[1]^2 + input[3]^2)
result[2] += 1//2 * (input[2]^2 + input[4]^2)
result[3] += input[1]*input[2] + input[3]*input[4]
return nothing
end
# kernel for nonlinear operator
(op::nonlinear_operator)(result, input, item) = (
# input = grad(u) written as a vector
# item[3] is the region number where operator is currently evaluated
# compute strain and subtract thermal strain (all in Voigt notation)
strain!(result, input);
result[1] -= op.ϵT[item[3]];
result[2] -= op.ϵT[item[3]];
# multiply with isotropic stress tensor
# (stored in input[5:7] using Voigt notation)
input[5] = op.λ[item[3]]*(result[1]+result[2]) + 2*op.μ[item[3]]*result[1];
input[6] = op.λ[item[3]]*(result[1]+result[2]) + 2*op.μ[item[3]]*result[2];
input[7] = 2*op.μ[item[3]]*result[3];
# write strain into result
result[1] = input[5];
result[2] = input[7];
result[3] = input[7];
result[4] = input[6];
return nothing
)
const op = nonlinear_operator([0.0,0.0],[0.0,0.0],[0.0,0.0])
# everything is wrapped in a main function
function main(;
ν = [0.3,0.3], # Poisson number for each region/material
E = [2.1,1.1], # Elasticity modulus for each region/material
ΔT = [580,580], # temperature for each region/material
α = [1.3e-5,2.4e-4], # thermal expansion coefficients
scale = [20,500], # scale of the bimetal, i.e. [thickness, width]
nref = 0, # refinement level
order = 2, # finite element order
periodic = false, # use periodic boundary conditions?
verbosity = 0, # steers talkativeness
Plotter = nothing)
# set log level
set_verbosity(verbosity)
# compute Lame' coefficients μ and λ from ν and E
# and thermal misfit strain and assign to operator operator
op.μ .= E ./ (2 .* (1 .+ ν.^(-1)))
op.λ .= E .* ν ./ ( (1 .- 2*ν) .* (1 .+ ν))
op.ϵT .= ΔT .* α
# generate bimetal mesh
xgrid = bimetal_strip2D(; scale = scale, n = 2*(nref+1))
# prepare nonlinear operator (one for each bimetal region)
nonlin_operator = NonlinearForm(Gradient, [Gradient], [1], op, [4,4,7]; name = "C(ϵ(#1)-ϵT):∇#T", regions = [1,2], dependencies = "I", bonus_quadorder = 3, sparse_jacobian = true, newton = true)
# generate problem description and assign nonlinear operators
Problem = PDEDescription("nonlinear elasticity problem")
add_unknown!(Problem; unknown_name = "u", equation_name = "displacement equation")
add_operator!(Problem, 1, nonlin_operator)
# create finite element space and solution vector
FES = FESpace{H1Pk{2,2,order}}(xgrid)
Solution = FEVector(FES)
if periodic
# periodic boundary conditions
# 1) couple dofs left (bregion 1) and right (bregion 3) in y-direction
dofsX, dofsY, factors = get_periodic_coupling_info(FES, xgrid, 1, 3, (f1,f2) -> abs(f1[2] - f2[2]) < 1e-14; factor_components = [0,1])
add_constraint!(Problem, CombineDofs(1, 1, dofsX, dofsY, factors))
# 2) find and fix point at [0, scale[1]]
xCoordinates = xgrid[Coordinates]
closest::Int = 0
dist::Float64 = 0
mindist::Float64 = 1e30
for j = 1 : num_nodes(xgrid)
dist = xCoordinates[1,j]^2 + (xCoordinates[2,j] - scale[1])^2
if dist < mindist
mindist = dist
closest = j
end
end
@show closest mindist scale
add_constraint!(Problem, FixedDofs(1, [closest], [0.0]))
else
add_boundarydata!(Problem, 1, [1], HomogeneousDirichletBoundary; mask = [1,0])
end
@show Problem
# solve
solve!(Solution, Problem; maxiterations = 20, target_residual = 1e-9, show_statistics = true)
# displace mesh and plot
p = GridVisualizer(; Plotter = Plotter, layout = (3,1), clear = true, resolution = (1000,1500))
grad_nodevals = nodevalues(Solution[1], Gradient)
strain_nodevals = zeros(Float64,3,num_nodes(xgrid))
for j = 1 : num_nodes(xgrid)
strain!(view(strain_nodevals,:,j), view(grad_nodevals,:,j))
end
scalarplot!(p[1,1], xgrid, view(strain_nodevals,1,:), levels = 3, colorbarticks = 7, xlimits = [-scale[2]/2-10, scale[2]/2+10], ylimits = [-30, scale[1] + 20], title = "ϵ(u)_xx + displacement")
scalarplot!(p[2,1], xgrid, view(strain_nodevals,2,:), levels = 1, colorbarticks = 7, xlimits = [-scale[2]/2-10, scale[2]/2+10], ylimits = [-30, scale[1] + 20], title = "ϵ(u)_yy + displacement")
vectorplot!(p[1,1], xgrid, evaluate(PointEvaluator(Solution[1], Identity)), spacing = [50,25], clear = false)
vectorplot!(p[2,1], xgrid, evaluate(PointEvaluator(Solution[1], Identity)), spacing = [50,25], clear = false)
displace_mesh!(xgrid, Solution[1])
gridplot!(p[3,1], xgrid, linewidth = 1, title = "displaced mesh")
end
# grid
function bimetal_strip2D(; scale = [1,1], n = 2, anisotropy_factor::Int = Int(ceil(scale[2]/(2*scale[1]))))
X=linspace(-scale[2]/2, 0, (n+1)*anisotropy_factor)
X2=linspace(0, scale[2]/2, (n+1)*anisotropy_factor)
append!(X, X2[2:end])
Y=linspace(0, scale[1], 2*n+1)
xgrid=simplexgrid(X,Y)
cellmask!(xgrid, [-scale[2]/2,0.0], [scale[2]/2,scale[1]/2], 1)
cellmask!(xgrid, [-scale[2]/2,scale[1]/2], [scale[2]/2,scale[1]], 2)
bfacemask!(xgrid, [-scale[2]/2,0.0], [-scale[2]/2,scale[1]/2], 1)
bfacemask!(xgrid, [-scale[2]/2,scale[1]/2], [-scale[2]/2,scale[1]], 1)
bfacemask!(xgrid, [-scale[2]/2,0.0], [scale[2]/2,0.0], 2)
bfacemask!(xgrid, [-scale[2]/2,scale[1]], [scale[2]/2,scale[1]], 2)
bfacemask!(xgrid, [scale[2]/2,0.0], [scale[2]/2,scale[1]], 3)
return xgrid
end
end
This page was generated using Literate.jl.