mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-10 13:17:42 +00:00
data structures, new testing concept
This commit is contained in:
+8
-1
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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 ###
|
||||
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user