Files
JuliaFEM.jl/src/problems.jl
T

443 lines
13 KiB
Julia
Raw Normal View History

# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
2017-07-20 20:46:57 +03:00
abstract type AbstractProblem end
abstract type FieldProblem<:AbstractProblem end
abstract type BoundaryProblem<:AbstractProblem end
abstract type MixedProblem<:AbstractProblem end
2016-02-03 06:49:42 +02:00
"""
General linearized problem to solve
2017-08-23 15:28:20 +03:00
(K₁+K₂)Δu + C1'*Δλ = f₁+f₂
C2Δu + D*Δλ = g
2016-02-03 06:49:42 +02:00
"""
type Assembly
2016-06-27 16:11:33 +03:00
2016-02-03 06:49:42 +02:00
M :: SparseMatrixCOO # mass matrix
2016-06-27 16:11:33 +03:00
# for field assembly
K :: SparseMatrixCOO # stiffness matrix
2016-05-30 01:03:34 +03:00
Kg :: SparseMatrixCOO # geometric stiffness matrix
2016-06-27 16:11:33 +03:00
f :: SparseMatrixCOO # force vector
2016-08-04 13:15:19 +03:00
fg :: SparseMatrixCOO #
2016-06-27 16:11:33 +03:00
2016-02-03 06:49:42 +02:00
# for boundary assembly
C1 :: SparseMatrixCOO
C2 :: SparseMatrixCOO
D :: SparseMatrixCOO
2016-02-03 06:49:42 +02:00
g :: SparseMatrixCOO
2016-02-06 21:08:35 +02:00
c :: SparseMatrixCOO
2016-02-03 20:39:03 +02:00
u :: Vector{Float64} # solution vector u
u_prev :: Vector{Float64} # previous solution vector u
u_norm_change :: Real # change of norm in u
la :: Vector{Float64} # solution vector la
la_prev :: Vector{Float64} # previous solution vector u
la_norm_change :: Real # change of norm in la
removed_dofs :: Vector{Int64} # manually remove dofs from assembly
end
2016-02-03 06:49:42 +02:00
function Assembly()
return Assembly(
SparseMatrixCOO(),
2016-02-01 09:12:41 +02:00
SparseMatrixCOO(),
SparseMatrixCOO(),
SparseMatrixCOO(),
2016-02-03 06:49:42 +02:00
SparseMatrixCOO(),
SparseMatrixCOO(),
SparseMatrixCOO(),
2016-02-06 21:08:35 +02:00
SparseMatrixCOO(),
2016-05-30 01:03:34 +03:00
SparseMatrixCOO(),
2016-06-27 16:11:33 +03:00
SparseMatrixCOO(),
2016-02-03 06:49:42 +02:00
[], [], Inf,
[], [], Inf,
[])
2016-02-01 09:12:41 +02:00
end
2016-05-30 01:03:34 +03:00
function empty!(assembly::Assembly)
2016-02-03 06:49:42 +02:00
empty!(assembly.K)
2016-05-30 01:03:34 +03:00
empty!(assembly.Kg)
2016-02-03 06:49:42 +02:00
empty!(assembly.f)
2016-06-27 16:11:33 +03:00
empty!(assembly.fg)
2016-02-03 06:49:42 +02:00
empty!(assembly.C1)
empty!(assembly.C2)
empty!(assembly.D)
empty!(assembly.g)
2016-02-06 21:08:35 +02:00
empty!(assembly.c)
2016-06-27 16:11:33 +03:00
end
function isempty(assembly::Assembly)
T = isempty(assembly.K)
T &= isempty(assembly.Kg)
T &= isempty(assembly.f)
T &= isempty(assembly.fg)
T &= isempty(assembly.C1)
T &= isempty(assembly.C2)
T &= isempty(assembly.D)
T &= isempty(assembly.g)
T &= isempty(assembly.c)
return T
end
2017-08-23 15:28:20 +03:00
"""
Defines types for Problem variables.
# Examples
The type of 'elements' is Vector{Element}
Add elements into the Problem element list.
```@example
a = [1, 2, 3]
Problem.elements = a
```
"""
2016-02-03 06:49:42 +02:00
type Problem{P<:AbstractProblem}
2017-08-23 15:28:20 +03:00
name :: AbstractString # descriptive name for the problem
2016-02-03 06:49:42 +02:00
dimension :: Int # degrees of freedom per node
2017-08-23 15:28:20 +03:00
parent_field_name :: AbstractString # (optional) name of the parent field e.g. "displacement"
2016-02-03 06:49:42 +02:00
elements :: Vector{Element}
2017-08-23 15:28:20 +03:00
dofmap :: Dict{Element, Vector{Int64}} # connects the element local dofs to the global dofs
2016-02-03 06:49:42 +02:00
assembly :: Assembly
2016-08-04 13:15:19 +03:00
fields :: Dict{AbstractString, Field}
2017-03-21 08:36:18 +02:00
postprocess_fields :: Vector{String}
2016-02-03 06:49:42 +02:00
properties :: P
end
2016-02-01 09:12:41 +02:00
2017-08-23 15:28:20 +03:00
"""
Problem(problem_type, problem_name::String, problem_dimension)
2017-08-23 15:28:20 +03:00
Construct a new field problem where `problem_type` is the type of the problem
(Elasticity, Dirichlet, etc.), `problem_name` is the name of the problem and
`problem_dimension` is the number of DOF:s in one node (2 in a 2D problem, 3
in an elastic 3D problem, 6 in a 3D beam problem, etc.).
2017-08-23 15:28:20 +03:00
# Examples
Create a vector-valued (dim=3) elasticity problem:
```@example
prob1 = Problem(Elasticity, "this is my problem", 3)
```
"""
function Problem{P<:FieldProblem}(::Type{P}, name::AbstractString, dimension::Int64)
2017-03-21 08:36:18 +02:00
return Problem{P}(name, dimension, "none", [], Dict(), Assembly(), Dict(), Vector(), P())
2016-02-01 09:12:41 +02:00
end
2017-08-23 15:28:20 +03:00
"""
Construct a new boundary problem.
2016-02-03 06:49:42 +02:00
Examples
--------
2017-08-23 15:28:20 +03:00
Create a Dirichlet boundary problem for a vector-valued (dim=3) elasticity problem.
2016-02-03 06:49:42 +02:00
julia> bc1 = Problem(Dirichlet, "support", 3, "displacement")
2016-08-04 13:15:19 +03:00
solver.
2016-02-01 09:12:41 +02:00
"""
2016-06-27 16:11:33 +03:00
function Problem{P<:BoundaryProblem}(::Type{P}, name, dimension, parent_field_name)
2017-03-21 08:36:18 +02:00
return Problem{P}(name, dimension, parent_field_name, [], Dict(), Assembly(), Dict(), Vector(), P())
2016-02-01 09:12:41 +02:00
end
2017-03-21 08:36:18 +02:00
function get_formulation_type(problem::Problem)
2016-06-19 20:01:37 +03:00
return :incremental
2016-02-01 09:12:41 +02:00
end
2017-03-21 08:36:18 +02:00
function get_unknown_field_name{P<:BoundaryProblem}(::Type{P})
return "lambda"
2016-02-01 09:12:41 +02:00
end
2016-02-03 06:49:42 +02:00
function get_assembly(problem)
return problem.assembly
end
2016-02-01 09:12:41 +02:00
"""
update!(problem.properties, attr...)
Update properties for a problem.
# Example
```julia
update!(body.properties, "finite_strain" => "false")
```
"""
function update!{P<:AbstractProblem}(problem::P, attr::Pair{String, String}...)
for (name, value) in attr
debug("$P: set $name to $value")
setfield!(problem, parse(name), parse(value))
end
end
2017-08-23 15:28:20 +03:00
"""
function initialize!(problem_type, element_name, time)
Initialize the element ready for calculation, where `problem_type` is the type
of the problem (Elasticity, Dirichlet, etc.), `element_name` is the name of a
constructed element (see Element(element_type, connectivity_vector)) and `time`
is the starting time of the initializing process.
"""
2016-07-14 12:43:41 +03:00
function initialize!(problem::Problem, element::Element, time::Float64)
2016-02-03 20:39:03 +02:00
field_name = get_unknown_field_name(problem)
field_dim = get_unknown_field_dimension(problem)
2016-07-14 12:43:41 +03:00
nnodes = length(element)
# initialize primary field
if !haskey(element, field_name)
if field_dim == 1
update!(element, field_name, time => zeros(nnodes))
else
update!(element, field_name, time => [zeros(field_dim) for i=1:nnodes])
2016-02-03 20:39:03 +02:00
end
end
2016-07-14 12:43:41 +03:00
2017-08-23 15:28:20 +03:00
# if a boundary problem, initialize also a field for the main problem
is_boundary_problem(problem) || return
field_name = get_parent_field_name(problem)
2016-07-14 12:43:41 +03:00
if !haskey(element, field_name)
if field_dim == 1
update!(element, field_name, time => zeros(nnodes))
else
update!(element, field_name, time => [zeros(field_dim) for i=1:nnodes])
end
end
2016-02-03 20:39:03 +02:00
end
2016-07-14 12:43:41 +03:00
function initialize!(problem::Problem, time::Float64=0.0)
for element in get_elements(problem)
initialize!(problem, element, time)
end
end
2016-02-03 20:39:03 +02:00
2017-08-23 15:28:20 +03:00
"""
update!(problem, assembly, u, la)
Update the problem solution vector for assembly.
"""
2017-03-21 08:36:18 +02:00
function update!(problem::Problem, assembly::Assembly, u::Vector, la::Vector)
2016-02-03 20:39:03 +02:00
# resize & fill with zeros vectors if length mismatch with current solution
2016-07-14 12:43:41 +03:00
2016-02-03 20:39:03 +02:00
if length(u) != length(assembly.u)
2016-07-14 12:43:41 +03:00
info("resizing solution vector u")
2016-02-03 20:39:03 +02:00
resize!(assembly.u, length(u))
fill!(assembly.u, 0.0)
end
2016-07-14 12:43:41 +03:00
2016-02-03 20:39:03 +02:00
if length(la) != length(assembly.la)
2017-03-21 08:36:18 +02:00
info("resizing lagrange multiplier vector la")
2016-02-03 20:39:03 +02:00
resize!(assembly.la, length(la))
fill!(assembly.la, 0.0)
2016-02-01 09:12:41 +02:00
end
2016-02-03 20:39:03 +02:00
# copy current solutions to previous ones and add/replace new solution
# TODO: here we have couple of options and they need to be clarified
# for total formulation we are solving total quantity Ku = f while in
2016-02-24 01:20:39 +02:00
# incremental formulation we solve KΔu = f and u = u + Δu
2016-02-03 20:39:03 +02:00
assembly.u_prev = copy(assembly.u)
assembly.la_prev = copy(assembly.la)
2016-08-04 13:15:19 +03:00
if get_formulation_type(problem) == :total
assembly.u = u
assembly.la = la
elseif get_formulation_type(problem) == :incremental
2016-02-03 20:39:03 +02:00
assembly.u += u
assembly.la = la
elseif get_formulation_type(problem) == :forwarddiff
assembly.u += u
2016-02-24 01:20:39 +02:00
assembly.la += la
2016-02-03 20:39:03 +02:00
else
info("$(problem.name): unknown formulation type, don't know what to do with results")
error("serious failure with problem formulation: $(get_formulation_type(problem))")
2016-02-03 20:39:03 +02:00
end
# calculate change of norm
assembly.u_norm_change = norm(assembly.u - assembly.u_prev)
assembly.la_norm_change = norm(assembly.la - assembly.la_prev)
2016-02-11 02:51:27 +02:00
return assembly.u, assembly.la
2016-02-03 20:39:03 +02:00
end
2017-08-23 15:28:20 +03:00
"""
get_global_solution(problem, assembly)
Return a global solution (u, la) for a problem.
2016-02-03 20:39:03 +02:00
Notes
-----
2017-08-23 15:28:20 +03:00
If the length of solution vector != number of nodes, i.e. the field dimension is
something else than 1, reshape vectors so that their length matches to the
number of nodes. This helps to get nodal results easily.
2016-02-03 20:39:03 +02:00
"""
2016-07-14 12:43:41 +03:00
function get_global_solution(problem::Problem, assembly::Assembly)
u = assembly.u
la = assembly.la
2016-02-03 20:39:03 +02:00
field_dim = get_unknown_field_dimension(problem)
2016-07-14 12:43:41 +03:00
if field_dim == 1
return u, la
else
nnodes = round(Int, length(u)/field_dim)
u = reshape(u, field_dim, nnodes)
u = Vector{Float64}[u[:,i] for i in 1:nnodes]
la = reshape(la, field_dim, nnodes)
la = Vector{Float64}[la[:,i] for i in 1:nnodes]
return u, la
2016-02-03 20:39:03 +02:00
end
end
2016-07-14 12:43:41 +03:00
2017-08-23 15:28:20 +03:00
"""
update!(problem, assembly, elements, time)
Update a solution from the assebly to elements.
"""
2016-07-14 12:43:41 +03:00
function update!{P<:FieldProblem}(problem::Problem{P}, assembly::Assembly, elements::Vector{Element}, time::Float64)
u, la = get_global_solution(problem, assembly)
field_name = get_unknown_field_name(problem)
2016-07-14 12:43:41 +03:00
# update solution u for elements
for element in elements
connectivity = get_connectivity(element)
update!(element, field_name, time => u[connectivity])
end
2016-07-14 12:43:41 +03:00
end
function update!{P<:BoundaryProblem}(problem::Problem{P}, assembly::Assembly, elements::Vector{Element}, time::Float64)
u, la = get_global_solution(problem, assembly)
parent_field_name = get_parent_field_name(problem) # displacement
2017-03-21 08:36:18 +02:00
field_name = get_unknown_field_name(problem) # lambda
# update solution and lagrange multipliers for boundary elements
2016-07-14 12:43:41 +03:00
for element in elements
connectivity = get_connectivity(element)
update!(element, parent_field_name, time => u[connectivity])
2017-03-21 08:36:18 +02:00
update!(element, field_name, time => la[connectivity])
2016-02-01 09:12:41 +02:00
end
end
2017-08-16 14:09:54 +03:00
"""
add_elements!(problem::Problem, elements)
Add new elements into the problem.
"""
function add_elements!(problem::Problem, elements)
for element in elements
push!(problem.elements, element)
end
end
2016-07-03 05:01:18 +03:00
function get_elements(problem::Problem)
2015-11-27 10:10:00 +02:00
return problem.elements
2015-10-09 23:45:28 +03:00
end
2016-07-14 12:43:41 +03:00
function get_assembly(problem::Problem)
return problem.assembly
end
2016-07-03 05:01:18 +03:00
function length(problem::Problem)
return length(problem.elements)
end
2016-07-14 12:43:41 +03:00
function update!(problem::Problem, field_name::AbstractString, data)
2016-10-01 13:37:15 +03:00
#if haskey(problem.fields, field_name)
# update!(problem.fields[field_name], field_name::AbstractString, data)
#else
# problem.fields[field_name] = Field(data)
#end
2016-07-14 12:43:41 +03:00
update!(problem.elements, field_name::AbstractString, data)
2016-06-25 04:12:53 +03:00
end
2016-08-04 13:15:19 +03:00
function haskey(problem::Problem, field_name::AbstractString)
return haskey(problem.fields, field_name)
end
function getindex(problem::Problem, field_name::AbstractString)
return problem.fields[field_name]
end
""" Return field calculated to nodal points for elements in problem p. """
2017-06-28 13:54:04 +03:00
function (problem::Problem)(field_name::AbstractString, time::AbstractFloat=0.0)
2016-10-01 13:37:15 +03:00
#if haskey(problem, field_name)
# return problem[field_name](time)
#end
2016-08-04 13:15:19 +03:00
f = nothing
for element in get_elements(problem)
haskey(element, field_name) || continue
for (c, v) in zip(get_connectivity(element), element(field_name, time))
if f == nothing
f = Dict(c => v)
continue
end
if haskey(f, c)
if !isapprox(f[c], v)
info("several values for single node when returning field $field_name")
info("already have: $(f[c]), and trying to set $v")
end
else
f[c] = v
end
end
end
2016-10-01 13:37:15 +03:00
#f == nothing && return f
#update!(problem, field_name, time => f)
2016-08-04 13:15:19 +03:00
return f
end
2015-11-27 10:10:00 +02:00
""" Return the dimension of the unknown field of this problem. """
function get_unknown_field_dimension(problem::Problem)
2016-02-03 06:49:42 +02:00
return problem.dimension
2015-10-28 04:29:14 +02:00
end
2015-11-27 10:10:00 +02:00
""" Return the name of the unknown field of this problem. """
2016-02-01 09:12:41 +02:00
function get_unknown_field_name{P}(problem::Problem{P})
2015-11-27 10:10:00 +02:00
return get_unknown_field_name(P)
2015-10-28 04:29:14 +02:00
end
2016-02-03 20:39:03 +02:00
""" Return the name of the parent field of this (boundary) problem. """
function get_parent_field_name{P<:BoundaryProblem}(problem::Problem{P})
return problem.parent_field_name
2015-10-28 04:29:14 +02:00
end
2015-12-17 15:33:51 +02:00
2016-07-01 02:55:56 +03:00
function push!(problem::Problem, elements...)
push!(problem.elements, elements...)
end
function push!(problem::Problem, elements::Vector)
push!(problem.elements, elements...)
end
2016-07-03 05:01:18 +03:00
function push!(problem::Problem, elements_::Vector...)
for elements in elements_
push!(problem.elements, elements...)
end
end
2016-05-22 02:34:38 +03:00
function get_gdofs(element::Element, dim::Int)
conn = get_connectivity(element)
if length(conn) == 0
error("element connectivity not defined, cannot determine global dofs for element: $element")
end
gdofs = vec([dim*(i-1)+j for j=1:dim, i in conn])
2016-05-22 02:34:38 +03:00
return gdofs
end
2016-06-27 16:11:33 +03:00
function empty!(problem::Problem)
empty!(problem.assembly)
end
""" Return global degrees of freedom for element.
2016-05-22 02:34:38 +03:00
Notes
-----
First look dofs from problem.dofmap, it not found, update dofmap from
element.element connectivity using formula gdofs = [dim*(nid-1)+j for j=1:dim]
1. look element dofs from problem.dofmap
2. if not found, use element.connectivity to update dofmap and 1.
"""
2016-05-22 02:34:38 +03:00
function get_gdofs(problem::Problem, element::Element)
2016-05-30 01:03:34 +03:00
if !haskey(problem.dofmap, element)
dim = get_unknown_field_dimension(problem)
problem.dofmap[element] = get_gdofs(element, dim)
end
return problem.dofmap[element]
2016-05-22 02:34:38 +03:00
end