Solving elasticity problems using JuliaFEM

Author(s): Jukka Aho

Abstract: Solving elasticity equations using JuliaFEM.

Weak form

Given function spaces \begin{align} \boldsymbol{\mathcal{U}} & =\left\{ \boldsymbol{u}\in H^{1}\left(\Omega\right)|\boldsymbol{u}\left(\boldsymbol{X},t\right)=\hat{\boldsymbol{u}}\left(\boldsymbol{X},t\right)\text{ on }\Gamma_{\mathrm{u}}\right\} ,\\ \boldsymbol{\mathcal{V}} & =\left\{ \delta\boldsymbol{u}\in H^{1}\left(\Omega\right)|\delta\boldsymbol{u}\left(\boldsymbol{X}\right)=0\text{ on }\Gamma_{\mathrm{u}}\right\} , \end{align} find $\boldsymbol{u}\in\boldsymbol{\mathcal{U}}$ such that \begin{equation} \delta\mathcal{W}:=\int_{\Omega_{0}}\rho_{0}\ddot{\boldsymbol{u}}\cdot\delta\boldsymbol{u}\,\mathrm{d}V_{0}+\int_{\Omega_{0}}\boldsymbol{S}:\delta\boldsymbol{E}\,\mathrm{d}V_{0}-\int_{\Omega_{0}}\hat{\boldsymbol{b}}_{0}\cdot\delta\boldsymbol{u}\,\mathrm{d}V_{0}-\int_{\Gamma_{\sigma}}\hat{\boldsymbol{t}}_{0}\cdot\delta\boldsymbol{u}\,\mathrm{d}A_{0} =0 \qquad\forall\delta\boldsymbol{u}\in\boldsymbol{\mathcal{V}} \end{equation}

Some formulas

\begin{align} J & =\det\left(F\right)\\ I_{c} & =\mbox{tr}\left(C\right)\\ \mathbf{C} & =\mathbf{F}^{\mathrm{T}}\mathbf{F}\\ \mathbf{F} & =\mathbf{I}+\nabla\mathbf{u}\\ \mathbf{E} & =\frac{1}{2}\left(\mathbf{F}^{\mathrm{T}}\mathbf{F}-\mathbf{I}\right) \end{align}

Potential energy

\begin{equation} \underset{u\in\boldsymbol{\mathcal{U}}}{\min}\Pi\left(\mathbf{u}\right) \end{equation}\begin{equation} \Pi\left(\mathbf{u}\right)=\int_{\Omega}\psi\left(\mathbf{u}\right)-\int_{\Omega}\hat{\mathbf{b}}_{0}\cdot\mathbf{u}-\int_{\Gamma_{\sigma}}\hat{\mathbf{t}}_{0}\cdot\mathbf{u}\,\mathrm{d}A_{0} \end{equation}

Material models

https://en.wikipedia.org/wiki/Strain_energy_density_function

Saint-Venant-Kirchhoff model https://en.wikipedia.org/wiki/Hyperelastic_material \begin{equation} \psi\left(\mathbf{E}\right)=\frac{\lambda}{2}\left[\mbox{tr}\left(\mathbf{E}\right)\right]^{2}+\mu\mbox{tr}\left(\mathbf{E}^2\right) \end{equation}

neo-Hookean material https://en.wikipedia.org/wiki/Neo-Hookean_solid \begin{equation} \psi=\frac{\mu}{2}\left(I_{c}-3\right)-\mu\ln\left(J\right)+\frac{\lambda}{2}\ln\left(J\right)^{2} \end{equation}


In [1]:
using JuliaFEM.API: Model, Material, add_material!, LoadCase, add_element_set!, ForceBC, DisplacementBC
using JuliaFEM.API: add_boundary_condition!, add_load_case!, add_solver!
using JuliaFEM.Interfaces: solve!
using JuliaFEM.Preprocess: parse_abaqus

In [2]:
@time begin
    # Linear models
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_8789_P1.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_16436_P1.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_27343_P1.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_45510_P1.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_75470_P1.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/wrench/wrench_128903_P1.inp")

    # Quadratic models
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_19611_P2.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_55950_P2.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "/Temp/piston_107168_P2.inp")
    #model = open(JuliaFEM.Core.parse_abaqus, "../geometry/piston/piston_345757_P2.inp")

    mesh = open(parse_abaqus, "/Temp/piston_107168_P2.inp")
    model = Model("piston model", mesh)
    info("model loaded.")
end


INFO: Parsing nodes
INFO: Parsing elements. Type: Tet10
INFO: Parsing elements. Type: Tri6
INFO: Creating elset BC1
INFO: Creating elset BC2
INFO: model loaded.
  4.209444 seconds (19.12 M allocations: 565.872 MB, 11.55% gc time)

In [3]:
mat = Material("steel")
mat["youngs modulus"] = 210.0e3
mat["poissons ratio"] = 0.3
add_material!(model, "PISTON", mat)


Out[3]:
JuliaFEM.API.Material("steel",Dict("poissons ratio"=>0.3,"youngs modulus"=>210000.0))

In [4]:
simulation = LoadCase(:ElasticityProblem)

# simulation = Simulation(:ElasticityProblem, "elasticity equations")
# add_element_set!(simulation, "PISTON")
# add_boundary_condition!(simulation, bc1, bc2)
# add_simulation!(model, simulation)
# add_solver!(simulation, :DirectSolver)
# solve!(model, "elasticity equations", 0.0)
# solve!(model, "heat equations", 0.0)
# solve!(model, "solve equations 3", 0.0)

add_element_set!(simulation, "PISTON")


Out[4]:
1-element Array{ASCIIString,1}:
 "PISTON"

In [5]:
traction = Vector{Float64}[[0.0, 0.0, -10.0] for i in 1:6]
bc1 = ForceBC("BC1", "displacement traction force" => traction)
bc2 = DisplacementBC("BC2", "displacement" => 0.0)

# JuliaFEM.API.add_boundary_condition!(prob, bc1, bc2)
add_boundary_condition!(simulation, bc1)
add_boundary_condition!(simulation, bc2)


Out[5]:
1-element Array{JuliaFEM.API.DirichletBC,1}:
 JuliaFEM.API.DirichletBC("BC2","displacement"=>0.0)

In [6]:
add_solver!(simulation, :DirectSolver)
add_load_case!(model, "elasticity equations", simulation)


Out[6]:
JuliaFEM.API.LoadCase(:ElasticityProblem,[JuliaFEM.API.NeumannBC("BC1","displacement traction force"=>[[0.0,0.0,-10.0],[0.0,0.0,-10.0],[0.0,0.0,-10.0],[0.0,0.0,-10.0],[0.0,0.0,-10.0],[0.0,0.0,-10.0]])],[JuliaFEM.API.DirichletBC("BC2","displacement"=>0.0)],:DirectSolver,ASCIIString["PISTON"])

In [7]:
solver = JuliaFEM.Interfaces.get_solver(model, "elasticity equations", 0.0);

In [11]:
celements = solver.field_problems[1].elements;
celements[1]


Out[11]:
JuliaFEM.Core.Element{JuliaFEM.Core.Tet10}([1,2,3,4,5,6,7,8,9,10],Dict{ASCIIString,JuliaFEM.Core.Field{A<:Union{JuliaFEM.Core.Continuous,JuliaFEM.Core.Discrete},B<:Union{JuliaFEM.Core.Constant,JuliaFEM.Core.Variable},C<:Union{JuliaFEM.Core.TimeInvariant,JuliaFEM.Core.TimeVariant}}}("poissons ratio"=>JuliaFEM.Core.Field{JuliaFEM.Core.Discrete,JuliaFEM.Core.Constant,JuliaFEM.Core.TimeInvariant}(0.3),"geometry"=>JuliaFEM.Core.Field{JuliaFEM.Core.Discrete,JuliaFEM.Core.Variable,JuliaFEM.Core.TimeInvariant}([[13.96894,1.54855,-2.99382],[15.71724,2.88794,-2.79243],[14.76115,1.1419,-1.23125],[15.93754,1.0623,-2.82067],[14.84309,2.21825,-2.89313],[15.2392,2.01492,-2.01184],[14.36505,1.34523,-2.11254],[14.95324,1.30543,-2.90725],[15.82739,1.97512,-2.80655],[15.34935,1.1021,-2.02596]]),"youngs modulus"=>JuliaFEM.Core.Field{JuliaFEM.Core.Discrete,JuliaFEM.Core.Constant,JuliaFEM.Core.TimeInvariant}(210000.0)))

In [12]:
surface_elements = filter((e) -> isa(e, JuliaFEM.Core.Element{JuliaFEM.Core.Tri6}), celements)
info("$(length(surface_elements)) surface elements")


INFO: 0 surface elements

In [13]:
length(celements)


Out[13]:
62454

In [7]:
solve!(model, "elasticity equations", 0.0)


INFO: # of field problems: 1
INFO: # of boundary problems: 1
INFO: Starting iteration 1
INFO: Assembling field problems...
INFO: Assembling body 1...
INFO: Assembly: 10.0 % done. 
INFO: Assembly: 20.0 % done. 
INFO: Assembly: 30.0 % done. 
INFO: Assembly: 40.0 % done. 
INFO: Assembly: 50.0 % done. 
INFO: Assembly: 60.0 % done. 
INFO: Assembly: 70.0 % done. 
INFO: Assembly: 80.0 % done. 
INFO: Assembly: 90.0 % done. 
INFO: Assembly: 100.0 % done. 
INFO: Assembling boundary problems...
INFO: Assembling boundary 1...
INFO: Assembly: 10.0 % done. 
INFO: Assembly: 20.0 % done. 
INFO: Assembly: 30.0 % done. 
INFO: Assembly: 40.0 % done. 
INFO: Assembly: 50.0 % done. 
INFO: Assembly: 60.0 % done. 
INFO: Assembly: 70.0 % done. 
INFO: Assembly: 80.0 % done. 
INFO: Assembly: 90.0 % done. 
INFO: Assembly: 100.0 % done. 
INFO: Solving system
INFO: all dofs = 321504
INFO: interior dofs = 312936
INFO: boundary dofs = 8568
INFO: preparation in 3.385999917984009 seconds
INFO: displacement on boundary solved.
INFO: norm[u_boundary_dofs] = 0.0
INFO: homogeneous dirichlet boundary
INFO: solve boundary = 0.42100000381469727
INFO: factorizations done in 14.133000135421753 seconds
INFO: solved interior in 0.2969999313354492 seconds. norm = 0.0
INFO: timing info for non-linear iteration:
Out[7]:
(1,true)
INFO: boundary assembly       : 2.1679999828338623
INFO: field assembly          : 279.9069998264313
INFO: dump matrices to disk   : 0.0
INFO: solve problem           : 19.37600016593933
INFO: update element data     : 1.6999998092651367
INFO: non-linear iteration    : 303.1509997844696
INFO: solver finished in 304.5089998245239 seconds.

Saving results to file


In [20]:
xdoc, xmodel = JuliaFEM.Postprocess.xdmf_new_model()
temporal_collection = JuliaFEM.Postprocess.xdmf_new_temporal_collection(xmodel)
grid = JuliaFEM.Postprocess.xdmf_new_grid(temporal_collection; time=0)


Out[20]:
<Grid Name="Grid">
  <Time Value="0"/>
</Grid>

Save geometry to xdmf file


In [21]:
nnodes = length(model["nodes"])
info("number of nodes in model: $nnodes")

X = zeros(3, nnodes)
for nid in keys(model["nodes"])
    X[:, perm[nid]] = model["nodes"][nid]
end

nelements = length(model["elsets"]["PISTON"])
info("Number of elements in model: $nelements")
elmap = zeros(Int64, 5, nelements)
#elmap[1,:] = 0x0026
elmap[1,:] = 0x6
for (i, elid) in enumerate(model["elsets"]["PISTON"])
    elmap[2:end,i] = Int64[perm[nid] for nid in model["elements"][elid]]
end


INFO: number of nodes in model: 8789
INFO: Number of elements in model: 31437

In [22]:
JuliaFEM.Postprocess.xdmf_new_mesh(grid, X, elmap)


Out[22]:
true

Save nodal data to model


In [23]:
using JuliaFEM.Core: get_connectivity

u = zeros(3, nnodes)

for element in values(elements)
    isa(element, Element{Tet4}) || continue
    connectivity = get_connectivity(element)
    field = element["displacement"](0.0)
    for (i, nid) in enumerate(connectivity)
        u[:, nid] = field[i]
    end
end
size(u)


Out[23]:
(3,8789)

In [24]:
JuliaFEM.Postprocess.xdmf_new_field(grid, "Displacement", "nodes", u)


Out[24]:
true

In [25]:
JuliaFEM.Postprocess.xdmf_save_model(xdoc, "/tmp/piston_8789_P1.xmf")


Out[25]:
1525505

In [26]:
u[:, 1:10]


Out[26]:
3x10 Array{Float64,2}:
 0.0582911   0.339694  0.431768    …   0.124351  0.346492    0.000617283
 0.0900323   0.22174   0.433814        0.140503  0.275036    0.0044649  
 0.0838928  -0.148224  0.00262483     -0.11171   0.0843743  -0.0175412  

In [27]:
minimum(u, 2)'


Out[27]:
1x3 Array{Float64,2}:
 -0.0471355  -0.468786  -0.222571

In [28]:
maximum(u, 2)'


Out[28]:
1x3 Array{Float64,2}:
 0.511168  0.563004  0.66328

In [30]:
piston_8789_P1_solution_norm = 38.52232721814439


Out[30]:
38.52232721814439

In [31]:
@assert isapprox(norm(vec(u)), piston_8789_P1_solution_norm)

In [ ]: