removed Equation type from code

This commit is contained in:
Jukka Aho
2015-11-27 10:10:00 +02:00
parent f5afcf2057
commit ef667e8f34
23 changed files with 730 additions and 1817 deletions
+7 -5
View File
@@ -56,7 +56,7 @@ function Base.linspace{T<:Array}(X1::T, X2::T, n)
end
# fields, see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/notebooks/2015-06-14-data-structures.ipynb
include("fields2.jl")
include("fields.jl")
#include("basis.jl") # interpolation of discrete fields
include("symbolic.jl") # a thin symbolic layer for fields
include("types.jl") # type definitions
@@ -65,24 +65,26 @@ include("types.jl") # type definitions
include("elements.jl")
include("lagrange.jl") # Lagrange elements
#include("hierarchical.jl") # P-elements
include("mortar_elements.jl") # Mortar elements
#include("mortar_elements.jl") # Mortar elements
### EQUATIONS ###
include("integrate.jl") # default integration points for elements
include("sparse.jl")
include("equations.jl")
include("problems.jl")
include("equations.jl")
### FORMULATIION ###
include("dirichlet.jl")
include("mortar.jl") # mortar projection
include("heat.jl")
include("elasticity.jl")
### ASSEMBLY + SOLVE ###
include("assembly.jl")
include("solvers.jl")
include("directsolver.jl") # parallel sparse direct solver for non-lniear problems
include("directsolver.jl") # parallel sparse direct solver for non-linear problems
### MORTAR STUFF ###
#include("mortar.jl") # mortar projection
# PRE AND POSTPROCESS
include("xdmf.jl")
+6 -6
View File
@@ -3,19 +3,19 @@
# Functions to handle global assembly of problem
function assemble!(assembly::Assembly, problem::Problem, time::Number=0.0, empty_assembly::Bool=true)
function assemble!(assembly::Assembly, problem::AllProblems, time::Float64, empty_assembly::Bool=true)
if empty_assembly
empty!(assembly)
end
for equation in get_equations(problem)
assemble!(assembly, equation, time, problem)
for element in get_elements(problem)
assemble!(assembly, problem, element, time)
end
end
function assemble(problem::Problem, time::Number=0.0)
function assemble(problem::AllProblems, time::Float64)
assembly = Assembly()
for equation in get_equations(problem)
assemble!(assembly, equation, time, problem)
for element in get_elements(problem)
assemble!(assembly, problem, element, time)
end
return assembly
end
-263
View File
@@ -1,266 +1,3 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
type Basis <: ContinuousField
basis :: Function
dbasisdxi :: Function
end
""" Evaluate basis. """
function Base.call(basis::Basis, xi::Vector, time::Number=0.0)
basis.basis(xi) # passing time does not make much sense actually for this...
end
""" Evaluate gradient of basis. This need geometry information to calculate Jacobian. """
function Base.call(basis::Basis, geometry::Increment, xi::Vector,
::Type{Val{:grad}})
dbasis = basis.dbasisdxi(xi)
J = sum([dbasis[:,i]*geometry[i]' for i=1:length(geometry)])
grad = inv(J)*dbasis
return grad
end
### INTERPOLATION IN SPATIAL DOMAIN ###
""" Interpolate increment in spatial domain using Basis. """
function Base.call(basis::Basis, increment::Increment, xi::Vector)
basis = basis.basis(xi)
sum([basis[i]*increment[i] for i=1:length(increment)])
end
""" Return gradient of increment in spatial domain using Basis.. """
function Base.call(basis::Basis, geometry::Increment, field::Increment,
xi::Vector, ::Type{Val{:grad}})
grad = basis(geometry, xi, Val{:grad})
gradf = sum([grad[:,i]*field[i]' for i=1:length(field)])'
return gradf
end
### INTERPOLATION IN TIME DOMAIN ###
""" Interpolate discrete field in time domain. Return Increment. """
function Base.call(field::DiscreteField, time::Number,
time_extrapolation::Symbol=:linear,
time_interpolation::Symbol=:linear)
# special cases, only 1 timestep defined or time = -Inf -> return first ts
if (length(field) == 1) || (time == -Inf)
#return field[1][end]
return first(field)
end
# special case, time = +Inf -> return last ts
if time == +Inf
#return field[end][end]
return last(field)
end
# very likely we are always near some defined timestep, usually field
# defined only on t = 0.0, test neighbourhood for timesteps
for i=1:length(field)
if isapprox(field[i].time, time)
return field[i][end]
end
end
# special case: out of time domain in positive direction, very likely
# to happen in incremental constitutive models
if time > field[end].time
if time_extrapolation == :constant
# constant time extrapolation, return last field
return field[end][end]
else
# multiple fields, pick last and second last and do linear interpolation
f1 = field[end-1]
f2 = field[end]
dt = abs(f2.time - f1.time)
i1 = f1[end]
i2 = f2[end]
di = i2 - i1
increment = Increment(i2 + di./dt * (time-f2.time))
return increment
end
end
# special case: out of time domain in negative direction
if time < field[1].time
if time_extrapolation == :constant
# constant time extrapolation, return first field
return field[1][end]
else
# multiple fields, pick first and second and do linear interpolation
f1 = field[1]
f2 = field[2]
dt = abs(f2.time - f1.time)
i1 = f1[end]
i2 = f2[end]
di = i2 - i1
increment = Increment(i1 - di./dt * (f1.time - time))
return increment
end
end
# find correct bin and perform interpolation
i = length(field)
while field[i].time >= time
i -= 1
end
if time_interpolation == :linear
t1 = field[i].time
t2 = field[i+1].time
inc1 = field[i][end]
inc2 = field[i+1][end]
dt = abs(t2 - t1)
t = (time-t1)/dt
increment = Increment((1-t)*inc1 + t*inc2)
return increment
end
if time_interpolation == :constant
# nearest neightbour interpolation, i.e. pick nearest defined field
t1 = field[i].time
t2 = field[i+1].time
dt1 = abs(t1-time)
dt2 = abs(t2-time)
if dt1 < dt2
return field[i][end]
else
return field[i+1][end]
end
end
end
""" Interpolate time derivative of field in some time t. This assumes linear
interpolation in time which is then differentiated.
Parameters
----------
field
Discrete field to interpolate. Must have timesteps and increments defined
time
Time to interpolate.
derivative
set Val{:diff} to activate this function
"""
function Base.call(field::DiscreteField, time::Number, ::Type{Val{:diff}})
# FieldSet -> Field -> TimeStep -> Increment -> data
if length(field) == 1
# just one timestep, time derivative cannot be evaluated.
error("Field length = $(length(field)), cannot evaluate time derivative")
end
function eval_field(i, j)
t1 = field[i]
t2 = field[j]
J = abs(t2.time - t1.time)
t = (time-t1.time)/J
result = 1/J*(-1*t1[end] + 1*t2[end])
return Increment(result)
end
# special cases, +Inf, -Inf, ~0.0
if (time > field[end].time) || isapprox(time, field[end].time)
return eval_field(endof(field)-1, endof(field))
end
if (time < field[1].time) || isapprox(time, field[1].time)
return eval_field(1, 2)
end
# search for a correct "bin" between time steps
i = length(field)
while (field[i].time > time) && !isapprox(field[i].time, time)
i -= 1
end
if isapprox(field[i].time, time)
# This is the hard case, maybe discontinuous time
# derivative if linear approximation.
# we are on the "mid node" in time axis
field1 = eval_field(i-1,i)
field2 = eval_field(i,i+1)
return 1/2*(field1 + field2)
end
return eval_field(i, i+1)
end
### ELEMENT FIELD BASIS = ELEMENT BASIS + FIELD
#=
""" Here we add field we are wanting to interpolate with ElementBasis. """
type ElementFieldBasis <: Basis
element_basis :: ElementBasis
field :: DiscreteField
time_extrapolation :: Symbol
time_interpolation :: Symbol
end
function Basis(basis::Function, dbasisdxi::Function, field::DiscreteField,
time_extrapolation=:linear, time_interpolation=:linear)
element_basis = ElementBasis(basis, dbasisdxi)
return ElementFieldBasis(element_basis, field, time_extrapolation,
time_interpolation)
end
function Base.call(basis::ElementFieldBasis, xi::Vector, time::Number)
increment = basis.field(time, basis.time_extrapolation, basis.time_interpolation)
return basis.element_basis(increment, xi)
end
=#
### ELEMENT GRADIENT BASIS = ELEMENT BASIS + GEOMETRY
#=
""" Gradient of ElementBasis, needs geometry information. """
type ElementGradientBasis <: Basis
element_basis :: ElementBasis
geometry :: DiscreteField
time_extrapolation :: Symbol
time_interpolation :: Symbol
end
function grad(N::ElementBasis, f::ElementFieldBasis, X::ElementFieldBasis)
f.time_extrapolation == X.time_extrapolation || error("interpolation mismatch")
f.time_interpolation == X.time_interpolation || error("interpolation mismatch")
dN = ElementGradientBasis(N, X.field, f.time_extrapolation, f.time_interpolation)
dfdX = ElementFieldGradientBasis(dN, f.field, f.time_extrapolation, f.time_interpolation)
return dfdX
end
function grad(N::ElementBasis, f::DiscreteField, X::DiscreteField)
dN = ElementGradientBasis(N, X)
dfdX = ElementFieldGradientBasis(dN, f)
end
function ElementGradientBasis(element_basis::ElementBasis, geometry::DiscreteField)
return ElementGradientBasis(element_basis, geometry, :linear, :linear)
end
=#
### ELEMENT FIELD GRADIENT BASIS = ELEMENT GRADIENT BASIS + FIELD
#=
""" Gradient of ElementFieldBasis, needs field to interpolate. """
type ElementFieldGradientBasis <: Basis
element_gradient_basis :: ElementGradientBasis
field :: DiscreteField
time_extrapolation :: Symbol
time_interpolation :: Symbol
end
function ElementFieldGradientBasis(element_gradient_basis::ElementGradientBasis,
field::DiscreteField)
return ElementFieldGradientBasis(element_gradient_basis, field, :linear, :linear)
end
=#
### INTERPOLATION IN TIME DOMAIN ###
+30 -29
View File
@@ -4,7 +4,7 @@
## Direct solver
type DirectSolver <: Solver
field_problems :: Vector{FieldProblem}
field_problems :: Vector{Problem}
boundary_problems :: Vector{BoundaryProblem}
parallel :: Bool
nonlinear_problem :: Bool
@@ -12,7 +12,7 @@ type DirectSolver <: Solver
tol :: Float64
end
function push!(solver::DirectSolver, problem::FieldProblem)
function push!(solver::DirectSolver, problem::Problem)
push!(solver.field_problems, problem)
end
@@ -45,24 +45,30 @@ function call(solver::DirectSolver, time::Number=0.0)
# for this increment
for field_problem in solver.field_problems
for equation in get_equations(field_problem)
element = get_element(equation)
gdofs = get_gdofs(field_problem, equation)
if !isapprox(last(element[field_name]).time, time)
last_data = copy(last(element[field_name]).data)
push!(element[field_name], time => last_data)
for element in get_elements(field_problem)
gdofs = get_gdofs(element, field_dim)
if haskey(element, field_name)
if !isapprox(last(element[field_name]).time, time)
last_data = copy(last(element[field_name]).data)
push!(element[field_name], time => last_data)
end
else
data = Vector{Float64}[zeros(field_dim) for i in 1:length(element)]
element[field_name] = (time => data)
end
end
end
for boundary_problem in solver.boundary_problems
for equation in get_equations(boundary_problem)
element = get_element(equation)
gdofs = get_gdofs(boundary_problem, equation)
eqdim = size(equation)[2]
data = Vector{Float64}[zeros(field_dim) for i in 1:eqdim]
if !isapprox(last(element["reaction force"]).time, time)
push!(element["reaction force"], time => data)
for element in get_elements(boundary_problem)
gdofs = get_gdofs(element, field_dim)
data = Vector{Float64}[zeros(field_dim) for i in 1:length(element)]
if haskey(element, "reaction force")
if !isapprox(last(element["reaction force"]).time, time)
push!(element["reaction force"], time => data)
end
else
element["reaction force"] = (time => data)
end
end
end
@@ -105,32 +111,27 @@ function call(solver::DirectSolver, time::Number=0.0)
# update elements in field problems
for field_problem in solver.field_problems
for equation in get_equations(field_problem)
element = get_element(equation)
gdofs = get_gdofs(field_problem, equation)
eqsize = size(equation)
for element in get_elements(field_problem)
gdofs = get_gdofs(element, field_dim)
local_sol = vec(full(sol[gdofs])) # incremental data for element
local_sol = reshape(local_sol, eqsize)
local_sol = Vector{Float64}[local_sol[:,i] for i=1:size(local_sol,2)]
local_sol = reshape(local_sol, field_dim, length(element))
local_sol = Vector{Float64}[local_sol[:,i] for i=1:length(element)]
last(element[field_name]).data += local_sol # <-- added
end
end
# update elements in boundary problems
for boundary_problem in solver.boundary_problems
for equation in get_equations(boundary_problem)
element = get_element(equation)
gdofs = get_gdofs(boundary_problem, equation) + dim
eqsize = size(equation)
for element in get_elements(boundary_problem)
gdofs = get_gdofs(element, field_dim) + dim
local_sol = vec(full(sol[gdofs]))
#info("local sol = $local_sol")
local_sol = reshape(local_sol, field_dim, eqsize[2])
local_sol = Vector{Float64}[local_sol[:,i] for i=1:size(local_sol,2)]
local_sol = reshape(local_sol, field_dim, length(element))
local_sol = Vector{Float64}[local_sol[:,i] for i=1:length(element)]
last(element["reaction force"]).data = local_sol # <-- replaced
end
end
info("Iteration took $(toq()) seconds")
info("Non-linear iteration took $(toq()) seconds")
if norm(sol[1:dim]) < solver.tol
return (iter, true)
+12 -53
View File
@@ -1,67 +1,26 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# Dirichlet boundary conditions in weak form
abstract DirichletProblem <: AbstractProblem
abstract DirichletEquation <: BoundaryEquation
### Dirichlet problem + equations
type DirichletProblem <: BoundaryProblem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Vector{DirichletEquation}
function DirichletProblem(parent_field_name, parent_field_dim, dim=1, elements=[])
return BoundaryProblem{DirichletProblem}(parent_field_name, parent_field_dim, dim, elements)
end
""" Initialize new Dirichlet boundary condition.
function assemble!{E}(assembly::Assembly, problem::BoundaryProblem{DirichletProblem}, element::Element{E}, time::Number)
Parameters
----------
dimension
dimension of unknown field (scalar, vector, ...)
# get dimension and name of PARENT field
field_dim = problem.parent_field_dim
field_name = problem.parent_field_name
Examples
--------
"""
function DirichletProblem(unknown_field_name::ASCIIString, dimension::Int=1)
DirichletProblem(unknown_field_name, dimension, [])
end
""" Dirichlet boundary condition element for 2 node line segment """
type DBC2D2 <: DirichletEquation
element :: Seg2
integration_points :: Vector{IntegrationPoint}
end
function Base.size(equation::DBC2D2)
return (1, 2)
end
function Base.convert(::Type{DirichletEquation}, element::Seg2)
integration_points = get_integration_points(element, Val{3})
if !haskey(element, "reaction force")
element["reaction force"] = (0.0 => Vector{Float64}[])
end
DBC2D2(element, integration_points)
end
function assemble!(assembly::Assembly, equation::DirichletEquation, time::Number=0.0, problem=nothing)
isa(problem, Void) && error("Dicihlet boundary condition needs problem defined")
field_dim = problem.unknown_field_dimension
field_name = problem.unknown_field_name
element = get_element(equation)
gdofs = get_gdofs(element, field_dim)
basis = get_basis(element)
detJ = det(basis)
for ip in get_integration_points(equation)
w = ip.weight * detJ(ip)
N = basis(ip, time)
for ip in get_integration_points(element)
w = ip.weight * det(element, ip, time)
N = element(ip, time)
A = w*N'*N
if haskey(element, field_name)
# add all dimensions at once
# add all dimensions at once if defined element["blaa"] = 0.0
for i=1:field_dim
g = element(field_name, ip, time)
ldofs = gdofs[i:field_dim:end]
@@ -71,8 +30,8 @@ function assemble!(assembly::Assembly, equation::DirichletEquation, time::Number
end
for i=1:field_dim
# add per dof if defined element["blaa 1"] = 1.0, element["blaa 2"] = 0.0 etc.
if haskey(element, field_name*" $i")
# add single component
g = element(field_name*" $i", ip, time)
ldofs = gdofs[i:field_dim:end]
add!(assembly.stiffness_matrix, ldofs, ldofs, A)
+36 -109
View File
@@ -3,42 +3,20 @@
# Elasticity problems
abstract ElasticityProblem <: FieldProblem
abstract ElasticityEquation <: Equation
abstract ElasticityProblem <: AbstractProblem
function get_unknown_field_name(equation::ElasticityEquation)
abstract PlaneStressElasticityProblem <: ElasticityProblem
function PlaneStressElasticityProblem(dim::Int=2, elements=[])
return Problem{PlaneStressElasticityProblem}(dim, elements)
end
function get_unknown_field_name{P<:ElasticityProblem}(::Type{P})
return "displacement"
end
### Formulation ###
""" Calculate internal energy for elasticity equation.
Override this to define your own material model. By default we use
Saint Venant-Kirchhoff material model, which is simply
S(E) = λtr(E) + 2μE
"""
function get_internal_energy(equation::ElasticityEquation, ip::IntegrationPoint, time::Number, F::Matrix)
element = get_element(equation)
basis = get_basis(element)
dbasis = grad(basis)
# material parameters
young = basis("youngs modulus", ip, time)
poisson = basis("poissons ratio", ip, time)
mu = young/(2*(1+poisson))
lambda = young*poisson/((1+poisson)*(1-2*poisson))
if isa(equation, PlaneStressElasticityEquation)
lambda = 2*lambda*mu/(lambda + 2*mu) # <- correction for 2d
end
# material model
E = 1/2*(F'*F - I) # strain
S = lambda*trace(E)*I + 2*mu*E
P = F*S
return P*dbasis(ip, time)
function get_unknown_field_type{P<:ElasticityProblem}(::Type{P})
return Vector{Float64}
end
""" Elasticity equations.
@@ -71,97 +49,44 @@ https://en.wikipedia.org/wiki/Plane_stress
https://en.wikipedia.org/wiki/Hooke's_law
"""
function get_residual_vector(equation::ElasticityEquation, ip::IntegrationPoint, time::Number; variation=nothing)
function get_residual_vector{EL<:CG}(problem::Problem{PlaneStressElasticityProblem}, element::Element{EL}, ip::IntegrationPoint, time::Number; variation=nothing)
element = get_element(equation)
basis = get_basis(element)
dbasis = grad(basis)
basis = element(ip, time)
dbasis = element(ip, time, Val{:grad})
u = basis("displacement", ip, time, variation)
gradu = dbasis("displacement", ip, time, variation)
u = element("displacement", ip, time, variation)
gradu = element("displacement", ip, time, Val{:grad}, variation)
F = I + gradu # deformation gradient
#info("Deformation gradient: $F")
# residual vector - internal energy
r = get_internal_energy(equation, ip, time, F)
#info("boundary element")
# internal forces
young = element("youngs modulus", ip, time)
poisson = element("poissons ratio", ip, time)
mu = young/(2*(1+poisson))
lambda = young*poisson/((1+poisson)*(1-2*poisson))
lambda = 2*lambda*mu/(lambda + 2*mu) # <- correction for 2d
E = 1/2*(F'*F - I) # strain
S = lambda*trace(E)*I + 2*mu*E
r = F*S*dbasis
# external forces - volume load
if haskey(element, "displacement load")
b = basis("displacement load", ip, time)
r -= b*basis(ip, time)
b = element("displacement load", ip, time)
r -= b*basis
end
return vec(r)
end
""" Surface load for plane stress model. """
function get_residual_vector(problem::Problem{PlaneStressElasticityProblem}, element::Element{Seg2}, ip::IntegrationPoint, time::Number; variation=nothing)
### Plane stress elasticity ###
abstract PlaneElasticityProblem <: ElasticityProblem
abstract PlaneStressElasticityEquation <: ElasticityEquation
type PlaneStressElasticityProblem <: PlaneElasticityProblem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Vector{PlaneStressElasticityEquation}
end
function PlaneStressElasticityProblem(equations=[])
return PlaneStressElasticityProblem("displacement", 2, equations)
end
### Equations ###
""" 4-node plane stress element. """
type CPS4 <: PlaneStressElasticityEquation
element :: Quad4
integration_points :: Vector{IntegrationPoint}
end
function Base.size(equation::CPS4)
return (2, 4)
end
function Base.convert(::Type{PlaneStressElasticityEquation}, element::Quad4)
integration_points = get_integration_points(element)
if !haskey(element, "displacement")
element["displacement"] = 0.0 => [zeros(2) for i=1:4]
end
CPS4(element, integration_points)
end
""" Boundary element for plane stress problem for surface loads. """
type CPS2 <: PlaneStressElasticityEquation
element :: Seg2
integration_points :: Vector{IntegrationPoint}
end
function Base.size(equation::CPS2)
return (2, 2)
end
function Base.convert(::Type{PlaneStressElasticityEquation}, element::Seg2)
integration_points = get_integration_points(element)
if !haskey(element, "displacement")
element["displacement"] = 0.0 => [zeros(2) for i=1:2]
end
CPS2(element, integration_points)
end
function get_residual_vector(equation::CPS2, ip::IntegrationPoint, time::Number; variation=nothing)
element = get_element(equation)
basis = get_basis(element)
u = basis("displacement", ip, time, variation)
r = zeros(size(equation))
u = element("displacement", ip, time, variation)
r = zeros(problem.dim, length(element))
if haskey(element, "displacement traction force")
T = basis("displacement traction force", ip, time)
r -= T*basis(ip, time)
T = element("displacement traction force", ip, time)
r -= T*element(ip, time)
end
return vec(r)
@@ -237,3 +162,5 @@ function get_residual_vector(equation::CPS2, ip::IntegrationPoint, time::Number;
end
=#
+51 -120
View File
@@ -1,10 +1,23 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using FactCheck
using ForwardDiff
abstract AbstractElement
abstract Element
type Element{E<:AbstractElement}
connectivity :: Vector{Int}
# integration_points :: Vector{IntegrationPoint}
fields :: Dict{ASCIIString, Field}
end
function convert{E}(::Type{Element{E}}, connectivity::Vector{Int})
# return Element{E}(connectivity, get_integration_points(E), Dict())
return Element{E}(connectivity, Dict())
end
function get_integration_points{E}(element::Element{E})
# return element.integration_points
return get_integration_points(E)
end
"""
Test routine for element. If this passes, element interface is properly
@@ -67,9 +80,9 @@ function Base.getindex(element::Element, field_name)
return element.fields[field_name]
end
#function get_integration_points(element)
# return get_default_integration_points(element)
#end
function Base.length{E}(element::Element{E})
size(E)[2]
end
"""Add new Field to element.
@@ -89,151 +102,69 @@ function Base.setindex!(element::Element, data::Tuple, name::ASCIIString)
element.fields[name] = Field(data...)
end
#function Base.setindex!(element::Element, field_data::Tuple, field_name)
# field = Field()
# for (time, data) in field_data
# ts = TimeStep(time, Increment[Increment(data)])
# push!(field, ts)
# end
# element[field_name] = field
#end
function get_connectivity(el::Element)
return el.connectivity
end
abstract AbstractFunctionSpace
type FunctionSpace <: AbstractFunctionSpace
basis :: CVTI
fields :: FieldSet
end
type GradientFunctionSpace <: AbstractFunctionSpace
basis :: CVTI
fields :: FieldSet
end
function get_basis(element::Element)
return FunctionSpace(element.basis, element.fields)
end
function get_dbasis(element::Element)
return GradientFunctionSpace(element.basis, element.fields)
end
function grad(u::FunctionSpace)
return GradientFunctionSpace(u.basis, u.fields)
end
""" If basis is called without a field, return basis functions evaluated at that point. """
function call(u::FunctionSpace, xi::Union{Vector, IntegrationPoint}, t::Number=0.0)
return u.basis(xi)
end
""" If gradient of basis is called without a field, return "empty" gradient evaluated at that point. """
function call(gradu::GradientFunctionSpace, xi::Union{Vector, IntegrationPoint}, t::Number=0.0)
geometry = gradu.fields["geometry"](t)
gradu.basis(geometry, xi, Val{:grad})
end
""" Evaluate field on element function space. """
function call(u::FunctionSpace, field_name, xi::Union{Vector, IntegrationPoint}, t::Number=0.0, variation=nothing)
field = !isa(variation, Void) ? variation : u.fields[field_name](t)
u.basis(field, xi)
end
""" Evaluate gradient of field on element function space. """
function call(gradu::GradientFunctionSpace, field_name, xi::Union{Vector, IntegrationPoint}, t::Number=0.0, variation=nothing)
field = !isa(variation, Void) ? variation : gradu.fields[field_name](t)
geometry = gradu.fields["geometry"](t)
gradu.basis(geometry, field, xi, Val{:grad})
end
typealias VecOrIP Union{Vector, IntegrationPoint}
function Base.call(element::Element, field_name::ASCIIString, xi::VecOrIP, time::Number)
return element.basis(element[field_name](time), xi)
function call(element::Element, field_name::ASCIIString, xi::VecOrIP, time::Number, variation=nothing)
field = isa(variation, Void) ? element[field_name](time) : variation
basis = get_basis(element)
return basis(field, xi)
end
function Base.call(element::Element, field_name::ASCIIString, xi::VecOrIP, time::Number, ::Type{Val{:grad}})
return element.basis(element["geometry"](time), element[field_name](time), xi, Val{:grad})
function call(element::Element, field_name::ASCIIString, xi::VecOrIP, time::Number, ::Type{Val{:grad}}, variation=nothing)
field = isa(variation, Void) ? element[field_name](time) : variation
basis = get_basis(element)
geom = element["geometry"](time)
return basis(geom, field, xi, Val{:grad})
end
function Base.call(element::Element, field_name::ASCIIString, xi::VecOrIP)
function call(element::Element, field_name::ASCIIString, xi::VecOrIP)
return element.basis(element[field_name], xi)
end
function Base.call(element::Element, field_name::ASCIIString, xi::VecOrIP, ::Type{Val{:grad}})
function call(element::Element, field_name::ASCIIString, xi::VecOrIP, ::Type{Val{:grad}})
return element.basis(element["geometry"], element[field_name], xi, Val{:grad})
end
function Base.call(element::Element, field_name::ASCIIString, time::Number)
function call(element::Element, field_name::ASCIIString, time::Number)
return element[field_name](time)
end
function Base.call(element::Element, xi::VecOrIP)
element.basis(xi)
function get_basis{E}(element::Element{E}, ip::IntegrationPoint)
return get_basis(E, ip.xi)
end
function Base.call(element::Element, xi::VecOrIP, ::Type{Val{:grad}})
element.basis(element["geometry"], xi, Val{:grad})
function call{E}(element::Element{E}, xi::VecOrIP, time::Float64=0)
return get_basis(element, xi)
end
function Base.call(element::Element, field_name::ASCIIString)
function get_basis{E}(element::Element{E})
basis = CVTI(
(xi::Vector) -> get_basis(E, xi),
(xi::Vector) -> get_dbasis(E, xi))
return basis
end
function call{E}(element::Element{E}, xi::VecOrIP, time::Float64, ::Type{Val{:grad}})
basis = get_basis(element)
return basis(element["geometry"], xi, Val{:grad})
end
function call(element::Element, field_name::ASCIIString)
return element[field_name]
end
# on-line functions to get api more easy to use, ip -> xi.ip
#call(u::FunctionSpace, ip::IntegrationPoint, t::Number=Inf) = call(u, ip.xi, t)
#call(u::GradientFunctionSpace, ip::IntegrationPoint, t::Number=Inf) = call(u, ip.xi, t)
# i think these will be the most called functions.
#call(u::FunctionSpace, field_name, ip::IntegrationPoint, t::Number=0.0, variation=nothing) = call(u, field_name, ip.xi, t, variation)
#call(u::GradientFunctionSpace, field_name, ip::IntegrationPoint, t::Number=0.0, variation=nothing) = call(u, field_name, ip.xi, t, variation)
#call(u::FunctionSpace, field_name) = (args...) -> call(u, field_name, args...)
#call(u::GradientFunctionSpace, field_name) = (args...) -> call(u, field_name, args...)
""" Return a field from function space. """
function get_field(u::FunctionSpace, field_name, time::Number=0.0)
return u.fields[field_name](time)
end
""" Return a field from function space. """
function get_field(u::FunctionSpace, field_name, time::Number=0.0, variation=nothing)
return !isa(variation, Void) ? variation : u.fields[field_name](time)
end
""" Return a field from function space. """
function get_fieldset(u::FunctionSpace, field_name)
return u.fields[field_name]
end
""" Get a determinant of element in point ξ. """
function LinAlg.det(u::FunctionSpace, xi::Vector, time::Number=0.0)
X = u.fields["geometry"](time)
dN = u.basis(xi, Val{:grad})
function LinAlg.det{E<:AbstractElement}(element::Element{E}, ip::IntegrationPoint, time::Number=0.0)
X = element("geometry", time)
dN = get_dbasis(E, ip.xi)
J = sum([dN[:,i]*X[i]' for i=1:length(X)])
m, n = size(J)
return m == n ? det(J) : norm(J)
end
function LinAlg.det(u::FunctionSpace, ip::IntegrationPoint, time::Number=0.0)
LinAlg.det(u, ip.xi, time)
end
function LinAlg.det(u::FunctionSpace)
return (args...) -> det(u, args...)
end
function LinAlg.det(element::Element)
return det(get_basis(element))
end
#Base.(:+)(u::FunctionSpace, v::FunctionSpace) = (args...) -> u(args...) + v(args...)
#Base.(:-)(u::FunctionSpace, v::FunctionSpace) = (args...) -> u(args...) - v(args...)
#Base.(:+)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(args...) + v(args...)
#Base.(:-)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(args...) - v(args...)
""" Check does field exist. """
function Base.haskey(element::Element, what)
haskey(element.fields, what)
+31 -64
View File
@@ -3,10 +3,6 @@
# Functions to handle element level things -- integration, assembly, ...
abstract Equation
abstract FieldEquation <: Equation
abstract BoundaryEquation <: Equation
type Assembly
mass_matrix :: SparseMatrixIJV
stiffness_matrix :: SparseMatrixIJV
@@ -41,58 +37,31 @@ end
function get_residual_vector
end
function has_mass_matrix(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
function has_mass_matrix(problem::Problem, element::Element)
default_args = Tuple{typeof(problem), typeof(element), IntegrationPoint, Float64}
return method_exists(get_mass_matrix, default_args)
end
function has_stiffness_matrix(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
function has_stiffness_matrix(problem::Problem, element::Element)
default_args = Tuple{typeof(problem), typeof(element), IntegrationPoint, Float64}
return method_exists(get_stiffness_matrix, default_args)
end
function has_force_vector(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
function has_force_vector(problem::Problem, element::Element)
default_args = Tuple{typeof(problem), typeof(element), IntegrationPoint, Float64}
return method_exists(get_force_vector, default_args)
end
function has_potential_energy(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
function has_potential_energy(problem::Problem, element::Element)
default_args = Tuple{typeof(problem), typeof(element), IntegrationPoint, Float64}
return method_exists(get_potential_energy, default_args)
end
function has_residual_vector(equation::Equation)
default_args = Tuple{typeof(equation), IntegrationPoint, Float64}
function has_residual_vector(problem::Problem, element::Element)
default_args = Tuple{typeof(problem), typeof(element), IntegrationPoint, Float64}
return method_exists(get_residual_vector, default_args)
end
function get_element(equation::Equation)
return equation.element
end
function get_integration_points(equation::Equation)
return equation.integration_points
end
function Base.size(equation::Equation, i::Int)
return size(equation)[i]
end
""" Return global degrees of freedom of element in matrix level.
Notes
-----
This is calculated from connectivity and equation dimension.
"""
function get_gdofs(equation::Equation)
element = get_element(equation)
conn = get_connectivity(element)
dim = size(equation, 1)
gdofs = vec(vcat([dim*conn'-i for i=dim-1:-1:0]...))
return gdofs
end
function get_gdofs(element::Element, dim::Int)
conn = get_connectivity(element)
gdofs = vec(vcat([dim*conn'-i for i=dim-1:-1:0]...))
@@ -100,26 +69,23 @@ function get_gdofs(element::Element, dim::Int)
end
""" Assemble element. """
function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, problem=nothing)
function assemble!(assembly::Assembly, problem::Problem, element::Element, time::Number)
element = get_element(equation)
gdofs = get_gdofs(equation)
basis = get_basis(element)
detJ = det(basis)
unknown_field_name = get_unknown_field_name(equation)
gdofs = get_gdofs(element, problem.dim)
unknown_field_name = get_unknown_field_name(problem)
# 1. if equations are defined we just integrate them, without caring how they are done
if has_mass_matrix(equation) || has_stiffness_matrix(equation) || has_force_vector(equation)
for ip in get_integration_points(equation)
s = ip.weight*detJ(ip)
if has_mass_matrix(equation)
add!(assembly.mass_matrix, gdofs, gdofs, s*get_mass_matrix(equation, ip, time))
if has_mass_matrix(problem, element) || has_stiffness_matrix(problem, element) || has_force_vector(problem, element)
for ip in get_integration_points(element)
s = ip.weight*det(element, ip, time)
if has_mass_matrix(element)
add!(assembly.mass_matrix, gdofs, gdofs, s*get_mass_matrix(problem, element, ip, time))
end
if has_stiffness_matrix(equation)
add!(assembly.stiffness_matrix, gdofs, gdofs, s*get_stiffness_matrix(equation, ip, time))
if has_stiffness_matrix(element)
add!(assembly.stiffness_matrix, gdofs, gdofs, s*get_stiffness_matrix(problem, element, ip, time))
end
if has_force_vector(equation)
add!(assembly.force_vector, gdofs, s*get_force_vector(equation, ip, time))
if has_force_vector(element)
add!(assembly.force_vector, gdofs, s*get_force_vector(problem, element, ip, time))
end
end
# external loads -- if any nodal loads is defined add to force vector
@@ -129,7 +95,7 @@ function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, pro
end
# 2. energy form -- user has defined potential energy W -> min!
if has_potential_energy(equation)
if has_potential_energy(problem, element)
field = element[unknown_field_name](time)
""" Wrapper for potential energy for ForwardDiff. """
@@ -137,9 +103,9 @@ function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, pro
W = 0.0
df = similar(field, data)
# integrate potential energy
for ip in get_integration_points(equation)
s = ip.weight*detJ(ip)
dw = get_potential_energy(equation, ip, time; variation=df)
for ip in get_integration_points(element)
s = ip.weight*det(element, ip, time)
dw = get_potential_energy(problem, element, ip, time; variation=df)
W += s*dw
end
# external energy -- if any nodal loads is defined, decrease from potential energy
@@ -156,7 +122,7 @@ function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, pro
end
# 3. virtual work -- user has defined some residual r = p - f = 0
if has_residual_vector(equation)
if has_residual_vector(problem, element)
field = DVTI(last(element[unknown_field_name]).data)
@@ -165,9 +131,9 @@ function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, pro
R = zeros(length(data))
df = similar(field, data)
# integrate residual vector
for ip in get_integration_points(equation)
s = ip.weight*detJ(ip)
dr = get_residual_vector(equation, ip, time; variation=df)
for ip in get_integration_points(element)
s = ip.weight*det(element, ip, time)
dr = get_residual_vector(problem, element, ip, time; variation=df)
R += s*dr
end
# external loads -- if any nodal loads is defined, decrease from residual
@@ -183,3 +149,4 @@ function assemble!(assembly::Assembly, equation::Equation, time::Number=0.0, pro
add!(assembly.force_vector, gdofs, -ForwardDiff.value(allresults))
end
end
+219 -210
View File
@@ -3,301 +3,310 @@
# https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/notebooks/2015-06-14-data-structures.ipynb
abstract Field
abstract AbstractField
abstract DiscreteField <: Field
abstract ContinuousField <: Field
abstract Discrete <: AbstractField
abstract Continuous <: AbstractField
abstract Constant <: AbstractField
abstract Variable <: AbstractField
abstract TimeVariant <: AbstractField
abstract TimeInvariant <: AbstractField
### DEFAULT DISCRETE FIELD ###
# 1. Increment
type Increment{T} <: AbstractVector{T}
data :: Vector{T}
type Field{A<:Union{Discrete,Continuous}, B<:Union{Constant,Variable}, C<:Union{TimeVariant,TimeInvariant}}
data
end
function Base.size(increment::Increment)
return size(increment.data)
### Basic data structure for discrete field
type Increment{T}
time :: Float64
data :: T
end
function Base.linearindexing(::Type{Increment})
return LinearFast()
function Base.convert{T}(::Type{Increment{T}}, data::Pair{Float64,T})
return Increment{T}(data[1], data[2])
end
function Base.getindex(increment::Increment, i::Int)
function Base.convert{T}(::Type{Increment{Vector{Vector{T}}}}, data::Pair{Float64, Matrix{T}})
time = data[1]
content = data[2]
return Increment(time, Vector{T}[content[:,i] for i=1:size(content,2)])
end
function Base.getindex{T}(increment::Increment{Vector{T}}, i::Int64)
return increment.data[i]
end
function Base.setindex!(increment::Increment, v, i::Int)
increment.data[i] = v
function Base.(:*)(d, increment::Increment)
return d*increment.data
end
function Base.dot(k::Number, increment::Increment)
return k*increment
### Basic data structure for continuous field
type Basis
basis :: Function
dbasis :: Function
end
function Base.convert(::Type{Increment}, data::Number)
return Increment([data])
function Base.call(basis::Basis, xi::Vector)
basis.basis(xi)
end
function Base.convert{T}(::Type{Increment}, data::Array{T, 2})
return Increment([data[:,i] for i=1:size(data, 2)])
function Base.call(basis::Basis, xi::Vector, ::Type{Val{:grad}})
basis.dbasis(xi)
end
function Base.convert{T}(::Type{Increment}, data::Array{T, 3})
return Increment([data[:,:,i] for i=1:size(data, 3)])
end
### Different field combinations and other typealiases
function Base.convert{T}(::Type{Increment}, data::Array{T, 4})
return Increment([data[:,:,:,i] for i=1:size(data, 4)])
end
typealias DCTI Field{Discrete, Constant, TimeInvariant}
typealias DVTI Field{Discrete, Variable, TimeInvariant}
typealias DCTV Field{Discrete, Constant, TimeVariant}
typealias DVTV Field{Discrete, Variable, TimeVariant}
typealias CCTI Field{Continuous, Constant, TimeInvariant}
typealias CVTI Field{Continuous, Variable, TimeInvariant} # can be used to interpolate in spatial dimension
typealias CCTV Field{Continuous, Constant, TimeVariant} # can be used to interpolate in time
typealias CVTV Field{Continuous, Variable, TimeVariant}
function Base.convert{T}(::Type{Increment}, data::Array{T, 5})
return Increment([data[:,:,:,:,i] for i=1:size(data, 5)])
end
typealias ScalarIncrement{T} Increment{T}
typealias VectorIncrement{T} Increment{Vector{T}}
typealias TensorIncrement{T} Increment{Matrix{T}}
function Base.zeros(::Type{Increment}, T, dims...)
return Increment(zeros(T, dims...))
end
typealias DiscreteField Union{DCTI, DVTI, DCTV, DVTV}
typealias ContinuousField Union{CCTI, CVTI, CCTV, CVTV}
typealias ConstantField Union{DCTI, DCTV, CCTI, CCTV}
typealias VariableField Union{DVTI, DVTV, CVTI, CVTV}
typealias TimeInvariantField Union{DCTI, DVTI, CCTI, CVTI}
typealias TimeVariantField Union{DCTV, DVTV, CCTV, CVTV}
""" Flatten increment to Vector.
Examples
--------
### Convenient functions to create fields
>>> inc = ones(Increment, 2, 4)
>>> vec(inc)
[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
"""
function Base.vec(increment::Increment)
return [increment...;]
end
function Base.similar{T}(increment::Increment, data::Vector{T})
return Increment(reshape(data, round(Int, length(data)/length(increment)), length(increment)))
end
function Base.convert{T}(::Type{Vector{T}}, increment::Increment)
return Increment[increment]
end
# 2. TimeStep
type TimeStep
time :: Float64
increments :: Vector{Increment}
end
function Base.size(timestep::TimeStep)
return size(timestep.increments)
end
function Base.endof(timestep::TimeStep)
return endof(timestep.increments)
end
function Base.length(timestep::TimeStep)
return length(timestep.increments)
end
function Base.linearindexing(::Type{TimeStep})
return Base.LinearFast()
end
function Base.getindex(timestep::TimeStep, i::Int)
return timestep.increments[i]
end
#function TimeStep(data::Union{Number, Array}...)
# return TimeStep(0.0, Increment[Increment(d) for d in data])
#function Base.convert(::Type{Field}, data)
# return Field(data)
#end
function TimeStep()
return TimeStep(0.0, [])
function Field(data)
return DCTI(data)
end
function TimeStep{T}(data::T...)
return TimeStep(0.0, Increment[Increment(d) for d in data])
function Field(data::Vector)
return DVTI(data)
end
function Base.convert(::Type{TimeStep}, value::Number)
return TimeStep(0.0, Increment[Increment(value)])
function Field{T}(data::Pair{Float64, T}...)
return DCTV([Increment{T}(d[1], d[2]) for d in data])
end
function Base.push!(timestep::TimeStep, increment::Increment)
push!(timestep.increments, increment)
function Field{T}(data::Pair{Float64, Vector{T}}...)
return DVTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
end
# FIXME: having some serious problems here to get tuple form working.
# 3. DefaultDiscreteField
immutable DefaultDiscreteField <: DiscreteField
timesteps :: Vector{TimeStep}
#=
function DefaultDiscreteField(data::Array)
if (typeof(data) == Vector{Int64}) || (typeof(data) == Vector{Float64})
new(TimeStep[TimeStep(data)])
else
new(data)
end
end
=#
function Base.convert{T}(::Type{DCTV}, data::Pair{Float64, Vector{T}}...)
return DCTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
end
#=
type DefaultDiscreteField <: DiscreteField
timesteps :: Vector{TimeStep}
function DefaultDiscreteField(data...)
timesteps = TimeStep[]
for (i, d) in enumerate(data)
@debug("i = $i, d = $d")
if isa(d, Tuple)
# contains time vector
increments = Increment[Increment(d[2])]
push!(timesteps, TimeStep(d[1], increments))
else
increments = Increment[Increment(d)]
push!(timesteps, TimeStep(i-1.0, increments))
end
end
new(timesteps)
function Field(func::Function)
if method_exists(func, Tuple{})
return CCTI(func)
elseif method_exists(func, Tuple{Float64})
return CCTV(func)
elseif method_exists(func, Tuple{Vector})
return CVTI(func)
elseif method_exists(func, Tuple{Vector, Number})
return CVTV(func)
else
error("no proper definition found for function: check methods.")
end
end
=#
function Base.size(field::DefaultDiscreteField)
return size(field.timesteps)
function CVTI(basis::Function, dbasis::Function)
return CVTI(Basis(basis, dbasis))
end
function Base.length(field::DefaultDiscreteField)
return length(field.timesteps)
function Field(basis::Function, dbasis::Function)
return CVTI(basis, dbasis)
end
function Base.start(::DefaultDiscreteField)
### Accessing and manipulating discrete fields
function Base.getindex(field::DVTV, i::Int64)
return field.data[i]
end
function Base.push!(field::DCTV, data::Pair)
push!(field.data, data)
end
function Base.push!(field::DVTV, data::Pair)
# info("field.data = \n$(field.data)")
# info("data = \n$data")
push!(field.data, data)
end
function Base.getindex(field::DVTV, i::Int64)
return field.data[i]
end
function Base.getindex(field::DVTI, i::Int64)
return field.data[i]
end
function Base.getindex(field::DCTV, i::Int64)
return field.data[i]
end
function Base.getindex(field::Field, i::Int64)
return field.data[i]
end
function Base.length(field::DVTI)
return length(field.data)
end
function Base.length(field::DCTI)
return 1
end
function Base.next(field::DefaultDiscreteField, state)
return (field[state+1], state+1)
function Base.length(field::DVTV)
return length(field.data)
end
function Base.done(field::DefaultDiscreteField, state)
return state > length(field)
function Base.length(field::DCTV)
return length(field.data)
end
function eltype(::Type{DefaultDiscreteField})
return TimeStep
for op = (:+, :*, :/, :-)
@eval ($op)(increment::Increment, field::DCTI) = ($op)(increment.data, field.data)
@eval ($op)(field::DCTI, increment::Increment) = ($op)(increment.data, field.data)
@eval ($op)(field1::DCTI, field2::DCTI) = ($op)(field1.data, field2.data)
@eval ($op)(field::DCTI, k) = ($op)(field.data, k)
@eval ($op)(k, field::DCTI) = ($op)(field.data, k)
end
function Base.linearindexing(::Type{DefaultDiscreteField})
return LinearFast()
function Base.vec(field::DVTI)
return [field.data...;]
end
function Base.getindex(field::DefaultDiscreteField, i::Int)
return field.timesteps[i]
function Base.vec(field::DCTV)
info("trying to vectorize $field")
error("does not make sense")
end
function Base.endof(field::DefaultDiscreteField)
return endof(field.timesteps)
function Base.endof(field::Field)
return endof(field.data)
end
function Base.first(field::DefaultDiscreteField)
return field[1][end]
#function Base.similar{T}(field::DVTI, data::Vector{T})
# return Increment(reshape(data, round(Int, length(data)/length(increment)), length(increment)))
#end
function Base.similar{T}(field::DVTI, data::Vector{T})
n = length(field.data)
data = reshape(data, round(Int, length(data)/n), n)
newdata = Vector[data[:,i] for i=1:n]
return typeof(field)(newdata)
end
function Base.last(field::DefaultDiscreteField)
return field[end][end]
function Base.start(::DVTI)
return 1
end
function Base.push!(field::DefaultDiscreteField, timestep::TimeStep)
push!(field.timesteps, timestep)
function Base.next(f::DVTI, state)
return f.data[state], state+1
end
function Base.push!(field::DefaultDiscreteField, data::Union{Vector, Matrix})
push!(field[end], Increment(data))
function Base.done(f::DVTI, s)
return s > length(f.data)
end
function Base.push!(field::DefaultDiscreteField, data::Pair)
ts = TimeStep(data[1], Increment(data[2]))
push!(field, ts)
### Accessing continuous fields
function Base.call(field::CVTI, xi::Vector)
field.data(xi)
end
"""Quickly create fields.
function Base.call(field::CVTI, xi::Vector, ::Type{Val{:grad}})
field.data(xi, Val{:grad})
end
Examples
--------
>>> Field([1, 2]) # creates field with one timestep and vector value [1, 2]
>>> Field(1, 2) # creates field with two timesteps, each having scalar value
>>> Field([1, 2], [3, 4]) # creates field with two timesteps, each having vector value
>>> Field( (0.0, [1, 2]), (0.5, [3, 4]) ) # like above, but give time also
"""
function Base.convert(::Type{DefaultDiscreteField}, data...)
timesteps = TimeStep[]
for (i, d) in enumerate(data)
if isa(d, Tuple)
@debug("is tuple, has time, d = $d")
# contains time vector
increments = Increment[Increment(d[2])]
push!(timesteps, TimeStep(d[1], increments))
else
# @debug("array without time, d = $d")
# @debug(typeof(d))
increments = Increment[Increment(d)]
push!(timesteps, TimeStep(i-1.0, increments))
function Base.convert(::Type{Basis}, field::CVTI)
return field.data
end
function Base.call(field::CCTV, time::Number)
return field.data(time)
end
### Interpolation
""" Interpolate time-invariant field in time direction. """
function Base.call(field::DVTI, time::Float64)
return field
end
function Base.call(field::DCTI, time::Float64)
return field
end
function Base.call(field::CVTI, time::Float64)
return field.data()
end
function Base.call(field::CCTI, time::Float64)
return field.data()
end
""" Interpolate time-variant field in time direction. """
function Base.call(field::DCTV, time::Float64)
for i=reverse(1:length(field))
if isapprox(field[i].time, time)
return DCTI(field[i].data)
end
end
field = DefaultDiscreteField(timesteps)
return field
info(field.data)
info(time)
error("interpolate DCTV: not implemented yet")
end
function Base.convert(::Type{DefaultDiscreteField}, data::Vector{TimeStep})
field = DefaultDiscreteField(data)
# @debug(field)
return field
function Base.call(field::DVTV, time::Float64, time_extrapolation::Symbol=:linear)
for i=reverse(1:length(field))
if isapprox(field[i].time, time)
return DVTI(field[i].data)
end
end
info(field.data)
info(time)
error("interpolate DVTV: not implemented yet")
end
### CONTINUOUS FIELDS ###
type DefaultContinuousField <: ContinuousField
field :: Function
""" Interpolate constant field in spatial dimension. """
function Base.call(basis::CVTI, field::DCTI, xi::Vector)
return field.data
end
function Base.call(field::DefaultContinuousField, xi::Vector, time::Number)
return field.field(xi, time)
""" Interpolate variable field in spatial dimension. """
function Base.call(basis::CVTI, values::DVTI, xi::Vector)
N = basis(xi)
return sum([N[i]*values[i] for i=1:length(N)])
end
function Base.convert(::Type{DefaultContinuousField}, f::Function)
return DefaultContinuousField(f)
function Base.call(basis::CVTI, geometry::DVTI, xi::Vector, ::Type{Val{:grad}})
dbasis = basis(xi, Val{:grad})
J = sum([dbasis[:,i]*geometry[i]' for i=1:length(geometry)])
invJ = isa(J, Vector) ? inv(J[1]) : inv(J)
grad = invJ * dbasis
return grad
end
function Base.call(basis::CVTI, geometry::DVTI, values::DVTI, xi::Vector, ::Type{Val{:grad}})
grad = call(basis, geometry, xi, Val{:grad})
gradf = sum([grad[:,i]*values[i]' for i=1:length(geometry)])'
return length(gradf) == 1 ? gradf[1] : gradf
end
function Base.call(basis::CVTI, xi::Vector, time::Number)
call(basis, xi)
end
### FIELDSET ###
typealias FieldSet Dict{ASCIIString, Field}
# 1. given numbers, arrays or tuples -> discrete field
function Base.convert(::Type{Field}, data::Union{Number, Array, Tuple}...)
return DiscreteField(data...)
end
function Base.convert(::Type{DiscreteField}, data::Union{Number, Array, Tuple}...)
return convert(DefaultDiscreteField, data...)
end
# 2. given function -> continuous field
function Base.convert(::Type{Field}, data::Function)
return ContinuousField(data)
end
function Base.convert(::Type{ContinuousField}, data::Function)
return convert(DefaultContinuousField, data)
end
function Base.length(::Field)
return 1
end
-306
View File
@@ -3,310 +3,4 @@
# https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/notebooks/2015-06-14-data-structures.ipynb
abstract AbstractField
abstract Discrete <: AbstractField
abstract Continuous <: AbstractField
abstract Constant <: AbstractField
abstract Variable <: AbstractField
abstract TimeVariant <: AbstractField
abstract TimeInvariant <: AbstractField
type Field{A<:Union{Discrete,Continuous}, B<:Union{Constant,Variable}, C<:Union{TimeVariant,TimeInvariant}}
data
end
### Basic data structure for discrete field
type Increment{T}
time :: Float64
data :: T
end
function Base.convert{T}(::Type{Increment{T}}, data::Pair{Float64,T})
return Increment{T}(data[1], data[2])
end
function Base.convert{T}(::Type{Increment{Vector{Vector{T}}}}, data::Pair{Float64, Matrix{T}})
time = data[1]
content = data[2]
return Increment(time, Vector{T}[content[:,i] for i=1:size(content,2)])
end
function Base.getindex{T}(increment::Increment{Vector{T}}, i::Int64)
return increment.data[i]
end
function Base.(:*)(d, increment::Increment)
return d*increment.data
end
### Basic data structure for continuous field
type Basis
basis :: Function
dbasis :: Function
end
function Base.call(basis::Basis, xi::Vector)
basis.basis(xi)
end
function Base.call(basis::Basis, xi::Vector, ::Type{Val{:grad}})
basis.dbasis(xi)
end
### Different field combinations and other typealiases
typealias DCTI Field{Discrete, Constant, TimeInvariant}
typealias DVTI Field{Discrete, Variable, TimeInvariant}
typealias DCTV Field{Discrete, Constant, TimeVariant}
typealias DVTV Field{Discrete, Variable, TimeVariant}
typealias CCTI Field{Continuous, Constant, TimeInvariant}
typealias CVTI Field{Continuous, Variable, TimeInvariant} # can be used to interpolate in spatial dimension
typealias CCTV Field{Continuous, Constant, TimeVariant} # can be used to interpolate in time
typealias CVTV Field{Continuous, Variable, TimeVariant}
typealias ScalarIncrement{T} Increment{T}
typealias VectorIncrement{T} Increment{Vector{T}}
typealias TensorIncrement{T} Increment{Matrix{T}}
typealias DiscreteField Union{DCTI, DVTI, DCTV, DVTV}
typealias ContinuousField Union{CCTI, CVTI, CCTV, CVTV}
typealias ConstantField Union{DCTI, DCTV, CCTI, CCTV}
typealias VariableField Union{DVTI, DVTV, CVTI, CVTV}
typealias TimeInvariantField Union{DCTI, DVTI, CCTI, CVTI}
typealias TimeVariantField Union{DCTV, DVTV, CCTV, CVTV}
### Convenient functions to create fields
#function Base.convert(::Type{Field}, data)
# return Field(data)
#end
function Field(data)
return DCTI(data)
end
function Field(data::Vector)
return DVTI(data)
end
function Field{T}(data::Pair{Float64, T}...)
return DCTV([Increment{T}(d[1], d[2]) for d in data])
end
function Field{T}(data::Pair{Float64, Vector{T}}...)
return DVTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
end
function Base.convert{T}(::Type{DCTV}, data::Pair{Float64, Vector{T}}...)
return DCTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
end
function Field(func::Function)
if method_exists(func, Tuple{})
return CCTI(func)
elseif method_exists(func, Tuple{Float64})
return CCTV(func)
elseif method_exists(func, Tuple{Vector})
return CVTI(func)
elseif method_exists(func, Tuple{Vector, Number})
return CVTV(func)
else
error("no proper definition found for function: check methods.")
end
end
function CVTI(basis::Function, dbasis::Function)
return CVTI(Basis(basis, dbasis))
end
function Field(basis::Function, dbasis::Function)
return CVTI(basis, dbasis)
end
### Accessing and manipulating discrete fields
function Base.getindex(field::DVTV, i::Int64)
return field.data[i]
end
function Base.push!(field::DCTV, data::Pair)
push!(field.data, data)
end
function Base.push!(field::DVTV, data::Pair)
# info("field.data = \n$(field.data)")
# info("data = \n$data")
push!(field.data, data)
end
function Base.getindex(field::DVTV, i::Int64)
return field.data[i]
end
function Base.getindex(field::DVTI, i::Int64)
return field.data[i]
end
function Base.getindex(field::DCTV, i::Int64)
return field.data[i]
end
function Base.getindex(field::Field, i::Int64)
return field.data[i]
end
function Base.length(field::DVTI)
return length(field.data)
end
function Base.length(field::DCTI)
return 1
end
function Base.length(field::DVTV)
return length(field.data)
end
function Base.length(field::DCTV)
return length(field.data)
end
for op = (:+, :*, :/, :-)
@eval ($op)(increment::Increment, field::DCTI) = ($op)(increment.data, field.data)
@eval ($op)(field::DCTI, increment::Increment) = ($op)(increment.data, field.data)
@eval ($op)(field1::DCTI, field2::DCTI) = ($op)(field1.data, field2.data)
@eval ($op)(field::DCTI, k) = ($op)(field.data, k)
@eval ($op)(k, field::DCTI) = ($op)(field.data, k)
end
function Base.vec(field::DVTI)
return [field.data...;]
end
function Base.vec(field::DCTV)
info("trying to vectorize $field")
error("does not make sense")
end
function Base.endof(field::Field)
return endof(field.data)
end
#function Base.similar{T}(field::DVTI, data::Vector{T})
# return Increment(reshape(data, round(Int, length(data)/length(increment)), length(increment)))
#end
function Base.similar{T}(field::DVTI, data::Vector{T})
n = length(field.data)
data = reshape(data, round(Int, length(data)/n), n)
newdata = Vector[data[:,i] for i=1:n]
return typeof(field)(newdata)
end
function Base.start(::DVTI)
return 1
end
function Base.next(f::DVTI, state)
return f.data[state], state+1
end
function Base.done(f::DVTI, s)
return s > length(f.data)
end
### Accessing continuous fields
function Base.call(field::CVTI, xi::Vector)
field.data(xi)
end
function Base.call(field::CVTI, xi::Vector, ::Type{Val{:grad}})
field.data(xi, Val{:grad})
end
function Base.convert(::Type{Basis}, field::CVTI)
return field.data
end
function Base.call(field::CCTV, time::Number)
return field.data(time)
end
### Interpolation
""" Interpolate time-invariant field in time direction. """
function Base.call(field::DVTI, time::Float64)
return field
end
function Base.call(field::DCTI, time::Float64)
return field
end
function Base.call(field::CVTI, time::Float64)
return field.data()
end
function Base.call(field::CCTI, time::Float64)
return field.data()
end
""" Interpolate time-variant field in time direction. """
function Base.call(field::DCTV, time::Float64)
for i=reverse(1:length(field))
if isapprox(field[i].time, time)
return DCTI(field[i].data)
end
end
info(field.data)
info(time)
error("interpolate DCTV: not implemented yet")
end
function Base.call(field::DVTV, time::Float64, time_extrapolation::Symbol=:linear)
for i=reverse(1:length(field))
if isapprox(field[i].time, time)
return DVTI(field[i].data)
end
end
info(field.data)
info(time)
error("interpolate DVTV: not implemented yet")
end
""" Interpolate constant field in spatial dimension. """
function Base.call(basis::CVTI, field::DCTI, xi::Vector)
return field.data
end
""" Interpolate variable field in spatial dimension. """
function Base.call(basis::CVTI, values::DVTI, xi::Vector)
N = basis(xi)
return sum([N[i]*values[i] for i=1:length(N)])
end
function Base.call(basis::CVTI, geometry::DVTI, xi::Vector, ::Type{Val{:grad}})
dbasis = basis(xi, Val{:grad})
J = sum([dbasis[:,i]*geometry[i]' for i=1:length(geometry)])
invJ = isa(J, Vector) ? inv(J[1]) : inv(J)
grad = invJ * dbasis
return grad
end
function Base.call(basis::CVTI, geometry::DVTI, values::DVTI, xi::Vector, ::Type{Val{:grad}})
grad = call(basis, geometry, xi, Val{:grad})
gradf = sum([grad[:,i]*values[i]' for i=1:length(geometry)])'
return length(gradf) == 1 ? gradf[1] : gradf
end
function Base.call(basis::CVTI, xi::Vector, time::Number)
call(basis, xi)
end
### FIELDSET ###
typealias FieldSet Dict{ASCIIString, Field}
+20 -68
View File
@@ -3,14 +3,20 @@
# Heat problems
abstract HeatProblem <: Problem
abstract HeatEquation <: Equation
abstract HeatProblem <: AbstractProblem
function get_unknown_field_name(equation::HeatEquation)
function HeatProblem(dim::Int=1, elements=[])
return Problem{HeatProblem}(dim, elements)
end
function get_unknown_field_name{P<:HeatProblem}(::Type{P})
return "temperature"
end
### Formulation ###
function get_unknown_field_type{P<:HeatProblem}(::Type{P})
# scalar field
return Float64
end
""" Heat equations.
@@ -36,83 +42,29 @@ References
https://en.wikipedia.org/wiki/Heat_equation
"""
function assemble!(assembly::Assembly, equation::HeatEquation, time::Number=0.0, problem=nothing)
function assemble!{E<:CG}(assembly::Assembly, problem::Problem{HeatProblem}, element::Element{E}, time::Number)
element = get_element(equation)
gdofs = get_gdofs(equation)
basis = get_basis(element)
dbasis = grad(basis)
detJ = det(basis)
for ip in get_integration_points(equation)
w = ip.weight*detJ(ip)
N = basis(ip, time)
gdofs = get_gdofs(element, problem.dim)
for ip in get_integration_points(element)
w = ip.weight*det(element, ip, time)
N = element(ip, time)
if haskey(element, "density")
rho = basis("density", ip, time)
rho = element("density", ip, time)
add!(assembly.mass_matrix, gdofs, gdofs, w*rho*N'*N)
end
if haskey(element, "temperature thermal conductivity")
dN = dbasis(ip, time)
k = basis("temperature thermal conductivity", ip, time)
dN = element(ip, time, Val{:grad})
k = element("temperature thermal conductivity", ip, time)
add!(assembly.stiffness_matrix, gdofs, gdofs, w*k*dN'*dN)
end
if haskey(element, "temperature load")
f = basis("temperature load", ip, time)
f = element("temperature load", ip, time)
add!(assembly.force_vector, gdofs, w*N'*f)
end
if haskey(element, "temperature flux")
info("assemble boundary flux")
g = basis("temperature flux", ip, time)
g = element("temperature flux", ip, time)
add!(assembly.force_vector, gdofs, w*N'*g)
end
end
end
### Equations ###
""" Diffusive heat transfer for 4-node bilinear element. """
type DC2D4 <: HeatEquation
element :: Quad4
integration_points :: Vector{IntegrationPoint}
end
function Base.size(equation::DC2D4)
return (1, 4)
end
""" Diffusive heat transfer for 2-node linear segment. """
type DC2D2 <: HeatEquation
element :: Seg2
integration_points :: Vector{IntegrationPoint}
end
function Base.size(equation::DC2D2)
return (1, 2)
end
# Conversions element -> equation
function Base.convert(::Type{HeatEquation}, element::Quad4)
integration_points = get_integration_points(element)
haskey(element, "temperature") || (element["temperature"] = 0.0 => zeros(4))
DC2D4(element, integration_points)
end
function Base.convert(::Type{HeatEquation}, element::Seg2)
integration_points = get_integration_points(element)
haskey(element, "temperature") || (element["temperature"] = 0.0 => zeros(2))
DC2D2(element, integration_points)
end
### Problems ###
type PlaneHeatProblem <: HeatProblem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Vector{HeatEquation}
end
""" Default constructor for problem takes no arguments. """
function PlaneHeatProblem(equations=[])
return PlaneHeatProblem("temperature", 1, equations)
end
+6 -6
View File
@@ -3,7 +3,7 @@
# Let's drop here all integration schemes and some defaults for different element types
function get_integration_points(Quad4::Element)
function get_integration_points(::Type{Quad4})
[
IntegrationPoint(1.0/sqrt(3.0)*[-1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, -1], 1.0),
@@ -14,13 +14,13 @@ end
typealias LineElement Union{Seg2, Seg3}
function get_integration_points(element::LineElement, ::Type{Val{1}})
function get_integration_points(::Type{Seg2}, ::Type{Val{1}})
[
IntegrationPoint([0.0], 2.0)
]
end
function get_integration_points(element::LineElement, ::Type{Val{2}})
function get_integration_points(::Type{Seg2}, ::Type{Val{2}})
[
IntegrationPoint([-sqrt(1/3)], 1)
IntegrationPoint([+sqrt(1/3)], 1)
@@ -54,12 +54,12 @@ function get_integration_points(element::LineElement, ::Type{Val{5}})
]
end
function get_integration_points(element::Seg2)
return get_integration_points(element, Val{1})
function get_integration_points(::Type{Seg2})
return get_integration_points(Seg2, Val{2})
end
function get_integration_points(element::Seg3)
return get_integration_points(element, Val{2})
return get_integration_points(element, Val{3})
end
### 3D elements
+14 -17
View File
@@ -3,7 +3,7 @@
# Lagrange (Continous Galerkin) finite elements
abstract CG <: Element
abstract CG <: AbstractElement
"""
Given polynomial P and coordinates of reference element, calculate
@@ -29,29 +29,26 @@ Examples
>>> @create_lagrange_element(Seg2, "2 node linear segment", X, P)
"""
macro create_lagrange_element(element_name, element_description, X, P)
# Logging.debug("Creating element ", element_name, ": ", element_description, "\n")
eltype = esc(element_name)
quote
global get_element_description
basis, dbasisdxi = calculate_lagrange_basis($P, $X)
type $eltype <: CG
connectivity :: Array{Int, 1}
basis :: CVTI
fields :: FieldSet
global get_basis, get_dbasis
basis, dbasis = calculate_lagrange_basis($P, $X)
abstract $eltype <: CG
function get_basis(::Type{$eltype}, xi::Vector{Float64})
return basis(xi)
end
function $eltype(connectivity, args...)
$eltype(connectivity, CVTI(basis, dbasisdxi), FieldSet())
function get_dbasis(::Type{$eltype}, xi::Vector{Float64})
return dbasis(xi)
end
function $eltype(args...)
return Element{$eltype}(args...)
end
function Base.size(::Type{$eltype})
return Base.size($X)
end
get_element_description(el::Type{$eltype}) = $element_description
Base.size(element::Type{$eltype}) = Base.size($X)
Base.size(element::$eltype) = Base.size($X)
end
end
# 0d Lagrange element
#@create_element(Point1, CG, "1 node point element")
# 1d Lagrange elements
@create_lagrange_element(Seg2, "2 node linear line element",
+1 -1
View File
@@ -6,7 +6,7 @@
# slave element = non-mortar element where integration happens
# master element = mortar element projected to non-mortar side
abstract MortarElement <: Element
abstract MortarElement <: AbstractElement
type MSeg2 <: MortarElement
connectivity :: Vector{Int}
+22 -63
View File
@@ -1,77 +1,36 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
abstract Problem
abstract BoundaryProblem <: Problem
abstract FieldProblem <: Problem
abstract AbstractProblem
""" Return all equations beloging to this problem. """
function get_equations(problem::Problem)
problem.equations
type Problem{T<:AbstractProblem}
dim :: Int
elements :: Vector{Element}
end
type BoundaryProblem{T<:AbstractProblem}
parent_field_name :: ASCIIString
parent_field_dim :: Int
dim :: Int
elements :: Vector{Element}
end
typealias AllProblems Union{Problem, BoundaryProblem}
function get_elements(problem::AllProblems)
return problem.elements
end
""" Return the dimension of the unknown field of this problem. """
function get_unknown_field_dimension(problem::Problem)
problem.unknown_field_dimension
return problem.dim
end
""" Return the name of the unknown field of this problem. """
function get_unknown_field_name(problem::Problem)
problem.unknown_field_name
function get_unknown_field_name{P<:AbstractProblem}(problem::Problem{P})
return get_unknown_field_name(P)
end
"""
Add new equation to problem.
Parameters
----------
problem
element
Notes
-----
Equation is automatically created during process based on problem
element -> equation mapping and element type.
"""
function Base.push!(problem::Problem, element::Element, args...)
# element_type = typeof(element)
# equation_type = problem.element_mapping[element_type]
# push!(problem.equations, equation_type(element, args...))
push!(problem.equations, element)
function Base.push!(problem::AllProblems, element::Element)
push!(problem.elements, element)
end
"""
Return the size of the problem, i.e.
maximum number of connectivity × unknown field dimension.
"""
function Base.size(problem::Problem)
mc = 0
for equation in get_equations(problem)
element = get_element(equation)
mc = max(mc, get_connectivity(element)...)
end
dim = get_unknown_field_dimension(problem)
return (dim, dim*mc)
end
""" Assign new equation mapping to problem for some element.
Examples
--------
>>> p = PlaneHeatProblem()
>>> p[Seg2] = DC2D2
"""
function Base.setindex!(problem::Problem, equation, element)
problem.element_mapping[element] = equation
end
""" Return global degrees of freedom of element in matrix level. """
function get_gdofs(problem::Problem, equation::Equation)
dim = get_unknown_field_dimension(problem)
element = get_element(equation)
conn = get_connectivity(element)
gdofs = vec(vcat([dim*conn'-i for i=dim-1:-1:0]...))
return gdofs
end
+43 -99
View File
@@ -5,39 +5,6 @@
abstract Solver
"""
Solve field equations for single element with some dofs fixed. This can be used
to test nonlinear element formulations.
"""
function solve!(equation::Equation, free_dofs::Vector{Int}, time::Number; max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false, callback=nothing)
unknown_field_name = get_unknown_field_name(equation)
element = get_element(equation)
x0 = element[unknown_field_name](0.0)
x = zeros(prod(size(equation)))
dx = fill!(similar(x), 0.0)
ass = Assembly()
for i=1:max_iterations
empty!(ass)
assemble!(ass, equation)
A = full(ass.stiffness_matrix)[free_dofs, free_dofs]
b = full(ass.force_vector)[free_dofs]
if dump_matrices
dump(full(A))
dump(full(b)')
end
dx[free_dofs] = A \ b
x += dx
eqsize = size(equation)
data = eqsize[1] != 1 ? reshape(x, eqsize) : x
push!(element[unknown_field_name], time => data)
norm(dx) < tolerance && return
if !isa(callback, Void)
callback(x)
end
end
error("Did not converge in $max_iterations iterations")
end
"""
Solve field equations for a single problem with some dofs fixed. This can be used
to test nonlinear element formulations. Dirichlet boundary is assumed to be homogeneous
@@ -72,13 +39,12 @@ function solve!(problem::Problem, free_dofs::Vector{Int}, time::Float64; max_ite
if !(isa(callback, Void))
callback(x)
end
for equation in get_equations(problem)
element = get_element(equation)
gdofs = get_gdofs(equation)
for element in get_elements(problem)
gdofs = get_gdofs(element, problem.dim)
data = full(x[gdofs])
eqsize = size(equation)
if eqsize[1] != 1
data = reshape(data, eqsize)
if length(data) != length(element)
data = reshape(data, problem.dim, length(element))
data = [data[:,i] for i=1:size(data,2)]
end
push!(element[field_name], time => data)
end
@@ -87,24 +53,11 @@ function solve!(problem::Problem, free_dofs::Vector{Int}, time::Float64; max_ite
error("Did not converge in $max_iterations iterations")
end
function Base.push!(solver::Solver, problem::Problem)
push!(solver.problems, problem)
end
""" Get all problems assigned to solver. """
function get_problems(solver::Solver)
return solver.problems
end
## SimpleSolver -- tiny direct demo solver
""" Simple solver for educational purposes. """
type SimpleSolver <: Solver
problems :: Vector{Problem}
end
""" Default initializer. """
function SimpleSolver()
SimpleSolver([])
""" Simple linear solver for educational purposes. """
type LinearSolver <: Solver
field_problem :: Problem
boundary_problem :: BoundaryProblem
end
"""
@@ -113,28 +66,30 @@ Call solver to solve a set of problems.
This is a simple direct solver for demonstration purposes. It handles the
common situation, i.e., some main field problem and it's Dirichlet boundary.
Au + C'λ = f
Ku + C'λ = f
Cu = g
"""
function call(solver::SimpleSolver, time::Number=0.0)
problem1, problem2 = get_problems(solver)
function call(solver::LinearSolver, time::Float64)
assembly1 = Assembly()
assemble!(assembly1, problem1, time)
assembly2 = Assembly()
assemble!(assembly2, problem2, time)
field_name = get_unknown_field_name(solver.field_problem)
field_dim = get_unknown_field_dimension(solver.field_problem)
info("solving $field_name problem, $field_dim dofs / nodes")
field_assembly = assemble(solver.field_problem, time)
boundary_assembly = assemble(solver.boundary_problem, time)
info("Creating sparse matrices")
K = sparse(field_assembly.stiffness_matrix)
dim = size(K, 1)
f = sparse(field_assembly.force_vector, dim, 1)
C = sparse(boundary_assembly.stiffness_matrix, dim, dim)
g = sparse(boundary_assembly.force_vector, dim, 1)
# info("Creating sparse matrices")
A1 = sparse(assembly1.stiffness_matrix)
dims = size(A1)
b1 = sparse(assembly1.force_vector, dims[1], 1)
A2 = sparse(assembly2.stiffness_matrix, dims[1], dims[2])
b2 = sparse(assembly2.force_vector, dims[1], 1)
# create a saddle point problem
A = [A1 A2; A2' zeros(A2)]
b = [b1; b2]
A = [K C'; C' zeros(C)]
b = [f; g]
# solve problem
nz = unique(rowvals(A)) # take only non-zero rows
@@ -142,37 +97,26 @@ function call(solver::SimpleSolver, time::Number=0.0)
x[nz] = lufact(A[nz,nz]) \ full(b[nz])
# get "problem-wise" solution vectors
x1 = x[1:length(b1)]
x2 = x[length(b1)+1:end]
u = x[1:dim]
la = x[dim+1:end]
# update field for elements in problem 1
for equation in get_equations(problem1)
element = get_element(equation)
field_name = get_unknown_field_name(problem1)
gdofs = get_gdofs(problem1, equation)
local_sol = vec(full(x1[gdofs]))
eqsize = size(equation)
if eqsize[1] != 1
local_sol = reshape(local_sol, eqsize)
for element in get_elements(solver.field_problem)
gdofs = get_gdofs(element, field_dim)
local_sol = vec(full(u[gdofs]))
# if solving vector field, modify local solution vector
# to array of vectors
if field_dim != 1
local_sol = reshape(local_sol, field_dim, length(element))
local_sol = [local_sol[:,i] for i=1:size(local_sol,2)]
end
if haskey(element, field_name)
push!(element[field_name], time => local_sol)
else
element[field_name] = (time => local_sol)
end
#info("problem1: pushing to $field_name")
push!(element[field_name], time => local_sol)
end
# update field for elements in problem 2 (Dirichlet boundary)
for equation in get_equations(problem2)
element = get_element(equation)
field_name = "reaction force" #get_unknown_field_name(problem2)
gdofs = get_gdofs(problem2, equation)
local_sol = vec(full(x1[gdofs]))
eqsize = size(equation)
if eqsize[1] != 1
local_sol = reshape(local_sol, eqsize)
end
#info("problem2: pushing to $field_name")
#push!(element[field_name], time => local_sol)
end
return norm(x1)
return norm(u)
end