mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-12 06:22:00 +00:00
more abaqus inp reading
This commit is contained in:
+429
-258
@@ -7,47 +7,63 @@ using JuliaFEM
|
||||
using JuliaFEM.Preprocess
|
||||
using JuliaFEM.Postprocess
|
||||
|
||||
global const ABAQUS_SECTIONS = [
|
||||
"HEADING", "NODE", "ELEMENT", "SOLID SECTION",
|
||||
"MATERIAL", "NSET", "SURFACE", "STEP"]
|
||||
|
||||
global const ABAQUS_SUBSECTIONS = [
|
||||
"ELASTIC", "DENSITY", "SPECIFIC HEAT", "CONDUCTIVITY",
|
||||
"STATIC", "BOUNDARY", "DSLOAD", "OUTPUT", "NODE FILE",
|
||||
"RESTART", "END STEP"]
|
||||
### Model definitions for ABAQUS data model
|
||||
|
||||
abstract AbstractMaterial
|
||||
abstract AbstractMaterialProperty
|
||||
abstract AbstractProperty
|
||||
abstract AbstractStep
|
||||
abstract AbstractBoundaryCondition
|
||||
abstract AbstractOutputRequest
|
||||
|
||||
type Model
|
||||
mesh :: Mesh
|
||||
materials :: Dict
|
||||
properties :: Vector
|
||||
steps :: Vector
|
||||
materials :: Dict{Symbol, AbstractMaterial}
|
||||
properties :: Vector{AbstractProperty}
|
||||
boundary_conditions :: Vector{AbstractBoundaryCondition}
|
||||
steps :: Vector{AbstractStep}
|
||||
problems :: Vector{Problem}
|
||||
end
|
||||
|
||||
function Model()
|
||||
return Model(Mesh(), Dict(), Vector(), Vector())
|
||||
type SolidSection <: AbstractProperty
|
||||
element_set :: Symbol
|
||||
material_name :: Symbol
|
||||
end
|
||||
|
||||
function push!(model::Model, property::AbstractProperty)
|
||||
push!(model.properties, property)
|
||||
type Material <: AbstractMaterial
|
||||
name :: Symbol
|
||||
properties :: Vector{AbstractMaterialProperty}
|
||||
end
|
||||
|
||||
function push!(model::Model, step::AbstractStep)
|
||||
push!(model.steps, step)
|
||||
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
|
||||
options
|
||||
end
|
||||
|
||||
function Keyword()
|
||||
return Keyword(nothing, nothing)
|
||||
name :: AbstractString
|
||||
options :: Vector{Union{AbstractString, Pair}}
|
||||
end
|
||||
|
||||
function getindex(kw::Keyword, s)
|
||||
@@ -55,17 +71,17 @@ function getindex(kw::Keyword, s)
|
||||
end
|
||||
|
||||
type AbaqusReaderState
|
||||
section
|
||||
subsection
|
||||
material
|
||||
property
|
||||
step
|
||||
data
|
||||
section :: Nullable{Keyword}
|
||||
material :: Nullable{AbstractMaterial}
|
||||
property :: Nullable{AbstractProperty}
|
||||
step :: Nullable{AbstractStep}
|
||||
data :: Vector{AbstractString}
|
||||
end
|
||||
|
||||
function parse(s::AbaqusReaderState)
|
||||
function get_data(state::AbaqusReaderState)
|
||||
data = []
|
||||
for row in s.data
|
||||
for row in state.data
|
||||
row = strip(row, [' ', ','])
|
||||
col = split(row, ',')
|
||||
col = map(parse, col)
|
||||
push!(data, col)
|
||||
@@ -73,10 +89,17 @@ function parse(s::AbaqusReaderState)
|
||||
return data
|
||||
end
|
||||
|
||||
function AbaqusReaderState()
|
||||
return AbaqusReaderState(Keyword(), Keyword(), nothing, nothing, nothing, [])
|
||||
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, "**")
|
||||
@@ -93,109 +116,94 @@ function parse_keyword(line; uppercase_keyword=true)
|
||||
if uppercase_keyword
|
||||
keyword_name = uppercase(keyword_name)
|
||||
end
|
||||
keyword_options = []
|
||||
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)
|
||||
push!(keyword.options, pair[1])
|
||||
elseif length(pair) == 2
|
||||
push!(keyword_options, pair[1] => pair[2])
|
||||
push!(keyword.options, pair[1] => pair[2])
|
||||
else
|
||||
error("Keyword failure: $line, $option, $pair")
|
||||
end
|
||||
end
|
||||
return Keyword(keyword_name, keyword_options)
|
||||
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)
|
||||
section.name in ABAQUS_SECTIONS || return false
|
||||
is_abaqus_keyword_registered(section.name) || return false
|
||||
return true
|
||||
end
|
||||
|
||||
function is_new_subsection(line)
|
||||
is_keyword(line) || return false
|
||||
subsection = parse_keyword(line)
|
||||
subsection.name in ABAQUS_SUBSECTIONS || return false
|
||||
return true
|
||||
end
|
||||
|
||||
function maybe_open_section!(model, state)
|
||||
section_name = Val{Symbol(state.section.name)}
|
||||
args = Tuple{Model, AbaqusReaderState, Type{section_name}}
|
||||
if method_exists(open_section!, args)
|
||||
info("Opening section $(state.section.name)")
|
||||
open_section!(model, state, section_name)
|
||||
end
|
||||
end
|
||||
|
||||
function maybe_close_section!(model, state)
|
||||
section_name = Val{Symbol(state.section.name)}
|
||||
args = Tuple{Model, AbaqusReaderState, Type{section_name}}
|
||||
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)
|
||||
info("Closing section $(state.section.name)")
|
||||
close_section!(model, state, section_name)
|
||||
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)
|
||||
maybe_close_subsection!(model, state)
|
||||
maybe_close_section!(model, state)
|
||||
function new_section!(model, state, line::AbstractString; verbose=true)
|
||||
maybe_close_section!(model, state; verbose=verbose)
|
||||
state.data = []
|
||||
state.section = parse_keyword(line)
|
||||
state.subsection = Keyword()
|
||||
info("New section: $(state.section.name) with options $(state.section.options)")
|
||||
maybe_open_section!(model, state)
|
||||
maybe_open_section!(model, state; verbose=verbose)
|
||||
end
|
||||
|
||||
function maybe_open_subsection!(model, state)
|
||||
section_name = Val{Symbol(state.section.name)}
|
||||
subsection_name = Val{Symbol(state.subsection.name)}
|
||||
args = Tuple{Model, AbaqusReaderState, Type{section_name}, Type{subsection_name}}
|
||||
if method_exists(open_subsection!, args)
|
||||
info("Opening subsection $(state.section.name) / $(state.subsection.name)")
|
||||
open_subsection!(model, state, section_name, subsection_name)
|
||||
end
|
||||
end
|
||||
|
||||
function maybe_close_subsection!(model, state)
|
||||
section_name = Val{Symbol(state.section.name)}
|
||||
subsection_name = Val{Symbol(state.subsection.name)}
|
||||
args = Tuple{Model, AbaqusReaderState, Type{section_name}, Type{subsection_name}}
|
||||
if method_exists(close_subsection!, args)
|
||||
info("Closing subsection $(state.section.name) / $(state.subsection.name)")
|
||||
close_subsection!(model, state, section_name, subsection_name)
|
||||
end
|
||||
end
|
||||
|
||||
function new_subsection!(model, state, line::AbstractString)
|
||||
maybe_close_subsection!(model, state)
|
||||
state.data = []
|
||||
state.subsection = parse_keyword(line)
|
||||
info("New subsection: $(state.subsection.name) with options $(state.subsection.options)")
|
||||
maybe_open_subsection!(model, state)
|
||||
end
|
||||
|
||||
# open_section! and open_subsection! are called right after keyword is found
|
||||
# open_section! is called right after keyword is found
|
||||
function open_section! end
|
||||
function open_subsection! end
|
||||
|
||||
# close_section! and close_subsection! are called at the end or section or before new keyword
|
||||
# close_section! is called at the end or section or before new keyword
|
||||
function close_section! end
|
||||
function close_subsection! end
|
||||
|
||||
function process_line!(model, state, line)
|
||||
if state.section.name == nothing
|
||||
info("unknown section, line = $line")
|
||||
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")
|
||||
info("($(state.section.name), $(state.subsection.name)) => $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)
|
||||
@@ -203,15 +211,13 @@ end
|
||||
|
||||
function abaqus_read_model(fn; read_mesh=true)
|
||||
|
||||
model = Model()
|
||||
model = Model(Mesh(), Dict(), Vector(), Vector(), Vector(), Vector())
|
||||
|
||||
if read_mesh
|
||||
model.mesh = abaqus_read_mesh(fn)
|
||||
else
|
||||
model.mesh = Mesh()
|
||||
end
|
||||
|
||||
state = AbaqusReaderState()
|
||||
state = AbaqusReaderState(nothing, nothing, nothing, nothing, [])
|
||||
|
||||
fid = open(fn)
|
||||
for line in eachline(fid)
|
||||
@@ -219,225 +225,392 @@ function abaqus_read_model(fn; read_mesh=true)
|
||||
is_comment(line) && continue
|
||||
if is_new_section(line)
|
||||
new_section!(model, state, line)
|
||||
elseif is_new_subsection(line)
|
||||
new_subsection!(model, state, line)
|
||||
else
|
||||
process_line!(model, state, line)
|
||||
end
|
||||
end
|
||||
maybe_close_subsection!(model, state)
|
||||
maybe_close_section!(model, state)
|
||||
close(fid)
|
||||
|
||||
maybe_close_section!(model, state)
|
||||
|
||||
return model
|
||||
end
|
||||
|
||||
### Model parse start
|
||||
### 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")
|
||||
typealias BOUNDARY_CONDITIONS Union{BOUNDARY, CLOAD, DLOAD, DSLOAD}
|
||||
|
||||
@register_abaqus_keyword("NODE PRINT")
|
||||
@register_abaqus_keyword("SECTION PRINT")
|
||||
typealias OUTPUT_REQUESTS Union{NODE_PRINT, SECTION_PRINT}
|
||||
|
||||
## Properties
|
||||
|
||||
type SolidSection <: AbstractProperty
|
||||
element_set
|
||||
material
|
||||
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, ::Type{Val{Symbol("SOLID SECTION")}})
|
||||
property = SolidSection(state.section["ELSET"], state.section["MATERIAL"])
|
||||
push!(model, property)
|
||||
function close_section!(model, state, ::SOLID_SECTION)
|
||||
state.property = nothing
|
||||
end
|
||||
|
||||
## Materials
|
||||
|
||||
abstract MaterialProperty
|
||||
|
||||
type Elastic <: MaterialProperty
|
||||
E
|
||||
nu
|
||||
end
|
||||
|
||||
type Material <: AbstractMaterial
|
||||
name
|
||||
properties
|
||||
end
|
||||
|
||||
function Material(name)
|
||||
return Material(name, [])
|
||||
end
|
||||
|
||||
function push!(material::Material, property::MaterialProperty)
|
||||
push!(material.properties, property)
|
||||
end
|
||||
|
||||
function open_section!(model, state, ::Type{Val{:MATERIAL}})
|
||||
state.material = Material(state.section["NAME"])
|
||||
end
|
||||
|
||||
function close_subsection!(model, state, ::Type{Val{:MATERIAL}}, ::Type{Val{:ELASTIC}})
|
||||
E, nu = parse(state)[1]
|
||||
push!(state.material, Elastic(E, nu))
|
||||
end
|
||||
|
||||
function close_section!(model, state, ::Type{Val{:MATERIAL}})
|
||||
material_name = state.material.name
|
||||
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] = state.material
|
||||
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
|
||||
|
||||
type Step <: AbstractStep
|
||||
content :: Vector
|
||||
function open_section!(model, state, ::STEP)
|
||||
step = Step(nothing, Vector(), Vector())
|
||||
state.step = step
|
||||
push!(model.steps, step)
|
||||
end
|
||||
|
||||
function push!(step::Step, data)
|
||||
push!(step.content, data)
|
||||
function open_section!(model, state, ::STATIC)
|
||||
isnull(state.step) && error("*STATIC outside *STEP ?")
|
||||
get(state.step).kind = :STATIC
|
||||
end
|
||||
|
||||
abstract AbstractBoundaryCondition
|
||||
|
||||
type Boundary <: AbstractBoundaryCondition
|
||||
data :: Vector
|
||||
function open_section!(model, state, ::END_STEP)
|
||||
state.step = nothing
|
||||
end
|
||||
|
||||
function getindex(b::Boundary, j::Int64)
|
||||
return b.data[j]
|
||||
## 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
|
||||
|
||||
type DSLoad <: AbstractBoundaryCondition
|
||||
data :: Vector
|
||||
## 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
|
||||
|
||||
function getindex(l::DSLoad, j::Int64)
|
||||
return l.data[j]
|
||||
end
|
||||
|
||||
function open_section!(model, state, ::Type{Val{:STEP}})
|
||||
state.step = Step([])
|
||||
end
|
||||
### Code to use JuliaFEM to run ABAQUS data model
|
||||
|
||||
function close_subsection!(model, state, ::Type{Val{:STEP}}, ::Type{Val{:BOUNDARY}})
|
||||
push!(state.step, Boundary(parse(state)))
|
||||
end
|
||||
|
||||
function close_subsection!(model, state, ::Type{Val{:STEP}}, ::Type{Val{:DSLOAD}})
|
||||
push!(state.step, DSLoad(parse(state)))
|
||||
end
|
||||
|
||||
function close_section!(model, state, ::Type{Val{:STEP}})
|
||||
push!(model.steps, state.step)
|
||||
end
|
||||
|
||||
### model parse end
|
||||
|
||||
# when model is called, run simulation
|
||||
|
||||
function determine_problem_type(model, element_set_name)
|
||||
function determine_problem_type(model::Model)
|
||||
# FIXME
|
||||
return Elasticity
|
||||
end
|
||||
|
||||
function determine_problem_dimension(model, element_set_name)
|
||||
function determine_problem_dimension(model::Model)
|
||||
# FIXME
|
||||
return 3
|
||||
end
|
||||
|
||||
function get_element_section(model, element_set_name)
|
||||
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, material_name)
|
||||
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 bc *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.surfaces[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(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)
|
||||
for row in bc.data
|
||||
node, dof, load = row
|
||||
element = Element(Poi1, [node])
|
||||
update!(element, "geometry", model.mesh.nodes)
|
||||
update!(element, "displacement traction force $dof", load)
|
||||
push!(problem.elements, element)
|
||||
end
|
||||
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, 2, 3]),
|
||||
:S2 => (:Tri3, [1, 4, 2]),
|
||||
:S3 => (:Tri3, [2, 4, 3]),
|
||||
:S4 => (:Tri3, [3, 4, 1])),
|
||||
: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
|
||||
|
||||
using DataFrames
|
||||
|
||||
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
|
||||
info("nodal output request with data $data and options $options")
|
||||
for row in data
|
||||
for code in row
|
||||
for problem in model.problems
|
||||
if code == :U
|
||||
vals = problem("displacement", solver.time)
|
||||
node_ids = sort(collect(keys(vals)))
|
||||
u1 = [vals[id][1] for id in node_ids]
|
||||
u2 = [vals[id][2] for id in node_ids]
|
||||
u3 = [vals[id][3] for id in node_ids]
|
||||
d = DataFrame(; id=node_ids, u1=u1, u2=u2, u3=u3)
|
||||
println(d)
|
||||
end
|
||||
end
|
||||
end
|
||||
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 call(model::Model)
|
||||
info("Starting JuliaFEM-ABAQUS solver.")
|
||||
|
||||
# 1. create field problems and add elements
|
||||
field_problems = []
|
||||
for (element_set_name, element_ids) in model.mesh.element_sets
|
||||
problem_type = determine_problem_type(model, element_set_name)
|
||||
problem_name = "BODY $element_set_name"
|
||||
problem_dimension = determine_problem_dimension(model, element_set_name)
|
||||
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)
|
||||
for mp in material.properties
|
||||
if isa(mp, Elastic)
|
||||
update!(problem.elements, "youngs modulus", mp.E)
|
||||
update!(problem.elements, "poissons ratio", mp.nu)
|
||||
end
|
||||
end
|
||||
push!(field_problems, problem)
|
||||
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]
|
||||
|
||||
child_element = Dict(
|
||||
:Tet4 => :Tri3,
|
||||
:Tet10 => :Tet6)
|
||||
|
||||
foobar = Dict(
|
||||
:Tet4 => Dict(
|
||||
:S1 => [1, 2, 3],
|
||||
:S2 => [1, 4, 2],
|
||||
:S3 => [2, 4, 3],
|
||||
:S4 => [3, 4, 1]))
|
||||
|
||||
# 2. loop steps
|
||||
# 3. loop steps
|
||||
for step in model.steps
|
||||
boundary_problems = []
|
||||
for bc in step.content
|
||||
if isa(bc, Boundary)
|
||||
problem = Problem(Dirichlet, "fix nodes", 3, "displacement")
|
||||
for (bc_name, dof) in bc.data
|
||||
nodes = model.mesh.node_sets[bc_name]
|
||||
elements = [Element(Poi1, [id]) for id in nodes]
|
||||
update!(elements, "displacement $dof", 0.0)
|
||||
update!(elements, "geometry", model.mesh.nodes)
|
||||
push!(problem, elements)
|
||||
end
|
||||
push!(boundary_problems, problem)
|
||||
end
|
||||
if isa(bc, DSLoad)
|
||||
problem = Problem(Elasticity, "pressure load", 3)
|
||||
for (bc_name, bc_type, pressure) in bc.data
|
||||
bc_type == :P || error("bc_type = $bc_type != :P")
|
||||
elements = []
|
||||
for (element_id, side) in model.mesh.surfaces[bc_name]
|
||||
parent_element = model.mesh.elements[element_id]
|
||||
parent_element_type = model.mesh.element_types[element_id]
|
||||
lcon = foobar[parent_element_type][side]
|
||||
boundary_element_type = child_element[parent_element_type]
|
||||
gcon = parent_element[lcon]
|
||||
info("parent element $parent_element_type, boundary element = $boundary_element_type, side $side, gcon = $gcon")
|
||||
boundary_element = Element(JuliaFEM.(boundary_element_type), gcon)
|
||||
push!(elements, boundary_element)
|
||||
end
|
||||
update!(elements, "geometry", model.mesh.nodes)
|
||||
update!(elements, "surface pressure", pressure)
|
||||
push!(problem, elements)
|
||||
end
|
||||
push!(boundary_problems, problem)
|
||||
end
|
||||
end
|
||||
|
||||
all_problems = [field_problems; boundary_problems]
|
||||
#solver_type = determine_solver_type(model, step)
|
||||
solver_type = Linear
|
||||
solver_description = "Step"
|
||||
# 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()
|
||||
# 3.3 postprocessing based on output requests
|
||||
for output_request in step.output_requests
|
||||
process_output_request(model, solver, output_request)
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function abaqus_read_results
|
||||
end
|
||||
|
||||
### JuliaFEM-ABAQUS interface entry point
|
||||
|
||||
""" Run ABAQUS .inp file. """
|
||||
function abaqus_run_model(fn)
|
||||
model = abaqus_read_model(fn)
|
||||
model()
|
||||
end
|
||||
|
||||
"""
|
||||
Run ABAQUS .inp file. This function can be used to run some abaqus inp file
|
||||
with the following extra feature: if file is not found from temporary directory,
|
||||
attempt to download if from internet, if environment variable ABAQUS_TEST_URL is
|
||||
set. This can be used to verify JuliaFEM code using well known ABAQUS test cases,
|
||||
if they are found from company intranet and are accessible using wget / curl.
|
||||
"""
|
||||
function abaqus_run_test(name; print_test_file=false)
|
||||
# get test file
|
||||
fn = tempdir()*"/$name.inp"
|
||||
@@ -467,5 +640,3 @@ function abaqus_run_test(name; print_test_file=false)
|
||||
abaqus_run_model(fn)
|
||||
end
|
||||
|
||||
function abaqus_read_results
|
||||
end
|
||||
|
||||
@@ -18,4 +18,17 @@ end
|
||||
@testset "ec38sfs2" begin
|
||||
abaqus_run_test("ec38sfs2"; print_test_file=true) || return
|
||||
results = abaqus_read_results("ec38sfs2")
|
||||
side = get_results(results, "SECTION"; name="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
|
||||
|
||||
Reference in New Issue
Block a user