data structures iteration #3

This commit is contained in:
Jukka Aho
2015-10-30 12:40:56 +02:00
parent 8bfff34e19
commit 42d81bc86d
16 changed files with 1995 additions and 926 deletions
File diff suppressed because it is too large Load Diff
@@ -89,13 +89,13 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 69,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"using JuliaFEM: Element, Field, FieldSet, Basis, Quad4"
"using JuliaFEM: Element, Field, FieldSet, Basis"
]
},
{
@@ -1724,6 +1724,109 @@
"source": [
"In this notebook the basic instructions how to develop JuliaFEM has been given. The most imporant concepts has been considered; how to develop own element with own basis, several ways how to define own equation, and how to finally assemble and calculate the problem using solver. Any comments and/or discussion about technical details, theory, programming, or from life in general is very desirable; our issue log is in address https://github.com/JuliaFEM/JuliaFEM.jl/issues"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Advanced stuff\n",
"\n",
"In last section of this tutorial we consider some of the more advanced things which may araise when developing own models.\n",
"\n",
"### Boundary element access to parent element + overriding equations in problems\n",
"\n",
"This kind of situation might happen when one is almost happy for some problem setting, but would like to change just one or two equations from it. For example boundary equation is not satisfying all the requirements and one would like to test something new. \n",
"\n",
"### Accessing integration points\n",
"\n",
"### Fields as a function of something.\n",
"- statistical variables\n",
"- field dependent from another field\n",
"- field dependent from time or spatial domain etc.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"using JuliaFEM: get_default_integration_points\n",
"\"\"\" 2-node radiation boundary element. \"\"\"\n",
"type DC2D2RAD <: Heat\n",
" element :: Seg2\n",
" integration_points :: Array{IntegrationPoint, 1}\n",
"end\n",
"function DC2D2RAD(element::Seg2)\n",
" integration_points = [\n",
" IntegrationPoint([0.0], 2.0)]\n",
" if !haskey(element, \"temperature\")\n",
" element[\"temperature\"] = FieldSet()\n",
" push!(element[\"temperature\"], Field([0.0, 0.0]))\n",
" end\n",
" DC2D2RAD(element, integration_points)\n",
"end\n",
"Base.size(equation::DC2D2RAD) = (1, 2)\n",
"\n",
"\"\"\" Calculate potential energy caused by radiation.\n",
"https://en.wikipedia.org/wiki/Stefan%E2%80%93Boltzmann_constant\n",
"\"\"\"\n",
"function JuliaFEM.get_potential_energy(equation::DC2D2RAD, ip, time; variation=nothing)\n",
" element = get_element(equation)\n",
" basis = get_basis(element)\n",
" eps = basis(\"emissivity\", ip, time)\n",
" #sig = basis(\"stefan-boltzmann constant\", ip, time)\n",
" sig = 5.670367e-8 # i guess stefan-boltzmann constant is constant ;)\n",
" T = basis(\"temperature\", ip, time, variation)\n",
" T_ext = basis(\"temperature external\", ip, time)\n",
" q = eps*sig*((T_ext+273.15)^4 - (T+273.15)^4)\n",
" println(ForwardDiff.value(q*T))\n",
" return q\n",
"end\n",
"JuliaFEM.has_potential_energy(equation::DC2D2RAD) = true\n",
"\n",
"Defining new equation mapping to old problem is one line command. Here we replace `DC2D2` $\\rightarrow$ `DC2DCRAD`\n",
"\n",
"function run_radiation_model()\n",
" # this is the same as before\n",
" element = Quad4([1, 2, 3, 4])\n",
" fieldset1 = FieldSet(\"geometry\", [Field(Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])])\n",
" fieldset2 = FieldSet(\"temperature thermal conductivity\", [Field(6.0)])\n",
" fieldset3 = FieldSet(\"temperature load\", [Field([12.0, 12.0, 12.0, 12.0])])\n",
" fieldset4 = FieldSet(\"density\", [Field(36.0)])\n",
" push!(element, fieldset1)\n",
" push!(element, fieldset2)\n",
" push!(element, fieldset3)\n",
" push!(element, fieldset4)\n",
"\n",
" # create boundary element\n",
" boundary_element = Seg2([1, 2])\n",
" push!(boundary_element, FieldSet(\"geometry\", [Field(Vector[[0.0, 0.0], [1.0, 0.0]])]))\n",
" push!(boundary_element, FieldSet(\"emissivity\", [Field(0.5)]))\n",
" push!(boundary_element, FieldSet(\"temperature external\", [Field(20.0)]))\n",
"\n",
" # set initial conditions\n",
" push!(element, FieldSet(\"temperature\", [Field([0.0, 0.0, 0.0, 0.0])]))\n",
" push!(boundary_element, FieldSet(\"temperature\", [Field([0.0, 1.0])]))\n",
" \n",
" # create problem, change element mapping\n",
" problem = PlaneHeatProblem()\n",
" problem[Seg2] = DC2D2RAD # Seg2 was previous DC2D2\n",
" push!(problem, element)\n",
" push!(problem, boundary_element)\n",
"\n",
" # run our \"unit test solver\"\n",
" free_dofs = [3, 4]\n",
" solve!(problem, free_dofs; max_iterations=4, dump_matrices=true)\n",
" basis = get_basis(boundary_element)\n",
" T = basis(\"temperature\", [0.0])\n",
" println(\"Temperature at the midpoint of element: $T\")\n",
"end\n",
"\n",
"run_radiation_model()"
]
}
],
"metadata": {
File diff suppressed because one or more lines are too long
+9 -5
View File
@@ -6,10 +6,14 @@ This is JuliaFEM -- Finite Element Package
"""
module JuliaFEM
using Logging
@Logging.configure(level=DEBUG)
#using Logging
#@Logging.configure(level=DEBUG)
#using Lexicon
macro debug(msg)
return :( println("DEBUG: ", $msg) )
end
using Lexicon
using ForwardDiff
autodiffcache = ForwardDiffCache()
@@ -24,12 +28,12 @@ Examples
[1.0]
"""
function Base.linspace(X1, X2, n)
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("types.jl") # type definitions
include("interpolate.jl") # interpolation routines
#include("interpolate.jl") # interpolation routines
### ELEMENTS ###
include("elements.jl")
+2 -2
View File
@@ -49,8 +49,8 @@ function calculate_global_assembly!(assembly::GlobalAssembly, problem::Problem,
unknown_field_name = get_unknown_field_name(problem)
initialize_global_assembly!(assembly, problem) # zero all
dim, ndofs = size(problem)
Logging.info("assembling problem for $unknown_field_name")
Logging.info("dimension of unknown field: $dim, problem dofs: $ndofs")
info("assembling problem for $unknown_field_name")
info("dimension of unknown field: $dim, problem dofs: $ndofs")
local_assembly = initialize_local_assembly()
for (i, equation) in enumerate(get_equations(problem))
calculate_local_assembly!(local_assembly, equation, unknown_field_name, time)
+1 -2
View File
@@ -121,8 +121,7 @@ end
function CPS4(element::Quad4)
integration_points = get_default_integration_points(element)
if !haskey(element, "displacement")
element["displacement"] = FieldSet()
push!(element["displacement"], Field(Vector[[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]))
element["displacement"] = zeros(2, 4)
end
CPS4(element, integration_points)
end
+7 -47
View File
@@ -69,56 +69,13 @@ end
"""Add new FieldSet to element.
Examples
--------
>>> field = Field(0.0, [1, 2, 3, 4])
>>> fieldset = FieldSet("geometry", Field[field])
>>> element["geometry"] = fieldset
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, fieldset::FieldSet, fieldset_name)
fieldset.name = fieldset_name
element.fields[fieldset.name] = fieldset
end
"""Add new FieldSet to element.
Examples
--------
>>> field = Field(0.0, [1, 2, 3, 4])
>>> element["geometry"] = field
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::Field, fieldset_name)
element[fieldset_name] = FieldSet(field)
end
"""Add new FieldSet to element.
Examples
--------
>>> element["geometry"] = [1, 2, 3, 4]
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::Union{Number, Array}, fieldset_name)
element[fieldset_name] = Field(field_data)
end
"""Add new FieldSet to element.
Notes
-----
This last version takes tuple and each cell in tuple is converted to new field.
Time in field is 0.0, 1.0, ..., n
Examples
--------
>>> element["load"] = (1, 2)
JuliaFEM.Quad4([1,2,3,4],JuliaFEM.Basis(basis,dbasisdxi),Dict("load"=>JuliaFEM.FieldSet("load",JuliaFEM.Field[JuliaFEM.Field{Int64}(0.0,0,1),JuliaFEM.Field{Int64}(1.0,0,2)])))
"""
function Base.setindex!(element::Element, field_data::Tuple, fieldset_name)
fields = Field[Field(Float64(i-1), field) for (i,field) in enumerate(field_data)]
element.fields[fieldset_name] = FieldSet(fieldset_name, fields)
function Base.setindex!(element::Element, field_data, field_name)
element.fields[field_name] = field_data
end
function get_connectivity(el::Element)
@@ -156,10 +113,13 @@ end
function call(u::FunctionSpace, field_name, xi::Vector, t::Number=Inf, variation=nothing)
f = !isa(variation, Void) ? variation : u.element[field_name](t)
if length(f) == 1
return f.values
return f.data[1]
end
h = u.element.basis.basis(xi)
return dot(vec(h), f)
#@debug("vec(h) = $(vec(h)), size(h) = $(size(vec(h)))")
#@debug("f = $f, size(f) = $(size(f))")
#return dot(vec(h), f)
return sum(vec(h).*f)
end
""" If basis is called without a field, return basis functions evaluated at that point. """
+4 -1
View File
@@ -182,7 +182,10 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
field = element[unknown_field_name](time)
function residual_vector(data::Vector)
fill!(assembly.residual_vector, 0.0)
df = similar(field, data)
#@debug("field: $field, length = $(size(field))")
#@debug("data: $data, size = $(size(data))")
#df = similar(field, data)
df = Increment(reshape(data, size(equation)...))
# integrate W
for ip in get_integration_points(equation)
dr = get_residual_vector(equation, ip, time; variation=df)
+4 -4
View File
@@ -46,8 +46,8 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::HeatEquati
w = ip.weight * detJ(ip)
N = basis(ip, time)
if haskey(element, "density")
ρ = basis("density", ip, time)
assembly.mass_matrix += w * ρ*N'*N
rho = basis("density", ip, time)
assembly.mass_matrix += w * rho*N'*N
end
if haskey(element, "temperature thermal conductivity")
dN = dbasis(ip, time)
@@ -92,7 +92,7 @@ end
function DC2D4(element::Quad4)
integration_points = get_default_integration_points(element)
if !haskey(element, "temperature")
element["temperature"] = FieldSet()
element["temperature"] = zeros(4)
end
DC2D4(element, integration_points)
end
@@ -106,7 +106,7 @@ end
function DC2D2(element::Seg2)
integration_points = get_default_integration_points(element)
if !haskey(element, "temperature")
element["temperature"] = FieldSet()
element["temperature"] = zeros(2)
end
DC2D2(element, integration_points)
end
+4 -3
View File
@@ -37,13 +37,14 @@ macro create_lagrange_element(element_name, element_description, X, P)
type $eltype <: CG
connectivity :: Array{Int, 1}
basis :: Basis
fields :: Dict{ASCIIString, FieldSet}
fields :: FieldSet
end
function $eltype(connectivity, args...)
$eltype(connectivity, Basis(basis, dbasisdxi), Dict())
$eltype(connectivity, Basis(basis, dbasisdxi), FieldSet())
end
get_element_description(el::Type{$eltype}) = $element_description
Base.size(el::Type{$eltype}) = Base.size($X)
Base.size(element::Type{$eltype}) = Base.size($X)
Base.size(element::$eltype) = Base.size($X)
end
end
+1 -3
View File
@@ -67,9 +67,7 @@ function solve!(problem::Problem, free_dofs::Array{Int, 1}, time::Number=Inf;
gdofs = vec(vcat([dim*conn'-i for i=dim-1:-1:0]...))
old_field = element[field_name](Inf)
new_field = similar(old_field, full(x[gdofs]))
new_field.time = time
new_field.increment = i
push!(element[field_name], new_field)
push!(element[field_name][end], new_field)
end
if norm(dx) < tolerance
return
+233 -125
View File
@@ -3,150 +3,272 @@
# https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/notebooks/2015-06-14-data-structures.ipynb
using ForwardDiff
#abstract AbstractField{T,N} <: AbstractArray{T,N}
""" Field is a fundamental data type which holds some values in some time t """
type Field{T}
time :: Number
increment :: Int64
values :: T
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
""" Initialize field. """
function Field(time, values)
Field(time, 0, values)
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 Field(values)
Field(0.0, 0, values)
function Base.convert{T}(::Type{Increment}, data::Array{T, 2})
Increment([data[:,i] for i=1:size(data, 2)])
end
""" Get length of a field (number of basis functions in practice). """
function Base.length(f::Field)
length(f.values)
function Base.convert{T}(::Type{Increment}, data::Array{T, 3})
Increment([data[:,:,i] for i=1:size(data, 3)])
end
""" Push value to field. """
function Base.push!(f::Field, value)
push!(f.values, value)
function Base.convert{T}(::Type{Increment}, data::Array{T, 4})
Increment([data[:,:,:,i] for i=1:size(data, 4)])
end
""" Get field discrete value at point i. """
function Base.getindex(f::Field, i::Int64)
f.values[i]
function Base.convert{T}(::Type{Increment}, data::Array{T, 5})
Increment([data[:,:,:,:,i] for i=1:size(data, 5)])
end
""" Multiply field with some constant k. """
function Base.(:*)(k::Number, f::Field)
Field(f.time, k*f.values)
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
""" Inner product of field and vector x. """
function Base.dot(x::Vector, f::Field)
@assert length(x) == length(f)
sum([f[i]*x[i] for i in 1:length(f)])
# 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.size(field::Field)
(length(field.values[1]), length(field.values))
function Base.push!(timestep::TimeStep, increment::Increment)
push!(timestep.increments, increment)
end
#""" Multiply field with some matrix x. """
# function Base.(:*){T}(x::Matrix, f::Field{Vector{T}})
#function Base.(:*)(x::Matrix, f::Field)
# sum([f[i]*x[:,i]' for i in 1:length(f)])
# 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
""" Sum two fields. """
function Base.(:+)(f1::Field, f2::Field)
@assert(f1.time == f2.time, "Cannot add fields: time mismatch, $(f1.time) != $(f2.time)")
Field(f1.time, f1.values + f2.values)
end
### FIELDSET ###
""" Return data from field as a long array.
typealias FieldSet Dict{ASCIIString, AbstractField}
"""Quicky add discrete field to fieldset.
Examples
--------
>>> f = Field(0.0, Vector[[1.0, 2.0], [3.0, 4.0]])
>>> f[:]
[1.0, 2.0, 3.0, 4.0]
>>> fs = FieldSet()
>>> fs["myfield"] = [1, 2, 3, 4]
"""
function Base.getindex(field::Field, c::Colon)
[field.values...;]
end
function Base.vec(field::Field)
[field.values...;]
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
""" Return field similar to input but with new data in it.
""" Quicky add several time steps at once in tuple.
Examples
--------
>>> f = Field(0.5, Vector[[1.0, 2.0], [3.0, 4.0]])
>>> similar(f, ones(4))
JuliaFEM.Field{Array{Array{T,1},1}}(0.5,1,Array{T,1}[[1.0,1.0],[1.0,1.0]])
>>> 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.similar(field::Field, data::Vector)
fdim = round(Int, length(data)/length(field)) # dimension of field variable
if fdim == 1
new_field = Field(field.time, data)
return new_field
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
new_field = Field(field.time, similar(field.values))
data = reshape(data, fdim, length(field))
for i=1:length(new_field)
new_field.values[i] = data[:,i]
end
return new_field
return DefaultDiscreteField(timesteps)
end
### BASIS ###
abstract AbstractBasis
""" FieldSet is set of fields, each field can have different time and/or increment. """
type FieldSet
name :: ASCIIString
fields :: Array{Field, 1}
end
""" Initializer for FieldSet. """
function FieldSet(field_name::ASCIIString)
FieldSet(field_name, [])
end
function FieldSet()
FieldSet("unknown field", [])
end
function FieldSet(fields::Array{Field, 1})
FieldSet("unknown field", fields)
end
""" Add new field to fieldset. """
function Base.push!(fs::FieldSet, field::Field)
push!(fs.fields, field)
end
""" Multiply fieldset with some vector x. """
Base.(:*)(x::Array{Float64, 1}, fs::FieldSet) = sum(x .* fs.fields)
""" Get length of a fieldset. """
function Base.length(fieldset::FieldSet)
length(fieldset.fields)
end
""" Return ith field from fieldset. """
function Base.getindex(fieldset::FieldSet, i::Int64)
fieldset.fields[i]
end
#""" Return last field from fieldset. """
function Base.endof(fieldset::FieldSet)
length(fieldset)
end
function Base.convert(fieldset::Type{FieldSet}, field::Field)
FieldSet(Field[field])
end
""" Basis function. """
type Basis
""" Defined to dimensionless coordinate ξ∈[-1,1]^n. """
type SpatialBasis <: AbstractBasis
basis :: Function
dbasisdxi :: Function
end
#""" Constructor of basis function. """
#function Basis(basis)
# Basis(basis, ForwardDiff.jacobian(basis))
#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 ###
"""
Integration point
@@ -160,7 +282,7 @@ attributes :: Dict{Any, Any}
material models.
"""
type IntegrationPoint
xi :: Array{Float64, 1}
xi :: Vector
weight :: Float64
fields :: Dict{ASCIIString, FieldSet}
end
@@ -168,20 +290,6 @@ function IntegrationPoint(xi, weight)
IntegrationPoint(xi, weight, Dict())
end
call(b::SpatialBasis, ip::IntegrationPoint) = b.basis(ip.xi)
# convenient functions -- maybe this is not correct place for them
""" Evaluate basis function in point ξ. """
call(b::Basis, xi::Vector) = b.basis(xi)
call(b::Basis, ip::IntegrationPoint) = b.basis(ip.xi)
Base.(:*)(basis::Basis, fs::FieldSet) = (xi, t) -> basis(xi)*fs(t)
#""" Interpolate field (h*f)(ξ) """
#Base.(:*)(f::Function, fld::Field) = (x) -> f(x)*fld
#""" Interpolate from set of fields with basis b, i.e. f(t) = b(t)*[f1, f2] """
#Base.(:*)(f::Function, fld::Field) = (x) -> f(x)*fld
#""" Interpolate field f using basis b. """
#Base.(:*)(b::Basis, f::Field) = (x) -> b(x)*f
#Base.(:*)(b::Basis, f::Array{Field}) = (t) -> b(t)*f
+1 -1
View File
@@ -155,7 +155,7 @@ function xdmf_new_field(grid, name, source, data)
typ = string(typeof(data))
datatype = "unknown"
@debug("typeof: ", typ)
@debug("typeof: $typ")
for j in ["Int", "Float"]
@debug(j)
if contains(typ, j)
+10 -9
View File
@@ -1,26 +1,27 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# unit tests for heat equations
using FactCheck
using Base.Test
using JuliaFEM: Quad4, Field, FieldSet, CPS4, get_basis, solve!, PlaneStressElasticityProblem
facts("test plane elasticity on single element, volume load") do
function run()
element = Quad4([1, 2, 3, 4])
element["geometry"] = FieldSet(Field(Vector[[0.0, 0.0], [10.0, 0.0], [10.0, 1.0], [0.0, 1.0]]))
element["youngs modulus"] = FieldSet(Field(500.0))
element["poissons ratio"] = FieldSet(Field(0.3))
element["displacement load"] = FieldSet(Field(0.0, Vector[[0.0, -10.0], [0.0, -10.0], [0.0, -10.0], [0.0, -10.0]]))
element["geometry"] = Vector[[0.0, 0.0], [10.0, 0.0], [10.0, 1.0], [0.0, 1.0]]
element["youngs modulus"] = 500.0
element["poissons ratio"] = 0.3
element["displacement load"] = Vector[[0.0, -10.0], [0.0, -10.0], [0.0, -10.0], [0.0, -10.0]]
equation = CPS4(element)
free_dofs = [3, 4, 5, 6]
problem = PlaneStressElasticityProblem([equation])
solve!(problem, free_dofs; max_iterations=10)
#solve!(equation, "displacement", free_dofs; max_iterations=10)
disp = get_basis(element)("displacement", [1.0, 1.0])[2]
Logging.info("displacement at tip: $disp")
info("displacement at tip: $disp")
# verified using Code Aster.
@fact disp --> roughly(-8.77303119819776E+00)
@test disp -8.77303119819776
end
run()
+293
View File
@@ -0,0 +1,293 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module TypesTests
using JuliaFEM: Increment, TimeStep, AbstractField, DefaultDiscreteField, FieldSet
using JuliaFEM: TemporalBasis, SpatialBasis, ContinuousField, DiscreteField
using JuliaFEM: Field
using Base.Test
function test_increment()
info("testing Increment")
# testing Increment
I1 = Increment([1, 2, 3])
I2 = Increment([2, 3, 4])
@test dot(I1, I2) == 20
@test dot([1,2,3], I2) == 20
@test dot(I1, [2,3,4]) == 20
@test 1/2*(I1+I2) == [1.5, 2.5, 3.5]
@test I1 + 1 == [2, 3, 4]
@test I1 - 1 == [0, 1, 2]
@test I1*3 == [3, 6, 9]
@test I1+I2 == [3, 5, 7]
f = zeros(Increment, 2, 4)
@test length(f) == 4
g = similar(f, ones(8))
@test typeof(f) == typeof(g)
@test length(f) == length(g)
# promotion of increment
@test typeof(I1+1) == typeof(I1)
@test typeof(I1-1) == typeof(I1)
@test typeof(I1*3) == typeof(I1)
# FIXME
#@test typeof(I1) == typeof(I1+I2)
#@test typeof(I1/2) == typeof(I1)
#@test typeof(1/2*S1) == typeof(I1)
end
test_increment()
function test_timestep()
info("testing TimeStep")
i1 = Increment([1, 2, 3])
i2 = Increment([2, 3, 4])
i3 = Increment([2, 3, 4])
i4 = Increment([3, 4, 5])
t1 = TimeStep(1.0, Increment[i1, i2])
t2 = TimeStep(2.0, Increment[i3, i4])
@test length(t1) == length(t2) == 2
t3 = TimeStep(3.0, i1+1)
end
test_timestep()
function test_watta_fak()
# TODO: this test will fail if Increment is typealiased to Vector
fs = FieldSet()
fs["discrete field"] = [1, 2, 3, 4]
T0 = last(fs["discrete field"])
info("last discrete field: $T0, ", typeof(T0))
T1 = T0 + 1
info("adding 1 to discrete field: $T1, ", typeof(T1))
ts = TimeStep(1.0, T1)
info("creating time step: $ts, ", typeof(ts))
push!(fs["discrete field"], ts)
info("last discrete field = ", last(fs["discrete field"]))
info("fieldset: $fs")
@test last(fs["discrete field"]) == [2, 3, 4, 5]
end
test_watta_fak()
function test_default_discrete_field()
info("testing DefaultDiscreteField")
i1 = Increment([1, 2, 3])
i2 = Increment([2, 3, 4])
i3 = Increment([2, 3, 4])
i4 = Increment([3, 4, 5])
t1 = TimeStep(1.0, Increment[i1, i2])
t2 = TimeStep(2.0, Increment[i3, i4])
timesteps = TimeStep[t1, t2]
f1 = DefaultDiscreteField(timesteps)
@test length(f1) == 2
@test isa(f1, AbstractField) == true
end
test_default_discrete_field()
function test_fieldset()
i1 = Increment([1, 2, 3])
i2 = Increment([2, 3, 4])
i3 = Increment([2, 3, 4])
i4 = Increment([3, 4, 5])
t1 = TimeStep(1.0, Increment[i1, i2])
t2 = TimeStep(2.0, Increment[i3, i4])
timesteps = TimeStep[t1, t2]
f1 = DefaultDiscreteField(timesteps)
info("testing adding discrete field to FieldSet")
fs = FieldSet()
fs["temperature"] = f1
@test length(fs) == 1
info("testing adding discrete fields quickly")
# the easy way
fs2 = FieldSet()
fs2["temperature"] = [1, 2, 3, 4]
@test fs2["temperature"][end][end] == [1, 2, 3, 4]
@test last(fs2["temperature"]) == [1, 2, 3, 4]
fs2 = FieldSet()
fs2["constant scalar field"] = 1
fs2["scalar field"] = [1, 2, 3, 4]
fs2["vector field"] = reshape(collect(1:8), 2, 4)
fs2["second order tensor field"] = reshape(collect(1:3*3*4), 3, 3, 4)
fs2["fourth order tensor field"] = reshape(collect(1:3*3*3*3*4), 3, 3, 3, 3, 4)
timestep = fs2["vector field"][end]
@test timestep.time == 0.0
info("testing adding timesteps")
# add another timestep
fs = FieldSet()
fs["temperature"] = [1, 2, 3, 4]
T0 = last(fs["temperature"]) # last increment of last field
info("last temperature = $T0")
T1 = T0 + 1
@test typeof(T0) == typeof(T1)
timestep = TimeStep(1.0, Increment[T1]) # new list of increments for timestep
push!(fs["temperature"], timestep)
T2 = last(fs["temperature"])
info("last temperature = $T2")
@test last(fs["temperature"]) == [2, 3, 4, 5]
# or more easily
timestep = TimeStep(2.0, T1)
push!(fs["temperature"], timestep)
@test length(fs["temperature"].timesteps) == 3
info("test adding several time steps at once")
fs3 = FieldSet()
fs3["time series 1"] = (0.0, [1, 2, 3, 4]), (0.5, [2, 3, 4, 5]), (1.0, [1, 1, 1, 1])
@test fs3["time series 1"][end].time == 1.0
fs3["time series 2"] = [1, 2, 3, 4], [2, 3, 4, 5], [1, 1, 1, 1]
@test fs3["time series 2"][end].time == 2.0
end
test_fieldset()
type MyFunnyContinuousField <: ContinuousField
basis :: Function
discretefield :: DiscreteField
end
function Base.call(field::MyFunnyContinuousField, xi::Vector, time::Number=1.0)
data = last(field.discretefield) # get the last timestep last increment
info("data = $data, typeof data = $(typeof(data))")
basis = time*field.basis(xi) # evaluate basis at point ξ.
sum([basis[i]*data[i] for i=1:length(data)]) # sum results
end
function test_continuous_field()
info("testing continuous field")
fs = FieldSet()
fs["discrete field"] = [1, 2, 3, 4]
basis(xi) = 1/4*[
(1-xi[1])*(1-xi[2]),
(1+xi[1])*(1-xi[2]),
(1+xi[1])*(1+xi[2]),
(1-xi[1])*(1+xi[2])]
fs["continuous field"] = MyFunnyContinuousField(basis, fs["discrete field"])
@test fs["continuous field"]([0.0, 0.0], 1.0) == 1/4*(1+2+3+4)
T0 = last(fs["discrete field"])
T1 = T0 + 1.0
ts = TimeStep(1.0, T1)
push!(fs["discrete field"], TimeStep(1.0, T0+1.0))
@test fs["continuous field"]([0.0, 0.0], 1.0) == 1/4*(2+3+4+5)
end
test_continuous_field()
type MyFunnyDiscreteField <: DiscreteField
discrete_points :: Vector
continuousfield :: ContinuousField
end
Base.length(field::MyFunnyDiscreteField) = length(field.discrete_points)
Base.endof(field::MyFunnyDiscreteField) = endof(field.discrete_points)
Base.last(field::MyFunnyDiscreteField) = Float64[field[i] for i=1:length(field)]
function Base.getindex(field::MyFunnyDiscreteField, idx::Int64)
field.continuousfield(field.discrete_points[idx])
end
function test_discrete_field()
info("testing discrete field")
fs = FieldSet()
fs["discrete field"] = [1, 2, 3, 4]
basis(xi) = 1/4*[
(1-xi[1])*(1-xi[2]),
(1+xi[1])*(1-xi[2]),
(1+xi[1])*(1+xi[2]),
(1-xi[1])*(1+xi[2])]
fs["continuous field"] = MyFunnyContinuousField(basis, fs["discrete field"])
discrete_points = 1.0/sqrt(3.0)*Vector[[-1, -1], [1, -1], [1, 1], [-1, 1]]
fs["discrete field 2"] = MyFunnyDiscreteField(discrete_points, fs["continuous field"])
@test last(fs["discrete field 2"]) [
1.7559830641437073,
2.0893163974770410,
2.9106836025229590,
3.2440169358562922]
end
test_discrete_field()
function test_interpolation_in_temporal_basis()
info("testing interpolation on temporal basis")
temporalbasis = TemporalBasis((t) -> [1-t, t], (t) -> [-1, 1])
@test temporalbasis(0.2) == [0.8, 0.2]
i1 = Increment([0.0])
i2 = Increment([1.0])
i3 = Increment([2.0])
t1 = TimeStep(0.0, Increment[i1])
t2 = TimeStep(2.0, Increment[i2])
t3 = TimeStep(4.0, Increment[i3])
field = Field(TimeStep[t1, t2, t3])
@test call(field, temporalbasis, -Inf) == [0.0]
@test call(field, temporalbasis, 0.0) == [0.0]
@test call(field, temporalbasis, 1.0) == [0.5]
@test call(field, temporalbasis, 2.0) == [1.0]
@test call(field, temporalbasis, 3.0) == [1.5]
@test call(field, temporalbasis, 4.0) == [2.0]
@test call(field, temporalbasis, +Inf) == [2.0]
@test call(field, temporalbasis, +Inf, Val{:derivative}) == [0.5]
@test call(field, temporalbasis, -Inf, Val{:derivative}) == [0.5]
@test call(field, temporalbasis, 0.0, Val{:derivative}) == [0.5]
@test call(field, temporalbasis, 0.5, Val{:derivative}) == [0.5]
@test call(field, temporalbasis, 1.0, Val{:derivative}) == [0.5]
@test call(field, temporalbasis, 1.5, Val{:derivative}) == [0.5]
@test call(field, temporalbasis, 2.0, Val{:derivative}) == [0.5]
fs = FieldSet()
t = collect(linspace(0, 2, 5))
x = 1/2*t.^2
x2 = tuple(collect(zip(t, x))...)
# => ((0.0,0.0),(0.5,0.125),(1.0,0.5),(1.5,1.125),(2.0,2.0))
fs["particle"] = x2
position = call(fs["particle"], temporalbasis, 1.0)[1]
@test position 0.50
velocity = call(fs["particle"], temporalbasis, 2.0, Val{:derivative})[1]
@test velocity (2.0-1.125)/0.5 # = 1.75
velocity = call(fs["particle"], temporalbasis, 1.0, Val{:derivative})[1]
v1 = (0.500 - 0.125)/0.5
v2 = (1.125 - 0.500)/0.5
info("v1 = $v1, v2 = $v2")
info(mean([v1, v2]))
@test velocity mean([v1, v2]) # = 1.00
# FIXME, returns wrong type.
#=
@test isa(position, Increment) == true
@test isa(velocity, Increment) == true
=#
end
test_interpolation_in_temporal_basis()
function test_interpolation_in_spatial_basis()
info("testing interpolation on spatial basis")
basis(xi) = 1/4*[
(1-xi[1])*(1-xi[2])
(1+xi[1])*(1-xi[2])
(1+xi[1])*(1+xi[2])
(1-xi[1])*(1+xi[2])]'
dbasis(xi) = 1/4*[
-(1-xi[2]) (1-xi[2]) (1+xi[2]) -(1+xi[2])
-(1-xi[1]) -(1+xi[1]) (1+xi[1]) (1-xi[1])]
spatialbasis = SpatialBasis(basis, dbasis)
@test spatialbasis.basis([0.0, 0.0]) == 1/4*[1 1 1 1]
fs = FieldSet()
fs["geometry"] = Vector{Float64}[[0.0,0.0], [1.0,0.0], [1.0,1.0], [0.0,1.0]]
fs["displacement"] = (0.0, zeros(2, 4)), (1.0, Vector[[0.0, 0.0], [0.0, 0.0], [0.25, 0.0], [0.0, 0.0]])
X = call(last(fs["geometry"]), spatialbasis, [0.0, 0.0])
u = call(last(fs["displacement"]), spatialbasis, [0.0, 0.0])
x = X+u
@test X 1/2*[1, 1]
@test x [9/16, 1/2]
gradu = call(last(fs["displacement"]), spatialbasis, [0.0, 0.0], last(fs["geometry"]), Val{:gradient})
@test gradu [0.125 0.125; 0.0 0.0]
end
test_interpolation_in_spatial_basis()
println("test_fields.jl: all test passing.")
end
+6 -6
View File
@@ -10,16 +10,16 @@ facts("tests on [0x1]x[0x1] domain") do
# volume element
element = Quad4([1, 2, 3, 4])
element["geometry"] = FieldSet(Field(Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]))
element["temperature thermal conductivity"] = FieldSet(Field(0.0, 6.0))
element["temperature load"] = FieldSet(Field(0.0, [12.0, 12.0, 12.0, 12.0]))
element["density"] = FieldSet(Field(0.0, 36.0))
element["geometry"] = Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]
element["temperature thermal conductivity"] = 6.0
element["temperature load"] = [12.0, 12.0, 12.0, 12.0]
element["density"] = 36.0
# boundary element
boundary_element = Seg2([1, 2])
boundary_element["geometry"] = FieldSet(Field(Vector[[0.0, 0.0], [1.0, 0.0]]))
boundary_element["geometry"] = Vector[[0.0, 0.0], [1.0, 0.0]]
# linear ramp from 1 to 6 in time 0 to 1
boundary_element["temperature flux"] = FieldSet(Field[Field(0.0, 0.0), Field(1.0, 6.0)])
boundary_element["temperature flux"] = (0.0, 0.0), (1.0, 6.0)
# Set constant source f=12 with k=6. Accurate solution is
# T=1 on free boundary, u(x,y) = -1/6*(1/2*f*x^2 - f*x)