mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-06 04:21:33 +00:00
feat(legacy): restore Dict-based DCTI/DVTI field containers
Bundle FEMBase-style discrete fields (`DCTI`, `DVTI`, time-variant Dict backs) inside `JuliaFEM.Legacy`. - Add constructors, interpolation hooks, and mutation helpers used by old Problems.
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/FEMBase.jl/blob/master/LICENSE
|
||||
|
||||
# Note: AbstractField is defined in fields/api.jl
|
||||
|
||||
function length(f::F) where F<:AbstractField
|
||||
return length(f.data)
|
||||
end
|
||||
|
||||
function size(f::F) where F<:AbstractField
|
||||
return size(f.data)
|
||||
end
|
||||
|
||||
function ==(x::F, y) where F<:AbstractField
|
||||
return ==(x.data, y)
|
||||
end
|
||||
|
||||
function ==(x, y::F) where F<:AbstractField
|
||||
return ==(x, y.data)
|
||||
end
|
||||
|
||||
function ==(x::F, y::F) where F<:AbstractField
|
||||
return ==(x.data, y.data)
|
||||
end
|
||||
|
||||
function getindex(f::F, i::Int) where F<:AbstractField
|
||||
return getindex(f.data, i)
|
||||
end
|
||||
|
||||
function interpolate_field(field::AbstractField, ::Any)
|
||||
return field.data
|
||||
end
|
||||
|
||||
function update_field!(field::AbstractField, data)
|
||||
field.data = data
|
||||
end
|
||||
|
||||
"""
|
||||
DCTI{T} <: AbstractField
|
||||
|
||||
Discrete, constant, time-invariant field.
|
||||
|
||||
This field is constant in both spatial direction and time direction,
|
||||
i.e. df/dX = 0 and df/dt = 0.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> DCTI(1)
|
||||
FEMBase.DCTI{Int64}(1)
|
||||
```
|
||||
"""
|
||||
mutable struct DCTI{T} <: AbstractField
|
||||
data :: T
|
||||
end
|
||||
|
||||
function getindex(field::DCTI, ::Int)
|
||||
return field.data
|
||||
end
|
||||
|
||||
"""
|
||||
DVTI{N,T} <: AbstractField
|
||||
|
||||
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 `Tuple`, and it is implicitly
|
||||
assumed that length of field matches to the number of shape functions, so that
|
||||
interpolation in spatial direction works.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> DVTI(1, 2, 3)
|
||||
FEMBase.DVTI{3,Int64}((1, 2, 3))
|
||||
```
|
||||
"""
|
||||
mutable struct DVTI{N,T} <: AbstractField
|
||||
data :: NTuple{N,T}
|
||||
end
|
||||
|
||||
function DVTI(data...)
|
||||
return DVTI(data)
|
||||
end
|
||||
|
||||
"""
|
||||
DCTV{T} <: AbstractField
|
||||
|
||||
Discrete, constant, time variant field. This type of field can change in time
|
||||
direction but not in spatial direction.
|
||||
|
||||
# Example
|
||||
|
||||
Field having value 5 at time 0.0 and value 10 at time 1.0:
|
||||
|
||||
```jldoctest
|
||||
julia> DCTV(0.0 => 5, 1.0 => 10)
|
||||
FEMBase.DCTV{Int64}(Pair{Float64,Int64}[0.0=>5, 1.0=>10])
|
||||
```
|
||||
|
||||
"""
|
||||
mutable struct DCTV{T} <: AbstractField
|
||||
data :: Vector{Pair{Float64,T}}
|
||||
end
|
||||
|
||||
function DCTV(data::Pair{Float64,T}...) where T
|
||||
return DCTV(collect(data))
|
||||
end
|
||||
|
||||
function update_field!(f::DCTV, data::Pair{Float64, T}) where T
|
||||
if isapprox(last(f.data).first, data.first)
|
||||
f.data[end] = data
|
||||
else
|
||||
push!(f.data, data)
|
||||
end
|
||||
end
|
||||
|
||||
function interpolate_field(field::DCTV, time)
|
||||
time < first(field.data).first && return first(field.data).second
|
||||
time > last(field.data).first && return last(field.data).second
|
||||
for i=reverse(1:length(field))
|
||||
isapprox(field.data[i].first, time) && return field.data[i].second
|
||||
end
|
||||
for i=length(field.data):-1:2
|
||||
t0 = field.data[i-1].first
|
||||
t1 = field.data[i].first
|
||||
if t0 < time < t1
|
||||
y0 = field.data[i-1].second
|
||||
y1 = field.data[i].second
|
||||
dy = y1-y0
|
||||
dt = t1-t0
|
||||
return y0 + (time-t0)*dy/dt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
DVTV{N,T} <: AbstractField
|
||||
|
||||
Discrete, variable, time variant field. The most general discrete field can
|
||||
change in both temporal and spatial direction.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> DVTV(0.0 => (1, 2), 1.0 => (2, 3))
|
||||
FEMBase.DVTV{2,Int64}(Pair{Float64,Tuple{Int64,Int64}}[0.0=>(1, 2), 1.0=>(2, 3)])
|
||||
```
|
||||
"""
|
||||
mutable struct DVTV{N,T} <: AbstractField
|
||||
data :: Vector{Pair{Float64,NTuple{N,T}}}
|
||||
end
|
||||
|
||||
function DVTV(data::Pair{Float64,NTuple{N,T}}...) where {N,T}
|
||||
return DVTV(collect(data))
|
||||
end
|
||||
|
||||
function update_field!(f::DVTV, data::Pair{Float64, NTuple{N,T}}) where {N,T}
|
||||
if isapprox(last(f.data).first, data.first)
|
||||
f.data[end] = data
|
||||
else
|
||||
push!(f.data, data)
|
||||
end
|
||||
end
|
||||
|
||||
function interpolate_field(field::DVTV{N,T}, time) where {N,T}
|
||||
time < first(field.data).first && return first(field.data).second
|
||||
time > last(field.data).first && return last(field.data).second
|
||||
for i=reverse(1:length(field))
|
||||
isapprox(field.data[i].first, time) && return field.data[i].second
|
||||
end
|
||||
for i=length(field.data):-1:2
|
||||
t0 = field.data[i-1].first
|
||||
t1 = field.data[i].first
|
||||
if t0 < time < t1
|
||||
y0 = field.data[i-1].second
|
||||
y1 = field.data[i].second
|
||||
dt = t1-t0
|
||||
return map((a,b) -> a + (time-t0)*(b-a)/dt, y0, y1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
CVTV <: AbstractField
|
||||
|
||||
Continuous, variable, time variant field.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> f = CVTV((xi,t) -> xi*t)
|
||||
FEMBase.CVTV(#1)
|
||||
```
|
||||
"""
|
||||
mutable struct CVTV <: AbstractField
|
||||
data :: Function
|
||||
end
|
||||
|
||||
function (f::CVTV)(xi, time)
|
||||
return f.data(xi, time)
|
||||
end
|
||||
|
||||
"""
|
||||
DVTId(X::Dict)
|
||||
|
||||
Discrete, variable, time invariant dictionary field.
|
||||
"""
|
||||
mutable struct DVTId{T} <: AbstractField
|
||||
data :: Dict{Int, T}
|
||||
end
|
||||
|
||||
function update_field!(field::DVTId{T}, data::Dict{Int, T}) where T
|
||||
merge!(field.data, data)
|
||||
end
|
||||
|
||||
"""
|
||||
DVTVd(time => data::Dict)
|
||||
|
||||
Discrete, variable, time variant dictionary field.
|
||||
"""
|
||||
mutable struct DVTVd{T} <: AbstractField
|
||||
data :: Vector{Pair{Float64,Dict{Int,T}}}
|
||||
end
|
||||
|
||||
function DVTVd(data::Pair{Float64,Dict{Int,T}}...) where T
|
||||
return DVTVd(collect(data))
|
||||
end
|
||||
|
||||
function interpolate_field(field::DVTVd{T}, time) where T
|
||||
time >= last(field.data).first && return last(field.data).second
|
||||
time <= first(field.data).first && return first(field.data).second
|
||||
for i=reverse(1:length(field))
|
||||
isapprox(field.data[i].first, time) && return field.data[i].second
|
||||
end
|
||||
for i=length(field.data):-1:2
|
||||
t0 = field.data[i-1].first
|
||||
t1 = field.data[i].first
|
||||
if t0 < time < t1
|
||||
y0 = field.data[i-1].second
|
||||
y1 = field.data[i].second
|
||||
f = (time-t0)/(t1-t0)
|
||||
new_data = empty(y0)
|
||||
for i in keys(y0)
|
||||
new_data[i] = f*y0[i] + (1-f)*y1[i]
|
||||
end
|
||||
return new_data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function update_field!(f::DVTVd, data::Pair{Float64,Dict{Int,T}}) where T
|
||||
if isapprox(last(f.data).first, data.first)
|
||||
f.data[end] = data
|
||||
else
|
||||
push!(f.data, data)
|
||||
end
|
||||
end
|
||||
|
||||
function new_field(data)
|
||||
return DCTI(data)
|
||||
end
|
||||
|
||||
function new_field(data...)
|
||||
return DVTI(data)
|
||||
end
|
||||
|
||||
function new_field(data::NTuple{N,T}) where {N,T}
|
||||
return DVTI(data)
|
||||
end
|
||||
|
||||
function new_field(data::Pair{Float64,T}...) where T
|
||||
return DCTV(collect(data))
|
||||
end
|
||||
|
||||
function new_field(data::Pair{Float64,NTuple{N,T}}...) where {N,T}
|
||||
return DVTV(collect(data))
|
||||
end
|
||||
|
||||
function new_field(data::Function)
|
||||
return CVTV(data)
|
||||
end
|
||||
|
||||
function new_field(data::Pair{Int, T}...) where T
|
||||
return DVTId(Dict(data))
|
||||
end
|
||||
|
||||
function new_field(data::Pair{Float64, NTuple{N, Pair{Int, T}}}...) where {N,T}
|
||||
return DVTVd(collect(t => Dict(d) for (t, d) in data))
|
||||
end
|
||||
|
||||
function new_field(data::Dict{Int,T}) where T
|
||||
return DVTId(data)
|
||||
end
|
||||
|
||||
function new_field(data::Pair{Float64, Dict{Int, T}}...) where T
|
||||
return DVTVd(collect(data))
|
||||
end
|
||||
|
||||
"""
|
||||
field(x)
|
||||
|
||||
Create new field. Field type is deduced from data type.
|
||||
"""
|
||||
function field(data...)
|
||||
return new_field(data...)
|
||||
end
|
||||
|
||||
"""
|
||||
interpolate(field, time)
|
||||
|
||||
Interpolate field in time direction.
|
||||
|
||||
# Examples
|
||||
|
||||
For time invariant fields [`DCTI`](@ref), [`DVTI`](@ref), [`DVTId`](@ref)
|
||||
solution is trivially the data inside field as fields does not depend from
|
||||
the time:
|
||||
|
||||
```jldoctest
|
||||
julia> a = field(1.0)
|
||||
FEMBase.DCTI{Float64}(1.0)
|
||||
|
||||
julia> interpolate(a, 0.0)
|
||||
1.0
|
||||
```
|
||||
|
||||
```jldoctest
|
||||
julia> a = field((1.0, 2.0))
|
||||
FEMBase.DVTI{2,Float64}((1.0, 2.0))
|
||||
|
||||
julia> interpolate(a, 0.0)
|
||||
(1.0, 2.0)
|
||||
```
|
||||
|
||||
```jldoctest
|
||||
julia> a = field(1=>1.0, 2=>2.0)
|
||||
FEMBase.DVTId{Float64}(Dict(2=>2.0,1=>1.0))
|
||||
|
||||
julia> interpolate(a, 0.0)
|
||||
Dict{Int64,Float64} with 2 entries:
|
||||
2 => 2.0
|
||||
1 => 1.0
|
||||
```
|
||||
|
||||
DVTId trivial solution is returned. For time variant fields DCTV, DVTV, DVTVd
|
||||
linear interpolation is performed.
|
||||
|
||||
# Other notes
|
||||
|
||||
First algorithm checks that is time out of range, i.e. time is smaller than
|
||||
time of first frame or larger than last frame. If that is the case, return
|
||||
first or last frame. Secondly algorithm finds is given time exact match to
|
||||
time of some frame and return that frame. At last, we find correct bin so
|
||||
that t0 < time < t1 and use linear interpolation.
|
||||
|
||||
"""
|
||||
function interpolate(field::AbstractField, time)
|
||||
return interpolate_field(field, time)
|
||||
end
|
||||
|
||||
"""
|
||||
interpolate(a, b)
|
||||
|
||||
A helper function for interpolate routines. Given iterables `a` and `b`,
|
||||
calculate c = aᵢbᵢ. Length of `a` can be less than `b`, but not vice versa.
|
||||
"""
|
||||
function interpolate(a, b)
|
||||
@assert length(a) <= length(b)
|
||||
return sum(a[i]*b[i] for i=1:length(a))
|
||||
end
|
||||
Reference in New Issue
Block a user