mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-17 10:59:08 +00:00
Use package AbaqusReader.jl (#127)
Source code related to read and parse ABAQUS .inp files is now living in it's own repository `AbaqusReader.jl` and in this commit we cleanup the same files from this repository. - add AbaqusReader to .travis.yml because it's not registered package yet - initialize Mesh from AbaqusReader.jl dict - remove ABAQUS tests and files moved to AbaqusReader.jl - remove references to old module Abaqus - move ABAQUS code to preprocess.jl (what is left) - close issue #122 - close issue #55
This commit is contained in:
@@ -18,6 +18,7 @@ before_script:
|
||||
- julia --color=yes -e 'Pkg.add("Lint")'
|
||||
- julia --color=yes -e 'Pkg.clone("https://github.com/ahojukka5/CheckHeader.jl.git")'
|
||||
- julia --color=yes -e 'Pkg.clone("https://github.com/ahojukka5/CheckTabs.jl.git")'
|
||||
- julia --color=yes -e 'Pkg.clone("https://github.com/JuliaFEM/AbaqusReader.jl.git")'
|
||||
script:
|
||||
- julia --color=yes -e 'Pkg.build("JuliaFEM")'
|
||||
- julia --color=yes -e 'using CheckHeader; checkheader("JuliaFEM")'
|
||||
|
||||
+4
-1
@@ -6,6 +6,7 @@
|
||||
```@meta
|
||||
DocTestSetup = quote
|
||||
using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
end
|
||||
```
|
||||
|
||||
@@ -15,7 +16,9 @@ Add here.
|
||||
|
||||
## Functions
|
||||
|
||||
Add here.
|
||||
```@docs
|
||||
JuliaFEM.Preprocess.create_surface_elements
|
||||
```
|
||||
|
||||
## Index
|
||||
|
||||
|
||||
+2
-9
@@ -137,15 +137,14 @@ export create_elements, Mesh, add_node!, add_nodes!, add_element!,
|
||||
add_elements!, add_element_to_element_set!, add_node_to_node_set!,
|
||||
find_nearest_nodes, find_nearest_node, reorder_element_connectivity!,
|
||||
create_node_set_from_element_set!
|
||||
include("preprocess_abaqus_reader.jl")
|
||||
export parse_abaqus, parse_section, parse_element_section,
|
||||
abaqus_read_mesh, abaqus_read_model
|
||||
|
||||
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, aster_read_data,
|
||||
aster_read_mesh_names, aster_read_node_sets, aster_read_nodes, RMEDFile
|
||||
|
||||
end
|
||||
|
||||
module Postprocess
|
||||
@@ -155,10 +154,4 @@ export calc_nodal_values!, get_nodal_vector, get_nodal_dict, copy_field!,
|
||||
calculate_second_moment_of_mass, extract
|
||||
end
|
||||
|
||||
module Abaqus
|
||||
include("abaqus.jl")
|
||||
export abaqus_read_model, abaqus_run_model, abaqus_open_results, create_surface_elements
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-765
@@ -1,765 +0,0 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
import Base: getindex, length
|
||||
|
||||
using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
|
||||
### Model definitions for ABAQUS data model
|
||||
|
||||
abstract type AbstractMaterial end
|
||||
abstract type AbstractMaterialProperty end
|
||||
abstract type AbstractProperty end
|
||||
abstract type AbstractStep end
|
||||
abstract type AbstractBoundaryCondition end
|
||||
abstract type AbstractOutputRequest end
|
||||
|
||||
type Model
|
||||
path :: AbstractString
|
||||
name :: AbstractString
|
||||
mesh :: Mesh
|
||||
materials :: Dict{Symbol, AbstractMaterial}
|
||||
properties :: Vector{AbstractProperty}
|
||||
boundary_conditions :: Vector{AbstractBoundaryCondition}
|
||||
steps :: Vector{AbstractStep}
|
||||
problems :: Vector{Problem}
|
||||
end
|
||||
|
||||
type SolidSection <: AbstractProperty
|
||||
element_set :: Symbol
|
||||
material_name :: Symbol
|
||||
end
|
||||
|
||||
type Material <: AbstractMaterial
|
||||
name :: Symbol
|
||||
properties :: Vector{AbstractMaterialProperty}
|
||||
end
|
||||
|
||||
type Elastic <: AbstractMaterialProperty
|
||||
E :: Float64
|
||||
nu :: Float64
|
||||
end
|
||||
|
||||
type Step <: AbstractStep
|
||||
kind :: Nullable{Symbol} # STATIC, ...
|
||||
boundary_conditions :: Vector{AbstractBoundaryCondition}
|
||||
output_requests :: Vector{AbstractOutputRequest}
|
||||
end
|
||||
|
||||
type BoundaryCondition <: AbstractBoundaryCondition
|
||||
kind :: Symbol # BOUNDARY, CLOAD, DLOAD, DSLOAD, ...
|
||||
data :: Vector
|
||||
options :: Dict
|
||||
end
|
||||
|
||||
type OutputRequest <: AbstractOutputRequest
|
||||
kind :: Symbol # NODE, EL, SECTION, ...
|
||||
data :: Vector
|
||||
options :: Dict
|
||||
target :: Symbol # PRINT, FILE
|
||||
end
|
||||
|
||||
### Utility functions to parse ABAQUS .inp file to data model
|
||||
|
||||
type Keyword
|
||||
name :: AbstractString
|
||||
options :: Vector{Union{AbstractString, Pair}}
|
||||
end
|
||||
|
||||
function getindex(kw::Keyword, s)
|
||||
return parse(Dict(kw.options)[s])
|
||||
end
|
||||
|
||||
type AbaqusReaderState
|
||||
section :: Nullable{Keyword}
|
||||
material :: Nullable{AbstractMaterial}
|
||||
property :: Nullable{AbstractProperty}
|
||||
step :: Nullable{AbstractStep}
|
||||
data :: Vector{AbstractString}
|
||||
end
|
||||
|
||||
function get_data(state::AbaqusReaderState)
|
||||
data = []
|
||||
for row in state.data
|
||||
row = strip(row, [' ', ','])
|
||||
col = split(row, ',')
|
||||
col = map(parse, col)
|
||||
push!(data, col)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
function get_options(state::AbaqusReaderState)
|
||||
return Dict(get(state.section).options)
|
||||
end
|
||||
|
||||
function get_option(state::AbaqusReaderState, what::AbstractString)
|
||||
return get_options(state)[what]
|
||||
end
|
||||
|
||||
function length(state::AbaqusReaderState)
|
||||
return length(state.data)
|
||||
end
|
||||
|
||||
function is_comment(line)
|
||||
return startswith(line, "**")
|
||||
end
|
||||
|
||||
function is_keyword(line)
|
||||
return startswith(line, "*") && !is_comment(line)
|
||||
end
|
||||
|
||||
function parse_keyword(line; uppercase_keyword=true)
|
||||
args = split(line, ",")
|
||||
args = map(strip, args)
|
||||
keyword_name = strip(args[1], '*')
|
||||
if uppercase_keyword
|
||||
keyword_name = uppercase(keyword_name)
|
||||
end
|
||||
keyword = Keyword(keyword_name, [])
|
||||
for option in args[2:end]
|
||||
pair = split(option, "=")
|
||||
if uppercase_keyword
|
||||
pair[1] = uppercase(pair[1])
|
||||
end
|
||||
if length(pair) == 1
|
||||
push!(keyword.options, pair[1])
|
||||
elseif length(pair) == 2
|
||||
push!(keyword.options, pair[1] => pair[2])
|
||||
else
|
||||
error("Keyword failure: $line, $option, $pair")
|
||||
end
|
||||
end
|
||||
return keyword
|
||||
end
|
||||
|
||||
macro register_abaqus_keyword(keyword)
|
||||
underscored = Symbol(replace(keyword, " ", "_"))
|
||||
quote
|
||||
global is_abaqus_keyword_registered
|
||||
typealias $underscored Type{Val{Symbol($keyword)}}
|
||||
is_abaqus_keyword_registered(::Type{Val{Symbol($keyword)}}) = true
|
||||
end
|
||||
end
|
||||
|
||||
function is_abaqus_keyword_registered(s::AbstractString)
|
||||
return is_abaqus_keyword_registered(Val{Symbol(s)})
|
||||
end
|
||||
|
||||
function is_abaqus_keyword_registered(others)
|
||||
return false
|
||||
end
|
||||
|
||||
function is_new_section(line)
|
||||
is_keyword(line) || return false
|
||||
section = parse_keyword(line)
|
||||
is_abaqus_keyword_registered(section.name) || return false
|
||||
return true
|
||||
end
|
||||
|
||||
function maybe_close_section!(model, state; verbose=true)
|
||||
isnull(state.section) && return
|
||||
section_name = get(state.section).name
|
||||
verbose && info("Close section: $section_name")
|
||||
args = Tuple{Model, AbaqusReaderState, Type{Val{Symbol(section_name)}}}
|
||||
if method_exists(close_section!, args)
|
||||
close_section!(model, state, Val{Symbol(section_name)})
|
||||
else
|
||||
verbose && warn("no close_section! found for $section_name")
|
||||
end
|
||||
state.section = nothing
|
||||
end
|
||||
|
||||
function maybe_open_section!(model, state; verbose=true)
|
||||
section_name = get(state.section).name
|
||||
section_options = get(state.section).options
|
||||
verbose && info("New section: $section_name with options $section_options")
|
||||
args = Tuple{Model, AbaqusReaderState, Type{Val{Symbol(section_name)}}}
|
||||
if method_exists(open_section!, args)
|
||||
open_section!(model, state, Val{Symbol(section_name)})
|
||||
else
|
||||
verbose && warn("no open_section! found for $section_name")
|
||||
end
|
||||
end
|
||||
|
||||
function new_section!(model, state, line::AbstractString; verbose=true)
|
||||
maybe_close_section!(model, state; verbose=verbose)
|
||||
state.data = []
|
||||
state.section = parse_keyword(line)
|
||||
maybe_open_section!(model, state; verbose=verbose)
|
||||
end
|
||||
|
||||
# open_section! is called right after keyword is found
|
||||
function open_section! end
|
||||
|
||||
# close_section! is called at the end or section or before new keyword
|
||||
function close_section! end
|
||||
|
||||
function process_line!(model, state, line; verbose=false)
|
||||
if isnull(state.section)
|
||||
verbose && info("section = nothing! line = $line")
|
||||
return
|
||||
end
|
||||
if is_keyword(line)
|
||||
warn("missing keyword? line = $line")
|
||||
# close section, this is probably keyword and collecting data should stop.
|
||||
maybe_close_section!(model, state)
|
||||
return
|
||||
end
|
||||
push!(state.data, line)
|
||||
end
|
||||
|
||||
function abaqus_read_model(fn; read_mesh=true)
|
||||
|
||||
model_path = dirname(fn)
|
||||
model_name = first(splitext(basename(fn)))
|
||||
model = Model(model_path, model_name, Mesh(), Dict(), [], [], [], [])
|
||||
|
||||
if read_mesh
|
||||
model.mesh = abaqus_read_mesh(fn)
|
||||
end
|
||||
|
||||
state = AbaqusReaderState(nothing, nothing, nothing, nothing, [])
|
||||
|
||||
fid = open(fn)
|
||||
for line in eachline(fid)
|
||||
line = strip(line)
|
||||
is_comment(line) && continue
|
||||
if is_new_section(line)
|
||||
new_section!(model, state, line)
|
||||
else
|
||||
process_line!(model, state, line)
|
||||
end
|
||||
end
|
||||
close(fid)
|
||||
maybe_close_section!(model, state)
|
||||
|
||||
return model
|
||||
end
|
||||
|
||||
### Code to parse ABAQUS .inp to data model
|
||||
|
||||
# add here only keywords when planning to define open_section! and/or
|
||||
# close_section!, i.e. actually parse keyword to model. little bit of
|
||||
# magic is happening here, but after calling macro there is typealias
|
||||
# defined i.e. typealias SOLID_SECTION Type{Val{Symbol("SOLID_SECTION")}}
|
||||
# and also is_keyword_registered("SOLID SECTION") returns true after
|
||||
# registration, also notice underscoring
|
||||
|
||||
@register_abaqus_keyword("SOLID SECTION")
|
||||
|
||||
@register_abaqus_keyword("MATERIAL")
|
||||
@register_abaqus_keyword("ELASTIC")
|
||||
|
||||
@register_abaqus_keyword("STEP")
|
||||
@register_abaqus_keyword("STATIC")
|
||||
@register_abaqus_keyword("END STEP")
|
||||
|
||||
@register_abaqus_keyword("BOUNDARY")
|
||||
@register_abaqus_keyword("CLOAD")
|
||||
@register_abaqus_keyword("DLOAD")
|
||||
@register_abaqus_keyword("DSLOAD")
|
||||
const BOUNDARY_CONDITIONS = Union{BOUNDARY,CLOAD,DLOAD,DSLOAD}
|
||||
|
||||
@register_abaqus_keyword("NODE PRINT")
|
||||
@register_abaqus_keyword("EL PRINT")
|
||||
@register_abaqus_keyword("SECTION PRINT")
|
||||
const OUTPUT_REQUESTS = Union{NODE_PRINT,EL_PRINT,SECTION_PRINT}
|
||||
|
||||
## Properties
|
||||
|
||||
function open_section!(model, state, ::SOLID_SECTION)
|
||||
element_set = get_option(state, "ELSET")
|
||||
material_name = get_option(state, "MATERIAL")
|
||||
property = SolidSection(element_set, material_name)
|
||||
state.property = property
|
||||
push!(model.properties, property)
|
||||
end
|
||||
|
||||
function close_section!(model, state, ::SOLID_SECTION)
|
||||
state.property = nothing
|
||||
end
|
||||
|
||||
## Materials
|
||||
|
||||
function open_section!(model, state, ::MATERIAL)
|
||||
material_name = Symbol(get_option(state, "NAME"))
|
||||
material = Material(material_name, [])
|
||||
state.material = material
|
||||
if haskey(model.materials, material_name)
|
||||
warn("Material $material_name already exists in model, skipping definition.")
|
||||
else
|
||||
model.materials[material_name] = material
|
||||
end
|
||||
end
|
||||
|
||||
function close_section!(model, state, ::ELASTIC)
|
||||
# FIXME
|
||||
@assert length(state) == 1
|
||||
E, nu = first(get_data(state))
|
||||
material_property = Elastic(E, nu)
|
||||
material = get(state.material)
|
||||
push!(material.properties, material_property)
|
||||
end
|
||||
|
||||
## Steps
|
||||
|
||||
function open_section!(model, state, ::STEP)
|
||||
step = Step(nothing, Vector(), Vector())
|
||||
state.step = step
|
||||
push!(model.steps, step)
|
||||
end
|
||||
|
||||
function open_section!(model, state, ::STATIC)
|
||||
isnull(state.step) && error("*STATIC outside *STEP ?")
|
||||
get(state.step).kind = :STATIC
|
||||
end
|
||||
|
||||
function open_section!(model, state, ::END_STEP)
|
||||
state.step = nothing
|
||||
end
|
||||
|
||||
## Steps -- boundary conditions
|
||||
|
||||
function close_section!(model, state, ::BOUNDARY_CONDITIONS)
|
||||
kind = Symbol(get(state.section).name)
|
||||
data = get_data(state)
|
||||
options = get_options(state)
|
||||
bc = BoundaryCondition(kind, data, options)
|
||||
if isnull(state.step)
|
||||
push!(model.boundary_conditions, bc)
|
||||
else
|
||||
step = get(state.step)
|
||||
push!(step.boundary_conditions, bc)
|
||||
end
|
||||
end
|
||||
|
||||
## Steps -- output requests
|
||||
|
||||
function close_section!(model, state, ::OUTPUT_REQUESTS)
|
||||
kind, target = map(parse, split(get(state.section).name, " "))
|
||||
data = get_data(state)
|
||||
options = get_options(state)
|
||||
request = OutputRequest(kind, data, options, target)
|
||||
step = get(state.step)
|
||||
push!(step.output_requests, request)
|
||||
end
|
||||
|
||||
|
||||
### Code to use JuliaFEM to run ABAQUS data model
|
||||
|
||||
function determine_problem_type(model::Model)
|
||||
# FIXME
|
||||
return Elasticity
|
||||
end
|
||||
|
||||
function determine_problem_dimension(model::Model)
|
||||
# FIXME
|
||||
return 3
|
||||
end
|
||||
|
||||
function get_element_section(model::Model, element_set_name::Symbol)
|
||||
sections = filter(s -> s.element_set == element_set_name, model.properties)
|
||||
length(sections) == 1 || error("Multiple sections found for element set $element_set_name")
|
||||
return sections[1]
|
||||
end
|
||||
|
||||
function get_material(model::Model, material_name)
|
||||
return model.materials[material_name]
|
||||
end
|
||||
|
||||
function create_problem(model::Model, element_set_name::Symbol; verbose=true)
|
||||
problem_type = determine_problem_type(model)
|
||||
problem_name = "$problem_type $element_set_name"
|
||||
problem_dimension = determine_problem_dimension(model)
|
||||
problem = Problem(problem_type, problem_name, problem_dimension)
|
||||
problem.elements = create_elements(model.mesh, element_set_name)
|
||||
section = get_element_section(model, element_set_name)
|
||||
material = get_material(model, section.material_name)
|
||||
for mp in material.properties
|
||||
if isa(mp, Elastic)
|
||||
verbose && info("$element_set_name: elastic material, E = $(mp.E), nu = $(mp.nu)")
|
||||
update!(problem.elements, "youngs modulus", mp.E)
|
||||
update!(problem.elements, "poissons ratio", mp.nu)
|
||||
end
|
||||
end
|
||||
return problem
|
||||
end
|
||||
|
||||
""" Dirichlet boundary condition. """
|
||||
function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition, ::BOUNDARY; verbose=true)
|
||||
dim = determine_problem_dimension(model)
|
||||
problem = Problem(Dirichlet, "Dirichlet boundary *BOUNDARY", dim, "displacement")
|
||||
for row in bc.data
|
||||
|
||||
if isa(row[1], AbstractString) # node set given
|
||||
nodes = model.mesh.node_sets[bc_name]
|
||||
else # single node given
|
||||
nodes = [row[1]]
|
||||
end
|
||||
|
||||
elements = [Element(Poi1, [id]) for id in nodes]
|
||||
update!(elements, "geometry", model.mesh.nodes)
|
||||
|
||||
for dof in row[2]:row[end]
|
||||
# FIXME
|
||||
val = 0.0
|
||||
update!(elements, "displacement $dof", val)
|
||||
verbose && info("Nodes ", join(nodes, ", "), " dof $dof => $val")
|
||||
end
|
||||
|
||||
push!(problem, elements)
|
||||
end
|
||||
return problem
|
||||
end
|
||||
|
||||
""" Distributed surface load (DSLOAD). """
|
||||
function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition, ::DSLOAD; verbose=false)
|
||||
dim = determine_problem_dimension(model)
|
||||
problem = Problem(Elasticity, "Distributed surface load *DSLOAD", dim)
|
||||
for row in bc.data
|
||||
bc_name, bc_type, pressure = row
|
||||
bc_type == :P || error("bc_type = $bc_type != :P")
|
||||
elements = []
|
||||
for (parent_element_id, parent_element_side) in model.mesh.surface_sets[bc_name]
|
||||
parent_element_type = model.mesh.element_types[parent_element_id]
|
||||
parent_element_connectivity = model.mesh.elements[parent_element_id]
|
||||
|
||||
child_element_type, child_element_lconn, child_element_connectivity =
|
||||
get_child_element(parent_element_type, parent_element_side,
|
||||
parent_element_connectivity)
|
||||
|
||||
verbose && info("parent element : $parent_element_id, $parent_element_type, $parent_element_connectivity, $parent_element_side")
|
||||
verbose && info("child element : $child_element_type, $child_element_connectivity")
|
||||
|
||||
child_element = Element(JuliaFEM.(child_element_type), child_element_connectivity)
|
||||
push!(elements, child_element)
|
||||
end
|
||||
update!(elements, "geometry", model.mesh.nodes)
|
||||
update!(elements, "surface pressure", pressure)
|
||||
push!(problem, elements)
|
||||
end
|
||||
return problem
|
||||
end
|
||||
|
||||
""" Distributed load (DLOAD). """
|
||||
function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition, ::DLOAD; verbose=false)
|
||||
dim = determine_problem_dimension(model)
|
||||
problem = Problem(Elasticity, "Distributed load *DLOAD", dim)
|
||||
for row in bc.data
|
||||
parent_element_id, parent_element_side, pressure = row
|
||||
parent_element_type = model.mesh.element_types[parent_element_id]
|
||||
parent_element_connectivity = model.mesh.elements[parent_element_id]
|
||||
|
||||
child_element_type, child_element_lconn, child_element_connectivity =
|
||||
get_child_element(parent_element_type, parent_element_side,
|
||||
parent_element_connectivity)
|
||||
|
||||
verbose && info("parent element : $parent_element_id, $parent_element_type, $parent_element_connectivity, $parent_element_side")
|
||||
verbose && info("child element : $child_element_type, $child_element_connectivity")
|
||||
|
||||
child_element = Element(getfield(JuliaFEM, child_element_type), child_element_connectivity)
|
||||
update!(child_element, "geometry", model.mesh.nodes)
|
||||
update!(child_element, "surface pressure", -pressure)
|
||||
push!(problem.elements, child_element)
|
||||
end
|
||||
return problem
|
||||
end
|
||||
|
||||
""" Concentrated load (CLOAD). """
|
||||
function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition, ::CLOAD; verbose=false)
|
||||
dim = determine_problem_dimension(model)
|
||||
problem = Problem(Elasticity, "Concentrated load *CLOAD", dim)
|
||||
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)
|
||||
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
|
||||
|
||||
function create_boundary_problem(model::Model, bc::AbstractBoundaryCondition)
|
||||
create_boundary_problem(model, bc, Val{bc.kind})
|
||||
end
|
||||
|
||||
""" Given element code, element side and global connectivity, determine boundary
|
||||
element. E.g. for Tet4 we have 4 sides S1..S4 and boundary element is of type Tri3.
|
||||
"""
|
||||
function get_child_element(element_type::Symbol, element_side::Symbol,
|
||||
element_connectivity::Vector{Int64})
|
||||
|
||||
element_mapping = Dict(
|
||||
:Tet4 => Dict(
|
||||
:S1 => (:Tri3, [1, 3, 2]),
|
||||
:S2 => (:Tri3, [1, 2, 4]),
|
||||
:S3 => (:Tri3, [2, 3, 4]),
|
||||
:S4 => (:Tri3, [1, 4, 3])),
|
||||
:Tet10 => Dict(
|
||||
:S1 => (:Tri6, [1, 3, 2, 7, 6, 5]),
|
||||
:S2 => (:Tri6, [1, 2, 4, 5, 9, 8]),
|
||||
:S3 => (:Tri6, [2, 3, 4, 6, 10, 9]),
|
||||
:S4 => (:Tri6, [1, 4, 3, 8, 10, 7])),
|
||||
:Hex8 => Dict(
|
||||
:P1 => (:Quad4, [1, 2, 3, 4]),
|
||||
:P2 => (:Quad4, [5, 8, 7, 6]),
|
||||
:P3 => (:Quad4, [1, 5, 6, 2]),
|
||||
:P4 => (:Quad4, [2, 6, 7, 3]),
|
||||
:P5 => (:Quad4, [3, 7, 8, 4]),
|
||||
:P6 => (:Quad4, [4, 8, 5, 1]))
|
||||
)
|
||||
|
||||
if !haskey(element_mapping, element_type)
|
||||
error("Unable to find child element for element of type $element_type for side $element_side, check mapping.")
|
||||
end
|
||||
|
||||
if !haskey(element_mapping[element_type], element_side)
|
||||
error("Unable to find child element side mapping for element of type $element_type for side $element_side, check mapping.")
|
||||
end
|
||||
|
||||
child_element, child_element_lconn = element_mapping[element_type][element_side]
|
||||
child_element_gconn = element_connectivity[child_element_lconn]
|
||||
return child_element, child_element_lconn, child_element_gconn
|
||||
end
|
||||
|
||||
function determine_solver_type(model::Model, step::AbstractStep)
|
||||
# FIXME
|
||||
return Linear
|
||||
end
|
||||
|
||||
function process_output_request(model::Model, solver::Solver, output_request::AbstractOutputRequest)
|
||||
kind = Val{output_request.kind}
|
||||
target = Val{output_request.target}
|
||||
process_output_request(model, solver, output_request, kind, target)
|
||||
end
|
||||
|
||||
function process_output_request(model::Model, solver::Solver, output_request::AbstractOutputRequest,
|
||||
::Type{Val{:NODE}}, ::Type{Val{:PRINT}})
|
||||
data = output_request.data
|
||||
options = output_request.options
|
||||
code_mapping = Dict(
|
||||
:COORD => "geometry",
|
||||
:U => "displacement",
|
||||
:CF => "concentrated force",
|
||||
:RF => "reaction force")
|
||||
abbr_mapping = Dict(:COORD => :COOR)
|
||||
for row in data
|
||||
info(repeat("-", 80))
|
||||
codes = join(row, ", ")
|
||||
info("*NODE 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, field_name, abbr, solver.time)
|
||||
#push!(tables, table)
|
||||
end
|
||||
length(tables) != 0 || continue
|
||||
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))
|
||||
#= to be fixed
|
||||
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()
|
||||
=#
|
||||
end
|
||||
end
|
||||
|
||||
function process_output_request(model::Model, solver::Solver, output_request::AbstractOutputRequest,
|
||||
::Type{Val{:SECTION}}, ::Type{Val{:PRINT}})
|
||||
data = output_request.data
|
||||
options = output_request.options
|
||||
info("SECTION PRINT output request, with data $data and options $options")
|
||||
end
|
||||
|
||||
function (model::Model)()
|
||||
info("Starting JuliaFEM-ABAQUS solver.")
|
||||
|
||||
# 1. create field problems and add elements
|
||||
element_sets = collect(keys(model.mesh.element_sets))
|
||||
info("Creating problems for element sets ", join(element_sets, ", "))
|
||||
model.problems = [create_problem(model, elset) for elset in element_sets]
|
||||
|
||||
# 2. create boundary problems (the ones defined before *STEP)
|
||||
info("Boundary conditions defined before *STEP")
|
||||
for (i, bc) in enumerate(model.boundary_conditions)
|
||||
info("$i $(bc.kind)")
|
||||
end
|
||||
boundary_problems = [create_boundary_problem(model, bc) for bc in model.boundary_conditions]
|
||||
|
||||
# 3. loop steps
|
||||
for step in model.steps
|
||||
# 3.1 add boundary conditions defined inside *STEP
|
||||
step_problems = [create_boundary_problem(model, bc) for bc in step.boundary_conditions]
|
||||
# 3.2 create solver and solve set of problems
|
||||
solver_type = determine_solver_type(model, step)
|
||||
solver_description = "$solver_type solver"
|
||||
solver = Solver(solver_type, solver_description)
|
||||
all_problems = [model.problems; boundary_problems; step_problems]
|
||||
push!(solver, all_problems...)
|
||||
solver()
|
||||
info(repeat("-", 80))
|
||||
info("Simulation ready, processing output requests")
|
||||
info(repeat("-", 80))
|
||||
# 3.3 postprocessing based on output requests
|
||||
for output_request in step.output_requests
|
||||
process_output_request(model, solver, output_request)
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function abaqus_download(name)
|
||||
fn = "$name.inp"
|
||||
if !haskey(ENV, "ABAQUS_DOWNLOAD_URL")
|
||||
info("""
|
||||
ABAQUS input file $fn not found and ABAQUS_DOWNLOAD_URL not set, unable to
|
||||
download file. To enable automatic model downloading, set environment variable
|
||||
ABAQUS_URL to point url to models.""")
|
||||
return 1
|
||||
end
|
||||
url = ENV["ABAQUS_DOWNLOAD_URL"]
|
||||
if haskey(ENV, "ABAQUS_DOWNLOAD_DIR")
|
||||
fn = rstrip(ENV["ABAQUS_DOWNLOAD_DIR"], '/') * "/" * fn
|
||||
end
|
||||
if !isfile(fn)
|
||||
info("Downloading model $name ...")
|
||||
download("$url/$name.inp", fn)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
""" Return input file name. """
|
||||
function abaqus_input_file_name(name)
|
||||
fn = "$name.inp"
|
||||
isfile(fn) && return fn
|
||||
if haskey(ENV, "ABAQUS_DOWNLOAD_DIR")
|
||||
fn = rstrip(ENV["ABAQUS_DOWNLOAD_DIR"], '/') * "/" * fn
|
||||
end
|
||||
isfile(fn) && return fn
|
||||
return ""
|
||||
end
|
||||
|
||||
function abaqus_input_file_path(name)
|
||||
return dirname(abaqus_input_file_name(name))
|
||||
end
|
||||
|
||||
function abaqus_open_results(name)
|
||||
path = abaqus_input_file_path(name)
|
||||
result_file = "$path/$name.xmf"
|
||||
return Xdmf(result_file)
|
||||
end
|
||||
|
||||
### JuliaFEM-ABAQUS interface entry point
|
||||
|
||||
"""
|
||||
Run ABAQUS model. If input file is not found, attempt to fetch it from internet
|
||||
if fetch is set to true and ABAQUS_DOWNLOAD_URL is set. Return exit code 0 if
|
||||
execution of model is success.
|
||||
"""
|
||||
function abaqus_run_model(name; fetch=false, verbose=false)
|
||||
|
||||
if !isfile("$name.inp") && fetch
|
||||
status = abaqus_download(name)
|
||||
status == 0 || return status # download failed
|
||||
end
|
||||
|
||||
fn = abaqus_input_file_name(name)
|
||||
|
||||
if verbose
|
||||
println(repeat("-", 80))
|
||||
println("Running ABAQUS model $name from file $fn")
|
||||
println(repeat("-", 80))
|
||||
println(readstring(fn))
|
||||
println(repeat("-", 80))
|
||||
end
|
||||
|
||||
model = abaqus_read_model(fn)
|
||||
status = model()
|
||||
return status
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
This function gerates surface elements from solid elements
|
||||
|
||||
slave = create_surface_elements(mesh, :slave_surf)
|
||||
master = create_surface_elements(mesh, :master_surf)
|
||||
"""
|
||||
function create_surface_elements(mesh::Mesh, surface_name::Symbol)
|
||||
elements = []
|
||||
for (parent_element_id, parent_element_side) in mesh.surface_sets[surface_name]
|
||||
parent_element_type = mesh.element_types[parent_element_id]
|
||||
parent_element_connectivity = mesh.elements[parent_element_id]
|
||||
|
||||
child_element_type, child_element_lconn, child_element_connectivity =
|
||||
get_child_element(parent_element_type, parent_element_side,
|
||||
parent_element_connectivity)
|
||||
|
||||
child_element = Element(getfield(JuliaFEM, child_element_type), child_element_connectivity)
|
||||
push!(elements, child_element)
|
||||
end
|
||||
update!(elements, "geometry", mesh.nodes)
|
||||
return elements
|
||||
end
|
||||
|
||||
function create_surface_elements(mesh::Mesh, surface_name::String)
|
||||
return create_surface_elements(mesh, Symbol(surface_name))
|
||||
end
|
||||
|
||||
@@ -31,6 +31,31 @@ function Mesh()
|
||||
return Mesh(Dict(), Dict(), Dict(), Dict(), Dict(), Dict(), Dict(), Dict())
|
||||
end
|
||||
|
||||
"""
|
||||
Mesh(m::Dict)
|
||||
|
||||
Create new `Mesh` using data `m`. It is assumed that `m` is in format what
|
||||
`abaqus_read_mesh` in `AbaqusReader.jl` is returning.
|
||||
"""
|
||||
function Mesh(m::Dict)
|
||||
mesh = Mesh()
|
||||
mesh.nodes = m["nodes"]
|
||||
mesh.elements = m["elements"]
|
||||
mesh.element_types = m["element_types"]
|
||||
mesh.surface_sets = m["surface_sets"]
|
||||
mesh.surface_types = m["surface_types"]
|
||||
for (nset_name, node_ids) in m["node_sets"]
|
||||
mesh.node_sets[Symbol(nset_name)] = Set(node_ids)
|
||||
end
|
||||
for (elset_name, element_ids) in m["element_sets"]
|
||||
mesh.element_sets[Symbol(elset_name)] = Set(element_ids)
|
||||
end
|
||||
for (surfset_name, surfaces) in m["surface_sets"]
|
||||
mesh.surface_sets[Symbol(surfset_name)] = surfaces
|
||||
end
|
||||
return mesh
|
||||
end
|
||||
|
||||
function add_node!(mesh::Mesh, nid::Int, ncoords::Vector{Float64})
|
||||
mesh.nodes[nid] = ncoords
|
||||
end
|
||||
@@ -203,3 +228,47 @@ function JuliaFEM.Problem{P<:BoundaryProblem}(mesh::Mesh, ::Type{P}, name, dimen
|
||||
problem.elements = create_elements(mesh, name)
|
||||
return problem
|
||||
end
|
||||
|
||||
using AbaqusReader
|
||||
|
||||
function abaqus_read_mesh(fn::String)
|
||||
m = AbaqusReader.abaqus_read_mesh(fn)
|
||||
return Mesh(m)
|
||||
end
|
||||
|
||||
"""
|
||||
create_surface_elements(mesh::Mesh, surface_name::Symbol)
|
||||
|
||||
Create a set of surface elements from solid elements.
|
||||
|
||||
Notation follow what is defined in ABAQUS. For example, if solid elements
|
||||
are Tet10, surface elements will be Tri6 and they can be used to define
|
||||
boundary conditions.
|
||||
"""
|
||||
function create_surface_elements(mesh::Mesh, surface_name::Symbol)
|
||||
elements = Element[]
|
||||
for (elid, elsi) in mesh.surface_sets[surface_name]
|
||||
elty = mesh.element_types[elid]
|
||||
elco = mesh.elements[elid]
|
||||
chel, chcon = AbaqusReader.create_surface_element(elty, elsi, elco)
|
||||
ch = Element(getfield(JuliaFEM, chel), chcon)
|
||||
push!(elements, ch)
|
||||
end
|
||||
update!(elements, "geometry", mesh.nodes)
|
||||
return elements
|
||||
end
|
||||
|
||||
"""
|
||||
create_surface_elements(mesh::Mesh, surface_name::String)
|
||||
|
||||
Create a set of surface elements from solid elements.
|
||||
|
||||
Notation follow what is defined in ABAQUS. For example, if solid elements
|
||||
are Tet10, surface elements will be Tri6 and they can be used to define
|
||||
boundary conditions.
|
||||
"""
|
||||
function create_surface_elements(mesh::Mesh, surface_name::String)
|
||||
return create_surface_elements(mesh, Symbol(surface_name))
|
||||
end
|
||||
|
||||
export abaqus_read_mesh, create_surface_elements
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
element_has_nodes(::Type{Val{:C3D4}}) = 4
|
||||
element_has_type( ::Type{Val{:C3D4}}) = :Tet4
|
||||
|
||||
element_has_nodes(::Type{Val{:C3D8}}) = 8
|
||||
element_has_type( ::Type{Val{:C3D8}}) = :Hex8
|
||||
|
||||
element_has_nodes(::Type{Val{:C3D10}}) = 10
|
||||
element_has_type(::Type{Val{:C3D10}}) = :Tet10
|
||||
|
||||
element_has_nodes(::Type{Val{:C3D20}}) = 20
|
||||
|
||||
element_has_nodes(::Type{Val{:C3D20E}}) = 20
|
||||
|
||||
element_has_nodes(::Type{Val{:S3}}) = 3
|
||||
element_has_type( ::Type{Val{:S3}}) = :Tri3
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
Checks for if line is a comment line or just empty
|
||||
"""
|
||||
function empty_or_comment_line{T<:AbstractString}(line::T)
|
||||
startswith(line, "**") || (length(line) == 0)
|
||||
end
|
||||
|
||||
"""
|
||||
Function for parsing nodes from the file
|
||||
"""
|
||||
function parse_section(model, lines, key::Symbol, idx_start,
|
||||
idx_end, ::Type{Val{:NODE}})
|
||||
|
||||
info("Parsing *NODE block between lines $idx_start .. $idx_end")
|
||||
nnodes = 0
|
||||
ids = Integer[]
|
||||
definition = lines[idx_start]
|
||||
debug("Definition line = $definition")
|
||||
for line in lines[idx_start + 1: idx_end]
|
||||
if !(empty_or_comment_line(line))
|
||||
m = matchall(r"[-0-9.eE+]+", line)
|
||||
node_id = parse(Int, m[1])
|
||||
coords = float(m[2:end])
|
||||
model["nodes"][node_id] = coords
|
||||
nnodes += 1
|
||||
end
|
||||
end
|
||||
info("$nnodes nodes found")
|
||||
has_set_def = match(r"NSET=([\w\_\-]+)", definition)
|
||||
if has_set_def != nothing
|
||||
set_name = has_set_def[1]
|
||||
info("Creating node set $set_name")
|
||||
model["nsets"][set_name] = ids
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Custon regex to find match from string. Index used if there are multiple matches
|
||||
"""
|
||||
function regex_match(regex_str, line, idx)
|
||||
return match(regex_str, line).captures[idx]
|
||||
println(eltype_sym)
|
||||
end
|
||||
|
||||
"""
|
||||
Simple iterator for comsuming element list. Depending
|
||||
on the used element, connectivity nodes might be listed
|
||||
in multiple lines, which is why iterator is used to handle
|
||||
this problem.
|
||||
"""
|
||||
function consumeList(arr, start, stop)
|
||||
function _it()
|
||||
for i=start:stop
|
||||
produce(arr[i])
|
||||
end
|
||||
end
|
||||
Task(_it)
|
||||
end
|
||||
|
||||
"""
|
||||
Parse elements from input.
|
||||
"""
|
||||
function parse_section(model, lines, key, idx_start, idx_end, ::Type{Val{:ELEMENT}})
|
||||
#definition = uppercase(lines[idx_start])
|
||||
definition = lines[idx_start]
|
||||
element_type = regex_match(r"TYPE=([\w\-\_]+)", definition, 1)
|
||||
eltype_sym = Symbol(element_type)
|
||||
eltype_nodes = element_has_nodes(Val{eltype_sym})
|
||||
element_type = element_has_type(Val{eltype_sym})
|
||||
info("Parsing elements. Type: $(element_type)")
|
||||
list_iterator = consumeList(lines, idx_start+1, idx_end)
|
||||
ids = Integer[]
|
||||
line = consume(list_iterator)
|
||||
while line != nothing
|
||||
arr_num_as_str = matchall(r"[0-9]+", line)
|
||||
numbers = map(x-> parse(Int, x), arr_num_as_str)
|
||||
if !(empty_or_comment_line(line))
|
||||
id = numbers[1]
|
||||
push!(ids, id)
|
||||
connectivity = numbers[2:end]
|
||||
while length(connectivity) != eltype_nodes
|
||||
@assert length(connectivity) < eltype_nodes
|
||||
line = consume(list_iterator)
|
||||
arr_num_as_str = matchall(r"[0-9]+", line)
|
||||
numbers = map(x-> parse(Int, x), arr_num_as_str)
|
||||
push!(connectivity, numbers...)
|
||||
end
|
||||
model["elements"][id] = Dict(("type"=>element_type),
|
||||
("connectivity"=>connectivity))
|
||||
end
|
||||
line = consume(list_iterator)
|
||||
end
|
||||
has_set_def = match(r"ELSET=([\w\_\-]+)", definition)
|
||||
if has_set_def != nothing
|
||||
set_name = has_set_def[1]
|
||||
info("Creating elset $set_name")
|
||||
model["elsets"][set_name] = ids
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Parsing Node- and ElementSets.
|
||||
"""
|
||||
function parse_section(model, lines, key, idx_start, idx_end, ::Union{Type{Val{:NSET}}, Type{Val{:ELSET}}})
|
||||
set_regex_string = Dict(:NSET => r"NSET=([\w\-\_]+)",
|
||||
:ELSET => r"ELSET=([\w\-\_]+)" )
|
||||
#definition = uppercase(lines[idx_start])
|
||||
# FIXME: do not uppercase set names
|
||||
definition = lines[idx_start]
|
||||
regex_string = set_regex_string[key]
|
||||
set_name = regex_match(regex_string, definition, 1)
|
||||
info("Creating $(lowercase(string(key))) $set_name")
|
||||
data = Integer[]
|
||||
if endswith(strip(definition), "GENERATE")
|
||||
line = lines[idx_start + 1]
|
||||
m = matchall(r"[0-9]+", line)
|
||||
first_id, last_id, step = map(x-> parse(Int, x), m)
|
||||
set_ids = collect(first_id:step:last_id)
|
||||
push!(data, set_ids...)
|
||||
else
|
||||
for line in lines[idx_start + 1: idx_end]
|
||||
if !(empty_or_comment_line(line))
|
||||
m = matchall(r"[0-9]+", line)
|
||||
set_ids = map(s -> parse(Int, s), m)
|
||||
try
|
||||
push!(data, set_ids...)
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
warn("Problems with element set creation")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
selected_set = key == :NSET ? "nsets" : "elsets"
|
||||
model[selected_set][set_name] = data
|
||||
end
|
||||
|
||||
"""
|
||||
Parse SURFACE keyword
|
||||
"""
|
||||
function parse_section(model, lines, key, idx_start, idx_end, ::Type{Val{:SURFACE}})
|
||||
debug("Parsing surface")
|
||||
#definition = uppercase(lines[idx_start])
|
||||
definition = lines[idx_start]
|
||||
#has_set_def = match(r"TYPE=([\w\_\-]+),.*NAME=([\w\_\-]+)", definition)
|
||||
has_set_def = Dict(map(y -> lowercase(strip(y[1])) => strip(y[2]), map(x -> split(x, "="), matchall(r"([\w\_\-]+[ ]*=[ ]*[\w\_\-]+)", definition))))
|
||||
has_set_def != nothing || return
|
||||
debug(has_set_def)
|
||||
set_type = Symbol(get(has_set_def, "type", "UNKNOWN"))
|
||||
set_name = Symbol(has_set_def["name"])
|
||||
data = Vector{Tuple{Int64, Symbol}}()
|
||||
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)
|
||||
if isa(m, Void)
|
||||
warn("read_abaqus, parsing surface: line $line")
|
||||
continue
|
||||
end
|
||||
element_id = parse(Int, m[:element_id])
|
||||
element_side = Symbol(m[:element_side])
|
||||
push!(data, (element_id, element_side))
|
||||
end
|
||||
model["surface_types"][set_name] = set_type
|
||||
model["surfaces"][set_name] = data
|
||||
return
|
||||
end
|
||||
|
||||
"""
|
||||
Find lines, which contain keywords, for example "*NODE"
|
||||
"""
|
||||
function find_keywords(lines)
|
||||
indexes = Integer[]
|
||||
for (idx, line) in enumerate(lines)
|
||||
if startswith(line, "*") && !startswith(line, "**")
|
||||
push!(indexes, idx)
|
||||
end
|
||||
end
|
||||
return indexes
|
||||
end
|
||||
|
||||
"""
|
||||
Main function for parsing Abaqus input file.
|
||||
"""
|
||||
function parse_abaqus(fid::IOStream)
|
||||
lines = readlines(fid)
|
||||
keyword_indexes = find_keywords(lines)
|
||||
nkeyword_indexes = length(keyword_indexes)
|
||||
debug("$nkeyword_indexes keyword indexes found: $keyword_indexes")
|
||||
|
||||
push!(keyword_indexes, length(lines)+1)
|
||||
idx_start = keyword_indexes[1]
|
||||
keyword_sym::Symbol = :none
|
||||
parser::Function = x->()
|
||||
model = Dict{AbstractString, Any}()
|
||||
model["nodes"] = Dict{Int64, Vector{Float64}}()
|
||||
model["nsets"] = Dict{AbstractString, Vector{Int64}}()
|
||||
model["elsets"] = Dict{AbstractString, Vector{Int64}}()
|
||||
model["elements"] = Dict{Integer, Any}()
|
||||
model["surfaces"] = Dict{Symbol, Vector{Tuple{Int64, Symbol}}}()
|
||||
model["surface_types"] = Dict{Symbol, Symbol}()
|
||||
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}}}
|
||||
if method_exists(parse_section, args)
|
||||
parse_section(model, lines, k_sym, idx_start, idx_end-1, Val{k_sym})
|
||||
else
|
||||
debug("Unknown section: '$(keyword)'")
|
||||
debug("keyword_line = '$keyword_line'")
|
||||
debug("idx_start = $idx_start, idx_end = $idx_end")
|
||||
end
|
||||
idx_start = idx_end
|
||||
end
|
||||
return model
|
||||
end
|
||||
|
||||
function abaqus_read_mesh(fn)
|
||||
model = open(parse_abaqus, fn)
|
||||
mesh = Mesh()
|
||||
mesh.nodes = model["nodes"]
|
||||
for (nset_name, node_ids) in model["nsets"]
|
||||
mesh.node_sets[Symbol(nset_name)] = Set(node_ids)
|
||||
end
|
||||
for (elid, eldata) in model["elements"]
|
||||
eltype = eldata["type"]
|
||||
elcon = eldata["connectivity"]
|
||||
mesh.elements[elid] = elcon
|
||||
mesh.element_types[elid] = eltype
|
||||
end
|
||||
for (elset_name, element_ids) in model["elsets"]
|
||||
mesh.element_sets[Symbol(elset_name)] = Set(element_ids)
|
||||
end
|
||||
mesh.surface_sets = model["surfaces"]
|
||||
mesh.surface_types = model["surface_types"]
|
||||
return mesh
|
||||
end
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
# 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.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Abaqus
|
||||
using JuliaFEM.Testing
|
||||
using DataFrames
|
||||
|
||||
# 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
|
||||
# ENV["ABAQUS_DOWNLOAD_DIR"] = "/tmp"
|
||||
|
||||
""" 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 "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
|
||||
#res = abaqus_open_results("ec38sfs2")
|
||||
|
||||
node_output1 = wsv"""
|
||||
NODE U1 U2 U3 COOR1 COOR2 COOR3
|
||||
1 -2.0000E-33 -2.0000E-33 -2.0000E-33 0.000 0.000 0.000
|
||||
2 -2.6667E-05 -1.0000E-33 -1.7333E-04 2.000 0.000 0.000
|
||||
3 -2.0000E-04 -2.6667E-05 -1.7333E-04 2.000 2.000 0.000
|
||||
4 -1.7333E-04 -2.6667E-05 -1.0000E-33 0.000 2.000 0.000
|
||||
5 -3.6777E-48 -8.6667E-05 -1.3333E-05 0.000 0.000 1.000
|
||||
6 -2.6667E-05 -8.6667E-05 -1.8667E-04 2.000 0.000 1.000
|
||||
7 -2.0000E-04 -1.1333E-04 -1.8667E-04 2.000 2.000 1.000
|
||||
8 -1.7333E-04 -1.1333E-04 -1.3333E-05 0.000 2.000 1.000
|
||||
"""
|
||||
|
||||
node_output_2 = wsv"""
|
||||
NODE RF1 RF2 RF3 CF1 CF2 CF3
|
||||
1 1500.000 1500.000 1000.000 0.000 0.000 0.000
|
||||
2 0.000 500.000 0.000 1500.000 0.000 0.000
|
||||
3 0.000 0.000 0.000 500.000 500.000 -1000.000
|
||||
4 0.000 0.000 0.000 500.000 1500.000 0.000
|
||||
5 -500.000 0.000 0.000 0.000 -500.000 1000.000
|
||||
6 0.000 0.000 0.000 -500.000 -1500.000 0.000
|
||||
7 0.000 0.000 0.000 -1500.000 -1500.000 -1000.000
|
||||
8 0.000 0.000 0.000 -1500.000 -500.000 0.000
|
||||
"""
|
||||
#= to check also results:
|
||||
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
|
||||
@@ -1,110 +0,0 @@
|
||||
# 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.Preprocess
|
||||
using JuliaFEM.Abaqus
|
||||
using JuliaFEM.Testing
|
||||
using JuliaFEM.Preprocess: element_has_nodes, element_has_type
|
||||
|
||||
@testset "read inp file" begin
|
||||
model = open(parse_abaqus, joinpath(Pkg.dir("JuliaFEM"),"geometry","3d_beam","palkki.inp"))
|
||||
@test length(model["nodes"]) == 298
|
||||
@test length(model["elements"]) == 120
|
||||
@test length(model["elsets"]["Body1"]) == 120
|
||||
@test length(model["nsets"]["SUPPORT"]) == 9
|
||||
@test length(model["nsets"]["LOAD"]) == 9
|
||||
@test length(model["nsets"]["TOP"]) == 83
|
||||
end
|
||||
|
||||
@testset "test read element section" begin
|
||||
data = """*ELEMENT, TYPE=C3D10, ELSET=BEAM
|
||||
1, 243, 240, 191, 117, 245, 242, 244,
|
||||
1, 2, 196
|
||||
2, 204, 199, 175, 130, 207, 208, 209,
|
||||
3, 4, 176
|
||||
"""
|
||||
data = split(data, "\n")
|
||||
model = Dict{AbstractString, Any}()
|
||||
model["nsets"] = Dict{AbstractString, Vector{Int}}()
|
||||
model["elsets"] = Dict{AbstractString, Vector{Int}}()
|
||||
model["elements"] = Dict{Integer, Any}()
|
||||
parse_section(model, data, :ELEMENT, 1, 5, Val{:ELEMENT})
|
||||
@test length(model["elements"]) == 2
|
||||
@test model["elements"][1]["connectivity"] == [243, 240, 191, 117, 245, 242, 244, 1, 2, 196]
|
||||
@test model["elements"][2]["connectivity"] == [204, 199, 175, 130, 207, 208, 209, 3, 4, 176]
|
||||
@test model["elsets"]["BEAM"] == [1, 2]
|
||||
end
|
||||
|
||||
@testset "parse nodes from abaqus .inp file to Mesh" begin
|
||||
fn = joinpath(Pkg.dir("JuliaFEM"), "test", "testdata","cube_tet4.inp")
|
||||
mesh = abaqus_read_mesh(fn)
|
||||
info(mesh.surface_sets)
|
||||
@test length(mesh.nodes) == 10
|
||||
@test length(mesh.elements) == 17
|
||||
@test haskey(mesh.elements, 1)
|
||||
@test mesh.elements[1] == [8, 10, 1, 2]
|
||||
@test mesh.element_types[1] == :Tet4
|
||||
@test haskey(mesh.node_sets, :SYM12)
|
||||
@test haskey(mesh.element_sets, :CUBE)
|
||||
@test haskey(mesh.surface_sets, :LOAD)
|
||||
@test haskey(mesh.surface_sets, :ORDER)
|
||||
@test length(mesh.surface_sets[:LOAD]) == 2
|
||||
@test mesh.surface_sets[:LOAD][1] == (16, :S1)
|
||||
@test mesh.surface_types[:LOAD] == :ELEMENT
|
||||
@test length(Set(map(size, values(mesh.nodes)))) == 1
|
||||
elements = create_surface_elements(mesh,:LOAD)
|
||||
@test get_connectivity(elements[1]) == [8,10,9]
|
||||
end
|
||||
|
||||
@testset "parse nodes from abaqus .inp file to Mesh (NX export)" begin
|
||||
fn = joinpath(Pkg.dir("JuliaFEM"), "test", "testdata","nx_export_problem.inp")
|
||||
mesh = abaqus_read_mesh(fn)
|
||||
@test length(mesh.nodes) == 3
|
||||
end
|
||||
|
||||
@testset "parse abaqus .inp created using hypermesh" begin
|
||||
data = """
|
||||
**
|
||||
** ABAQUS Input Deck Generated by HyperMesh Version : 14.0.120.28
|
||||
** Generated using HyperMesh-Abaqus Template Version : 14.0.120
|
||||
**
|
||||
** Template: ABAQUS/STANDARD 3D
|
||||
**
|
||||
*NODE , Nset = nset_csys0
|
||||
1, 2.649428 , -21.93735 , 217.2934
|
||||
2, 27.54531 , 1.108443 , 228.8077
|
||||
"""
|
||||
fn = tempname() * ".inp"
|
||||
open(fn, "w") do fid write(fid, data) end
|
||||
mesh = abaqus_read_mesh(fn)
|
||||
@test length(mesh.nodes) == 2
|
||||
end
|
||||
|
||||
@testset "Elemement types and nodes" begin
|
||||
@test element_has_nodes(Val{:C3D4}) == 4
|
||||
@test element_has_type(Val{:C3D4}) == :Tet4
|
||||
@test element_has_nodes(Val{:C3D8}) == 8
|
||||
@test element_has_type(Val{:C3D8}) == :Hex8
|
||||
@test element_has_nodes(Val{:C3D10}) == 10
|
||||
@test element_has_type(Val{:C3D10}) == :Tet10
|
||||
@test element_has_nodes(Val{:C3D20}) == 20
|
||||
@test element_has_nodes(Val{:C3D20E}) == 20
|
||||
@test element_has_nodes(Val{:S3}) == 3
|
||||
@test element_has_type(Val{:S3}) == :Tri3
|
||||
@test element_has_nodes(Val{:STRI65}) == 6
|
||||
@test element_has_type(Val{:STRI65}) == :Tri6
|
||||
@test element_has_nodes(Val{:CPS4}) == 4
|
||||
@test element_has_type(Val{:CPS4}) == :Quad4
|
||||
end
|
||||
|
||||
@testset "GENERATE keyword" begin
|
||||
data = """
|
||||
*NSET, NSET=testgen, GENERATE
|
||||
7,13,2
|
||||
"""
|
||||
fn = tempname() * ".inp"
|
||||
open(fn, "w") do fid write(fid, data) end
|
||||
mesh = abaqus_read_mesh(fn)
|
||||
@test mesh.node_sets[:testgen] == Set([7,9,13,11])
|
||||
end
|
||||
@@ -5,7 +5,6 @@ using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Testing
|
||||
using JuliaFEM.Abaqus: create_surface_elements
|
||||
|
||||
tet4_meshfile = "test_problems_contact_3d/tet4.inp"
|
||||
tet10_meshfile = "test_problems_contact_3d/tet10.inp"
|
||||
|
||||
@@ -5,7 +5,6 @@ using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Testing
|
||||
using JuliaFEM.Abaqus: create_surface_elements
|
||||
|
||||
### temperature patch tests, sl tet4, dl tet4, sl tet10, dl tet 10
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Testing
|
||||
using JuliaFEM.Abaqus: create_surface_elements
|
||||
|
||||
@testset "forget to add elements to problem" begin
|
||||
X = Dict(
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# 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.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Abaqus
|
||||
using JuliaFEM.Testing
|
||||
|
||||
@testset "parse abaqus inp file to AbaqusModel" begin
|
||||
fn = Pkg.dir("JuliaFEM") * "/test/testdata/cube_tet4.inp"
|
||||
model = abaqus_read_model(fn)
|
||||
|
||||
@test length(model.properties) == 1
|
||||
section = first(model.properties)
|
||||
@test section.element_set == :CUBE
|
||||
@test section.material_name == :MAT
|
||||
|
||||
@test haskey(model.materials, :MAT)
|
||||
material = model.materials[:MAT]
|
||||
@test isapprox(first(material.properties).E, 208.0e3)
|
||||
|
||||
@test length(model.steps) == 1
|
||||
step = first(model.steps)
|
||||
@test length(step.boundary_conditions) == 2
|
||||
|
||||
bc = step.boundary_conditions[1]
|
||||
@test bc.data[1] == [:SYM12, 3]
|
||||
@test bc.data[2] == [:SYM23, 1]
|
||||
@test bc.data[3] == [:SYM13, 2]
|
||||
|
||||
load = step.boundary_conditions[2]
|
||||
@test load.data[1] == [:LOAD, :P, 1.00000]
|
||||
end
|
||||
|
||||
#=
|
||||
@testset "given abaqus model solve field" begin
|
||||
fn = Pkg.dir("JuliaFEM") * "/test/testdata/cube_tet4.inp"
|
||||
model = abaqus_read_model(fn)
|
||||
problems = model()
|
||||
body = first(problems)
|
||||
info(body("displacement", 0.0))
|
||||
result = XDMF()
|
||||
xdmf_new_result!(result, body, 0.0)
|
||||
xdmf_save_field!(result, body, 0.0, "displacement"; field_type="Vector")
|
||||
xdmf_save!(result, "/tmp/cube_tet4.xmf")
|
||||
end
|
||||
=#
|
||||
@@ -5,7 +5,6 @@ using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
using JuliaFEM.Testing
|
||||
using JuliaFEM.Abaqus: create_surface_elements
|
||||
|
||||
datadir = first(splitext(basename(@__FILE__)))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user