Files
JuliaFEM.jl/src/fields.jl
T

423 lines
9.5 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
2017-07-20 20:46:57 +03:00
abstract type AbstractField end
2015-11-20 08:48:15 +02:00
2017-07-20 20:46:57 +03:00
abstract type Discrete<:AbstractField end
abstract type Continuous<:AbstractField end
abstract type Constant<:AbstractField end
abstract type Variable<:AbstractField end
abstract type TimeVariant<:AbstractField end
abstract type TimeInvariant<:AbstractField end
2015-11-01 18:47:33 +02: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
2017-07-20 20:46:57 +03:00
const FieldSet = Dict{String,Field}
2015-11-01 18:47:33 +02:00
2015-11-27 10:10:00 +02:00
### Different field combinations and other typealiases
2015-11-01 18:47:33 +02:00
2017-07-20 20:46:57 +03:00
const DCTI = Field{Discrete,Constant,TimeInvariant}
const DVTI = Field{Discrete,Variable,TimeInvariant}
const DCTV = Field{Discrete,Constant,TimeVariant}
const DVTV = Field{Discrete,Variable,TimeVariant}
const CCTI = Field{Continuous,Constant,TimeInvariant}
const CVTI = Field{Continuous,Variable,TimeInvariant} # can be used to interpolate in spatial dimension
const CCTV = Field{Continuous,Constant,TimeVariant} # can be used to interpolate in time
const CVTV = Field{Continuous,Variable,TimeVariant}
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
# Discrete fields
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
""" Discrete, constant, time-invariant field. This is constant in both spatial
direction and time direction, i.e. df/dX = 0 and df/dt = 0.
2017-01-30 12:28:33 +02:00
This is the most basic type of field having no anything special functionality.
Examples
--------
julia> f = DCTI()
julia> update!(f, 1.0)
Multiplying by constant works:
julia> 2*f
2.0
Interpolation in time direction gives the same constant:
julia> f(1.0)
1.0
2017-01-30 12:28:33 +02:00
By default, when calling Field with scalar, DCTI is assumed, i.e.
julia> Field(0.0) == DCTI(0.0)
true
"""
function DCTI()
return DCTI(nothing)
end
function Field()
return DCTI()
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
2017-01-30 12:28:33 +02:00
function ==(x::DCTI, y::DCTI)
return ==(x.data, y.data)
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
function ==(x::DCTI, y)
return ==(x.data, y)
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
function isapprox(x::DCTI, y::DCTI)
isapprox(x.data, y.data)
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
function isapprox(x::DCTI, y)
isapprox(x.data, y)
2016-08-04 13:15:19 +03:00
end
2017-01-30 12:28:33 +02:00
function length(f::DCTI)
return 1
2016-08-04 13:15:19 +03:00
end
function *(c::Number, f::DCTI)
2017-01-30 12:28:33 +02:00
return c*f.data
2016-08-04 13:15:19 +03:00
end
2017-01-30 12:28:33 +02:00
""" Kind of spatial interpolation of DCTI. """
function *(N::Matrix, f::DCTI)
2017-01-30 12:28:33 +02:00
@assert length(N) == 1
return N[1]*f.data
end
function update!(field::DCTI, data)
field.data = data
end
""" Interpolate time-invariant field in time direction. """
function (field::DCTI)(time::Float64)
return field.data
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
""" Discrete, variable, time-invariant field. This is constant in time direction,
but not in spatial direction, i.e. df/dt = 0 but df/dX != 0. The basic structure
of data is Vector, and it is implicitly assumed that length of field matches to
the number of shape functions, so that interpolation in spatial direction works.
2016-02-10 22:21:30 +02:00
Examples
--------
"""
2017-01-30 12:28:33 +02:00
function DVTI()
return DVTI([])
2016-02-10 22:21:30 +02:00
end
2017-01-30 12:28:33 +02:00
""" For vector data, DVTI is automatically created.
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
julia> DVTI([1.0, 2.0]) == Field([1.0, 2.0])
true
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
"""
function Field(data::Vector)
return DVTI(data)
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
""" For dictionary data, DVTI is automatically created.
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
Define e.g. nodal coordinates in dictionary
julia> X = Dict(1 => [1.0, 2.0], 2 => [3.0, 4.0])
julia> Field(X) == DVTI(X)
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
"""
function Field(data::Dict)
return DVTI(data)
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
function ==(x::DVTI, y::DVTI)
return ==(x.data, y.data)
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
function isapprox(x::DVTI, y)
return isapprox(x.data, y)
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
""" Default slicing of field.
2017-01-30 12:28:33 +02:00
julia> f = DVTI([1.0, 2.0])
julia> f[1]
1.0
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02: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
2017-01-30 12:28:33 +02:00
""" Multi-slicing of field.
2017-01-30 12:28:33 +02:00
julia> f = DVTI([1.0, 2.0, 3.0])
julia> f[[1, 3]]
[1.0, 3.0]
2017-01-30 12:28:33 +02:00
"""
function getindex(field::DVTI, I::Array{Int64, 1})
return [field.data[i] for i in I]
end
2017-01-30 12:28:33 +02:00
function length(field::DVTI)
2015-11-27 10:10:00 +02:00
return length(field.data)
end
2017-01-30 12:28:33 +02:00
function start(field::DVTI)
return 1
end
function +(f1::DVTI, f2::DVTI)
return DVTI(f1.data + f2.data)
end
function -(f1::DVTI, f2::DVTI)
2016-03-01 05:52:42 +02:00
return DVTI(f1.data - f2.data)
end
2017-01-30 12:28:33 +02:00
function update!(field::DVTI, data::Union{Vector, Dict})
field.data = data
end
2017-01-30 12:28:33 +02:00
""" Take scalar product of DVTI and constant T. """
function *(T::Number, field::DVTI)
2017-01-30 12:28:33 +02:00
return DVTI(T*field.data)
2016-05-22 00:22:17 +03:00
end
2017-01-30 12:28:33 +02:00
""" Take dot product of DVTI field and vector T. Vector length must match to the
field length and this can be used mainly for interpolation purposes, i.e., u = ∑ Nᵢuᵢ.
"""
2017-07-20 20:46:57 +03:00
function *(T::Union{Vector, RowVector}, f::DVTI)
@assert length(T) <= length(f)
return sum([T[i]*f[i] for i=1:length(T)])
end
2017-01-30 12:28:33 +02:00
""" Take outer product of DVTI field and matrix T. """
function *(T::Matrix, f::DVTI)
2017-01-30 12:28:33 +02:00
n, m = size(T)
return sum([kron(T[:,i], f[i]') for i=1:m])'
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
function vec(field::DVTI)
return [field.data...;]
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
""" Interpolate time-invariant field in time direction. """
function (field::DVTI)(time::Float64)
return field
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
""" Create a similar DVTI field from vector data.
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
julia> f1 = DVTI(Vector[[1.0, 2.0], [3.0, 4.0]])
julia> f2 = similar(f1, [2.0, 3.0, 4.0, 5.0])
julia> f2 == DVTI(Vector[[2.0, 3.0], [4.0, 5.0]])
true
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
"""
function similar(field::DVTI, data::Vector)
n = length(field)
m = length(data)
dim = round(Int, m/n)
@assert dim*n == m
new_data = reshape(data, dim, n)
new_field = DVTI()
new_field.data = [new_data[:,i] for i=1:n]
return new_field
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
2017-01-30 12:28:33 +02:00
""" Simple time frame / increment to contain both time and data. """
type Increment{T}
time :: Float64
data :: T
end
""" Discrete, constant, time variant field. This is constant in spatial
direction but non-constant in time direction, i.e. df/dX = 0 but df/dt != 0.
2016-05-25 22:33:47 +03:00
Examples
--------
2017-01-30 12:28:33 +02:00
julia> t0 = 0.0; t1=1.0; y0 = 0.0; y1 = 1.0
julia> f = DCTV(t0 => y0, t1 => y1)
2016-05-25 22:33:47 +03:00
"""
2017-01-30 12:28:33 +02:00
function DCTV(data::Pair...)
return DCTV([Increment(d[1],d[2]) for d in data])
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +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
2017-01-30 12:28:33 +02:00
function getindex(field::DCTV, i::Int64)
return field.data[i]
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
function length(field::DCTV)
return length(field.data)
2016-06-27 16:11:33 +03:00
end
2017-01-30 12:28:33 +02:00
function first(field::DCTV)
return field[1]
2015-11-27 10:10:00 +02:00
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. """
2017-01-30 12:28:33 +02:00
function (field::DCTV)(time::Number)
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
end
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
function endof(field::DCTV)
return endof(field.data)
end
""" Discrete, variable, time variant fields. """
function DVTV()
return DVTV(Increment[])
end
function DVTV{T<:Union{Vector, Dict}}(data::Pair{Float64, T}...)
return DVTV([Increment{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 length(field::DVTV)
return length(field.data)
end
function getindex(field::DVTV, i::Int64)
return field.data[i]
end
function first(field::DVTV)
return field[1]
end
function endof(field::DVTV)
return endof(field.data)
end
""" Interpolate discrete, variable, time-variant field in time direction. """
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
2015-11-01 18:47:33 +02:00
end
2017-01-30 12:28:33 +02:00
""" Update time-dependent fields with new values.
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
Examples
--------
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
julia> f = Field(0.0 => 1.0)
julia> update!(f, 1.0 => 2.0)
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
Now field has two (time, value) pairs: (0.0, 1.0) and (1.0, 2.0)
2015-11-01 18:47:33 +02:00
2017-01-30 12:28:33 +02:00
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.
2015-11-11 00:52:16 +02:00
2017-01-30 12:28:33 +02:00
"""
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
2015-12-04 07:27:40 +02:00
end
2017-01-30 12:28:33 +02:00
### Basic data structure for continuous field
type Basis
basis :: Function
dbasis :: Function
end
2017-01-30 12:28:33 +02:00
### Convenient functions to create fields
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, Float64})
return CVTV(func)
else
error("no proper definition found for function: check methods.")
end
end
2017-01-30 12:28:33 +02:00
### Accessing continuous fields
function (field::CCTI)(xi::Vector, time::Number)
return field.data()
end
2017-01-30 12:28:33 +02:00
function (field::CVTI)(xi::Vector, time::Number)
return field.data(xi)
end
2017-01-30 12:28:33 +02:00
function (field::CCTV)(xi::Vector, time::Number)
return field.data(time)
end
2016-08-04 13:15:19 +03:00
2017-01-30 12:28:33 +02:00
function (field::CVTV)(xi::Vector, time::Number)
return field.data(xi, time)
2016-08-04 13:15:19 +03:00
end
2017-01-30 12:28:33 +02:00