From 56f818bcc2a201c5078b873b5048541bfab2c910 Mon Sep 17 00:00:00 2001 From: Jan-Timo Hesse Date: Thu, 11 Jan 2024 14:13:31 +0100 Subject: [PATCH 1/4] added support for C3D8R, COH3D8 and CPS4R --- src/parse_mesh.jl | 67 +++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/parse_mesh.jl b/src/parse_mesh.jl index 2551ac2..4624611 100644 --- a/src/parse_mesh.jl +++ b/src/parse_mesh.jl @@ -5,16 +5,22 @@ import Base.parse # 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 @@ -28,10 +34,10 @@ 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 @@ -39,6 +45,9 @@ 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 @@ -53,7 +62,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 @@ -79,10 +88,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 @@ -102,7 +111,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]) @@ -157,7 +166,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) @@ -182,10 +191,10 @@ 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] @@ -193,12 +202,12 @@ function parse_section(model, lines, key, idx_start, idx_end, ::Union{Type{Val{: @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...) @@ -211,7 +220,7 @@ 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) @@ -219,7 +228,7 @@ function parse_section(model, lines, ::Symbol, idx_start, idx_end, ::Type{Val{:S 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\d+),.*(?PS\d+).*", line) element_id = parse(Int, m[:element_id]) @@ -249,29 +258,29 @@ 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}() + 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)'") end From d8e42f8a526c7d2a08cb29a5650974cd84a7467a Mon Sep 17 00:00:00 2001 From: Jan-Timo Hesse Date: Thu, 11 Jan 2024 14:30:39 +0100 Subject: [PATCH 2/4] Ignore Manifest --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2eb7458..1d3099f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ docs/build/ docs/site/ *.swp *.cov +Manifest.toml From 10c0c58fa21e4d8bd75f163752e28103f0c50923 Mon Sep 17 00:00:00 2001 From: Jan-Timo Hesse Date: Thu, 11 Jan 2024 14:30:47 +0100 Subject: [PATCH 3/4] Added verbose option --- Project.toml | 3 ++- src/parse_mesh.jl | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index f9c8b41..ba75ec7 100644 --- a/Project.toml +++ b/Project.toml @@ -4,11 +4,12 @@ authors = ["Jukka Aho "] 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" diff --git a/src/parse_mesh.jl b/src/parse_mesh.jl index 4624611..f72b645 100644 --- a/src/parse_mesh.jl +++ b/src/parse_mesh.jl @@ -2,6 +2,7 @@ # 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 @@ -296,6 +297,10 @@ 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) +function abaqus_read_mesh(fn::String; kwargs...) + verbose = get(kwargs, :verbose, true) + if !verbose + Logging.disable_logging(Logging.Error) + end return open(parse_abaqus, fn) end From 23d5d5fee2f8c95247e91502aa3288d08b183fb0 Mon Sep 17 00:00:00 2001 From: Jan-Timo Hesse Date: Thu, 11 Jan 2024 15:20:54 +0100 Subject: [PATCH 4/4] Added verbose option --- src/parse_mesh.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/parse_mesh.jl b/src/parse_mesh.jl index f72b645..27e4941 100644 --- a/src/parse_mesh.jl +++ b/src/parse_mesh.jl @@ -258,7 +258,7 @@ end Function parses Abaqus input file and generates a dictionary of all the available keywords. """ -function parse_abaqus(fid::IOStream) +function parse_abaqus(fid::IOStream, verbose::Bool) model = Dict{String,Dict}() model["nodes"] = Dict{Int,Vector{Float64}}() model["node_sets"] = Dict{String,Vector{Int}}() @@ -283,7 +283,9 @@ function parse_abaqus(fid::IOStream) if hasmethod(parse_section, args) 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 @@ -299,8 +301,5 @@ not the actual model with boundary conditions, load steps and so on. """ function abaqus_read_mesh(fn::String; kwargs...) verbose = get(kwargs, :verbose, true) - if !verbose - Logging.disable_logging(Logging.Error) - end - return open(parse_abaqus, fn) + return parse_abaqus(open(fn),verbose) end