mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-21 10:23:37 +00:00
lot of new tests, echangement of modal solver, eigenvalue analysis with mesh tie
This commit is contained in:
+2
-1
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
@@ -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
@@ -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})
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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.")
|
||||
|
||||
Reference in New Issue
Block a user