updated developers guide + tests

This commit is contained in:
Jukka Aho
2015-11-11 00:52:16 +02:00
parent a6a76fa17e
commit 9b8842f5f3
18 changed files with 1274 additions and 501 deletions
File diff suppressed because one or more lines are too long
+4 -2
View File
@@ -45,12 +45,14 @@ function Base.call(field::DiscreteField, time::Number,
# special cases, only 1 timestep defined or time = -Inf -> return first ts
if (length(field) == 1) || (time == -Inf)
return field[1][end]
#return field[1][end]
return first(field)
end
# special case, time = +Inf -> return last ts
if time == +Inf
return field[end][end]
#return field[end][end]
return last(field)
end
# very likely we are always near some defined timestep, usually field
+1 -1
View File
@@ -35,7 +35,7 @@ function DBC2D2(element::Seg2)
IntegrationPoint([-sqrt(1/3)], 1.0),
IntegrationPoint([+sqrt(1/3)], 1.0)]
if !haskey(element, "reaction force")
element["reaction force"] = FieldSet()
element["reaction force"] = zeros(1, 2)
end
DBC2D2(element, integration_points)
end
+69 -74
View File
@@ -42,9 +42,9 @@ function test_element(element_type)
end
# try to interpolate some scalar field
element["field1"] = Field(0.0, collect(1:n))
element["field1"] = Field(collect(1:n))
# TODO: how to parametrize this?
element["geometry"] = Field(0.0, Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
element["geometry"] = Field(Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
# evaluate basis functions at middle point of element
basis = get_basis(element)
@@ -55,7 +55,7 @@ function test_element(element_type)
val2 = basis("field1", mid, 0.0)
info("field val at $mid: $val2")
val3 = dbasis(mid, 0.0)
info("derivative of basis at $mid: $val3")
info("derivative of basis at $mid:\n$val3")
val4 = dbasis("field1", mid, 0.0)
info("field val at $mid: $val4")
@@ -64,135 +64,130 @@ end
""" Get FieldSet from element. """
function Base.getindex(element::Element, field_name)
element.fields[field_name]
return element.fields[field_name]
end
"""Add new FieldSet to element.
"""Add new Field 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])])))
>>> element["temperature"] = [1, 2, 3, 4]
>>> element["temperature"] = (0.0, [0, 0, 0, 0]), (1.0, [1, 2, 3, 4])
>>> element["temperature"] = (0.0 => [0, 0, 0, 0], 1.0 => [1, 2, 3, 4])
"""
function Base.setindex!(element::Element, field_data, field_name)
#element.fields[field_name] = field_data
setindex!(element.fields, field_data, field_name)
end
function Base.setindex!(element::Element, field_data::Tuple, field_name)
field = Field()
for (time, data) in field_data
ts = TimeStep(time, Increment[Increment(data)])
push!(field, ts)
end
element[field_name] = field
end
function get_connectivity(el::Element)
el.connectivity
return el.connectivity
end
abstract AbstractFunctionSpace
type FunctionSpace <: AbstractFunctionSpace
element :: Element
basis :: Basis
fields :: FieldSet
end
type GradientFunctionSpace <: AbstractFunctionSpace
element :: Element
end
type MixedFunctionSpace <: AbstractFunctionSpace
element1 :: Element
element2 :: Element
basis :: Basis
fields :: FieldSet
end
function get_basis(element::Element)
return FunctionSpace(element)
return FunctionSpace(element.basis, element.fields)
end
function get_dbasis(element::Element)
return GradientFunctionSpace(element)
return GradientFunctionSpace(element.basis, element.fields)
end
function grad(u::FunctionSpace)
return GradientFunctionSpace(u.element)
end
""" Evaluate field on element function space. """
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.data[1]
end
h = u.element.basis.basis(xi)
#@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)
return GradientFunctionSpace(u.basis, u.fields)
end
""" If basis is called without a field, return basis functions evaluated at that point. """
function call(u::FunctionSpace, xi::Vector, t::Number=Inf)
return u.element.basis.basis(xi)
end
""" Evaluate gradient of field on element function space. """
function call(gradu::GradientFunctionSpace, field_name, xi::Vector, t::Number=Inf, variation=nothing)
f = !isa(variation, Void) ? variation : gradu.element[field_name](t)
X = gradu.element["geometry"](t)
dN = gradu.element.basis.dbasisdxi(xi)
J = sum([dN[:,i]*X[i]' for i=1:length(X)])
grad = inv(J)*dN
gradf = sum([grad[:,i]*f[i]' for i=1:length(f)])'
return gradf
function call(u::FunctionSpace, xi::Union{Vector, IntegrationPoint}, t::Number=0.0)
return u.basis(xi)
end
""" If gradient of basis is called without a field, return "empty" gradient evaluated at that point. """
function call(gradu::GradientFunctionSpace, xi::Vector, t::Number=Inf)
X = gradu.element["geometry"](t)
dN = gradu.element.basis.dbasisdxi(xi)
J = sum([dN[:,i]*X[i]' for i=1:length(X)])
grad = inv(J)*dN
return grad
function call(gradu::GradientFunctionSpace, xi::Union{Vector, IntegrationPoint}, t::Number=0.0)
geometry = gradu.fields["geometry"](t)
gradu.basis(geometry, xi, Val{:grad})
end
""" Evaluate field on element function space. """
function call(u::FunctionSpace, field_name, xi::Union{Vector, IntegrationPoint}, t::Number=0.0, variation=nothing)
field = !isa(variation, Void) ? variation : u.fields[field_name](t)
if length(field) == 1
return field.data[1]
end
u.basis(field, xi)
end
""" Evaluate gradient of field on element function space. """
function call(gradu::GradientFunctionSpace, field_name, xi::Union{Vector, IntegrationPoint}, t::Number=0.0, variation=nothing)
field = !isa(variation, Void) ? variation : gradu.fields[field_name](t)
geometry = gradu.fields["geometry"](t)
gradu.basis(geometry, field, xi, Val{:grad})
end
# on-line functions to get api more easy to use, ip -> xi.ip
call(u::FunctionSpace, ip::IntegrationPoint, t::Number=Inf) = call(u, ip.xi, t)
call(u::GradientFunctionSpace, ip::IntegrationPoint, t::Number=Inf) = call(u, ip.xi, t)
#call(u::FunctionSpace, ip::IntegrationPoint, t::Number=Inf) = call(u, ip.xi, t)
#call(u::GradientFunctionSpace, ip::IntegrationPoint, t::Number=Inf) = call(u, ip.xi, t)
# i think these will be the most called functions.
call(u::FunctionSpace, field_name, ip::IntegrationPoint, t::Number=Inf, variation=nothing) = call(u, field_name, ip.xi, t, variation)
call(u::GradientFunctionSpace, field_name, ip::IntegrationPoint, t::Number=Inf, variation=nothing) = call(u, field_name, ip.xi, t, variation)
call(u::FunctionSpace, field_name) = (args...) -> call(u, field_name, args...)
call(u::GradientFunctionSpace, field_name) = (args...) -> call(u, field_name, args...)
#call(u::FunctionSpace, field_name, ip::IntegrationPoint, t::Number=0.0, variation=nothing) = call(u, field_name, ip.xi, t, variation)
#call(u::GradientFunctionSpace, field_name, ip::IntegrationPoint, t::Number=0.0, variation=nothing) = call(u, field_name, ip.xi, t, variation)
#call(u::FunctionSpace, field_name) = (args...) -> call(u, field_name, args...)
#call(u::GradientFunctionSpace, field_name) = (args...) -> call(u, field_name, args...)
""" Return a field from function space. """
function get_field(u::FunctionSpace, field_name, time=Inf)
return u.element[field_name](time)
function get_field(u::FunctionSpace, field_name, time::Number=0.0)
return u.fields[field_name](time)
end
""" Return a field from function space. """
function get_field(u::FunctionSpace, field_name, time=Inf, variation=nothing)
return !isa(variation, Void) ? variation : u.element[field_name](time)
function get_field(u::FunctionSpace, field_name, time::Number=0.0, variation=nothing)
return !isa(variation, Void) ? variation : u.fields[field_name](time)
end
""" Return a fieldset from function space. """
""" Return a field from function space. """
function get_fieldset(u::FunctionSpace, field_name)
return u.element[field_name]
return u.fields[field_name]
end
""" Get a determinant of element in point ξ. """
function LinAlg.det(u::FunctionSpace, xi::Vector, t::Number=Inf)
X = u.element["geometry"](t)
dN = u.element.basis.dbasisdxi(xi)
function LinAlg.det(u::FunctionSpace, xi::Vector, time::Number=0.0)
X = u.fields["geometry"](time)
dN = u.basis.dbasisdxi(xi)
J = sum([dN[:,i]*X[i]' for i=1:length(X)])
m, n = size(J)
return m == n ? det(J) : norm(J)
end
function LinAlg.det(u::FunctionSpace, ip::IntegrationPoint, t::Number=Inf)
LinAlg.det(u, ip.xi, t)
function LinAlg.det(u::FunctionSpace, ip::IntegrationPoint, time::Number=0.0)
LinAlg.det(u, ip.xi, time)
end
function LinAlg.det(u::FunctionSpace)
return (args...) -> det(u, args...)
end
#Base.(:+)(u::FunctionSpace, v::FunctionSpace) = (args...) -> u(args...) + v(args...)
#Base.(:-)(u::FunctionSpace, v::FunctionSpace) = (args...) -> u(args...) - v(args...)
#Base.(:+)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(args...) + v(args...)
#Base.(:-)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(args...) - v(args...)
Base.(:+)(u::FunctionSpace, v::FunctionSpace) = (args...) -> u(args...) + v(args...)
Base.(:-)(u::FunctionSpace, v::FunctionSpace) = (args...) -> u(args...) - v(args...)
Base.(:+)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(args...) + v(args...)
Base.(:-)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(args...) - v(args...)
""" Check does fieldset exist. """
""" Check does field exist. """
function Base.haskey(element::Element, what)
haskey(element.fields, what)
end
+12 -11
View File
@@ -57,10 +57,10 @@ function initialize_local_assembly!(assembly::LocalAssembly, equation::Equation)
end
has_mass_matrix(equation::Equation) = false
function get_mass_matrix(equation::Equation, ip, time=Inf, problem=nothing)
function get_mass_matrix(equation::Equation, ip, time=0.0, problem=nothing)
get_mass_matrix(equation, ip, time)
end
function get_mass_matrix(equation::Equation, ip, time=Inf)
function get_mass_matrix(equation::Equation, ip, time=0.0)
get_mass_matrix(equation, ip)
end
function get_mass_matrix(equation::Equation, ip)
@@ -68,10 +68,10 @@ function get_mass_matrix(equation::Equation, ip)
end
has_stiffness_matrix(equation::Equation) = false
function get_stiffness_matrix(equation::Equation, ip, time=Inf, problem=nothing)
function get_stiffness_matrix(equation::Equation, ip, time=0.0, problem=nothing)
get_stiffness_matrix(equation, ip, time)
end
function get_stiffness_matrix(equation::Equation, ip, time=Inf)
function get_stiffness_matrix(equation::Equation, ip, time=0.0)
get_stiffness_matrix(equation, ip)
end
function get_stiffness_matrix(equation::Equation, ip)
@@ -79,10 +79,10 @@ function get_stiffness_matrix(equation::Equation, ip)
end
has_force_vector(equation::Equation) = false
function get_force_vector(equation::Equation, ip, time=Inf, problem=nothing)
function get_force_vector(equation::Equation, ip, time=0.0, problem=nothing)
get_force_vector(equation, ip, time)
end
function get_force_vector(equation::Equation, ip, time=Inf)
function get_force_vector(equation::Equation, ip, time=0.0)
get_force_vector(equation, ip)
end
function get_force_vector(equation::Equation, ip)
@@ -90,10 +90,10 @@ function get_force_vector(equation::Equation, ip)
end
has_residual_vector(equation::Equation) = false
function get_residual_vector(equation::Equation, ip, time=Inf, problem=nothing)
function get_residual_vector(equation::Equation, ip, time=0.0, problem=nothing)
get_residual_vector(equation, ip, time)
end
function get_residual_vector(equation::Equation, ip, time=Inf)
function get_residual_vector(equation::Equation, ip, time=0.0)
get_residual_vector(equation, ip)
end
function get_residual_vector(equation::Equation, ip)
@@ -101,10 +101,10 @@ function get_residual_vector(equation::Equation, ip)
end
has_potential_energy(equation::Equation) = false
function get_potential_energy(equation::Equation, ip, time=Inf, problem=nothing)
function get_potential_energy(equation::Equation, ip, time=0.0, problem=nothing)
get_potential_energy(equation, ip, time)
end
function get_potential_energy(equation::Equation, ip, time=Inf)
function get_potential_energy(equation::Equation, ip, time=0.0)
get_potential_energy(equation, ip)
end
function get_potential_energy(equation::Equation, ip)
@@ -117,7 +117,7 @@ get_integration_points(equation::Equation) = equation.integration_points
""" Return a local assembly for element. """
function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
unknown_field_name::ASCIIString, time::Number=Inf,
unknown_field_name::ASCIIString, time::Number=0.0,
problem=nothing)
initialize_local_assembly!(assembly, equation) # zero all
@@ -174,6 +174,7 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
assembly.stiffness_matrix += hessian
assembly.force_vector -= ForwardDiff.gradient(allresults) # <--- minus explained in tutorial
assembly.potential_energy = ForwardDiff.value(allresults)
#info("potential energy of system: $(assembly.potential_energy)")
end
# 3. virtual work form - user has defined residual vector δW_int(u,δu) + δW_ext(u,δu) = 0 ∀ v
+13
View File
@@ -214,6 +214,15 @@ function Base.push!(field::DefaultDiscreteField, timestep::TimeStep)
push!(field.timesteps, timestep)
end
function Base.push!(field::DefaultDiscreteField, data::Union{Vector, Matrix})
push!(field[end], Increment(data))
end
function Base.push!(field::DefaultDiscreteField, data::Pair)
ts = TimeStep(data[1], Increment(data[2]))
push!(field, ts)
end
"""Quickly create fields.
Examples
@@ -287,3 +296,7 @@ function Base.convert(::Type{ContinuousField}, data::Function)
return convert(DefaultContinuousField, data)
end
function Base.length(::Field)
return 1
end
+45
View File
@@ -16,3 +16,48 @@ function get_default_integration_points(element::Seg2)
IntegrationPoint([0.0], 2.0)
]
end
function line3()
[
IntegrationPoint([0.0], 8/9),
IntegrationPoint([-sqrt(3/5)], 5/9),
IntegrationPoint([+sqrt(3/5)], 5/9)
]
end
function line5()
[
IntegrationPoint([-1/3*sqrt(5 + 2*sqrt(10/7))], (322-13*sqrt(70))/900),
IntegrationPoint([-1/3*sqrt(5 - 2*sqrt(10/7))], (322+13*sqrt(70))/900),
IntegrationPoint([0.0], 128/225),
IntegrationPoint([ 1/3*sqrt(5 - 2*sqrt(10/7))], (322+13*sqrt(70))/900),
IntegrationPoint([ 1/3*sqrt(5 + 2*sqrt(10/7))], (322-13*sqrt(70))/900)
]
end
#integration_points = [
# IntegrationPoint([ 0.0000000000000000], 0.5688888888888889),
# IntegrationPoint([-0.5384693101056831], 0.4786286704993665),
# IntegrationPoint([ 0.5384693101056831], 0.4786286704993665),
# IntegrationPoint([-0.9061798459386640], 0.2369268850561891),
# IntegrationPoint([ 0.9061798459386640], 0.2369268850561891)
#]
#integration_points = [
# IntegrationPoint([+sqrt(3/7 - 2/7*sqrt(6/5))], (18+sqrt(30))/36)
# IntegrationPoint([-sqrt(3/7 - 2/7*sqrt(6/5))], (18+sqrt(30))/36)
# IntegrationPoint([+sqrt(3/7 + 2/7*sqrt(6/5))], (18-sqrt(30))/36)
# IntegrationPoint([-sqrt(3/7 + 2/7*sqrt(6/5))], (18-sqrt(30))/36)
#]
#integration_points = [
# IntegrationPoint([0.0], 8/9),
# IntegrationPoint([-sqrt(3/5)], 5/9),
# IntegrationPoint([+sqrt(3/5)], 5/9)
#]
#integration_points = [
# IntegrationPoint([-sqrt(1/3)], 1)
# IntegrationPoint([+sqrt(1/3)], 1)
#]
#integration_points = [
# IntegrationPoint([0.0], 2)
#]
+15 -20
View File
@@ -10,10 +10,10 @@ Solve field equations for single element with some dofs fixed. This can be used
to test nonlinear element formulations.
"""
function solve!(equation::Equation, unknown_field_name::ASCIIString,
free_dofs::Array{Int, 1}, time::Number=Inf;
free_dofs::Array{Int, 1}, time::Number=0.0;
max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false)
element = get_element(equation)
x0 = element[unknown_field_name](-Inf)
x0 = element[unknown_field_name](0.0)
x = zeros(prod(size(equation)))
dx = fill!(similar(x), 0.0)
la = initialize_local_assembly()
@@ -27,15 +27,10 @@ function solve!(equation::Equation, unknown_field_name::ASCIIString,
end
dx[free_dofs] = A \ b
x += dx
new_field = similar(x0, x)
new_field.time = time
new_field.increment = i
push!(element[unknown_field_name], new_field)
if norm(dx) < tolerance
return
end
push!(element[unknown_field_name], reshape(x, size(equation)))
norm(dx) < tolerance && return
end
Logging.err("Did not converge in $max_iterations iterations")
error("Did not converge in $max_iterations iterations")
end
"""
@@ -44,15 +39,18 @@ to test nonlinear element formulations. Dirichlet boundary is assumed to be homo
and degrees of freedom are eliminated. So if boundary condition is known in nodal
points and everything is zero this should be quite good.
"""
function solve!(problem::Problem, free_dofs::Array{Int, 1}, time::Number=Inf;
function solve!(problem::Problem, free_dofs::Array{Int, 1}, time::Number=1.0;
max_iterations::Int=10, tolerance::Float64=1.0e-12, dump_matrices::Bool=false)
info("start solver")
ga = initialize_global_assembly(problem)
x = zeros(ga.ndofs)
dx = fill!(similar(x), 0.0)
field_name = get_unknown_field_name(problem)
dim = get_unknown_field_dimension(problem)
for i=1:max_iterations
info("calculate global assembly")
calculate_global_assembly!(ga, problem)
info("done")
A = ga.stiffness_matrix[free_dofs, free_dofs]
b = ga.force_vector[free_dofs]
if dump_matrices
@@ -60,20 +58,17 @@ function solve!(problem::Problem, free_dofs::Array{Int, 1}, time::Number=Inf;
dump(full(b)')
end
dx[free_dofs] = lufact(A) \ full(b)
info("Difference in solution norm: $(norm(dx))")
x += dx
for equation in get_equations(problem)
element = get_element(equation)
conn = get_connectivity(element)
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]))
push!(element[field_name][end], new_field)
end
if norm(dx) < tolerance
return
gdofs = get_gdofs(problem, equation)
data = reshape(full(x[gdofs]), size(equation))
push!(element[field_name], data)
end
norm(dx) < tolerance && return
end
Logging.err("Did not converge in $max_iterations iterations")
error("Did not converge in $max_iterations iterations")
end
""" Add new problem to solver. """
+16
View File
@@ -31,3 +31,19 @@ end
function Base.convert(::Type{Number}, ip::IntegrationPoint)
return ip.xi
end
function Base.call(basis::Basis, ip::IntegrationPoint)
return basis(ip.xi)
end
function Base.call(basis::Basis, increment::Increment, ip::IntegrationPoint)
return call(basis, increment, ip.xi)
end
function Base.call(basis::Basis, increment::Increment, ip::IntegrationPoint, ::Type{Val{:grad}})
return call(basis, increment, ip.xi, Val{:grad})
end
function Base.call(basis::Basis, geometry::Increment, field::Increment, ip::IntegrationPoint, ::Type{Val{:grad}})
return call(basis, geometry, field, ip.xi, Val{:grad})
end
+18
View File
@@ -0,0 +1,18 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module TestAutoDiffWeakForm
using JuliaFEM.Test
using JuliaFEM
using JuliaFEM: Seg2, DirichletProblem
function test_dirichlet_problem()
element = Seg2([3, 4])
element["geometry"] = Vector[[1.0, 1.0], [0.0, 1.0]]
problem = DirichletProblem(1)
push!(problem, element)
end
end
+4 -2
View File
@@ -4,7 +4,9 @@
module ElasticityTests
using JuliaFEM.Test
using JuliaFEM: Quad4, Field, FieldSet, CPS4, get_basis, solve!, PlaneStressElasticityProblem
using JuliaFEM: Quad4, Field, FieldSet, CPS4,
get_basis, solve!,
PlaneStressElasticityProblem
function test_elasticity_one_element()
@@ -21,7 +23,7 @@ function test_elasticity_one_element()
disp = get_basis(element)("displacement", [1.0, 1.0])[2]
info("displacement at tip: $disp")
# verified using Code Aster.
@test disp -8.77303119819776
@test isapprox(disp, -8.77303119819776)
end
+44 -2
View File
@@ -19,7 +19,11 @@ end
function MockElement(connectivity)
h(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])]
h(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])]'
dh(xi) = 1/4*[
-(1-xi[2]) (1-xi[2]) (1+xi[2]) -(1+xi[2])
@@ -39,9 +43,47 @@ end
""" test adding fieldsets and fields to element"""
function test_add_fields_to_element()
el = MockElement([1, 2, 3, 4])
el["geometry"] = [0.0, 0.0, 0.0, 0.0], [1.0, 1.0, 1.0, 1.0]
#geometry = Field([0.0, 0.0, 0.0, 0.0])
el["geometry"] = Field([0.0, 0.0, 0.0, 0.0])
@test el["geometry"][1].time == 0.0
@test last(el["geometry"]) == [0.0, 0.0, 0.0, 0.0]
el["geometry"] = Field(Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
@test last(el["geometry"])[3] == [1.0, 1.0]
el["geometry"] = Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]
@test last(el["geometry"])[3] == [1.0, 1.0]
el["geometry"] = [0.0 0.0; 1.0 0.0; 1.0 1.0; 0.0 1.0]'
@test last(el["geometry"])[3] == [1.0, 1.0]
el["geometry"] = (0.0, [0.0, 0.0, 0.0, 0.0]), (1.0, [1.0, 1.0, 1.0, 1.0])
field = el["geometry"]
@test length(field) == 2 # two time steps
el["boundary flux"] = (0.0, 0.0), (1.0, 6.0)
end
function test_add_fields_to_element_2()
el = MockElement([1, 2, 3, 4])
el["data"] = (0.0 => [1, 2], 1.0 => [2, 3])
@test length(el["data"]) == 2
@test el["data"][1].time == 0.0
@test el["data"][2].time == 1.0
@test last(el["data"][1]) == [1, 2]
@test last(el["data"][2]) == [2, 3]
end
function test_add_data_to_element_using_push()
el = MockElement([1, 2, 3, 4])
el["data"] = [0, 0, 0, 0]
push!(el["data"], [1, 2, 3, 4])
@test length(el["data"]) == 1
@test length(el["data"][1]) == 2
@test el["data"][1].time == 0.0
push!(el["data"], 1.0 => [2, 3, 4, 5]) # creates new timestep at t=1.0
push!(el["data"], [3, 4, 5, 6]) # adds new increment data to last timestep
@test length(el["data"]) == 2
@test length(el["data"][2]) == 2
@test el["data"][2].time == 1.0
end
#=
+197
View File
@@ -0,0 +1,197 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module ElementTests
using JuliaFEM.Test
using JuliaFEM
using JuliaFEM: Equation, Quad4, IntegrationPoint, initialize_local_assembly,
get_element, get_basis, grad, calculate_local_assembly!,
PlaneHeatProblem, Seg2, HeatEquation, Problem, solve!
""" Diffusive heat transfer for 4-node bilinear element, with a nonlinear source term. """
type DC2D4NL <: Equation
element :: Quad4
integration_points :: Array{IntegrationPoint, 1}
end
function DC2D4NL(element::Quad4, initial_temperature=zeros(4))
integration_points = [
IntegrationPoint(1.0/sqrt(3.0)*[-1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, 1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[-1, 1], 1.0)]
if !haskey(element, "temperature")
element["temperature"] = initial_temperature
end
DC2D4NL(element, integration_points)
end
function Base.size(equation::DC2D4NL)
return (1, 4)
end
""" Nonlinear flux term. """
type DC2D2NL <: Equation
element :: Seg2
integration_points :: Array{IntegrationPoint, 1}
end
function DC2D2NL(element::Seg2, initial_temperature=zeros(2))
#integration_points = [
# IntegrationPoint([0.0], 2.0)]
integration_points = JuliaFEM.line5()
if !haskey(element, "temperature")
element["temperature"] = initial_temperature
end
DC2D2NL(element, integration_points)
end
function Base.size(equation::DC2D2NL)
return (1, 2)
end
""" Calculate a potential Π = Wint - Wext of system. """
function JuliaFEM.get_potential_energy(equation::DC2D4NL, ip, time; variation=nothing)
element = get_element(equation)
basis = get_basis(element)
k = basis("temperature thermal conductivity", ip, time)
f = basis("temperature load", ip, time)
T = basis("temperature", ip, time, variation)
c = basis("temperature nonlinearity coefficient", ip, time)
gradT = grad(basis)("temperature", ip, time, variation)
Wint = (k + c*T) * 1/2*vecdot(gradT, gradT)
#Wint = k*1/2*vecdot(gradT, gradT)
Wext = f*T
#Wext = 0.0
return Wint - Wext
end
function JuliaFEM.has_potential_energy(eq::DC2D4NL)
return true
end
function JuliaFEM.get_potential_energy(equation::DC2D2NL, ip, time; variation=nothing)
element = get_element(equation)
basis = get_basis(element)
T = basis("temperature", ip, time, variation)[1]
Wint = 0.0
sig = 5.7e-8
eps = basis("emissivity", ip, time)[1]
T_ext = basis("temperature external", ip, time)[1]
q0 = eps*sig*((T_ext+273.15)^4 - (T+273.15)^4)
Wext = q0*T
W = Wint - Wext
return W
end
function JuliaFEM.has_potential_energy(eq::DC2D2NL)
return true
end
function test_potential_energy_method()
# create model -- start
element = Quad4([1, 2, 3, 4])
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"] = [0.0, 0.0, 0.0, 0.0]
element["temperature nodal load"] = [3.0, 3.0, 0.0, 0.0]
element["temperature nonlinearity coefficient"] = [6.0, 6.0, 6.0, 6.0]
equation = DC2D4NL(element)
# create model -- end
la = initialize_local_assembly() # create workspace for local matrices
T = zeros(4) # create workspace for solution vector
dT = zeros(4) #
fd = [1, 2] # free dofs
tic()
# start loops, in principle solve ∂r(u)/∂uΔu = -r(u) and update.
for i=1:10
calculate_local_assembly!(la, equation, "temperature") # calculate local matrices
dT[fd] = la.stiffness_matrix[fd,fd] \ la.force_vector[fd]
T += dT
push!(element["temperature"], T) # add new increment to model
info("T = $T")
@printf("increment %2d, |du| = %8.5f\n", i, norm(dT))
err = last(element["temperature"])[1] - 2/3
isapprox(err, 0.0) && break
end
toc()
err = last(element["temperature"])[1] - 2/3
info("error: $err")
@test isapprox(err, 0.0)
end
type TestProblem <: Problem
unknown_field_name :: ASCIIString
unknown_field_dimension :: Int
equations :: Array{Equation, 1}
element_mapping :: Dict{DataType, DataType}
end
function TestProblem(equations=[])
element_mapping = Dict(
Quad4 => DC2D4NL,
Seg2 => DC2D2NL)
TestProblem("temperature", 1, equations, element_mapping)
end
function test_potential_energy_method_2()
# create model -- start
N = Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]
element = Quad4([1, 2, 3, 4])
element["geometry"] = Vector[N[1], N[2], N[3], N[4]]
element["temperature thermal conductivity"] = 6.0
element["temperature load"] = [0.0, 0.0, 0.0, 0.0]
element["temperature nonlinearity coefficient"] = [0.0, 0.0, 0.0, 0.0]
#equation1 = DC2D4NL(element, initial_temperature=ones(4))
equation1 = DC2D4NL(element)
boundary_element = Seg2([1, 2])
boundary_element["geometry"] = Vector[N[1], N[2]]
boundary_element["emissivity"] = 0.5
boundary_element["temperature external"] = 10.0
#equation2 = DC2D2NL(boundary_element, initial_temperature=ones(4))
equation2 = DC2D2NL(boundary_element)
# create model -- end
element["temperature"] = ones(4)
boundary_element["temperature"] = ones(2)
equations = [equation1, equation2]
la = initialize_local_assembly() # create workspace for local matrices
T = zeros(4) # create workspace for solution vector
dT = zeros(4) #
fd = [1, 2] # free dofs
info("equation 1")
calculate_local_assembly!(la, equation1, "temperature")
info("stiffness matrix: $(la.stiffness_matrix)")
# info("force vector: $(la.force_vector)")
info("equation 2")
calculate_local_assembly!(la, equation2, "temperature")
# info("stiffness matrix: $(la.stiffness_matrix)")
info("force vector: $(la.force_vector)")
info("Creating problem")
#problem = PlaneHeatProblem("temperature", 1, equations, Dict())
problem = TestProblem(equations)
free_dofs = [1, 2]
tic()
solve!(problem, free_dofs; max_iterations=10)
toc()
temp = get_basis(boundary_element)("temperature", [0.0])[1]
info("temperature = $temp")
#err = last(element["temperature"])[1] - 2/3
#info("error: $err")
# 0.3888756709834147 tulee jostakin syysta...
# tai -0.39411350336960116
info(boundary_element["temperature"])
@test isapprox(temp, 2.93509690572300E+00) # tested using Code Aster
end
end
+34
View File
@@ -0,0 +1,34 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module RandomFieldTests
using JuliaFEM
using JuliaFEM: DiscreteField, Field, Increment, Quad4
using JuliaFEM.Test
type RandomField <: DiscreteField
mu :: Float64
std :: Float64
end
Base.first(field::RandomField) = Increment(randn(2, 4).*field.std^2 + field.mu)
function test_interpolate_in_time()
r = RandomField(10.0, 0.0)
f = Increment(ones(2, 4)*10.0)
@test r(0.0) == f
@test r(-Inf) == f
@test r(+Inf) == f
@test r(1.0) == f
end
function test_interpolate_in_spatial_domain()
basis = Quad4([1, 2, 3, 4]).basis
r = RandomField(10.0, 0.0)
feval = basis(r(0.0), [0.0, 0.0])
@test feval == [10.0, 10.0]
end
end
+81
View File
@@ -0,0 +1,81 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module TestAutoDiffWeakForm
using JuliaFEM.Test
using JuliaFEM
using JuliaFEM: Quad4, Equation, IntegrationPoint,
solve!, get_field, get_element, get_basis,
grad
""" Plane stress formulation for 4-node bilinear element. """
type CPS4 <: Equation
element :: Quad4
integration_points :: Array{IntegrationPoint, 1}
end
function CPS4(element::Quad4)
integration_points = [
IntegrationPoint(1.0/sqrt(3.0)*[-1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, -1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[ 1, 1], 1.0),
IntegrationPoint(1.0/sqrt(3.0)*[-1, 1], 1.0)]
if !haskey(element, "displacement")
# initial field must be defined if using autodiff
element["displacement"] = zeros(2, 4)
end
CPS4(element, integration_points)
end
JuliaFEM.size(eq::CPS4) = (2, 4)
function JuliaFEM.get_residual_vector(equation::CPS4, ip, time; variation=nothing)
element = get_element(equation)
basis = get_basis(element)
dbasis = grad(basis)
# material parameters
E = basis("youngs modulus", ip, time)
nu = basis("poissons ratio", ip, time)
mu = E/(2*(1+nu))
la = E*nu/((1+nu)*(1-2*nu))
la = 2*la*mu/(la + 2*mu) # <- correction for 2d
# elasticity formulation
u = basis("displacement", ip, time, variation)
gradu = dbasis("displacement", ip, time, variation)
F = I + gradu
b = basis("displacement volume load", ip, time)
E = 1/2*(F'*F - I)
S = la*trace(E)*I + 2*mu*E
P = F*S
# residual vector
r_int = P*dbasis(ip,time)
r_ext = b*basis(ip,time)
r = r_int - r_ext
return vec(r)
end
JuliaFEM.has_residual_vector(equation::CPS4) = true
function test_residual_form()
# create model -- start
element = Quad4([1, 2, 3, 4])
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 volume load"] = Vector[[0.0,-10.0], [0.0,-10.0], [0.0,-10.0], [0.0,-10.0]]
equation = CPS4(element)
# create model -- end
free_dofs = [3, 4, 5, 6]
solve!(equation, "displacement", free_dofs) # launch a newton solver for single element
disp = get_basis(element)("displacement", [1.0, 1.0])[2]
println("displacement at tip: $disp")
# verified using Code Aster.
@test isapprox(disp, -8.77303119819776E+00)
end
end
@@ -0,0 +1,74 @@
# Nonlinear radiation term on free boundary
# B1 = boundary element
DEBUT()
MAIL = LIRE_MAILLAGE()
MO = AFFE_MODELE(
MAILLAGE=MAIL,
AFFE = _F(MAILLE=('B1','E1'), PHENOMENE='THERMIQUE', MODELISATION='PLAN'))
CONDUC = DEFI_FONCTION(
NOM_PARA='TEMP',
NOM_RESU='LAMBDA',
VALE=(0.0, 6.0,
1.0, 6.0),
PROL_DROITE='LINEAIRE',
PROL_GAUCHE='LINEAIRE')
ENTHAL = DEFI_FONCTION(
NOM_PARA='TEMP',
NOM_RESU='CP',
VALE= (0.0, 0.0,
1.0, 0.0),
PROL_DROITE='LINEAIRE',
PROL_GAUCHE='LINEAIRE')
MAT = DEFI_MATERIAU(
THER_NL=_F(
LAMBDA=CONDUC,
BETA=ENTHAL))
#MAT = DEFI_MATERIAU(
# THER_NL = _F(LAMBDA=6.0))
CHMAT = AFFE_MATERIAU(
MAILLAGE = MAIL,
AFFE = _F(MAILLE = ('E1','B1'), MATER = MAT))
BC = AFFE_CHAR_THER( # Dirichlet boundary condition on 0 <= X <= 1, Y = 1
MODELE = MO,
TEMP_IMPO = (_F(NOEUD = ('N3','N4'), TEMP=0.0)))
# Heat flux on free boundary, radiation term.
LO = AFFE_CHAR_THER(
MODELE = MO,
RAYONNEMENT = _F(
MAILLE="B1",
SIGMA=5.7e-8,
EPSILON=0.5,
TEMP_EXT=10.0))
LIST = DEFI_LIST_REEL(
DEBUT = 0,
INTERVALLE = _F(JUSQU_A=1.0, NOMBRE=1))
RESU = THER_NON_LINE(
MODELE=MO,
CHAM_MATER=CHMAT,
EXCIT=(
_F(CHARGE=BC),
_F(CHARGE=LO)),
# ETAT_INIT=_F(STATIONNAIRE='OUI'),
NEWTON=_F(REAC_ITER=1),
# INCREMENT=_F(LIST_INST=LIST),
# CONVERGENCE=_F(RESI_GLOB_RELA=1.0E-12)
)
IMPR_RESU(
MODELE = MO,
FORMAT = 'RESULTAT',
RESU = _F(RESULTAT = RESU))
FIN()
@@ -0,0 +1,17 @@
COOR_2D
N1 0.0 0.0
N2 1.0 0.0
N3 1.0 1.0
N4 0.0 1.0
FINSF
QUAD4
E1 N1 N2 N3 N4
FINSF
SEG2
B1 N1 N2
FINSF
FIN
@@ -0,0 +1,99 @@
-- CODE_ASTER -- VERSION : EXPLOITATION (stable) --
Version 11.4.0 du 05/06/2013
Copyright EDF R&D 1991 - 2015
Exécution du : Tue Nov 10 22:17:18 2015
Nom de la machine : jukka-desktop
Architecture : 64bit
Type de processeur : x86_64
Système d'exploitation : Linux 3.13.0-67-generic
Langue des messages : en (UTF-8)
!------------------------------------------------------------------------------------!
! <A> <SUPERVIS2_2> !
! !
! Vous utilisez une vieille version de Code_Aster. !
! !
! En mettant à jour votre version, vous bénéficierez des dernières améliorations !
! apportées au code depuis 15 mois. !
! Si vous avez des développements privés, vous risquez d'avoir un travail !
! important de portage si vous ne suivez pas les mises à jour. !
! !
! !
! Ceci est une alarme. Si vous ne comprenez pas le sens de cette !
! alarme, vous pouvez obtenir des résultats inattendus ! !
!------------------------------------------------------------------------------------!
Parallélisme MPI : inactif
Parallélisme OpenMP : actif
Nombre de processus utilisés : 1
Version de la librairie HDF5 : 1.8.8
Version de la librairie MED : 3.0.6
Librairie MUMPS : installée
Version de la librairie SCOTCH : 5.1.10
Mémoire limite pour l'exécution : 4096.00 Mo
consommée par l'initialisation : 197.46 Mo
par les objets du jeu de commandes : 1.54 Mo
reste pour l'allocation dynamique : 3897.01 Mo
Taille limite des fichiers d'échange : 48.00 Go
--------------------------------------------------------------------------------
ASTER 11.04.00 CONCEPT RESU CALCULE LE 10/11/2015 A 22:17:18 DE TYPE EVOL_THER
======>
------>
CHAMP AUX NOEUDS DE NOM SYMBOLIQUE TEMP
NUMERO D'ORDRE: 0 INST: 0.00000000000000E+00
NOEUD TEMP
N1 2.93509690572300E+00
N2 2.93509690572300E+00
N3 0.00000000000000E+00
N4 0.00000000000000E+00
------>
CARTE DE NOM SYMBOLIQUE COMPORTHER
NUMERO D'ORDRE: 0 INST: 0.00000000000000E+00
<I> <FIN> FERMETURE DE LA BASE "GLOBALE" EFFECTUEE.
<FIN> Arrêt normal dans "FIN".
<I> <FIN> ARRET NORMAL DANS "FIN" PAR APPEL A "JEFINI".
<I> <FIN> MEMOIRE JEVEUX MINIMALE REQUISE POUR L'EXECUTION : 21.00 Mo
<I> <FIN> MEMOIRE JEVEUX OPTIMALE REQUISE POUR L'EXECUTION : 27.32 Mo
<I> <FIN> MAXIMUM DE MEMOIRE UTILISEE PAR LE PROCESSUS LORS DE L'EXECUTION : 226.64 Mo
********************************************************************************
* COMMAND : USER : SYSTEM : USER+SYS : ELAPSED *
********************************************************************************
* init (jdc) : 0.16 : 0.01 : 0.17 : 0.17 *
* . compile : 0.00 : 0.00 : 0.00 : 0.00 *
* . exec_compile : 0.05 : 0.01 : 0.06 : 0.06 *
* . report : 0.00 : 0.00 : 0.00 : 0.00 *
* . build : 0.00 : 0.00 : 0.00 : 0.00 *
* DEBUT : 0.02 : 0.02 : 0.04 : 0.03 *
* LIRE_MAILLAGE : 0.00 : 0.00 : 0.00 : 0.00 *
* AFFE_MODELE : 0.00 : 0.00 : 0.00 : 0.00 *
* DEFI_FONCTION : 0.00 : 0.00 : 0.00 : 0.01 *
* DEFI_FONCTION : 0.00 : 0.00 : 0.00 : 0.00 *
* DEFI_MATERIAU : 0.01 : 0.00 : 0.01 : 0.00 *
* AFFE_MATERIAU : 0.00 : 0.00 : 0.00 : 0.00 *
* AFFE_CHAR_THER : 0.00 : 0.00 : 0.00 : 0.00 *
* AFFE_CHAR_THER : 0.00 : 0.00 : 0.00 : 0.00 *
* DEFI_LIST_REEL : 0.00 : 0.00 : 0.00 : 0.00 *
* THER_NON_LINE : 0.03 : 0.00 : 0.03 : 0.03 *
* IMPR_RESU : 0.01 : 0.00 : 0.01 : 0.01 *
* FIN : 0.01 : 0.01 : 0.02 : 0.02 *
* . part Superviseur : 0.20 : 0.03 : 0.23 : 0.22 *
* . part Fortran : 0.04 : 0.01 : 0.05 : 0.06 *
********************************************************************************
* TOTAL_JOB : 0.24 : 0.04 : 0.28 : 0.28 *
********************************************************************************