data structures, new testing concept

This commit is contained in:
Jukka Aho
2015-11-01 18:44:50 +02:00
parent 42d81bc86d
commit b985ebf50c
17 changed files with 875 additions and 847 deletions
+8 -1
View File
@@ -11,6 +11,9 @@ module JuliaFEM
#using Lexicon
macro debug(msg)
if !haskey(ENV, "JuliaFEM_LOG_LEVEL")
return :()
end
return :( println("DEBUG: ", $msg) )
end
@@ -32,8 +35,10 @@ function Base.linspace{T<:Array}(X1::T, X2::T, n)
[1/2*(1-ti)*X1 + 1/2*(1+ti)*X2 for ti in linspace(-1, 1, n)]
end
include("fields.jl") # fields, see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/notebooks/2015-06-14-data-structures.ipynb
include("basis.jl") # interpolation of discrete fields
include("types.jl") # type definitions
#include("interpolate.jl") # interpolation routines
### ELEMENTS ###
include("elements.jl")
@@ -58,6 +63,8 @@ include("solvers.jl")
include("xdmf.jl")
include("abaqus_reader.jl")
include("test.jl")
end # module
FEM = JuliaFEM
+124
View File
@@ -0,0 +1,124 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
abstract AbstractBasis
""" Defined to dimensionless coordinate ξ∈[-1,1]^n. """
type SpatialBasis <: AbstractBasis
basis :: Function
dbasisdxi :: Function
end
typealias Basis SpatialBasis
""" Defined to to interval t∈[0, 1]. """
type TemporalBasis <: AbstractBasis
basis :: Function
dbasisdt :: Function
end
function TemporalBasis()
basis(t) = [1-t, t]
dbasis(t) = [-1, 1]
return TemporalBasis(basis, dbasis)
end
function call(b::TemporalBasis, value::Number)
b.basis(value)
end
function call(b::SpatialBasis, value::Vector)
b.basis(value)
end
### INTERPOLATION IN TIME DOMAIN ###
function Base.call(field::Field, basis::TemporalBasis, time)
# FieldSet -> Field -> TimeStep -> Increment -> data
# special cases, -Inf, +Inf and ~0.0
if time > field[end].time
return field[end][end]
end
if (time < field[1].time) || abs(time-field[1].time) < 1.0e-12
return field[1][end]
end
i = length(field)
while field[i].time >= time
i -= 1
end
field[i].time == time && return field[i][end]
t1 = field[i].time
t2 = field[i+1].time
inc1 = field[i][end]
inc2 = field[i+1][end]
# TODO: may there be some reasons for "unphysical" jumps in
# fields w.r.t time which should be taken account in some way?
# i.e. dt between two fields → 0
dt = t2 - t1
b = basis.basis((time-t1)/dt)
r = Increment[inc1, inc2]
return dot(b, r)
end
function Base.call(field::DiscreteField, time)
return Base.call(field, TemporalBasis(), time)
end
function Base.call(field::Field, basis::TemporalBasis, time,
derivative::Type{Val{:derivative}})
# 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)
timesteps = TimeStep[field[i], field[j]]
increments = Increment[timesteps[1][end], timesteps[2][end]]
J = norm(timesteps[2].time - timesteps[1].time)
dbasisdt = basis.dbasisdt( (time-timesteps[1].time)/J )
return dot(dbasisdt, increments)/J
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
### INTERPOLATION IN SPATIAL DOMAIN ###
function Base.call(increment::Increment, basis::SpatialBasis, xi::Vector)
basis = basis.basis(xi)
sum([basis[i]*increment[i] for i=1:length(increment)])
end
function Base.call(increment::Increment, basis::SpatialBasis, xi::Vector,
geometry::Increment, gradient::Type{Val{:gradient}})
dbasis = basis.dbasisdxi(xi)
J = sum([dbasis[:,i]*geometry[i]' for i=1:length(geometry)])
grad = inv(J)*dbasis
gradf = sum([grad[:,i]*increment[i]' for i=1:length(increment)])'
return gradf
end
+12 -11
View File
@@ -20,22 +20,22 @@ Raises
This uses FactCheck and throws exceptions if element is not passing all tests.
"""
function test_element(element_type)
Logging.info("Testing element $element_type")
info("Testing element $element_type")
local element
dim = nothing
n = nothing
try
dim, n = size(element_type)
catch
Logging.error("Unable to determine element dimensions. Define Base.size(element::Type{$elementtype}) = (dim, nbasis) where dim is spatial dimension of element and nbasis is number of basis functions of element.")
error("Unable to determine element dimensions. Define Base.size(element::Type{$elementtype}) = (dim, nbasis) where dim is spatial dimension of element and nbasis is number of basis functions of element.")
end
Logging.info("element dimension: $dim x $n")
info("element dimension: $dim x $n")
Logging.info("Initializing element")
info("Initializing element")
try
element = element_type(collect(1:n))
catch
Logging.error("""
error("""
Unable to create element with default constructor define function
$eltype(connectivity) which initializes this element.""")
return false
@@ -51,15 +51,15 @@ function test_element(element_type)
dbasis = grad(basis)
mid = zeros(dim)
val1 = basis(mid, 0.0)
Logging.info("basis at $mid: $val1")
info("basis at $mid: $val1")
val2 = basis("field1", mid, 0.0)
Logging.info("field val at $mid: $val2")
info("field val at $mid: $val2")
val3 = dbasis(mid, 0.0)
Logging.info("derivative of basis at $mid: $val3")
info("derivative of basis at $mid: $val3")
val4 = dbasis("field1", mid, 0.0)
Logging.info("field val at $mid: $val4")
info("field val at $mid: $val4")
Logging.info("Element $element_type passed tests.")
info("Element $element_type passed tests.")
end
""" Get FieldSet from element. """
@@ -75,7 +75,8 @@ Examples
JuliaFEM.Quad4([1,2,3,4],JuliaFEM.Basis(basis,dbasisdxi),Dict("geometry"=>JuliaFEM.FieldSet("geometry",JuliaFEM.Field[JuliaFEM.Field{Array{Int64,1}}(0.0,0,[1,2,3,4])])))
"""
function Base.setindex!(element::Element, field_data, field_name)
element.fields[field_name] = field_data
#element.fields[field_name] = field_data
setindex!(element.fields, field_data, field_name)
end
function get_connectivity(el::Element)
-65
View File
@@ -1,65 +0,0 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using JuliaFEM: Basis, Field, FieldSet, diff
"""
Interpolate field u using basis N in point xi.
"""
function interpolate{T}(N::Basis, u::Field{Vector{T}}, xi::Array{Float64,1})
N(xi)*u
end
"""
Interpolate field u using basis N in set of points xi. Convenient function.
"""
function interpolate{T}(N::Basis, u::Field{Vector{T}}, xis::Array{Array{Float64,1},1})
T[N(xi)*u for xi in xis]
end
function interpolate{T}(N::Basis, u::Field{T}, xi::Array{Float64,1})
u.values
end
"""
Interpolate a field from fieldset for some time t.
"""
function interpolate(fields::FieldSet, t::Number)
if length(fields) == 0
throw("Empty set of fields: $fields")
end
if t <= fields[1].time
return Field(t, fields[1].values)
end
if t >= fields[end].time
return Field(t, fields[end].values)
end
i = length(fields)
while fields[i].time >= t
i -= 1
end
if fields[i].time == t
return fields[i]
end
#Logging.debug("doing linear interpolation between fields $i and $(i+1)")
f1 = fields[i]
t1 = f1.time
f2 = fields[i+1]
t2 = f2.time
dt = t2 - t1
nw = (t2-t)/dt*f1.values + (t-t1)/dt*f2.values
f = Field(t, nw)
return f
end
function call(fieldset::FieldSet, time::Number)
interpolate(fieldset, time)
end
function interpolate(basis::Basis, field::Field, ip::IntegrationPoint)
interpolate(basis, field, ip.xi)
end
#function dinterpolate(basis::Basis, u::Field, xi::Array{Float64, 1})
# basis.dbasisdxi(xi)*u
#end
-267
View File
@@ -1,273 +1,6 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/notebooks/2015-06-14-data-structures.ipynb
#abstract AbstractField{T,N} <: AbstractArray{T,N}
abstract AbstractField
abstract DiscreteField <: AbstractField
abstract ContinuousField <: AbstractField
abstract TimeContinuousField <: ContinuousField
abstract SpatialContinuousField <: ContinuousField
abstract TimeAndSpatialContinuousField <: ContinuousField
# should we introduce time and spatial discontinuous fields
# for discontinuous galerkin?
### DEFAULT DISCRETE FIELD ###
# 1. Increment
# FIXME: This should be Vector.
#typealias Increment Vector
type Increment{T} <: AbstractVector{T}
data :: Vector{T}
end
Base.size(increment::Increment) = Base.size(increment.data)
Base.linearindexing(::Type{Increment}) = Base.LinearFast()
Base.getindex(increment::Increment, i::Int) = increment.data[i]
Base.setindex!(increment::Increment, v, i::Int) = (increment.data[i] = v)
Base.similar{T}(increment::Increment, ::Type{T}) = Increment(similar(increment.data))
Base.dot(v::Number, i::Increment) = v*i
function Base.convert(::Type{Increment}, data::Number)
Increment([data])
end
function Base.convert{T}(::Type{Increment}, data::Array{T, 2})
Increment([data[:,i] for i=1:size(data, 2)])
end
function Base.convert{T}(::Type{Increment}, data::Array{T, 3})
Increment([data[:,:,i] for i=1:size(data, 3)])
end
function Base.convert{T}(::Type{Increment}, data::Array{T, 4})
Increment([data[:,:,:,i] for i=1:size(data, 4)])
end
function Base.convert{T}(::Type{Increment}, data::Array{T, 5})
Increment([data[:,:,:,:,i] for i=1:size(data, 5)])
end
function Base.zeros(::Type{Increment}, dims...)
Increment(zeros(dims...))
end
function Base.vec(increment::Increment)
[increment.data...;]
end
function Base.similar{T}(increment::Increment{Vector{T}}, data::Vector{T})
Increment(reshape(data, round(Int, length(data)/length(increment)), length(increment)))
end
# 2. TimeStep
type TimeStep{T} <: AbstractVector{T}
time :: Float64
increments :: Vector{T}
end
Base.size(timestep::TimeStep) = Base.size(timestep.increments)
Base.linearindexing(::Type{TimeStep}) = Base.LinearFast()
Base.getindex(timestep::TimeStep, i::Int) = timestep.increments[i]
function Base.convert(::Type{TimeStep}, time::Number, increment::Increment)
TimeStep(time, Increment[increment])
end
function Base.push!(timestep::TimeStep, increment::Increment)
push!(timestep.increments, increment)
end
# 3. DefaultDiscreteField
type DefaultDiscreteField <: DiscreteField
timesteps :: Vector{TimeStep}
end
Base.size(field::DefaultDiscreteField) = Base.size(field.timesteps)
Base.linearindexing(::Type{DefaultDiscreteField}) = Base.LinearFast()
Base.getindex(field::DefaultDiscreteField, i::Int) = field.timesteps[i]
Base.length(field::DefaultDiscreteField) = length(field.timesteps)
Base.endof(field::DefaultDiscreteField) = endof(field.timesteps)
Base.first(field::DefaultDiscreteField) = field[1][end]
Base.last(field::DefaultDiscreteField) = field[end][end]
function Base.push!(field::DefaultDiscreteField, timestep::TimeStep)
push!(field.timesteps, timestep)
end
typealias Field DefaultDiscreteField
### CONTINUOUS FIELDS ###
# fix print_matrix
#function Base.print_matrix(::Base.AbstractIOBuffer, field::ContinuousField, args...)
# TODO: anything nice to print?
#end
### FIELDSET ###
typealias FieldSet Dict{ASCIIString, AbstractField}
"""Quicky add discrete field to fieldset.
Examples
--------
>>> fs = FieldSet()
>>> fs["myfield"] = [1, 2, 3, 4]
"""
function Base.convert(::Type{AbstractField}, data::Union{Array, Number})
increment = Increment(data)
timestep = TimeStep(0.0, Increment[increment])
field = DefaultDiscreteField(TimeStep[timestep])
return field
end
""" Quicky add several time steps at once in tuple.
Examples
--------
>>> fs = FieldSet()
>>> fs["myfield"] = (0.0, [1, 2, 3, 4]), (0.5, [2, 3, 4, 5])
or
>>> fs["myfield"] = [1, 2, 3, 4], [2, 3, 4, 5]
"""
function Base.convert(::Type{AbstractField}, data::Tuple)
timesteps = TimeStep[]
for (i, timestep) in enumerate(data)
if isa(timestep, Tuple)
push!(timesteps, TimeStep(Float64(timestep[1]), Increment(timestep[2])))
else
push!(timesteps, TimeStep(Float64(i-1), Increment(timestep)))
end
end
return DefaultDiscreteField(timesteps)
end
### BASIS ###
abstract AbstractBasis
""" Defined to dimensionless coordinate ξ∈[-1,1]^n. """
type SpatialBasis <: AbstractBasis
basis :: Function
dbasisdxi :: Function
end
typealias Basis SpatialBasis
""" Defined to to interval t∈[0, 1]. """
type TemporalBasis <: AbstractBasis
basis :: Function
dbasisdt :: Function
end
function TemporalBasis()
basis(t) = [1-t, t]
dbasis(t) = [-1, 1]
return TemporalBasis(basis, dbasis)
end
function call(b::TemporalBasis, value::Number)
b.basis(value)
end
function call(b::SpatialBasis, value::Vector)
b.basis(value)
end
### INTERPOLATION IN TIME DOMAIN ###
function Base.call(field::Field, basis::TemporalBasis, time)
# FieldSet -> Field -> TimeStep -> Increment -> data
# special cases, -Inf, +Inf and ~0.0
if time > field[end].time
return field[end][end]
end
if (time < field[1].time) || abs(time-field[1].time) < 1.0e-12
return field[1][end]
end
i = length(field)
while field[i].time >= time
i -= 1
end
field[i].time == time && return field[i][end]
t1 = field[i].time
t2 = field[i+1].time
inc1 = field[i][end]
inc2 = field[i+1][end]
# TODO: may there be some reasons for "unphysical" jumps in
# fields w.r.t time which should be taken account in some way?
# i.e. dt between two fields → 0
dt = t2 - t1
b = basis.basis((time-t1)/dt)
r = Increment[inc1, inc2]
return dot(b, r)
end
function Base.call(field::DiscreteField, time)
return Base.call(field, TemporalBasis(), time)
end
function Base.call(field::Field, basis::TemporalBasis, time,
derivative::Type{Val{:derivative}})
# 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)
timesteps = TimeStep[field[i], field[j]]
increments = Increment[timesteps[1][end], timesteps[2][end]]
J = norm(timesteps[2].time - timesteps[1].time)
dbasisdt = basis.dbasisdt( (time-timesteps[1].time)/J )
return dot(dbasisdt, increments)/J
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 + 1.0e-12
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
### INTERPOLATION IN SPATIAL DOMAIN ###
function Base.call(increment::Increment, basis::SpatialBasis, xi::Vector)
basis = basis.basis(xi)
sum([basis[i]*increment[i] for i=1:length(increment)])
end
function Base.call(increment::Increment, basis::SpatialBasis, xi::Vector,
geometry::Increment, gradient::Type{Val{:gradient}})
dbasis = basis.dbasisdxi(xi)
J = sum([dbasis[:,i]*geometry[i]' for i=1:length(geometry)])
grad = inv(J)*dbasis
gradf = sum([grad[:,i]*increment[i]' for i=1:length(increment)])'
return gradf
end
### INTEGRATIONPOINT ###
"""