Testing/code coverage (#83)

Change the code coverage to green. 

* removed duplicate code

* Removed unused code

* removed unmaintained code

* DCTI + DVTI refactored

* discrete fields refactored and tested

* fields are now tested quite well.

* Removed obsolete code not used anywhere

* Element descriptions to common dictionary

* size in global const dictionary also

* Added coverage to sparse tools and removed couple unused functions

* get nonzero rows from SparseMatrixCSC

* bugfix: extending element basis now working and tested

* Removed two unused functions from elements.jl

* removed useless function

* Useless conversion

* remove elasticity assembly using ForwardDiff because it's not used anywhere'

* Added basic testing for NURBS. Fixed bug in NSolid interpolation.

* removed unused functions

* Removed some debug stuff

* renamed file

* removed field assembly posthook, i think not good idea at all

* test for nnz(K) == 0 and automatic determination of dofs

* Testing that solver is throwing error if having problems with boundary assembly

* Removed some unused options. Refactoring.

* Moved solver non-related code to elements.jl

* Removed custom exception (no need)

* unneeded postprocess code

* More tests for NURBS elements.

* Removed unfinished .mail parser

* proper use of Logging package

* also read results

* renamed test file

* create_surface_elements accepts surface name in String now

* bugfix: remove zero rows from constraint matrix after manually removing dofs from some boundary assemblies.

* New test, displacement 3d patch test

* skip displacement field in surface element splitting if not defined

* test element splitting and linear surface elements, fails.

* Bugfix: Xdmf, not XDMF

* removed nonworking tests, requires bugfix

* abaqus_read_results is not working -> bug
This commit is contained in:
Jukka Aho
2017-01-30 12:28:33 +02:00
committed by Tero Frondelius
parent ddabc9d82b
commit c307c1482c
25 changed files with 2061 additions and 1766 deletions
+284 -307
View File
@@ -10,48 +10,11 @@ abstract Variable <: AbstractField
abstract TimeVariant <: AbstractField
abstract TimeInvariant <: AbstractField
type Field{A<:Union{Discrete,Continuous}, B<:Union{Constant,Variable}, C<:Union{TimeVariant,TimeInvariant}}
data
end
typealias FieldSet Dict{AbstractString, Field}
### Basic data structure for discrete field
type Increment{T}
time :: Float64
data :: T
end
function convert{T}(::Type{Increment{T}}, data::Pair{Float64,T})
return Increment{T}(data[1], data[2])
end
function 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)])
end
function getindex{T}(increment::Increment{Vector{T}}, i::Int64)
return increment.data[i]
end
### Basic data structure for continuous field
type Basis
basis :: Function
dbasis :: Function
end
function (basis::Basis)(xi::Vector)
basis.basis(xi)
end
function (basis::Basis)(xi::Vector, ::Type{Val{:grad}})
basis.dbasis(xi)
end
typealias FieldSet Dict{String, Field}
### Different field combinations and other typealiases
@@ -64,156 +27,158 @@ typealias CVTI Field{Continuous, Variable, TimeInvariant} # can be used to inter
typealias CCTV Field{Continuous, Constant, TimeVariant} # can be used to interpolate in time
typealias CVTV Field{Continuous, Variable, TimeVariant}
typealias ScalarIncrement{T} Increment{T}
typealias VectorIncrement{T} Increment{Vector{T}}
typealias TensorIncrement{T} Increment{Matrix{T}}
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}
# Discrete fields
### Convenient functions to create fields
""" Discrete, constant, time-invariant field. This is constant in both spatial
direction and time direction, i.e. df/dX = 0 and df/dt = 0.
#function Base.convert(::Type{Field}, data)
# return Field(data)
#end
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
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
function Field(data)
return DCTI(data)
end
function ==(x::DCTI, y::DCTI)
return ==(x.data, y.data)
end
function ==(x::DCTI, y)
return ==(x.data, y)
end
function isapprox(x::DCTI, y::DCTI)
isapprox(x.data, y.data)
end
function isapprox(x::DCTI, y)
isapprox(x.data, y)
end
function length(f::DCTI)
return 1
end
function Base.:*(c::Number, f::DCTI)
return c*f.data
end
""" Kind of spatial interpolation of DCTI. """
function Base.:*(N::Matrix, f::DCTI)
@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
end
""" 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.
Examples
--------
"""
function DVTI()
return DVTI([])
end
""" For vector data, DVTI is automatically created.
julia> DVTI([1.0, 2.0]) == Field([1.0, 2.0])
true
"""
function Field(data::Vector)
return DVTI(data)
end
function Field{T}(data::Pair{Float64, T}...)
return DCTV([Increment{T}(d[1], d[2]) for d in data])
end
#=
function Field{T}(data::Pair{Float64, Vector{T}}...)
return DVTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
end
""" For dictionary data, DVTI is automatically created.
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
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)
"""
function Field(data::Dict)
return DVTI(data)
end
function convert{T}(::Type{DCTV}, data::Pair{Real, Vector{T}}...)
return DCTV([Increment{Vector{T}}(d[1], d[2]) for d in data])
function ==(x::DVTI, y::DVTI)
return ==(x.data, y.data)
end
""" Create new discrete, constant, time variant field.
function isapprox(x::DVTI, y)
return isapprox(x.data, y)
end
Examples
--------
julia> t0 = 0.0; t1=1.0; y0 = 0.0; y1 = 1.0
julia> f = DCTV(t0 => y0, t1 => y1)
""" Default slicing of field.
julia> f = DVTI([1.0, 2.0])
julia> f[1]
1.0
"""
#function convert{T,v<:Real}(::Type{DCTV}, data::Pair{v, T}...)
# return DCTV([Increment(d[1],d[2]) for d in data])
#end
function DCTV(data::Pair...)
return DCTV([Increment(d[1],d[2]) for d in data])
end
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
end
function CVTI(basis::Function, dbasis::Function)
return CVTI(Basis(basis, dbasis))
end
function Field(basis::Function, dbasis::Function)
return CVTI(basis, dbasis)
end
### Accessing and manipulating discrete fields
function getindex(field::DVTV, i::Int64)
return field.data[i]
end
function push!(field::DCTV, data::Pair)
push!(field.data, data)
end
function push!(field::DVTV, data::Pair)
push!(field.data, data)
end
function getindex(field::DVTI, i::Int64)
return field.data[i]
end
""" Multi-slicing of field.
julia> f = DVTI([1.0, 2.0, 3.0])
julia> f[[1, 3]]
[1.0, 3.0]
"""
function getindex(field::DVTI, I::Array{Int64, 1})
return [field.data[i] for i in I]
end
function getindex(field::DCTV, i::Int64)
return field.data[i]
end
function getindex(field::Field, i::Int64)
return field.data[i]
end
function length(field::DVTI)
return length(field.data)
end
function length(field::DCTI)
function start(field::DVTI)
return 1
end
function length(field::DVTV)
return length(field.data)
end
function length(field::DCTV)
return length(field.data)
end
function first(field::Union{DCTV, DVTV})
return field[1]
end
function isapprox(f1::DCTI, f2::DCTI)
isapprox(f1.data, f2.data)
end
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
function Base.:+(f1::DVTI, f2::DVTI)
return DVTI(f1.data + f2.data)
end
@@ -222,49 +187,55 @@ function Base.:-(f1::DVTI, f2::DVTI)
return DVTI(f1.data - f2.data)
end
function Base.:*{T<:Real}(c::T, field::DVTI)
return DVTI(c*field.data)
function update!(field::DVTI, data::Union{Vector, Dict})
field.data = data
end
function Base.:*(N::Matrix, f::DCTI)
return f.data*N'
""" Take scalar product of DVTI and constant T. """
function Base.:*(T::Number, field::DVTI)
return DVTI(T*field.data)
end
# 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ᵢ
""" 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ᵢ.
"""
function Base.:*(T::Vector, f::DVTI)
@assert length(T) <= length(f)
return sum([T[i]*f[i] for i=1:length(T)])
end
""" Take outer product of DVTI field and matrix T. """
function Base.:*(T::Matrix, f::DVTI)
n, m = size(T)
return sum([kron(T[:,i], f[i]') for i=1:m])'
end
function vec(field::DVTI)
return [field.data...;]
end
function vec(field::DCTV)
error("trying to vectorize $field does not make sense")
""" Interpolate time-invariant field in time direction. """
function (field::DVTI)(time::Float64)
return field
end
function endof(field::Field)
return endof(field.data)
end
""" Create a similar DVTI field from vector data.
#function Base.similar{T}(field::DVTI, data::Vector{T})
# return Increment(reshape(data, round(Int, length(data)/length(increment)), length(increment)))
#end
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
function similar{T}(field::DVTI, data::Vector{T})
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)
end
function start(::DVTI)
return 1
"""
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
end
function next(f::DVTI, state)
@@ -275,6 +246,114 @@ function done(f::DVTI, s)
return s > length(f.data)
end
""" 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.
Examples
--------
julia> t0 = 0.0; t1=1.0; y0 = 0.0; y1 = 1.0
julia> f = DCTV(t0 => y0, t1 => y1)
"""
function DCTV(data::Pair...)
return DCTV([Increment(d[1],d[2]) for d in data])
end
function Field{T}(data::Pair{Float64, T}...)
return DCTV([Increment{T}(d[1], d[2]) for d in data])
end
function getindex(field::DCTV, i::Int64)
return field.data[i]
end
function length(field::DCTV)
return length(field.data)
end
function first(field::DCTV)
return field[1]
end
""" Interpolate constant time-variant field in time direction. """
function (field::DCTV)(time::Number)
time < first(field).time && return DCTI(first(field).data)
time > last(field).time && return DCTI(last(field).data)
for i=reverse(1:length(field))
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
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
return DCTI(new_data)
end
end
end
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. """
function (field::DVTV)(time::Float64)
time < first(field).time && return DVTI(first(field).data)
time > last(field).time && return DVTI(last(field).data)
for i=reverse(1:length(field))
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
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
return DVTI(new_data)
end
end
end
""" Update time-dependent fields with new values.
Examples
@@ -300,146 +379,44 @@ function update!{T}(field::Union{DCTV, DVTV}, val::Pair{Float64, T})
end
end
function update!{T}(field::Union{DCTI, DVTI}, val::T)
field.data = val
### Basic data structure for continuous field
type Basis
basis :: Function
dbasis :: Function
end
### 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
### Accessing continuous fields
function (field::CVTI)(xi::Vector)
function (field::CCTI)(xi::Vector, time::Number)
return field.data()
end
function (field::CVTI)(xi::Vector, time::Number)
return field.data(xi)
end
function (field::CVTV)(xi, time::Float64)
return field.data(xi, time)
end
function (field::CVTI)(xi::Vector, ::Type{Val{:Grad}})
return field.data(xi, Val{:Grad})
end
function (field::CCTV)(time::Float64)
function (field::CCTV)(xi::Vector, time::Number)
return field.data(time)
end
function convert(::Type{Basis}, field::CVTI)
return field.data
function (field::CVTV)(xi::Vector, time::Number)
return field.data(xi, time)
end
### Interpolation
""" Interpolate time-invariant field in time direction. """
function (field::DVTI)(time::Float64)
return field
end
function (field::DCTI)(time::Float64)
return field.data
end
function (field::CVTI)(time::Float64)
return field.data()
end
function (field::CCTI)(time::Float64)
return field.data()
end
""" Interpolate constant time-variant field in time direction. """
function (field::DCTV)(time::Real)
time < first(field).time && return DCTI(first(field).data)
time > last(field).time && return DCTI(last(field).data)
for i=reverse(1:length(field))
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
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
return DCTI(new_data)
end
end
error("interpolate DCTV: unknown failure when interpolating $(field.data) for time $time")
end
function (field::DVTV)(time::Float64)
time < first(field).time && return DVTI(first(field).data)
time > last(field).time && return DVTI(last(field).data)
for i=reverse(1:length(field))
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
y0 = field[i-1].data
y1 = field[i].data
dt = t1-t0
new_data = y0*(1-(time-t0)/dt) + y1*(1-(t1-time)/dt)
return DVTI(new_data)
end
end
error("interpolate DVTV: unknown failure when interpolating $(field.data) for time $time")
end
""" Interpolate constant field in spatial dimension. """
function (basis::CVTI)(field::DCTI, xi::Vector)
return field.data
end
""" Interpolate variable field in spatial dimension. """
function (basis::CVTI)(values::DVTI, xi::Vector)
N = basis(xi)
return sum([N[i]*values[i] for i=1:length(N)])
end
function (basis::CVTI)(geometry::DVTI, xi::Vector, ::Type{Val{:grad}})
dbasis = basis(xi, Val{:grad})
# J = sum([dbasis[:,i]*geometry[i]' for i=1:length(geometry)])
J = sum([kron(dbasis[:,i], geometry[i]') for i=1:length(geometry)])
invJ = isa(J, Vector) ? inv(J[1]) : inv(J)
grad = invJ * dbasis
return grad
end
function (basis::CVTI)(geometry::DVTI, values::DVTI, xi::Vector, ::Type{Val{:grad}})
grad = basis(geometry, xi, Val{:grad})
# gradf = sum([grad[:,i]*values[i]' for i=1:length(geometry)])'
gradf = sum([kron(grad[:,i], values[i]') for i=1:length(values)])'
return length(gradf) == 1 ? gradf[1] : gradf
end
function (basis::CVTI)(xi::Vector, time::Number)
basis(xi)
end
function Base.:*(grad::Matrix, field::DVTI)
n, m = size(grad)
return sum([kron(grad[:,i], field[i]') for i=1:m])'
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
function setindex!(field::Field, val, idx::Int64)
field.data[idx] = val
end