Files
JuliaFEM.jl/src/fields.jl
T

446 lines
11 KiB
Julia
Raw Normal View History

2015-11-01 18:47:33 +02:00
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
2015-11-27 10:10:00 +02:00
abstract AbstractField
2015-11-20 08:48:15 +02:00
2015-11-27 10:10:00 +02:00
abstract Discrete <: AbstractField
abstract Continuous <: AbstractField
abstract Constant <: AbstractField
abstract Variable <: AbstractField
abstract TimeVariant <: AbstractField
abstract TimeInvariant <: AbstractField
2015-11-01 18:47:33 +02:00
2016-07-03 21:45:22 +03:00
2015-11-27 10:10:00 +02:00
type Field{A<:Union{Discrete,Continuous}, B<:Union{Constant,Variable}, C<:Union{TimeVariant,TimeInvariant}}
data
end
typealias FieldSet Dict{AbstractString, Field}
2016-07-03 21:45:22 +03:00
2015-11-27 10:10:00 +02:00
### Basic data structure for discrete field
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
type Increment{T}
time :: Float64
data :: T
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
function Base.convert{T}(::Type{Increment{T}}, data::Pair{Float64,T})
return Increment{T}(data[1], data[2])
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
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)])
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
function Base.getindex{T}(increment::Increment{Vector{T}}, i::Int64)
return increment.data[i]
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function Base.:*(d, increment::Increment)
2015-11-27 10:10:00 +02:00
return d*increment.data
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
### Basic data structure for continuous field
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
type Basis
basis :: Function
dbasis :: Function
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function (basis::Basis)(xi::Vector)
2015-11-27 10:10:00 +02:00
basis.basis(xi)
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function (basis::Basis)(xi::Vector, ::Type{Val{:grad}})
2015-11-27 10:10:00 +02:00
basis.dbasis(xi)
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
### Different field combinations and other typealiases
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
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}
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
typealias ScalarIncrement{T} Increment{T}
typealias VectorIncrement{T} Increment{Vector{T}}
typealias TensorIncrement{T} Increment{Matrix{T}}
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
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}
2015-11-27 10:10:00 +02:00
### Convenient functions to create fields
2015-11-27 10:10:00 +02:00
#function Base.convert(::Type{Field}, data)
# return Field(data)
#end
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
function Field(data)
return DCTI(data)
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
function Field(data::Vector)
return DVTI(data)
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
function Field{T}(data::Pair{Float64, T}...)
return DCTV([Increment{T}(d[1], d[2]) for d in data])
2015-11-01 18:47:33 +02:00
end
2016-08-04 13:15:19 +03:00
#=
2015-11-27 10:10:00 +02:00
function Field{T}(data::Pair{Float64, Vector{T}}...)
return DVTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
2015-11-01 18:47:33 +02:00
end
2016-08-04 13:15:19 +03:00
function Field{T}(data::Pair{Float64, Dict{Int64, T}}...)
return DVTV([Increment{Dict{Int64, T}}(d[1], d[2]) for d in data])
end
=#
function Field{T<:Union{Vector, Dict}}(data::Pair{Float64, T}...)
return DVTV([Increment{T}(d[1], d[2]) for d in data])
end
function Field(data::Dict)
return DVTI(data)
end
2016-06-27 16:11:33 +03:00
function convert{T}(::Type{DCTV}, data::Pair{Real, Vector{T}}...)
2015-11-27 10:10:00 +02:00
return DCTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
2015-11-01 18:47:33 +02:00
end
2016-02-10 22:21:30 +02:00
""" Create new discrete, constant, time variant field.
Examples
--------
julia> t0 = 0.0; t1=1.0; y0 = 0.0; y1 = 1.0
julia> f = DCTV(t0 => y0, t1 => y1)
"""
2016-06-27 16:11:33 +03:00
function convert{T,v<:Real}(::Type{DCTV}, data::Pair{v, T}...)
2016-02-10 22:21:30 +02:00
return DCTV([Increment(d[1],d[2]) for d in data])
end
#function Base.convert(::Type{DCTV}, data::Pair{Real, Any}...)
# return DCTV([Increment{Vector}(d[1], d[2]) for d in data])
#end
2015-11-27 10:10:00 +02:00
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
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
function CVTI(basis::Function, dbasis::Function)
return CVTI(Basis(basis, dbasis))
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
function Field(basis::Function, dbasis::Function)
return CVTI(basis, dbasis)
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
### Accessing and manipulating discrete fields
2015-11-01 18:47:33 +02:00
2016-07-03 05:01:18 +03:00
function getindex(field::DVTV, i::Int64)
2015-11-27 10:10:00 +02:00
return field.data[i]
2015-11-01 18:47:33 +02:00
end
2016-07-03 05:01:18 +03:00
function push!(field::DCTV, data::Pair)
2015-11-27 10:10:00 +02:00
push!(field.data, data)
2015-11-01 18:47:33 +02:00
end
2016-07-03 05:01:18 +03:00
function push!(field::DVTV, data::Pair)
2015-11-27 10:10:00 +02:00
push!(field.data, data)
2015-11-01 18:47:33 +02:00
end
2016-07-03 05:01:18 +03:00
function getindex(field::DVTI, i::Int64)
2015-11-27 10:10:00 +02:00
return field.data[i]
2015-11-01 18:47:33 +02:00
end
2016-07-03 05:01:18 +03:00
function getindex(field::DCTV, i::Int64)
2015-11-27 10:10:00 +02:00
return field.data[i]
end
2015-11-01 18:47:33 +02:00
2016-07-03 05:01:18 +03:00
function getindex(field::Field, i::Int64)
2015-11-27 10:10:00 +02:00
return field.data[i]
2015-11-01 18:47:33 +02:00
end
2016-07-03 05:01:18 +03:00
function length(field::DVTI)
2015-11-27 10:10:00 +02:00
return length(field.data)
end
2016-07-03 05:01:18 +03:00
function length(field::DCTI)
return 1
end
2016-07-03 05:01:18 +03:00
function length(field::DVTV)
2015-11-27 10:10:00 +02:00
return length(field.data)
end
2016-07-03 05:01:18 +03:00
function length(field::DCTV)
2015-11-27 10:10:00 +02:00
return length(field.data)
end
2016-07-03 05:01:18 +03:00
function first(field::Union{DCTV, DVTV})
2016-02-10 22:21:30 +02:00
return field[1]
end
2016-07-03 05:01:18 +03:00
function isapprox(f1::DCTI, f2::DCTI)
2016-02-10 22:21:30 +02:00
isapprox(f1.data, f2.data)
end
2015-11-27 10:10:00 +02:00
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::Number) = ($op)(field.data, k)
@eval ($op)(k::Number, field::DCTI) = ($op)(field.data, k)
end
2016-11-13 13:24:08 +02:00
function Base.:+(f1::DVTI, f2::DVTI)
return DVTI(f1.data + f2.data)
end
2016-11-13 13:24:08 +02:00
function Base.:-(f1::DVTI, f2::DVTI)
2016-03-01 05:52:42 +02:00
return DVTI(f1.data - f2.data)
end
2016-11-13 13:24:08 +02:00
function Base.:*{T<:Real}(c::T, field::DVTI)
return DVTI(c*field.data)
end
2016-11-13 13:24:08 +02:00
function Base.:*(N::Matrix, f::DCTI)
2016-05-22 00:22:17 +03:00
return f.data*N'
end
2016-07-03 21:45:22 +03:00
2016-07-03 21:45:22 +03:00
# Multiply DVTI field with another vector T. Vector length
# must match to the field length and this can be used mainly
# for interpolation purposes, i.e., u = ∑ Nᵢuᵢ
2016-11-13 13:24:08 +02:00
function Base.:*(T::Vector, f::DVTI)
@assert length(T) <= length(f)
return sum([T[i]*f[i] for i=1:length(T)])
end
2016-06-27 16:11:33 +03:00
function vec(field::DVTI)
2015-11-27 10:10:00 +02:00
return [field.data...;]
2015-11-01 18:47:33 +02:00
end
2016-06-27 16:11:33 +03:00
function vec(field::DCTV)
2016-07-03 05:01:18 +03:00
error("trying to vectorize $field does not make sense")
2015-11-01 18:47:33 +02:00
end
2016-06-27 16:11:33 +03:00
function endof(field::Field)
2015-11-27 10:10:00 +02:00
return endof(field.data)
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
#function Base.similar{T}(field::DVTI, data::Vector{T})
# return Increment(reshape(data, round(Int, length(data)/length(increment)), length(increment)))
#end
2015-11-01 18:47:33 +02:00
2016-06-27 16:11:33 +03:00
function similar{T}(field::DVTI, data::Vector{T})
2015-11-27 10:10:00 +02:00
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)
2015-11-01 18:47:33 +02:00
end
2016-06-27 16:11:33 +03:00
function start(::DVTI)
2015-11-27 10:10:00 +02:00
return 1
2015-11-01 18:47:33 +02:00
end
2016-06-27 16:11:33 +03:00
function next(f::DVTI, state)
2015-11-27 10:10:00 +02:00
return f.data[state], state+1
2015-11-11 00:52:16 +02:00
end
2016-06-27 16:11:33 +03:00
function done(f::DVTI, s)
2015-11-27 10:10:00 +02:00
return s > length(f.data)
2015-11-11 00:52:16 +02:00
end
2016-05-25 22:33:47 +03:00
""" Update time-dependent fields with new values.
Examples
--------
julia> f = Field(0.0 => 1.0)
julia> update!(f, 1.0 => 2.0)
Now field has two (time, value) pairs: (0.0, 1.0) and (1.0, 2.0)
Notes
-----
Time vector is assumed to be ordered t_i-1 < t_i < t_i+1. If updating
field with already existing time the old value is replaced with new one.
"""
function update!{T}(field::Union{DCTV, DVTV}, val::Pair{Float64, T})
time, data = val
if isapprox(last(field).time, time)
last(field).data = data
else
push!(field.data, Increment(val...))
end
end
function update!{T}(field::Union{DCTI, DVTI}, val::T)
field.data = val
end
2015-11-27 10:10:00 +02:00
### Accessing continuous fields
2015-11-01 18:47:33 +02:00
2016-11-13 13:24:08 +02:00
function (field::CVTI)(xi::Vector)
2016-06-27 16:11:33 +03:00
return field.data(xi)
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function (field::CVTV)(xi, time::Float64)
2016-06-27 16:11:33 +03:00
return field.data(xi, time)
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function (field::CVTI)(xi::Vector, ::Type{Val{:Grad}})
2016-06-27 16:11:33 +03:00
return field.data(xi, Val{:Grad})
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function (field::CCTV)(time::Float64)
2015-11-27 10:10:00 +02:00
return field.data(time)
2015-11-01 18:47:33 +02:00
end
2016-06-27 16:11:33 +03:00
function convert(::Type{Basis}, field::CVTI)
return field.data
end
2016-02-23 15:00:30 +02:00
### Interpolation
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
""" Interpolate time-invariant field in time direction. """
2016-11-13 13:24:08 +02:00
function (field::DVTI)(time::Float64)
2015-11-27 10:10:00 +02:00
return field
end
2016-11-13 13:24:08 +02:00
function (field::DCTI)(time::Float64)
2016-11-15 22:01:00 +02:00
return field.data
2015-11-27 10:10:00 +02:00
end
2016-11-13 13:24:08 +02:00
function (field::CVTI)(time::Float64)
2015-11-27 10:10:00 +02:00
return field.data()
end
2016-11-13 13:24:08 +02:00
function (field::CCTI)(time::Float64)
2015-11-27 10:10:00 +02:00
return field.data()
end
2015-11-01 18:47:33 +02:00
2016-02-10 22:21:30 +02:00
""" Interpolate constant time-variant field in time direction. """
2016-11-13 13:24:08 +02:00
function (field::DCTV)(time::Real)
2016-02-10 22:21:30 +02:00
time < first(field).time && return DCTI(first(field).data)
time > last(field).time && return DCTI(last(field).data)
2015-11-27 10:10:00 +02:00
for i=reverse(1:length(field))
2016-02-10 22:21:30 +02:00
isapprox(field[i].time, time) && return DCTI(field[i].data)
end
for i=reverse(2:length(field))
t0 = field[i-1].time
t1 = field[i].time
if t0 < time < t1
2016-02-24 01:20:39 +02:00
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
2016-02-10 22:21:30 +02:00
return DCTI(new_data)
2015-11-27 10:10:00 +02:00
end
end
2016-02-10 22:21:30 +02:00
error("interpolate DCTV: unknown failure when interpolating $(field.data) for time $time")
2015-11-27 10:10:00 +02:00
end
2015-11-01 18:47:33 +02:00
2016-11-13 13:24:08 +02:00
function (field::DVTV)(time::Float64)
2016-02-10 22:21:30 +02:00
time < first(field).time && return DVTI(first(field).data)
time > last(field).time && return DVTI(last(field).data)
2015-11-27 10:10:00 +02:00
for i=reverse(1:length(field))
2016-02-10 22:21:30 +02:00
isapprox(field[i].time, time) && return DVTI(field[i].data)
end
for i=reverse(2:length(field))
t0 = field[i-1].time
t1 = field[i].time
if t0 < time < t1
2016-02-24 01:20:39 +02:00
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
2016-02-10 22:21:30 +02:00
return DVTI(new_data)
2015-11-27 10:10:00 +02:00
end
end
2016-02-10 22:21:30 +02:00
error("interpolate DVTV: unknown failure when interpolating $(field.data) for time $time")
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
""" Interpolate constant field in spatial dimension. """
2016-11-13 13:24:08 +02:00
function (basis::CVTI)(field::DCTI, xi::Vector)
2015-11-27 10:10:00 +02:00
return field.data
2015-11-01 18:47:33 +02:00
end
2015-11-27 10:10:00 +02:00
""" Interpolate variable field in spatial dimension. """
2016-11-13 13:24:08 +02:00
function (basis::CVTI)(values::DVTI, xi::Vector)
2015-11-27 10:10:00 +02:00
N = basis(xi)
return sum([N[i]*values[i] for i=1:length(N)])
end
2015-11-01 18:47:33 +02:00
2016-11-13 13:24:08 +02:00
function (basis::CVTI)(geometry::DVTI, xi::Vector, ::Type{Val{:grad}})
2015-11-27 10:10:00 +02:00
dbasis = basis(xi, Val{:grad})
2015-12-04 07:27:40 +02:00
# J = sum([dbasis[:,i]*geometry[i]' for i=1:length(geometry)])
J = sum([kron(dbasis[:,i], geometry[i]') for i=1:length(geometry)])
2015-11-27 10:10:00 +02:00
invJ = isa(J, Vector) ? inv(J[1]) : inv(J)
grad = invJ * dbasis
return grad
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function (basis::CVTI)(geometry::DVTI, values::DVTI, xi::Vector, ::Type{Val{:grad}})
grad = basis(geometry, xi, Val{:grad})
2015-12-04 07:27:40 +02:00
# gradf = sum([grad[:,i]*values[i]' for i=1:length(geometry)])'
gradf = sum([kron(grad[:,i], values[i]') for i=1:length(values)])'
2015-11-27 10:10:00 +02:00
return length(gradf) == 1 ? gradf[1] : gradf
2015-11-01 18:47:33 +02:00
end
2016-11-13 13:24:08 +02:00
function (basis::CVTI)(xi::Vector, time::Number)
basis(xi)
2015-11-11 00:52:16 +02:00
end
2016-11-13 13:24:08 +02:00
function Base.:*(grad::Matrix, field::DVTI)
n, m = size(grad)
return sum([kron(grad[:,i], field[i]') for i=1:m])'
2015-12-04 07:27:40 +02:00
end
function DVTV(data::Pair{Float64, Vector}...)
return DVTV([Increment(d[1], d[2]) for d in data])
end
function start(f::DVTV)
return start(f.data)
end
function next(f::DVTV, state)
return next(f.data, state)
end
function done(f::DVTV, state)
return done(f.data, state)
end
""" Return time vector from time variable field. """
function keys(field::DVTV)
return Float64[increment.time for increment in field]
end
2016-08-04 13:15:19 +03:00
function setindex!(field::Field, val, idx::Int64)
field.data[idx] = val
end