From 282b87cd1fac377c3560523dcc790abbe3b2df39 Mon Sep 17 00:00:00 2001 From: Jukka Aho Date: Tue, 12 Jul 2016 06:30:42 +0300 Subject: [PATCH] more abaqus inp reading --- src/abaqus.jl | 687 +++++++++++++++++++------------ test/test_abaqus_verification.jl | 13 + 2 files changed, 442 insertions(+), 258 deletions(-) diff --git a/src/abaqus.jl b/src/abaqus.jl index 082123b..e06a97f 100644 --- a/src/abaqus.jl +++ b/src/abaqus.jl @@ -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 diff --git a/test/test_abaqus_verification.jl b/test/test_abaqus_verification.jl index 9fc57b1..83d0a3c 100644 --- a/test/test_abaqus_verification.jl +++ b/test/test_abaqus_verification.jl @@ -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