Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for C3D8R, COH3D8, CPS4R and verbose option #71

Merged
merged 4 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ docs/build/
docs/site/
*.swp
*.cov
Manifest.toml
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ authors = ["Jukka Aho <[email protected]>"]
version = "0.2.7"

[deps]
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Nullables = "4d1e1d77-625e-5b40-9113-a560ec7a8ecd"

[compat]
julia = "1"
Nullables = "1"
julia = "1"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
79 changes: 46 additions & 33 deletions src/parse_mesh.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@
# License is MIT: see https://github.com/JuliaFEM/AbaqusReader.jl/blob/master/LICENSE

import Base.parse
using Logging

# Define element type and number of nodes in element
element_has_nodes(::Type{Val{:C3D4}}) = 4
element_has_type( ::Type{Val{:C3D4}}) = :Tet4
element_has_type(::Type{Val{:C3D4}}) = :Tet4

element_has_nodes(::Type{Val{:C3D6}}) = 6
element_has_type( ::Type{Val{:C3D6}}) = :Wedge6
element_has_type(::Type{Val{:C3D6}}) = :Wedge6

element_has_nodes(::Type{Val{:C3D4H}}) = 4
element_has_type( ::Type{Val{:C3D4H}}) = :Tet4
element_has_type(::Type{Val{:C3D4H}}) = :Tet4

element_has_nodes(::Type{Val{:C3D8}}) = 8
element_has_type( ::Type{Val{:C3D8}}) = :Hex8
element_has_type(::Type{Val{:C3D8}}) = :Hex8

element_has_nodes(::Type{Val{:C3D8R}}) = 8
element_has_type(::Type{Val{:C3D8R}}) = :Hex8

element_has_nodes(::Type{Val{:COH3D8}}) = 8
element_has_type(::Type{Val{:COH3D8}}) = :Hex8

element_has_nodes(::Type{Val{:C3D10}}) = 10
element_has_type(::Type{Val{:C3D10}}) = :Tet10
Expand All @@ -28,17 +35,20 @@ element_has_type(::Type{Val{:C3D20}}) = :Hex20
element_has_nodes(::Type{Val{:C3D20E}}) = 20

element_has_nodes(::Type{Val{:S3}}) = 3
element_has_type( ::Type{Val{:S3}}) = :Tri3
element_has_type(::Type{Val{:S3}}) = :Tri3

element_has_nodes(::Type{Val{:CPS3}}) = 3
element_has_type( ::Type{Val{:CPS3}}) = :CPS3
element_has_type(::Type{Val{:CPS3}}) = :CPS3

element_has_nodes(::Type{Val{:STRI65}}) = 6
element_has_type(::Type{Val{:STRI65}}) = :Tri6

element_has_nodes(::Type{Val{:CPS4}}) = 4
element_has_type(::Type{Val{:CPS4}}) = :Quad4

element_has_nodes(::Type{Val{:CPS4R}}) = 4
element_has_type(::Type{Val{:CPS4R}}) = :Quad4

element_has_nodes(::Type{Val{:T2D2}}) = 2
element_has_type(::Type{Val{:T2D2}}) = :Seg2

Expand All @@ -53,7 +63,7 @@ element_has_type(::Type{Val{:B33}}) = :Seg2
Function return true, if line starts with comment character "**"
or has length of 0
"""
function empty_or_comment_line(line::T) where T<:AbstractString
function empty_or_comment_line(line::T) where {T<:AbstractString}
startswith(line, "**") || (length(line) == 0)
end

Expand All @@ -79,10 +89,10 @@ end

"""Parse all the numbers from string
"""
function parse_numbers(line, type_::Type{T})::Vector{T} where T
function parse_numbers(line, type_::Type{T})::Vector{T} where {T}
regexp = r"[0-9]+"
matches = collect((m.match for m = eachmatch(regexp, line)))
map(x-> Base.parse(type_, x), matches)
map(x -> Base.parse(type_, x), matches)
end

"""Add set to model, if set exists
Expand All @@ -102,7 +112,7 @@ function parse_section(model, lines, ::Symbol, idx_start, idx_end, ::Type{Val{:N
nnodes = 0
ids = Int[]
definition = lines[idx_start]
for line in lines[idx_start + 1: idx_end]
for line in lines[idx_start+1:idx_end]
if !(empty_or_comment_line(line))
m = collect((m.match for m = eachmatch(r"[-0-9.eE+]+", line)))
node_id = parse(Int, m[1])
Expand Down Expand Up @@ -157,7 +167,7 @@ function parse_section(model, lines, ::Symbol, idx_start, idx_end, ::Type{Val{:E
eltype_nodes = element_has_nodes(Val{eltype_sym})
element_type = element_has_type(Val{eltype_sym})
@debug("Parsing elements. Type: $(m[1]). Topology: $(element_type)")
list_iterator = consumeList(lines, idx_start+1, idx_end)
list_iterator = consumeList(lines, idx_start + 1, idx_end)
line = list_iterator()
while line != nothing
numbers = parse_numbers(line, Int)
Expand All @@ -182,23 +192,23 @@ end
"""Parse node and elementset from input lines
"""
function parse_section(model, lines, key, idx_start, idx_end, ::Union{Type{Val{:NSET}},
Type{Val{:ELSET}}})
Type{Val{:ELSET}}})
data = Int[]
set_regex_string = Dict(:NSET => r"((?<=NSET=)([\w\-\_]+)|(?<=NSET=\")([\w\-\_\ ]+)(?=\"))"i,
:ELSET => r"((?<=ELSET=)([\w\-\_]+)|(?<=ELSET=\")([\w\-\_\ ]+)(?=\"))"i)
set_regex_string = Dict(:NSET => r"((?<=NSET=)([\w\-\_]+)|(?<=NSET=\")([\w\-\_\ ]+)(?=\"))"i,
:ELSET => r"((?<=ELSET=)([\w\-\_]+)|(?<=ELSET=\")([\w\-\_\ ]+)(?=\"))"i)
selected_set = key == :NSET ? "node_sets" : "element_sets"
definition = lines[idx_start]
regex_string = set_regex_string[key]
set_name = regex_match(regex_string, definition, 1)
@debug("Creating $(lowercase(string(key))) $set_name")

if endswith(strip(uppercase(definition)), "GENERATE")
line = lines[idx_start + 1]
line = lines[idx_start+1]
first_id, last_id, step_ = parse_numbers(line, Int)
set_ids = collect(first_id:step_:last_id)
push!(data, set_ids...)
else
for line in lines[idx_start + 1: idx_end]
for line in lines[idx_start+1:idx_end]
if !(empty_or_comment_line(line))
set_ids = parse_numbers(line, Int)::Vector{Int}
push!(data, set_ids...)
Expand All @@ -211,15 +221,15 @@ end
"""Parse SURFACE keyword
"""
function parse_section(model, lines, ::Symbol, idx_start, idx_end, ::Type{Val{:SURFACE}})
data = Vector{Tuple{Int, Symbol}}()
data = Vector{Tuple{Int,Symbol}}()
definition = lines[idx_start]

has_set_def = parse_definition(definition)
has_set_def != nothing || return
set_type = get(has_set_def, "type", "UNKNOWN")
set_name = has_set_def["name"]

for line in lines[idx_start + 1: idx_end]
for line in lines[idx_start+1:idx_end]
empty_or_comment_line(line) && continue
m = match(r"(?P<element_id>\d+),.*(?P<element_side>S\d+).*", line)
element_id = parse(Int, m[:element_id])
Expand Down Expand Up @@ -248,32 +258,34 @@ end
Function parses Abaqus input file and generates a dictionary of
all the available keywords.
"""
function parse_abaqus(fid::IOStream)
model = Dict{String, Dict}()
model["nodes"] = Dict{Int, Vector{Float64}}()
model["node_sets"] = Dict{String, Vector{Int}}()
model["elements"] = Dict{Int, Vector{Int}}()
model["element_types"] = Dict{Int, Symbol}()
model["element_sets"] = Dict{String, Vector{Int}}()
model["surface_sets"] = Dict{String, Vector{Tuple{Int, Symbol}}}()
model["surface_types"] = Dict{String, Symbol}()
function parse_abaqus(fid::IOStream, verbose::Bool)
model = Dict{String,Dict}()
model["nodes"] = Dict{Int,Vector{Float64}}()
model["node_sets"] = Dict{String,Vector{Int}}()
model["elements"] = Dict{Int,Vector{Int}}()
model["element_types"] = Dict{Int,Symbol}()
model["element_sets"] = Dict{String,Vector{Int}}()
model["surface_sets"] = Dict{String,Vector{Tuple{Int,Symbol}}}()
model["surface_types"] = Dict{String,Symbol}()
keyword_sym::Symbol = :none

lines = readlines(fid)
keyword_indexes = find_keywords(lines)
nkeyword_indexes = length(keyword_indexes)
push!(keyword_indexes, length(lines)+1)
push!(keyword_indexes, length(lines) + 1)
idx_start = keyword_indexes[1]

for idx_end in keyword_indexes[2:end]
keyword_line = strip(uppercase(lines[idx_start]))
keyword = strip(regex_match(r"\s*([\w ]+)", keyword_line, 1))
k_sym = Symbol(keyword)
args = Tuple{Dict, Vector{Int}, Symbol, Int, Int, Type{Val{k_sym}}}
args = Tuple{Dict,Vector{Int},Symbol,Int,Int,Type{Val{k_sym}}}
if hasmethod(parse_section, args)
parse_section(model, lines, k_sym, idx_start, idx_end-1, Val{k_sym})
parse_section(model, lines, k_sym, idx_start, idx_end - 1, Val{k_sym})
else
@warn("Unknown section: '$(keyword)'")
if verbose
@warn("Unknown section: '$(keyword)'")
end
end
idx_start = idx_end
end
Expand All @@ -287,6 +299,7 @@ Read ABAQUS mesh from file `fn`. Returns a dict with elements, nodes,
element sets, node sets and other topologically imporant things, but
not the actual model with boundary conditions, load steps and so on.
"""
function abaqus_read_mesh(fn::String)
return open(parse_abaqus, fn)
function abaqus_read_mesh(fn::String; kwargs...)
verbose = get(kwargs, :verbose, true)
return parse_abaqus(open(fn),verbose)
end