postprocessing utility

This commit is contained in:
Jukka Aho
2016-07-14 12:43:41 +03:00
parent 8f92a41b8a
commit 1bd1ec9fa9
21 changed files with 499 additions and 276 deletions
+4 -2
View File
@@ -65,7 +65,8 @@ export AbstractSolver, Solver, Nonlinear, NonlinearSolver, Linear, LinearSolver,
get_unknown_field_name, get_formulation_type, get_problems,
get_field_problems, get_boundary_problems,
get_field_assembly, get_boundary_assembly,
initialize!, create_projection, eliminate_interior_dofs
initialize!, create_projection, eliminate_interior_dofs,
is_field_problem, is_boundary_problem
include("solvers_modal.jl")
export Modal
@@ -135,7 +136,8 @@ export calc_nodal_values!,
copy_field!,
calculate_area,
calculate_center_of_mass,
calculate_second_moment_of_mass
calculate_second_moment_of_mass,
extract
include("postprocess_xdmf.jl")
export XDMF, xdmf_new_result!, xdmf_save_field!, xdmf_save!
export DataFrame
+62 -18
View File
@@ -6,7 +6,6 @@ importall Base
using JuliaFEM
using JuliaFEM.Preprocess
using JuliaFEM.Postprocess
using LightXML
### Model definitions for ABAQUS data model
@@ -265,8 +264,9 @@ end
typealias BOUNDARY_CONDITIONS Union{BOUNDARY, CLOAD, DLOAD, DSLOAD}
@register_abaqus_keyword("NODE PRINT")
@register_abaqus_keyword("EL PRINT")
@register_abaqus_keyword("SECTION PRINT")
typealias OUTPUT_REQUESTS Union{NODE_PRINT, SECTION_PRINT}
typealias OUTPUT_REQUESTS Union{NODE_PRINT, EL_PRINT, SECTION_PRINT}
## Properties
@@ -391,7 +391,7 @@ end
""" Dirichlet boundary condition. """
function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition, ::BOUNDARY; verbose=true)
dim = determine_problem_dimension(model)
problem = Problem(Dirichlet, "Dirichlet bc *BOUNDARY", dim, "displacement")
problem = Problem(Dirichlet, "Dirichlet boundary *BOUNDARY", dim, "displacement")
for row in bc.data
if isa(row[1], AbstractString) # node set given
@@ -462,7 +462,7 @@ function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition, ::
child_element = Element(JuliaFEM.(child_element_type), child_element_connectivity)
update!(child_element, "geometry", model.mesh.nodes)
update!(child_element, "surface pressure", pressure)
update!(child_element, "surface pressure", -pressure)
push!(problem.elements, child_element)
end
return problem
@@ -472,13 +472,18 @@ end
function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition, ::CLOAD; verbose=false)
dim = determine_problem_dimension(model)
problem = Problem(Elasticity, "Concentrated load *CLOAD", dim)
for row in bc.data
node, dof, load = row
nodes = sort(unique([row[1] for row in bc.data]))
elements = Dict()
for node in nodes
element = Element(Poi1, [node])
update!(element, "geometry", model.mesh.nodes)
update!(element, "displacement traction force $dof", load)
push!(problem.elements, element)
elements[node] = element
end
for row in bc.data
node, dof, load = row
update!(elements[node], "concentrated force $dof", load)
end
problem.elements = collect(values(elements))
return problem
end
@@ -531,8 +536,6 @@ function get_child_element(element_type::Symbol, element_side::Symbol,
process_output_request(model, solver, output_request, kind, target)
end
using DataFrames
function process_output_request(model::Model, solver::Solver, output_request::AbstractOutputRequest,
::Type{Val{:NODE}}, ::Type{Val{:PRINT}})
data = output_request.data
@@ -546,7 +549,7 @@ function process_output_request(model::Model, solver::Solver, output_request::Ab
for row in data
info(repeat("-", 80))
codes = join(row, ", ")
info("*NODE OUTPUT request, with fields $codes")
info("*NODE PRINT request, with fields $codes")
if length(options) != 0
info("Additional options: $options")
end
@@ -554,15 +557,56 @@ function process_output_request(model::Model, solver::Solver, output_request::Ab
tables = Any[]
for code in row
haskey(code_mapping, code) || continue
for problem in model.problems
field_name = code_mapping[code]
abbr = get(abbr_mapping, code, code)
table = problem(DataFrame, field_name, abbr, solver.time)
push!(tables, table)
end
field_name = code_mapping[code]
abbr = get(abbr_mapping, code, code)
table = solver(DataFrame, field_name, abbr, solver.time)
push!(tables, table)
end
length(tables) != 0 || continue
results = join(tables..., on=:id, kind=:outer)
results = join(tables..., on=:NODE, kind=:outer)
sort!(results, cols=[:NODE])
println()
println(results)
println()
end
end
function process_output_request(model::Model, solver::Solver, output_request::AbstractOutputRequest,
::Type{Val{:EL}}, ::Type{Val{:PRINT}})
data = output_request.data
options = output_request.options
code_mapping = Dict(
:COORD => "geometry",
:S => "stress",
:E => "strain")
abbr_mapping = Dict(:COORD => :COOR)
for row in data
info(repeat("-", 80))
codes = join(row, ", ")
info("*EL PRINT request, with fields $codes")
if length(options) != 0
info("Additional options: $options")
end
info(repeat("-", 80))
tables = Any[]
for code in row
haskey(code_mapping, code) || continue
field_name = code_mapping[code]
abbr = get(abbr_mapping, code, code)
table = solver(DataFrame, solver.time, Val{code})
push!(tables, table)
end
length(tables) != 0 || continue
results = first(tables)
if length(tables) > 1
for i=2:length(tables)
results = join(results, tables[i], on=:ELEMENT, kind=:outer)
end
end
sort!(results, cols=[:ELEMENT, :IP])
# filter out elements with id -1, they are automatically created boundary elements
fel = find(results[:ELEMENT] .!= Symbol("E-1"))
results = results[fel, :]
println()
println(results)
println()
+11 -1
View File
@@ -168,11 +168,13 @@ function update!(element::Element, field_name::AbstractString, datas::Union{Real
end
end
function update!(element::Element, field_name, datas::Pair...)
#=
function update!(element::Element, field_name, data::Pair...)
for data in datas
update!(element, field_name, data)
end
end
=#
function update!(element::Element, field_name, data::Pair{Float64, Vector{Any}})
if haskey(element, field_name)
@@ -190,6 +192,14 @@ function update!(element::Element, field_name, data::Pair{Float64, Vector{Int64}
end
end
function update!(element::Element, field_name, data::Pair{Float64, Vector{Float64}})
if haskey(element, field_name)
update!(element[field_name], data)
else
element[field_name] = data
end
end
function update!(element::Element, field_name, data::Pair{Float64, Vector{Vector{Float64}}})
if haskey(element, field_name)
update!(element[field_name], data)
+4
View File
@@ -22,6 +22,10 @@ function get_basis(element::Element{Poi1}, ip, time)
return [1]
end
function get_dbasis(element::Element{Poi1}, ip, time)
return [0]
end
function call(element::Element{Poi1}, ip, time::Float64, ::Type{Val{:detJ}})
return 1.0
end
+106 -21
View File
@@ -7,6 +7,7 @@ using JuliaFEM
using DataFrames
using HDF5
using LightXML
using StringUtils
import HDF5: h5read, h5write
@@ -47,21 +48,14 @@ function convert(::Type{DataFrame}, dfs::AbstractString)
return readtable(fn)
end
function getindex(df::DataFrame, ids::Vector{Symbol}, cols::Vector{Symbol})
rows = Int64[find(df[:id] .== id)[1] for id in ids]
return df[rows, cols]
end
function getindex(df::DataFrame, id::Symbol, col::Symbol)
return getindex(df, [id], [col])
end
function getindex(df::DataFrame, id::Symbol, cols::Vector{Symbol})
return getindex(df, [id], cols)
end
function getindex(df::DataFrame, ids::Vector{Symbol}, col::Symbol)
return getindex(df, ids, [col])
function extract(df::DataFrame, args...; kwargs...)
result = copy(df)
for (k,v) in kwargs
rows = find(df[k] .== v)
result = result[rows, :]
end
foo = Symbol[si for si in args]
return result[foo]
end
function vec(df::DataFrame)
@@ -72,6 +66,12 @@ function isapprox(d1::DataFrame, d2::Vector)
return isapprox(vec(d1), d2)
end
""" A more appropriate representation for floats in results. """
function DataFrames.ourshowcompact(io::IO, x::Float64)
print(io, u"\% 0.4E(x)")
return
end
"""
Calculate field values to nodal points from Gauss points using least-squares fitting.
"""
@@ -201,9 +201,13 @@ end
function call(problem::Problem, field_name::AbstractString, time::Float64=0.0)
f = Dict()
for element in get_elements(problem)
haskey(element, field_name) || continue
for (c, v) in zip(get_connectivity(element), element(field_name, time))
if haskey(f, c)
@assert isapprox(f[c], v)
if !isapprox(f[c], v)
info("several values for single node when returning field $field_name")
info("already have: $(f[c]), and trying to set $v")
end
end
f[c] = v
end
@@ -211,11 +215,10 @@ function call(problem::Problem, field_name::AbstractString, time::Float64=0.0)
return f
end
function call(problem::Problem, ::Type{DataFrame}, field_name::AbstractString,
abbreviation::Symbol, time::Float64=0.0)
u = problem(field_name, time)
function to_dataframe(u::Dict, abbreviation::Symbol)
length(u) != 0 || return DataFrame()
node_ids = collect(keys(u))
column_names = [:id]
column_names = [:NODE]
n = length(u[first(node_ids)])
index = [Symbol("N$id") for id in node_ids]
result = Any[index]
@@ -224,10 +227,92 @@ function call(problem::Problem, ::Type{DataFrame}, field_name::AbstractString,
push!(column_names, Symbol("$abbreviation$dof"))
end
df = DataFrame(result, column_names)
sort!(df, cols=[:id])
sort!(df, cols=[:NODE])
return df
end
function call(problem::Problem, ::Type{DataFrame}, field_name::AbstractString,
abbreviation::Symbol, time::Float64=0.0)
u = problem(field_name, time)
return to_dataframe(u, abbreviation)
end
function call(solver::Solver, ::Type{DataFrame}, field_name::AbstractString,
abbreviation::Symbol, time::Float64=0.0)
u = Dict()
for problem in get_problems(solver)
u = merge(u, problem(field_name, time))
end
return to_dataframe(u, abbreviation)
end
function get_components(n, m)
if n == m
if n == 1
return Vector{Int}[[1,1]]
end
if n == 2
return Vector{Int}[[1,1], [2,2], [1,2]]
elseif n == 3
return Vector{Int}[[1,1], [2,2], [3,3], [1,2], [1,3], [2,3]]
else
error("get_components, n=$n, m=$m!")
end
end
end
""" Return T in integration points. """
function call{T}(problem::Problem, ::Type{DataFrame}, element::Element, time::Float64,
::Type{Val{T}})
column_names = [:ELEMENT, :IP]
ips = get_integration_points(element)
field = Any[problem(element, ip, time, Val{T}) for ip in ips]
# FIXME, handle better ..?
first(field) == nothing && return DataFrame()
m = length(field)
n = length(first(field))
result = Any[]
push!(result, [Symbol("E$(element.id)") for i=1:m])
push!(result, [Symbol("P$i") for i=1:m])
is_tensor_field = isa(first(field), Matrix)
if is_tensor_field
n, m = size(first(field))
components = get_components(n, m)
for (j, k) in components
push!(column_names, Symbol("$T$j$k"))
push!(result, [S[j,k] for S in field])
end
else
components = collect(1:n)
for j in components
push!(column_names, Symbol("$T$j"))
push!(result, [S[j] for S in field])
end
end
df = DataFrame(result, column_names)
sort!(df, cols=[:IP])
end
function call{T}(problem::Problem, ::Type{DataFrame}, time::Float64, ::Type{Val{T}})
tables = [problem(DataFrame, element, time, Val{T}) for element in get_elements(problem)]
results = [tables...;]
return results
end
function call{T}(solver::Solver, ::Type{DataFrame}, time::Float64, ::Type{Val{T}})
problems = get_problems(solver)
tables = Any[]
for problem in get_problems(solver)
try
push!(tables, problem(DataFrame, time, Val{T}))
catch
warn("Unable to obtain results $T for problem $(problem.name)")
end
end
results = [tables...;]
return results
end
""" Interpolate field from a set of elements. """
function call(problem::Problem, field_name::AbstractString, X::Vector, time::Float64=0.0; fillna=NaN)
for element in get_elements(problem)
+11 -4
View File
@@ -91,13 +91,21 @@ function filter_by_element_set(mesh::Mesh, set_name)
filter_by_element_id(mesh::Mesh, collect(mesh.element_sets[set_name]))
end
function create_element(mesh::Mesh, id::Int)
connectivity = mesh.elements[id]
element_type = JuliaFEM.(mesh.element_types[id])
element = Element(element_type, connectivity)
update!(element, "geometry", mesh.nodes)
element.id = id
return element
end
function create_elements(mesh::Mesh; element_type=nothing)
element_ids = collect(keys(mesh.elements))
if element_type != nothing
filter!(id -> mesh.element_types[id] == element_type, element_ids)
end
elements = [Element(JuliaFEM.(mesh.element_types[id]), mesh.elements[id]) for id in element_ids]
update!(elements, "geometry", mesh.nodes)
elements = [create_element(mesh, id) for id in element_ids]
return elements
end
@@ -115,8 +123,7 @@ function create_elements(mesh::Mesh, element_sets::Symbol...; element_type=nothi
filter!(id -> mesh.element_types[id] == element_type, element_ids)
end
elements = [Element(JuliaFEM.(mesh.element_types[id]), mesh.elements[id]) for id in element_ids]
update!(elements, "geometry", mesh.nodes)
elements = [create_element(mesh, id) for id in element_ids]
return elements
end
+71 -69
View File
@@ -141,63 +141,53 @@ function get_assembly(problem)
return problem.assembly
end
""" Initialize unknown field ready for nonlinear iterations, i.e.,
take last known value and set it as a initial quess for next
time increment.
"""
function initialize!(problem::Problem, time=0.0)
""" Initialize element ready for calculation. """
function initialize!(problem::Problem, element::Element, time::Float64)
field_name = get_unknown_field_name(problem)
field_dim = get_unknown_field_dimension(problem)
for element in get_elements(problem)
gdofs = get_gdofs(problem, element)
if haskey(element, field_name)
# if field is found, copy last known solution to new time as initial guess
field = last(element[field_name])
if !isa(field, TimeVariantField)
info("Unable to initialize field $field_name for problem, is not time variant?")
continue
end
nnodes = length(element)
if !isapprox(field.time, time)
last_data = copy(last(element[field_name]).data)
push!(element[field_name], time => last_data)
end
else # if field not found at all, initialize new zero field.
data = Vector{Float64}[zeros(field_dim) for i in 1:length(element)]
element[field_name] = (time => data)
# initialize primary field
if !haskey(element, field_name)
if field_dim == 1
update!(element, field_name, time => zeros(nnodes))
else
update!(element, field_name, time => [zeros(field_dim) for i=1:nnodes])
end
end
# if this is boundary problem and not dirichlet problem, initialize field
# for primary variable too
# if boundary problem, initialize field for main problem too
is_boundary_problem(problem) || return
#is_dirichlet_problem(problem) && return
field_name = get_parent_field_name(problem)
for element in get_elements(problem)
gdofs = get_gdofs(problem, element)
if haskey(element, field_name)
# if field is found, copy last known solution to new time as initial guess
if !isapprox(last(element[field_name]).time, time)
last_data = copy(last(element[field_name]).data)
push!(element[field_name], time => last_data)
end
else # if field not found at all, initialize new zero field.
data = Vector{Float64}[zeros(field_dim) for i in 1:length(element)]
element[field_name] = (time => data)
if !haskey(element, field_name)
if field_dim == 1
update!(element, field_name, time => zeros(nnodes))
else
update!(element, field_name, time => [zeros(field_dim) for i=1:nnodes])
end
end
end
""" Update problem solution vector for assembly. """
function update_assembly!(problem, u, la; verbose=false)
function initialize!(problem::Problem, time::Float64=0.0)
for element in get_elements(problem)
initialize!(problem, element, time)
end
end
assembly = get_assembly(problem)
""" Update problem solution vector for assembly. """
function update!(problem::Problem, assembly::Assembly, u::Vector, la::Vector; verbose=false)
# resize & fill with zeros vectors if length mismatch with current solution
if length(u) != length(assembly.u)
info("resizing solution vector u")
resize!(assembly.u, length(u))
fill!(assembly.u, 0.0)
end
if length(la) != length(assembly.la)
info("resizing lagrange multipliers vector u")
resize!(assembly.la, length(la))
fill!(assembly.la, 0.0)
end
@@ -228,47 +218,54 @@ function update_assembly!(problem, u, la; verbose=false)
# calculate change of norm
assembly.u_norm_change = norm(assembly.u - assembly.u_prev)
assembly.la_norm_change = norm(assembly.la - assembly.la_prev)
#return assembly.u_norm_change, assembly.la_norm_change
return assembly.u, assembly.la
end
""" Update solutions to elements.
""" Return global solution (u, la) for problem.
Notes
-----
This assumes that element is properly initialized so that last known field data
is from current time. For boundary problems solution is updated from lambda vector
and for field problems from actual solution vector.
If length of solution vector != number of nodes, i.e. field dimension is
something other than 1, reshape vectors so it's length matches to the
number of nodes so that one can easily get nodal results.
"""
function update_elements!{P<:FieldProblem}(problem::Problem{P}, u, la)
field_name = get_unknown_field_name(problem)
function get_global_solution(problem::Problem, assembly::Assembly)
u = assembly.u
la = assembly.la
field_dim = get_unknown_field_dimension(problem)
nnodes = round(Int, length(u)/field_dim)
solution = reshape(u, field_dim, nnodes)
for element in get_elements(problem)
connectivity = get_connectivity(element) # node ids
local_sol = Vector{Float64}[solution[:, node_id] for node_id in connectivity]
last(element[field_name]).data = local_sol
if field_dim == 1
return u, la
else
nnodes = round(Int, length(u)/field_dim)
u = reshape(u, field_dim, nnodes)
u = Vector{Float64}[u[:,i] for i in 1:nnodes]
la = reshape(la, field_dim, nnodes)
la = Vector{Float64}[la[:,i] for i in 1:nnodes]
return u, la
end
end
function update_elements!{P<:BoundaryProblem}(problem::Problem{P}, u, la)
""" Update solution from assebly to elements. """
function update!{P<:FieldProblem}(problem::Problem{P}, assembly::Assembly, elements::Vector{Element}, time::Float64)
u, la = get_global_solution(problem, assembly)
field_name = get_unknown_field_name(problem)
field_dim = get_unknown_field_dimension(problem)
nnodes = round(Int, length(u)/field_dim)
solution = reshape(la, field_dim, nnodes)
for element in get_elements(problem)
connectivity = get_connectivity(element) # node ids
local_sol = Vector{Float64}[solution[:, node_id] for node_id in connectivity]
last(element[field_name]).data = local_sol
# update solution u for elements
for element in elements
connectivity = get_connectivity(element)
update!(element, field_name, time => u[connectivity])
end
# if boundary problem is not dirichlet, update also data of main problem
# is_dirichlet_problem(problem) && return
field_name = get_parent_field_name(problem)
solution = reshape(u, field_dim, nnodes)
for element in get_elements(problem)
connectivity = get_connectivity(element) # node ids
local_sol = Vector{Float64}[solution[:, node_id] for node_id in connectivity]
last(element[field_name]).data = local_sol
end
function update!{P<:BoundaryProblem}(problem::Problem{P}, assembly::Assembly, elements::Vector{Element}, time::Float64)
u, la = get_global_solution(problem, assembly)
parent_field_name = get_parent_field_name(problem) # displacement
field_name = get_unknown_field_name(problem) # reaction force
# update solution u and reaction force λ for boundary elements
for element in elements
connectivity = get_connectivity(element)
update!(element, parent_field_name, time => u[connectivity])
# FIXME
update!(element, field_name, time => -la[connectivity])
end
end
@@ -276,12 +273,16 @@ function get_elements(problem::Problem)
return problem.elements
end
function get_assembly(problem::Problem)
return problem.assembly
end
function length(problem::Problem)
return length(problem.elements)
end
function update!(problem::Problem, field_name, field)
update!(problem.elements, field_name, field)
function update!(problem::Problem, field_name::AbstractString, data)
update!(problem.elements, field_name::AbstractString, data)
end
""" Return the dimension of the unknown field of this problem. """
@@ -380,3 +381,4 @@ function find_nodes_by_dofs(dim, dofs)
end
return nodes
end
+38
View File
@@ -530,6 +530,7 @@ function assemble{El<:Elasticity3DSurfaceElements}(problem::Problem{Elasticity},
Kg = zeros(dim*nnodes, dim*nnodes)
f = zeros(dim*nnodes)
has_concentrated_forces = false
for ip in get_integration_points(element)
detJ = element(ip, time, Val{:detJ})
w = ip.weight*detJ
@@ -543,6 +544,11 @@ function assemble{El<:Elasticity3DSurfaceElements}(problem::Problem{Elasticity},
T = element("displacement traction force $i", ip, time)
f[i:dim:end] += w*vec(T*N)
end
if haskey(element, "concentrated force $i")
has_concentrated_forces = true
T = element("concentrated force $i", ip, time)
f[i:dim:end] += w*vec(T*N)
end
end
if haskey(element, "surface pressure")
J = element(ip, time, Val{:Jacobian})'
@@ -553,6 +559,9 @@ function assemble{El<:Elasticity3DSurfaceElements}(problem::Problem{Elasticity},
f += w*p*vec(n*N)
end
end
if has_concentrated_forces
update!(element, "concentrated force", time => Any[f])
end
return Km, Kg, f
end
@@ -710,3 +719,32 @@ end
=#
function call(problem::Problem, element::Element, ip, time::Float64, ::Type{Val{:E}})
haskey(element, "displacement") || return nothing
gradu = element("displacement", ip, time, Val{:Grad})
eps = 0.5*(gradu + gradu')
return eps
end
function call(problem::Problem, element::Element, ip, time::Float64, ::Type{Val{:S}})
haskey(element, "displacement") || return nothing
props = problem.properties
eps = problem(element, ip, time, Val{:E})
eps == nothing && return nothing
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))
if props.formulation in [:plane_stress, :plane_strain]
la = 2.0*la*mu/(la+2.0*mu)
end
S = la*trace(eps)*I + 2.0*mu*eps
return S
end
function call(problem::Problem, element::Element, ip, time::Float64, ::Type{Val{:COORD}})
haskey(element, "geometry") || return nothing
return element("geometry", ip, time)
end
+6 -2
View File
@@ -408,8 +408,12 @@ function update!(solver::Solver, u::Vector, la::Vector; show_info=true)
show_info && info("Updating problems ...")
t0 = Base.time()
for problem in solver.problems
u_new, la_new = update_assembly!(problem, u, la)
update_elements!(problem, u_new, la_new)
assembly = get_assembly(problem)
elements = get_elements(problem)
# update solution, first for assembly (u,la) ...
update!(problem, assembly, u, la)
# .. and then from assembly (u,la) to elements
update!(problem, assembly, elements, solver.time)
end
t1 = round(Base.time()-t0, 2)
show_info && info("Updated problems in $t1 seconds.")
+10 -8
View File
@@ -101,21 +101,23 @@ function call(solver::Solver{Modal}; show_info=true, debug=false)
info("Eigenvalues computed in $t1 seconds. Eigenvalues: $om2")
for i=1:length(om2)
freq = real(sqrt(om2[i])/(2.0*pi))
u = props.eigvecs[:,i]
field_dim = get_unknown_field_dimension(solver)
field_name = get_unknown_field_name(solver)
nnodes = round(Int, length(u)/field_dim)
solution = reshape(u, field_dim, nnodes)
if field_dim != 1
nnodes = round(Int, length(u)/field_dim)
u = reshape(u, field_dim, nnodes)
u = Vector{Float64}[u[:,i] for i in 1:nnodes]
end
for problem in get_problems(solver)
local_sol = Dict{Int64, Vector{Float64}}()
for node_id in get_connectivity(problem)
local_sol[node_id] = solution[:, node_id]
for element in get_elements(problem)
connectivity = get_connectivity(element)
update!(element, field_name, freq => u[connectivity])
end
freq = real(sqrt(om2[i])/(2.0*pi))
update!(problem, field_name, freq => local_sol)
end
end
return true
end
+57 -29
View File
@@ -8,36 +8,64 @@ using JuliaFEM.Abaqus
using JuliaFEM.Testing
# to turn on automatic file download, set
# ENV["ABAQUS_DOWNLOAD_URL"] = http://<domain>:2080/v2016/books/eif
# if don't want to download all stuff to current directory,
# set also e.g. ENV["ABAQUS_DOWNLOAD_DIR"] = "/tmp"
# ENV["ABAQUS_DOWNLOAD_URL"] = "http://<domain>:2080/v2016/books/eif"
# if don't want to download all stuff to current directory, set also
# ENV["ABAQUS_DOWNLOAD_DIR"] = "/tmp"
#=
test_name = "ecs4sfs1"
@testset "$test_name" begin
abaqus_run_test(test_name) || return
results = abaqus_read_results(test_name)
""" Run test, return true if simulation is succesfull, i.e. no errors raise
during parsing .inp file or execution of model. This doesn't mean that results
are meaningful; they must be checked in separately. Running model only verifies
that no catastrophic failures happen during file parsing. """
function abaqus_run_test(name)
return_code = abaqus_run_model(name; fetch=true, verbose=true)
return_code == 0 && return true
return false
end
=#
@testset "ec38sfs2" begin
return_code = abaqus_run_model("ec38sfs2"; fetch=true, verbose=true)
return_code == 0 || return
@test return_code == 0
#=
xdmf = abaqus_open_results("ec38sfs2")
side, opts = read_result(xdmf, "SECTION/side")
@test isapprox(side["SOFM"], 3464.0)
@test isapprox(side["SOF1"], 2000.0)
@test isapprox(side["SOF2"], 2000.0)
@test isapprox(side["SOF3"], 2000.0)
@test isapprox(side["SOMM"], 2828.0)
@test isapprox(side["SOM1"], 0.0)
@test isapprox(side["SOM2"], 2000.0)
@test isapprox(side["SOM3"], -2000.0)
@test isapprox(side["SOAREA"], 2.000)
@test isapprox(side["SOCF1"], 2/3)
@test isapprox(side["SOCF2"], 2/3)
@test isapprox(side["SOCF3"], 1/6)
=#
@testset "JuliaFEM-ABAQUS interface" begin
@testset "1 Element Verification" begin
@testset "1.2 Eigenvalue tests" begin
@testset "1.2.1 Eigenvalue extraction for single unconstrained elements" begin
@testset "Acoustic elements" begin
@testset "AC1D2 elements." begin
# abaqus_run_test("ec12afe1") || return
end
end
@testset "Three-dimensional continuum elements" begin
@testset "C3D10 elements." begin
# abaqus_run_test("ec3asfe1") || return
end
end
end
end
@testset "1.3 Simple load tests" begin
@testset "1.3.1 Membrane loading of plane stress, plane strain, membrane, and shell elements" begin
@testset "CPS4 elements." begin
# abaqus_run_test("ecs4sfs1") || return
end
end
@testset "1.3.3 Three-dimensional solid elements" begin
@testset "C3D8 elements." begin
abaqus_run_test("ec38sfs2") || return
#= to check also results:
xdmf = abaqus_open_results("ec38sfs2")
side, opts = read_result(xdmf, "SECTION/side")
@test isapprox(side["SOFM"], 3464.0)
@test isapprox(side["SOF1"], 2000.0)
@test isapprox(side["SOF2"], 2000.0)
@test isapprox(side["SOF3"], 2000.0)
@test isapprox(side["SOMM"], 2828.0)
@test isapprox(side["SOM1"], 0.0)
@test isapprox(side["SOM2"], 2000.0)
@test isapprox(side["SOM3"], -2000.0)
@test isapprox(side["SOAREA"], 2.000)
@test isapprox(side["SOCF1"], 2/3)
@test isapprox(side["SOCF2"], 2/3)
@test isapprox(side["SOCF3"], 1/6)
=#
end
end
end
end
end
+2 -2
View File
@@ -122,7 +122,7 @@ end
slaves = get_slave_elements(contact)
node_ids, la = get_nodal_vector(slaves, "reaction force", 0.0)
node_ids, n = get_nodal_vector(slaves, "normal", 0.0)
pres = [dot(ni, lai) for (ni, lai) in zip(n, la)]
pres = [dot(ni, -lai) for (ni, lai) in zip(n, la)]
#@test isapprox(maximum(pres), 4060.010799583303)
# 12 % error in maximum pressure
@test isapprox(maximum(pres), 3585.0; rtol = 12.0e-2)
@@ -137,7 +137,7 @@ end
n = sel("normal", ip, time)
t = Q'*n
la = sel("reaction force", ip, time)
Rn += w*dot(n, la)
Rn += w*dot(n, -la)
Rt += w*dot(t, la)
end
end
+26
View File
@@ -0,0 +1,26 @@
# 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.Testing
@testset "1d strain" begin
X = Dict{Int64, Vector{Float64}}(
1 => [0.0, 0.0, 0.0],
2 => [1.0, 1.0, 1.0])
u = Dict{Int64, Vector{Float64}}(
1 => [0.0, 0.0, 0.0],
2 => [1.0, 1.0, 1.0])
element = Element(Seg2, [1, 2])
update!(element, "geometry", X)
detJ = element([0.0], 0.0, Val{:detJ})
info("detJ = $detJ")
@test isapprox(detJ, sqrt(3)/2)
J = element([0.0], 0.0, Val{:Jacobian})
info("J = $J")
@test isapprox(J, [0.5 0.5 0.5])
update!(element, "displacement", u)
# FIXME
# gradu = element("displacement", [0.0], 0.0, Val{:Grad})
# info("1d bar: ∇u = $gradu")
end
@@ -7,9 +7,15 @@ using JuliaFEM.Postprocess
using JuliaFEM.Testing
#=
- solve 2d plane stress problem with known solution
- test postprocessing of nodal fields: (geometry, displacement
reaction force, concentrated force)
- solve 2d plane stress problem with known solution:
surface traction force in 2d
volume load in 2d
reaction force
- test postprocessing of nodal fields:
geometry
displacement
reaction force
concentrated force
=#
@testset "test 2d linear elasticity with surface + volume load" begin
meshfile = "/geometry/2d_block/BLOCK_1elem.med"
@@ -42,8 +48,13 @@ using JuliaFEM.Testing
update!(bc_sym_13, "displacement 2", 0.0)
solver = LinearSolver(block, traction, bc_sym_23, bc_sym_13)
# assemble!(solver)
# dump(full(bc_sym_23.assembly.C1))
solver()
info("u = ", block.assembly.u)
info("λ = ", block.assembly.la)
f = 288.0
g = 576.0
E = 288.0
@@ -51,40 +62,41 @@ using JuliaFEM.Testing
u3_expected = f/E*[-nu, 1] + g/(2*E)*[-nu, 1]
# fetch nodal results X + u and join them into one table using DataFrames
X = block(DataFrame, "geometry", :COOR, 0.0)
u = block(DataFrame, "displacement", :U, 0.0)
results = join(X, u, on=:id, kind=:outer)
X = solver(DataFrame, "geometry", :COOR)
u = solver(DataFrame, "displacement", :U)
la = solver(DataFrame, "reaction force", :RF)
f = solver(DataFrame, "concentrated force", :CF)
results = join(X, u, on=:NODE, kind=:outer)
results = join(results, la, on=:NODE, kind=:outer)
length(f) != 0 && (results = join(results, f, on=:NODE, kind=:outer))
sort!(results, cols=[:NODE])
println(results)
u3 = results[:N3, [:U1, :U2]]
u3 = extract(results, NODE=:N3, :U1, :U2)
@test isapprox(u3, u3_expected)
#=
info("strain")
for ip in get_integration_points(block.elements[1])
eps = ip("strain")
@printf "%i | %8.3f %8.3f | %8.3f %8.3f %8.3f\n" ip.id ip.coords[1] ip.coords[2] eps[1] eps[2] eps[3]
# TODO: to postprocess ...?
#@test isapprox(eps, [u3[1], u3[2], 0.0])
end
# element details
el = first(block.elements)
S1 = block(el, [0.0, 0.0], 0.0, Val{:S})
S1 = S1[[1,4,2]]
E1= block(el, [0.0, 0.0], 0.0, Val{:E})
E1 = E1[[1,4,2]]
C1 = block(el, [0.0, 0.0], 0.0, Val{:COORD})
info("strain = $E1, stress = $S1, at $C1")
@test isapprox(E1, [-2/3, 2.0, 0.0])
@test isapprox(S1, [0.0, 576.0, 0.0])
@test isapprox(C1, [0.5, 0.5])
info("stress")
for ip in get_integration_points(block.elements[1])
sig = ip("stress")
@printf "%i | %8.3f %8.3f | %8.3f %8.3f %8.3f\n" ip.id ip.coords[1] ip.coords[2] sig[1] sig[2] sig[3]
# TODO: to postprocess
#@test isapprox(sig, [0.0, g, 0.0])
end
S1 = block(DataFrame, 0.0, Val{:S})
E1 = block(DataFrame, 0.0, Val{:E})
C1 = block(DataFrame, 0.0, Val{:COORD})
calc_nodal_values!(block.elements, "strain", 3, 0.0)
calc_nodal_values!(block.elements, "stress", 3, 0.0)
info(block.elements[1]["stress"](0.0))
node_ids, strain = get_nodal_vector(block.elements, "strain", 0.0)
node_ids, stress = get_nodal_vector(block.elements, "stress", 0.0)
# TODO: to postprocess
#@test isapprox(stress[1], [0.0, g, 0.0])
#@test isapprox(strain[1], [u3[1], u3[2], 0.0])
=#
println(S1)
println(E1)
println(C1)
S = solver(DataFrame, 0.0, Val{:S})
println(S)
end
+2
View File
@@ -106,6 +106,7 @@ end
@test isapprox(fb, 1.0)
end
#= unnecessary feature
@testset "add two time dependent fields to element at once" begin
el = Element(Seg2, [1, 2])
update!(el, "foo1", 1.0 => 1.0)
@@ -113,6 +114,7 @@ end
update!(el, "foo2", 1.0 => 1.0, 2.0 => 2.0)
@test isapprox(el("foo1", 1.5), el("foo2", 1.5))
end
=#
@testset "add elements to elements" begin
el1 = Element(Seg2, [1, 2])
+2 -1
View File
@@ -58,7 +58,8 @@ end
el2 = Element(Seg2, [1, 2])
update!(el2, "geometry", X)
# linear ramp from 0 -> 6 in time 0 -> 1
update!(el2, "temperature flux", 0.0 => 0.0, 1.0 => 6.0)
update!(el2, "temperature flux", 0.0 => 0.0)
update!(el2, "temperature flux", 1.0 => 6.0)
# define heat problem and push elements to problem
problem = Problem(Heat, "one element heat problem", 1)
+25 -33
View File
@@ -4,7 +4,7 @@
using JuliaFEM
using JuliaFEM.Testing
@testset "test eigenvalues for single tet4 element" begin
function get_model()
X = Dict{Int, Vector{Float64}}(
1 => [2.0, 3.0, 4.0],
2 => [6.0, 3.0, 2.0],
@@ -19,33 +19,38 @@ using JuliaFEM.Testing
e2 = Element(Tri3, [1, 2, 3])
update!([e1, e2], "geometry", X)
update!([e1, e2], "displacement", 0.0 => u)
update!(e1, "youngs modulus" => 96.0,
"poissons ratio" => 1.0/3.0,
"density" => 420.0)
update!(e2, "displacement 1" => 0.0,
"displacement 2" => 0.0,
"displacement 3" => 0.0)
update!(e1, "youngs modulus" => 96.0)
update!(e1, "poissons ratio" => 1.0/3.0)
update!(e1, "density" => 420.0)
update!(e2, "displacement 1" => 0.0)
update!(e2, "displacement 2" => 0.0)
update!(e2, "displacement 3" => 0.0)
p1 = Problem(Elasticity, 3)
p1.properties.finite_strain = false
p1.properties.geometric_stiffness = false
p2 = Problem(Dirichlet, p1)
push!(p1, e1)
push!(p2, e2)
s1 = Solver(Modal)
s1.properties.which = :LM
push!(s1, p1, p2)
solver = Solver(Modal)
solver.properties.which = :LM
push!(solver, p1, p2)
return solver
end
s1(; debug=true)
@test isapprox(s1.properties.eigvals, [4/3, 1/3])
@testset "test eigenvalues for single tet4 element" begin
solver = get_model()
solver(; debug=true)
@test isapprox(solver.properties.eigvals, [4/3, 1/3])
end
empty!(p1)
empty!(p2)
empty!(p1.assembly.M)
# p1.properties.finite_strain = true
p1.properties.geometric_stiffness = true
s1.properties.geometric_stiffness = true
s1(; debug=true)
@test isapprox(s1.properties.eigvals, [5/3, 2/3])
@testset "test eigenvalues for single tet4 element, with geometric stiffness" begin
solver = get_model()
problem = first(solver.problems)
# problem.properties.finite_strain = true
problem.properties.geometric_stiffness = true
solver.properties.geometric_stiffness = true
solver(; debug=true)
@test isapprox(solver.properties.eigvals, [5/3, 2/3])
end
@testset "test poisson problem modal analysis without tie" begin
@@ -58,10 +63,6 @@ end
6 => [1.0, 3.0],
7 => [1.0, 9.0],
8 => [0.0, 9.0])
T = Dict{Int64, Float64}()
for i=1:8
T[i] = 0.0
end
el1 = Element(Quad4, [1, 2, 3, 4])
el2 = Element(Quad4, [4, 3, 7, 8])
el3 = Element(Seg2, [1, 2])
@@ -69,15 +70,12 @@ end
update!([el1, el2, el3, el4], "geometry", X)
update!([el1, el2], "density", 6.0)
update!([el1, el2], "temperature thermal conductivity", 36.0)
#update!([el1, el2], "temperature", 0.0 => T)
update!([el1, el2], "temperature", T)
update!([el3, el4], "temperature 1", 0.0)
p1 = Problem(Heat, "combined body", 1)
p1.properties.formulation = "2D"
p2 = Problem(Dirichlet, "fixed ends", 1, "temperature")
push!(p1, el1, el2)
push!(p2, el3, el4)
solver = Solver(Modal)
push!(solver, p1, p2)
solver()
@@ -94,10 +92,6 @@ end
6 => [1.0, 3.0],
7 => [1.0, 9.0],
8 => [0.0, 9.0])
T = Dict{Int64, Float64}()
for i=1:8
T[i] = 0.0
end
el1 = Element(Quad4, [1, 2, 3, 4])
el2 = Element(Quad4, [5, 6, 7, 8])
el3 = Element(Seg2, [1, 2])
@@ -105,8 +99,6 @@ end
el5 = Element(Seg2, [3, 4])
el6 = Element(Seg2, [5, 6])
update!([el1, el2, el3, el4, el5, el6], "geometry", X)
#update!([el1, el2], "temperature", 0.0 => T)
update!([el1, el2], "temperature", T)
update!([el1, el2], "density", 6.0)
update!([el1, el2], "temperature thermal conductivity", 36.0)
update!([el3, el4], "temperature 1", 0.0)
+2 -1
View File
@@ -81,5 +81,6 @@ end
la = slave("reaction force", [0.0], 0.0)
info("u = $u, la = $la")
@test isapprox(u, [-0.2, -0.15])
@test isapprox(la, [0.0, 30.375])
@test isapprox(la, [0.0, -30.375])
# FIXME
end
+2 -1
View File
@@ -187,8 +187,9 @@ end
slave_elements = get_slave_elements(interface)
node_ids, la = get_nodal_vector(slave_elements, "reaction force", 0.0)
for lai in la
@test isapprox(lai, [0.0, 10.0])
@test isapprox(lai, [0.0, -10.0])
end
# FIXME
end
function JuliaFEM.get_mesh(::Type{Val{Symbol("curved 2d block splitted to upper and lower")}})
-52
View File
@@ -72,58 +72,6 @@ testdata = """\
</Xdmf>
"""
function test_write_to_xml()
nodes = Vector{Float64}[
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[0.5, 0.0, 0.0],
[0.5, 0.5, 0.0],
[0.0, 0.5, 0.0],
[0.0, 0.0, 0.5],
[0.5, 0.0, 0.5],
[0.0, 0.5, 0.5],
[1.0, 1.0, 1.0],
[2.0, 1.0, 1.0],
[1.0, 2.0, 1.0],
[1.0, 1.0, 2.0],
[1.5, 1.0, 1.0],
[1.5, 1.5, 1.0],
[1.0, 1.5, 1.0],
[1.0, 1.0, 1.5],
[1.5, 1.0, 1.5],
[1.0, 1.5, 1.5]]
elements = [
(:Tet10, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
(:Tet10, [11, 12, 13, 14, 15, 16, 17, 18, 19, 20])]
displacement_field = nodes # same structure
xdoc, model = JuliaFEM.Postprocess.xdmf_new_model()
temporal_collection = JuliaFEM.Postprocess.xdmf_new_temporal_collection(model)
grid = JuliaFEM.Postprocess.xdmf_new_grid(temporal_collection; time=1)
JuliaFEM.Postprocess.xdmf_new_mesh!(grid, nodes, elements)
JuliaFEM.Postprocess.xdmf_new_nodal_field!(grid, "Displacement", displacement_field)
JuliaFEM.Postprocess.xdmf_save_model(xdoc, "/tmp/foo.xmf")
#info("exported data model: \n$(string(xdoc))")
#@test string(xdoc) == testdata
d1 = split(string(xdoc), "\n")
# d2 = split(testdata, "\n")
d2 = open(readlines, Pkg.dir("JuliaFEM")*"/test/testdata/quad_two_tet10.xmf")
println("comparing string")
for i in 1:length(d1)
println("d1: $(d1[i])")
println("d2: $(d2[i])")
#status = d1 == d2 ? "MATCHES" : "NO MATCH"
#info("line: $(d1[i]) $status")
#if d1 != d2
# info("should be:\n$(d2[i])")
#end
d1 == d2 || error("No match")
end
end
@testset "write simple xmf file" begin
X = Dict{Int64, Vector{Float64}}(
1 => [0.0, 0.0],
+15 -1
View File
@@ -4,7 +4,7 @@
using JuliaFEM
using JuliaFEM.Testing
@testset "test initialize field problem" begin
@testset "test initialize scalar field problem" begin
el = Element(Seg2, [1, 2])
pr = Problem(Heat, 1)
push!(pr, el)
@@ -19,6 +19,20 @@ using JuliaFEM.Testing
@test length(last(el, "temperature").data) == 2
end
@testset "test initialize vector field problem" begin
el = Element(Seg2, [1, 2])
pr = Problem(Elasticity, 2)
push!(pr, el)
initialize!(pr)
@test haskey(el, "displacement")
@test length(el["displacement"]) == 1
# this way we access to field at default time t=0.0, it's different than ^!
@test length(el("displacement")) == 2
# length of single increment
@test length(el("displacement", 0.0)) == 2
@test length(last(el, "displacement").data) == 2
end
@testset "test initialize boundary problem" begin
el = Element(Seg2, [1, 2])
pr = Problem(Dirichlet, "bc", 1, "temperature")