mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-10-01 22:09:20 +00:00
dcd24e8e01
this allows installing and loading the package from elsewhere
190 lines
7.1 KiB
Julia
190 lines
7.1 KiB
Julia
# This file is a part of JuliaFEM.
|
||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||
|
||
using JuliaFEM
|
||
using JuliaFEM.Preprocess
|
||
using JuliaFEM.Testing
|
||
|
||
function get_stress_tensor(element, ip, time)
|
||
haskey(element, "displacement") || return nothing
|
||
gradu = element("displacement", ip, time, Val{:Grad})
|
||
eps = 0.5*(gradu' + gradu)
|
||
E = element("youngs modulus", ip, time)
|
||
nu = element("poissons ratio", ip, time)
|
||
mu = E/(2.0*(1.0+nu))
|
||
la = E*nu/((1.0+nu)*(1.0-2.0*nu))
|
||
S = la*trace(eps)*I + 2.0*mu*eps
|
||
return S
|
||
end
|
||
|
||
""" Return stress vector in "ABAQUS" order 11, 22, 33, 12, 23, 13. """
|
||
function get_stress(element, ip, time)
|
||
S = get_stress_tensor(element, ip, time)
|
||
return [S[1,1], S[2,2], S[3,3], S[1,2], S[2,3], S[1,3]]
|
||
end
|
||
|
||
""" Return principal stresses. """
|
||
function get_stress_principal(element, ip, time)
|
||
S = get_stress_tensor(element, ip, time)
|
||
return sort(eigvals(S))
|
||
end
|
||
|
||
""" Make least squares fit for some field to nodes. """
|
||
function lsq_fit(elements, field, time)
|
||
A = SparseMatrixCOO()
|
||
b = SparseMatrixCOO()
|
||
volume = 0.0
|
||
for element in elements
|
||
gdofs = get_connectivity(element)
|
||
# increase integration order by 1 from default
|
||
for ip in get_integration_points(element, 1)
|
||
detJ = element(ip, time, Val{:detJ})
|
||
w = ip.weight*detJ
|
||
N = element(ip, time)
|
||
f = field(element, ip, time)
|
||
add!(A, gdofs, gdofs, w*kron(N', N))
|
||
for i=1:length(f)
|
||
add!(b, gdofs, w*f[i]*N, i)
|
||
end
|
||
volume += w
|
||
end
|
||
end
|
||
info("Mass matrix for least-squares fit is assembled. Total volume to fit: $volume")
|
||
A = sparse(A)
|
||
b = sparse(b)
|
||
A = 1/2*(A + A')
|
||
|
||
SparseArrays.droptol!(A, 1.0e-6)
|
||
SparseArrays.dropzeros!(A)
|
||
nz = get_nonzero_rows(A)
|
||
F = ldltfact(A[nz,nz])
|
||
|
||
x = zeros(size(b)...)
|
||
x[nz, :] = F \ b[nz, :]
|
||
|
||
nodal_values = Dict(i => vec(x[i,:]) for i in nz)
|
||
return nodal_values
|
||
end
|
||
|
||
#=
|
||
test subjects:
|
||
- surface pressure load in curved surface
|
||
- verification of elements wedge6 and wedge15
|
||
- stress interpolation from gauss points to nodes
|
||
|
||
from Code Aster, for linear model
|
||
N38 -8.85861895037377E-01 -3.46944695195361E-18 -3.46944695195361E-18
|
||
coords of N38 = (1.0, 0.0, 0.0)
|
||
N49 -4.50934684240566E-01 -4.46908405333021E-01 -5.92329377847994E-01
|
||
coords of N49 = (0.521002, 0.515482, 0.68032) # quite middle of surface
|
||
|
||
Analytical solution
|
||
uᵣ(r) = b³p/(2Er²(a³-b³)) * (a³(ν+1) + r³(-4ν+2)), where
|
||
a = inner surface radial distance, b = outer surface ...
|
||
|
||
if a=0.9, b=1.0, ν=1/3, E = 24580 and p = 7317 equation yields
|
||
-9/10 for radial displacement
|
||
|
||
http://mms2.ensmp.fr/emms_paris/plasticite3D/exercices/eSpherePress.pdf
|
||
=#
|
||
function test_wedge_sphere(model, u_CA, S_CA)
|
||
mesh_file = @__DIR__() * "/testdata/primitives.med"
|
||
mesh = aster_read_mesh(mesh_file, model)
|
||
body = Problem(Elasticity, "hollow sphere 1/8 model", 3)
|
||
body.elements = create_elements(mesh, "HOLLOWSPHERE8")
|
||
update!(body, "youngs modulus", 24580.0)
|
||
update!(body, "poissons ratio", 1/3)
|
||
bc = Problem(Dirichlet, "symmetry bc", 3, "displacement")
|
||
el1 = create_elements(mesh, "FACE1")
|
||
update!(el1, "displacement 3", 0.0)
|
||
el2 = create_elements(mesh, "FACE2")
|
||
update!(el2, "displacement 2", 0.0)
|
||
el3 = create_elements(mesh, "FACE3")
|
||
update!(el3, "displacement 1", 0.0)
|
||
bc.elements = [el1; el2; el3]
|
||
lo = Problem(Elasticity, "pressure load", 3)
|
||
lo.elements = create_elements(mesh, "OUTER")
|
||
update!(lo, "surface pressure", 7317.0)
|
||
solver = LinearSolver(body, bc, lo)
|
||
solver()
|
||
|
||
X = lo("geometry")
|
||
u = lo("displacement")
|
||
nids = sort(collect(keys(X)))
|
||
umag = Float64[norm(u[id]) for id in nids]
|
||
um = mean(umag)
|
||
us = std(umag)
|
||
rtol = norm(um - 0.9) / max(norm(um), 0.9) * 100.0
|
||
info("mean umag = $um, std umag = $us, rtol = $rtol")
|
||
@test rtol < 1.5 # percents
|
||
|
||
info("Verifying displacement against Code Aster solution.. ")
|
||
for nid in keys(u_CA)
|
||
rtol = norm(u[nid] - u_CA[nid]) / max(norm(u[nid]), norm(u_CA[nid])) * 100.0
|
||
info("Node id $nid, rel diff to CA = $rtol %")
|
||
@test isapprox(u[nid], u_CA[nid])
|
||
end
|
||
|
||
S = lsq_fit(body.elements, get_stress, 0.0)
|
||
|
||
info("Verifying stress against Code Aster solution.. ")
|
||
for nid in keys(S_CA)
|
||
rtol = norm(S[nid] - S_CA[nid]) / max(norm(S[nid]), norm(S_CA[nid])) * 100.0
|
||
info("Node id $nid, S=$(S[nid]), S_CA=$(S_CA[nid]), rel diff to CA = $rtol %")
|
||
#@test isapprox(S[nid], S_CA[nid])
|
||
# http://code-aster.org/doc/default/en/man_r/r3/r3.06.03.pdf
|
||
@test rtol < 10.0 # percents
|
||
end
|
||
|
||
# Calculate principal stresses in nodes
|
||
Sp = Dict()
|
||
for (nid, s) in S
|
||
# order is: 11, 22, 33, 12, 23, 13
|
||
stress_tensor = [
|
||
s[1] s[4] s[6]
|
||
s[4] s[2] s[5]
|
||
s[6] s[5] s[3]]
|
||
Sp[nid] = sort(eigvals(stress_tensor))
|
||
end
|
||
|
||
node_ids = sort(collect(keys(Sp)))
|
||
for (i, nid) in enumerate(node_ids)
|
||
println("$nid -> $(Sp[nid])")
|
||
if i > 9
|
||
println("...")
|
||
break
|
||
end
|
||
end
|
||
# Stress state in M141 element
|
||
S_CA_gp = Dict()
|
||
S_CA_gp[1] = [-2.31497155149131E+04, -2.30873958541450E+04, -2.59731750416165E+04, 1.16262216932559E+04, 1.05883267606309E+04, 1.04255059462671E+04]
|
||
S_CA_gp[2] = [-2.20268425477595E+04, -2.59602179285979E+04, -2.45141753387488E+04, 1.08344827948196E+04, 1.02393080182646E+04, 1.18221520216293E+04]
|
||
S_CA_gp[3] = [-2.12175032863505E+04, -2.38106746858742E+04, -2.75848287641255E+04, 1.23383626529382E+04, 9.45231798421561E+03, 1.04600859263009E+04]
|
||
S_CA_gp[4] = [-2.88674347639302E+04, -2.90047144010521E+04, -3.19545639967296E+04, 1.24436733999204E+04, 1.14033189825355E+04, 1.09837505652098E+04]
|
||
S_CA_gp[5] = [-2.76741820176108E+04, -3.20576001061179E+04, -3.04041166591832E+04, 1.16023096459047E+04, 1.10324243354755E+04, 1.24679360552984E+04]
|
||
S_CA_gp[6] = [-2.68141147396280E+04, -2.97733271271234E+04, -3.36672334497799E+04, 1.32004501509553E+04, 1.01961070966382E+04, 1.10204979599285E+04]
|
||
|
||
time = 0.0
|
||
for element in body.elements
|
||
element.id == 141 || continue
|
||
for (i, ip) in enumerate(get_integration_points(element))
|
||
S = get_stress(element, ip, time)
|
||
rtol = norm(S - S_CA_gp[i]) / max(norm(S), norm(S_CA_gp[i]))
|
||
info("$i $S, rtol=$rtol")
|
||
@test isapprox(S, S_CA_gp[i])
|
||
end
|
||
end
|
||
end
|
||
|
||
@testset """1/8 hollow sphere with surface load""" begin
|
||
u_CA = Dict()
|
||
u_CA[38] = [-8.85861895037377E-01, -3.46944695195361E-18, -3.46944695195361E-18]
|
||
u_CA[49] = [-4.50934684240566E-01, -4.46908405333021E-01, -5.92329377847994E-01]
|
||
S_CA = Dict()
|
||
S_CA[38] = [-1.94504819940510E+03, -3.47014655438479E+04, -3.40876135857114E+04, 1.70379805227866E+03, 2.16707698388864E+03, 4.32009983342141E-12]
|
||
S_CA[49] = [-2.36067010168718E+04, -2.33820775692753E+04, -1.80606705820104E+04, 8.85783922092890E+03, 1.12122431934777E+04, 1.14035796957343E+04]
|
||
test_wedge_sphere("HOLLOWSPHERE8_WEDGE6", u_CA, S_CA)
|
||
#test_wedge_sphere("HOLLOWSPHERE8_WEDGE15")
|
||
end
|
||
|