lot of new tests, echangement of modal solver, eigenvalue analysis with mesh tie

This commit is contained in:
Jukka Aho
2016-07-27 18:12:24 +03:00
parent fbcc1912d8
commit 67ace8acdc
21 changed files with 717 additions and 35 deletions
+2 -1
View File
@@ -115,7 +115,8 @@ include("preprocess_aster_reader.jl")
export aster_create_elements, parse_aster_med_file, is_aster_mail_keyword,
parse_aster_header, aster_parse_nodes, aster_renumber_nodes!,
aster_renumber_elements!, aster_combine_meshes, aster_read_mesh,
filter_by_element_set, filter_by_element_id, MEDFile
filter_by_element_set, filter_by_element_id, MEDFile, aster_read_data,
aster_read_mesh_names, aster_read_node_sets, aster_read_nodes, RMEDFile
end
function get_mesh(mesh_name::AbstractString, args...; kwargs...)
+1 -1
View File
@@ -672,7 +672,7 @@ function abaqus_download(name)
fn = rstrip(ENV["ABAQUS_DOWNLOAD_DIR"], '/') * "/" * fn
end
if !isfile(fn)
info("Downloading model $name from $url to $fn")
info("Downloading model $name ...")
download("$url/$name.inp", fn)
end
return 0
+14 -2
View File
@@ -130,7 +130,7 @@ function calc_nodal_values!(elements::Vector, field_name, field_dim, time;
end
end
function calc_nodal_values!(problem::Problem, field_name, field_dim, time)
function calc_nodal_values!(problem::Problem, field_name::AbstractString, field_dim::Int, time::Float64)
# after all, it's just a mass matrix ...
# isempty(problem.assembly.M) && assemble!(problem, time, Val{:mass_matrix}; density=1.0, dual_basis=false, dim=1)
# M = sparse(problem.assembly.M)
@@ -141,7 +141,7 @@ end
"""
Return node ids + vector of values
"""
function get_nodal_vector(elements, field_name, time)
function get_nodal_vector(elements::Vector, field_name::AbstractString, time::Float64)
f = Dict()
for element in elements
for (c, v) in zip(get_connectivity(element), element[field_name](time))
@@ -337,6 +337,18 @@ function call(problem::Problem, field_name::AbstractString, X::Vector, time::Flo
return fillna
end
function call(solver::Solver, field_name::AbstractString, X::Vector, time::Float64; fillna=NaN)
for problem in get_problems(solver)
for element in get_elements(problem)
if inside(element, X, time)
xi = get_local_coordinates(element, X, time)
return element(field_name, xi, time)
end
end
end
return fillna
end
""" Calculate area of cross-section. """
function calculate_area(problem::Problem, X=[0.0, 0.0], time=0.0)
A = 0.0
+12
View File
@@ -165,6 +165,18 @@ function reorder_element_connectivity!(mesh::Mesh, mapping::Dict{Symbol, Vector{
end
end
function JuliaFEM.Problem{P<:FieldProblem}(mesh::Mesh, ::Type{P}, name::AbstractString, dimension::Int64)
problem = Problem{P}(name, dimension, "none", [], Dict(), Assembly(), P())
problem.elements = create_elements(mesh, name)
return problem
end
function JuliaFEM.Problem{P<:BoundaryProblem}(mesh::Mesh, ::Type{P}, name, dimension, parent_field_name)
problem = Problem{P}(name, dimension, parent_field_name, [], Dict(), Assembly(), P())
problem.elements = create_elements(mesh, name)
return problem
end
"""
Swap surface element connectivity s.t. normals point outward
"""
+70 -6
View File
@@ -55,19 +55,17 @@ function parse(mesh, ::Type{Val{:CODE_ASTER_MAIL}})
end
"""
Code Aster binary file (.med), which is exported from SALOME.
"""
""" Code Aster binary file (.med). """
type MEDFile
data :: Dict
end
function MEDFile(fn)
MEDFile(h5read(fn, "/"))
return MEDFile(h5read(fn, "/"))
end
function get_mesh_names(med::MEDFile)
return collect(keys(med.data["FAS"]))
return sort(collect(keys(med.data["FAS"])))
end
function get_nodes(med::MEDFile, nsets, mesh_name)
@@ -91,6 +89,13 @@ end
function get_node_sets(med::MEDFile, mesh_name)
ns = Dict{Int64, Symbol}(0 => :NALL)
if !haskey(med.data["FAS"], mesh_name)
warn("Mesh $mesh_name not found from med file.")
meshes = get_mesh_names(med)
all_meshes = join(meshes, ", ")
warn("Available meshes: $all_meshes")
error("Mesh $mesh_name not found.")
end
haskey(med.data["FAS"][mesh_name], "NOEUD") || return ns
nsets = med.data["FAS"][mesh_name]["NOEUD"]
for nset in keys(nsets)
@@ -250,4 +255,63 @@ function aster_read_mesh(fn, mesh_name=nothing; reorder_element_connectivity=tru
return mesh
end
# TODO: refactor and remove obsolete stuff.
""" Code Aster result file (.rmed). """
type RMEDFile
data :: Dict
end
function RMEDFile(fn)
return RMEDFile(h5read(fn, "/"))
end
""" Return nodes from result med file. """
function aster_read_nodes(rmed::RMEDFile)
increments = keys(rmed.data["ENS_MAA"]["MAIL"])
@assert length(increments) == 1
increment = first(increments)
nodes = rmed.data["ENS_MAA"]["MAIL"][increment]["NOE"]
node_names = nodes["NOM"]
node_coords = nodes["COO"]
nnodes = length(node_names)
dim = round(Int, length(node_coords)/nnodes)
node_coords = reshape(node_coords, nnodes, dim)'
stripper(node_name) = strip(ascii(pointer(convert(Vector{UInt8}, node_name))))
node_names = map(stripper, node_names)
# INFO: quite safe assumption is that id is in node name, i.e. N1 => 1, N123 => 123
node_id(node_name) = parse(matchall(r"\d+", node_name)[1])
node_ids = map(node_id, node_names)
nodes = Dict([j => node_coords[:,j] for j in node_ids])
return nodes
end
""" Read nodal field from rmed file. """
function aster_read_data(rmed::RMEDFile, field_name; field_type=:NODE,
info_fields=true, node_ids=nothing)
if contains(field_name, "ELGA")
field_type = :GAUSS
end
if node_ids == nothing
nodes = aster_read_nodes(rmed)
node_ids = sort(collect(keys(nodes)))
end
if info_fields
field_names = keys(rmed.data["CHA"])
all_fields = join(field_names, ", ")
info("results: $all_fields")
end
chdata = rmed.data["CHA"]["RESU____$field_name"]
@assert length(chdata) == 1
increment = chdata[first(keys(chdata))]
if field_type == :NODE
data = increment["NOE"]["MED_NO_PROFILE_INTERNAL"]["CO"]
results = Dict([j => data[j] for j in node_ids])
else
error("Unable to read result of type $field_type: not implemented")
end
return results
end
+4 -4
View File
@@ -104,10 +104,10 @@ julia> prob2 = Problem(Elasticity, 3)
"""
function Problem{P<:FieldProblem}(::Type{P}, name::AbstractString, dimension::Int64)
Problem{P}(name, dimension, "none", [], Dict(), Assembly(), P())
return Problem{P}(name, dimension, "none", [], Dict(), Assembly(), P())
end
function Problem{P<:FieldProblem}(::Type{P}, dimension::Int64)
Problem{P}("$P problem", dimension, "none", [], Dict(), Assembly(), P())
return Problem{P}("$P problem", dimension, "none", [], Dict(), Assembly(), P())
end
""" Construct a new boundary problem.
@@ -120,13 +120,13 @@ julia> bc1 = Problem(Dirichlet, "support", 3, "displacement")
"""
function Problem{P<:BoundaryProblem}(::Type{P}, name, dimension, parent_field_name)
Problem{P}(name, dimension, parent_field_name, [], Dict(), Assembly(), P())
return Problem{P}(name, dimension, parent_field_name, [], Dict(), Assembly(), P())
end
function Problem{P<:BoundaryProblem}(::Type{P}, main_problem::Problem)
name = "$P problem"
dimension = get_unknown_field_dimension(main_problem)
parent_field_name = get_unknown_field_name(main_problem)
Problem{P}(name, dimension, parent_field_name, [], Dict(), Assembly(), P())
return Problem{P}(name, dimension, parent_field_name, [], Dict(), Assembly(), P())
end
function get_formulation_type{P<:FieldProblem}(problem::Problem{P})
+17 -2
View File
@@ -86,6 +86,21 @@ function get_cells(P, C)
info("indices = $indices")
end
""" Test does P contain q. """
function contains{T}(P::Vector{T}, q::T; check_is_close=true, rtol=1.0e-5)
if q in P
return true
end
if check_is_close
for p in P
if isapprox(p, q; rtol=rtol)
return true
end
end
end
return false
end
function get_polygon_clip(xs, xm, n; debug=false)
# objective: search does line xm1 - xm2 clip xs
nm = length(xm)
@@ -103,7 +118,7 @@ function get_polygon_clip(xs, xm, n; debug=false)
# 2. test is slave point inside master, if yes, add to clip
for i=1:ns
if vertex_inside_polygon(xs[i], xm)
xs[i] in P && continue
contains(P, xs[i]) && continue
debug && info("2. $(xs[i]) inside M -> push")
push!(P, xs[i])
end
@@ -126,7 +141,7 @@ function get_polygon_clip(xs, xm, n; debug=false)
q = xs1 + t*(xs2 - xs1)
#info("t=$t, q=$q, q ∈ xm ? $(vertex_inside_polygon(q, xm))")
if vertex_inside_polygon(q, xm)
q in P && continue
contains(P, q) && continue
debug && info("3. $q inside M -> push")
push!(P, q)
end
+29 -2
View File
@@ -217,6 +217,19 @@ function create_projection(C::SparseMatrixCSC, g; S=nothing, tol=1.0e-12)
return P, h
end
""" Assume C is invertible. """
function create_projection(C, g, ::Type{Val{:invertible}})
nz1, nz2 = get_nonzeros(C)
P = spzeros(size(C)...)
for j=1:size(C,1)
j in nz1 && continue
P[j,j] = 1.0
end
v = lufact(C[nz1,nz2]) \ full(g[nz1])
return P, v
end
"""
Solve linear system using LDLt factorization (SuiteSparse). This version
@@ -332,20 +345,34 @@ function solve_linear_system(solver::Solver; F=nothing, empty_assemblies_before_
end
""" Default assembler for solver. """
function assemble!(solver::Solver; show_info=true)
function assemble!(solver::Solver; show_info=true, timing=true)
show_info && info("Assembling problems ...")
t0 = Base.time()
assembly_times = Dict()
nproblems = 0
ndofs = 0
for problem in solver.problems
t00 = Base.time()
empty!(problem.assembly)
assemble!(problem, solver.time)
nproblems += 1
ndofs = max(ndofs, size(problem.assembly.K, 2))
Ks = size(problem.assembly.K, 2)
Cs = size(problem.assembly.C1, 2)
ndofs = max(ndofs, Ks, Cs)
t11 = Base.time()
assembly_times[problem.name] = t11-t00
end
solver.ndofs = ndofs
t1 = round(Base.time()-t0, 2)
show_info && info("Assembled $nproblems problems in $t1 seconds. ndofs = $ndofs.")
if timing
info("Assembly times:")
for (i, problem) in enumerate(solver.problems)
pn = problem.name
pt = round(assembly_times[pn], 2)
info("$i $pn $pt")
end
end
end
function get_unknown_fields(solver::Solver)
+66 -10
View File
@@ -23,7 +23,7 @@ function Modal(nev=10, which=:SM)
solver = Modal(false, Vector(), Matrix(), nev, which)
end
function call(solver::Solver{Modal}; show_info=true, debug=false)
function call(solver::Solver{Modal}; show_info=true, debug=false, bc_invertible=false)
show_info && info(repeat("-", 80))
show_info && info("Starting natural frequency solver")
show_info && info("Increment time t=$(round(solver.time, 3))")
@@ -48,16 +48,59 @@ function call(solver::Solver{Modal}; show_info=true, debug=false)
if solver.properties.geometric_stiffness
K += Kg
end
@assert nnz(D) == 0
@assert C1 == C2
tic()
P, h = create_projection(C1, g)
if bc_invertible
P, h = create_projection(C1, g, Val{:invertible})
else
P, h = create_projection(C1, g)
end
K_red = P'*K*P
M_red = P'*M*P
# make sure matrices are symmetric
K_red = 1/2*(K_red + K_red')
M_red = 1/2*(M_red + M_red')
#=
ndim = size(C1,1)
nz = get_nonzero_rows(C1)
nz = setdiff(collect(1:ndim), nz)
g = zeros(ndim)
P = spzeros(ndim, ndim)
for j in nz
P[j,j] = 1.0
end
K_red = P'*K*P
M_red = P'*M*P
# make sure matrices are symmetric
K_red = 1/2*(K_red + K_red')
M_red = 1/2*(M_red + M_red')
#=
K_red = K[nz,nz]
M_red = M[nz,nz]
# make sure matrices are symmetric
K_red = 1/2*(K_red + K_red')
M_red = 1/2*(M_red + M_red')
=#
#=
K_red = copy(K)
M_red = copy(M)
for j=1:size(K_red)
j in nz && continue
K_red[j,:] = 0.0
K_red[:,j] = 0.0
M_red[j,:] = 0.0
M_red[:,j] = 0.0
end
=#
=#
t1 = round(toq(), 2)
info("Eliminated dirichlet boundaries in $t1 seconds.")
@@ -79,16 +122,30 @@ function call(solver::Solver{Modal}; show_info=true, debug=false)
om2, X = eigs(K_red[nz,nz], M_red[nz,nz]; nev=props.nev, which=props.which)
catch
info("failed to calculate eigenvalues")
info("K sym?", issym(K_red[nz,nz]))
info("M sym?", issym(M_red[nz,nz]))
info("K posdef?", isposdef(K_red[nz,nz]))
info("M posdef?", isposdef(M_red[nz,nz]))
info("reduced system")
info("is K symmetric? ", issym(K_red[nz,nz]))
info("is M symmetric? ", issym(M_red[nz,nz]))
info("is K positive definite? ", isposdef(K_red[nz,nz]))
info("is M positive definite? ", isposdef(M_red[nz,nz]))
k1 = maximum(abs(K_red[nz,nz] - K_red[nz,nz]'))
m1 = maximum(abs(M_red[nz,nz] - M_red[nz,nz]'))
info("K skewness ", k1)
info("M skewness ", m1)
info("K 'skewness' (max(abs(K - K'))) = ", k1)
info("M 'skewness' (max(abs(M - M'))) = ", m1)
info("original matrix")
info("is K symmetric? ", issym(K[nz,nz]))
info("is M symmetric? ", issym(M[nz,nz]))
info("is K positive definite? ", isposdef(K[nz,nz]))
info("is M positive definite? ", isposdef(M[nz,nz]))
k1 = maximum(abs(K[nz,nz] - K[nz,nz]'))
m1 = maximum(abs(M[nz,nz] - M[nz,nz]'))
info("K 'skewness' (max(abs(K - K'))) = ", k1)
info("M 'skewness' (max(abs(M - M'))) = ", m1)
rethrow()
end
info("Eigenvalues computed in $t1 seconds. Eigenvalues: $om2")
props.eigvals = om2
props.eigvecs = zeros(ndofs, length(om2))
v = zeros(ndofs)
@@ -98,7 +155,6 @@ function call(solver::Solver{Modal}; show_info=true, debug=false)
props.eigvecs[:,i] = P*v + g
end
t1 = round(toq(), 2)
info("Eigenvalues computed in $t1 seconds. Eigenvalues: $om2")
for i=1:length(om2)
freq = real(sqrt(om2[i])/(2.0*pi))
+7 -1
View File
@@ -163,6 +163,12 @@ function get_nonzero_columns(A::Union{SparseMatrixCOO, Matrix})
return get_nonzero_columns(sparse(A))
end
function get_nonzeros(C::Union{SparseMatrixCSC, Matrix})
nz1 = get_nonzero_rows(C)
nz2 = get_nonzero_columns(C)
return (nz1, nz2)
end
function size(A::SparseMatrixCOO)
isempty(A) && return (0, 0)
return maximum(A.I), maximum(A.J)
@@ -173,7 +179,7 @@ function size(A::SparseMatrixCOO, idx::Int)
end
""" Matrix norm. Automatically convert to dense when asking for 2-norm for small matrices. """
function Base.norm(A::SparseMatrixCOO, p=Inf; maxdim=1000)
function norm(A::SparseMatrixCOO, p=Inf; maxdim=1000)
dim = size(A, 1)
if p == 2 && dim > maxdim
info("Assembly norm: dim = $dim > $maxdim and p=$p, not making dense matrices for operation.")