integration points

This commit is contained in:
Jukka Aho
2016-05-25 22:33:47 +03:00
parent 070e42c7fc
commit d606bde168
16 changed files with 424 additions and 370 deletions
+9 -4
View File
@@ -15,16 +15,17 @@ autodiffcache = ForwardDiffCache()
include("common.jl")
include("fields.jl")
export DCTI, Field
export Field, DCTI, DVTI, DCTV, DVTV
include("types.jl") # data types: Point, IntegrationPoint, ...
export AbstractPoint, Point, IntegrationPoint, IP, Node
#include("basis.jl") # interpolation of discrete fields
#include("symbolic.jl") # a thin symbolic layer for fields
#include("types.jl") # type definitions
### ELEMENTS ###
include("elements.jl") # common element routines
export Node, AbstractElement, Element, update!, get_connectivity
include("lagrange_macro.jl") # Continuous Galerkin (Lagrange) elements generated using macro
export Seg2, Tri3, Tri6, Quad4, Hex8, Tet4, Tet10
export Seg2, Seg3, Tri3, Tri6, Quad4, Hex8, Tet4, Tet10
#include("hierarchical.jl") # P-elements
#include("mortar_elements.jl") # Mortar elements
@@ -48,7 +49,11 @@ export Dirichlet
include("heat.jl")
export Heat
export assemble
export assemble, assemble!
function assemble!(problem::Problem, element::Element, time=0.0)
assemble!(problem.assembly, problem, element, time)
end
### ASSEMBLY + SOLVE ###
include("assembly.jl")
+17 -19
View File
@@ -30,20 +30,17 @@ function assemble!(assembly::Assembly, problem::Problem{Dirichlet}, element::Ele
field_name = get_parent_field_name(problem)
gdofs = get_gdofs(element, field_dim)
# if problem.properties.formulation == :dual_basis
if problem.properties.dual_basis
De, Me, Ae = get_dualbasis(element, time)
# else
# Ae = eye(nnodes)
# De = zeros(nnodes, nnodes)
# for (w, xi) in get_integration_points(element, Val{3})
# N = element(xi, time)
# detJ = element(xi, time, Val{:detJ})
# De += w*N'*N*detJ
# end
# end
# De = Ae = eye(nnodes)
else
Ae = eye(nnodes)
De = zeros(nnodes, nnodes)
for ip in get_integration_points(element)
N = element(ip, time)
detJ = element(ip, time, Val{:detJ})
De += ip.weight*N'*N*detJ
end
end
# left hand side
for i=1:field_dim
@@ -55,20 +52,21 @@ function assemble!(assembly::Assembly, problem::Problem{Dirichlet}, element::Ele
end
# right hand side
for (w, xi) in get_integration_points(element, Val{3})
detJ = element(xi, time, Val{:detJ})
N = element(xi, time)
for ip in get_integration_points(element)
detJ = element(ip, time, Val{:detJ})
w = ip.weight*detJ
N = element(ip, time)
for i=1:field_dim
ldofs = gdofs[i:field_dim:end]
if haskey(element, field_name*" $i")
g = element(field_name*" $i", xi, time)
g = element(field_name*" $i", ip, time)
if true
haskey(element, "displacement") || continue
g_prev = element(field_name, xi, time)
g_prev = element(field_name, ip, time)
g -= g_prev[i]
end
add!(assembly.g, ldofs, w*g*Ae*N'*detJ)
add!(assembly.g, ldofs, w*g*Ae*N')
end
end
+48 -55
View File
@@ -1,55 +1,40 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
""" Concrete Elasticity type. """
""" Elasticity problem
"""
type Elasticity <: FieldProblem
# these are found from problem.properties for type Problem{Elasticity}
formulation :: Symbol
finite_strain :: Bool
use_forwarddiff :: Bool
end
function Elasticity()
# formulations: plane_stress, plane_strain, continuum
return Elasticity(:continuum, true, false)
return Elasticity(:continuum, true)
end
# in case of experimenting new things;
# 1. import JuliaFEM.Core: assemble!
# 2. copy/paste assemble! code to notebook
# 3. change to last argument, i.e. ::Type{Val{:plane_stress}} to ::Type{Val{:my_formulation}}
# 4. when running code: set problem.properties.formulation = :my_formulation
# 5. let multiple dispatch do the magic for you
function get_unknown_field_name(::Type{Elasticity})
function get_unknown_field_name(problem::Problem{Elasticity})
return "displacement"
end
function get_formulation_type(problem::Problem{Elasticity})
# we are solving residual and add increment to previous solution vector
return :incremental
#return :total
end
function assemble!(assembly::Assembly, problem::Problem{Elasticity}, element::Element, time::Real)
function assemble!(assembly::Assembly, problem::Problem{Elasticity}, element::Element, time=0.0)
props = problem.properties
gdofs = get_gdofs(problem, element)
if props.use_forwarddiff
Kt, f = assemble(problem, element, time, Val{:forwarddiff})
elseif props.formulation == :continuum
Kt, f = assemble(problem, element, time, Val{:continuum})
elseif (props.formulation == :plane_stress) || (props.formulation == :plane_strain)
if problem.properties.formulation in [:plane_stress, :plane_strain]
Kt, f = assemble(problem, element, time, Val{:plane})
else
Kt, f = assemble(problem, element, time, Val{problem.properties.formulation})
end
add!(assembly.K, gdofs, gdofs, Kt)
add!(assembly.f, gdofs, f)
end
function assemble(problem::Problem{Elasticity}, element::Element, time=0.0)
problem.properties
if problem.properties.formulation in [:plane_stress, :plane_strain]
return assemble(problem, element, time, Val{:plane})
end
return assemble(problem, element, time, Val{problem.properties.formulation})
return Kt, f
end
""" Elasticity equations for 2d cases. """
@@ -63,16 +48,17 @@ function assemble{El<:Union{Tri3,Tri6,Quad4}}(problem::Problem{Elasticity}, elem
Kt = zeros(dim*nnodes, dim*nnodes)
f = zeros(dim*nnodes)
for (w, xi) in get_integration_points(element)
for ip in get_integration_points(element)
detJ = element(xi, time, Val{:detJ})
N = element(xi, time)
dN = element(xi, time, Val{:Grad})
detJ = element(ip, time, Val{:detJ})
w = ip.weight*detJ
N = element(ip, time)
dN = element(ip, time, Val{:Grad})
# kinematics; calculate deformation gradient and strain
gradu = zeros(dim, dim)
if haskey(element, "displacement")
gradu += element("displacement", xi, time, Val{:Grad})
gradu += element("displacement", ip, time, Val{:Grad})
end
strain = zeros(dim , dim)
strain += 1/2*(gradu' + gradu)
@@ -84,8 +70,8 @@ function assemble{El<:Union{Tri3,Tri6,Quad4}}(problem::Problem{Elasticity}, elem
# constitutive equations; material model (isotropic linear material here)
# get_material(problem, element, ...)
E = element("youngs modulus", xi, time)
nu = element("poissons ratio", xi, time)
E = element("youngs modulus", ip, time)
nu = element("poissons ratio", ip, time)
if props.formulation == :plane_stress
D = E/(1.0 - nu^2) .* [
1.0 nu 0.0
@@ -99,9 +85,15 @@ function assemble{El<:Union{Tri3,Tri6,Quad4}}(problem::Problem{Elasticity}, elem
else
error("unknown plane formulation: $(props.formulation)")
end
# calculate stress
S = D*[strain[1,1]; strain[2,2]; 2*strain[1,2]]
strain_vec = [strain[1,1]; strain[2,2]; 2*strain[1,2]]
stress_vec = D*strain_vec
stress = [stress_vec[1] stress_vec[3]; stress_vec[3] stress_vec[2]]
cauchy_stress = F'*stress*F/det(F)
cauchy_stress = [cauchy_stress[1,1]; cauchy_stress[2,2]; cauchy_stress[1,2]]
update!(ip, "strain", time => strain_vec)
update!(ip, "stress", time => cauchy_stress)
# add contributions: material and geometric stiffness + internal forces
fill!(BL, 0.0)
@@ -121,29 +113,29 @@ function assemble{El<:Union{Tri3,Tri6,Quad4}}(problem::Problem{Elasticity}, elem
BNL[4, 2*(i-1)+2] = dN[2,i]
end
S2 = zeros(2*dim, 2*dim)
S2[1,1] = S[1]
S2[2,2] = S[2]
S2[1,2] = S2[2,1] = S[3]
S2[1,1] = stress_vec[1]
S2[2,2] = stress_vec[2]
S2[1,2] = S2[2,1] = stress_vec[3]
S2[3:4,3:4] = S2[1:2,1:2]
Kt += w*BL'*D*BL*detJ # material stiffness
Kt += w*BL'*D*BL # material stiffness
if props.finite_strain # add geometric stiffness
Kt += w*BNL'*S2*BNL*detJ # geometric stiffness
Kt += w*BNL'*S2*BNL # geometric stiffness
end
if get_formulation_type(problem) == :incremental
f -= w*BL'*S*detJ # internal force
f -= w*BL'*stress_vec # internal force
end
# volume load
if haskey(element, "displacement load")
b = element("displacement load", xi, time)
f += w*vec(N'*b)*detJ
b = element("displacement load", ip, time)
f += w*vec(N'*b)
end
for i=1:dim
if haskey(element, "displacement load $i")
b = element("displacement load $i", xi, time)
f[i:dim:end] += w*vec(b*N)*detJ
b = element("displacement load $i", ip, time)
f[i:dim:end] += w*vec(b*N)
end
end
@@ -160,29 +152,30 @@ function assemble{El<:Union{Seg2,Seg3}}(problem::Problem{Elasticity}, element::E
Kt = zeros(dim*nnodes, dim*nnodes)
f = zeros(dim*nnodes)
for (w, xi) in get_integration_points(element)
for ip in get_integration_points(element)
detJ = element(xi, time, Val{:detJ})
N = element(xi, time)
detJ = element(ip, time, Val{:detJ})
w = ip.weight*detJ
N = element(ip, time)
if haskey(element, "displacement traction force")
T = element("displacement traction force", xi, time)
f += w*vec(T*N)*detJ
T = element("displacement traction force", ip, time)
f += w*vec(T*N)
end
for i=1:dim
# traction force for ith component
if haskey(element, "displacement traction force $i")
T = element("displacement traction force $i", xi, time)
f[i:dim:end] += w*vec(T*N)*detJ
T = element("displacement traction force $i", ip, time)
f[i:dim:end] += w*vec(T*N)
end
end
if haskey(element, "nt displacement traction force")
# traction force given in normal-tangential direction
T = element("nt displacement traction force", xi, time)
Q = element("normal-tangential coordinates", xi, time)
f += w*vec(Q'*T*N)*detJ
T = element("nt displacement traction force", ip, time)
Q = element("normal-tangential coordinates", ip, time)
f += w*vec(Q'*T*N)
end
end
+49 -26
View File
@@ -3,17 +3,19 @@
abstract AbstractElement
typealias Node Vector{Float64}
type Element{E<:AbstractElement}
id :: Int
connectivity :: Vector{Int}
integration_points :: Vector{IP}
fields :: Dict{ASCIIString, Field}
properties :: E
end
function Element{E<:AbstractElement}(::Type{E}, connectivity=[], id=-1, fields=Dict(), properties...)
Element{E}(id, connectivity, fields, E(properties...))
variant = E(properties...)
ips = get_integration_points(variant)
integration_points = [IP(i, w, xi) for (i, (w, xi)) in enumerate(ips)]
Element{E}(id, connectivity, integration_points, fields, variant)
end
function getindex(element::Element, field_name::ASCIIString)
@@ -28,26 +30,19 @@ function call(element::Element, field_name::ASCIIString, time=0.0)
return element[field_name](time)
end
function call(element::Element, xi::Vector, time=0.0)
get_basis(element, xi, time)
function call(element::Element, ip, time=0.0)
get_basis(element, ip, time)
end
function call(element::Element, field_name::ASCIIString, xi::Vector, time=0.0)
field = element[field_name](time)
isa(field, DCTI) && return field.data
basis = element(xi, time)
return basis*field
end
function call(element::Element, xi::Vector, time, ::Type{Val{:Jacobian}})
function call(element::Element, ip, time, ::Type{Val{:Jacobian}})
X = element["geometry"](time)
dN = get_dbasis(element, xi, time)
dN = get_dbasis(element, ip, time)
J = sum([kron(dN[:,i], X[i]') for i=1:length(X)])
return J
end
function call(element::Element, xi::Vector, time, ::Type{Val{:detJ}})
J = element(xi, time, Val{:Jacobian})
function call(element::Element, ip, time, ::Type{Val{:detJ}})
J = element(ip, time, Val{:Jacobian})
n, m = size(J)
if n == m # volume element
return det(J)
@@ -60,13 +55,23 @@ function call(element::Element, xi::Vector, time, ::Type{Val{:detJ}})
end
end
function call(element::Element, xi::Vector, time, ::Type{Val{:Grad}})
J = element(xi, time, Val{:Jacobian})
return inv(J)*get_dbasis(element, xi, time)
function call(element::Element, ip, time, ::Type{Val{:Grad}})
J = element(ip, time, Val{:Jacobian})
return inv(J)*get_dbasis(element, ip, time)
end
function call(element::Element, field_name, xi::Vector, time, ::Type{Val{:Grad}})
element(xi, time, Val{:Grad})*element[field_name](time)
function call(element::Element, field_name::ASCIIString, ip, time, ::Type{Val{:Grad}})
element(ip, time, Val{:Grad})*element[field_name](time)
end
function call(element::Element, field_name::ASCIIString, ip, time=0.0)
field = element[field_name](time)
isa(field, DCTI) && return field.data
basis = element(ip, time)
n = length(element)
m = length(field)
@assert n == m
return sum([field[i]*basis[i] for i=1:n])
end
#function get_jacobian{E}(element::Element{E}, xi::Vector, time=0.0)
@@ -122,6 +127,9 @@ function get_dbasis(element::Element, xi::Vector, time)
basis(xi) = vec(get_basis(element, xi, time))
return ForwardDiff.jacobian(basis, xi, cache=autodiffcache)'
end
function get_dbasis(element::Element, ip::IP, time)
get_dbasis(element, ip.coords, time)
end
""" Check existence of field. """
function haskey(element::Element, field_name)
@@ -132,6 +140,20 @@ function get_connectivity(element::Element)
return element.connectivity
end
function get_integration_points(element::Element)
return element.integration_points
end
""" This is a special case, temporarily change order
of integration scheme mainly for mass matrix.
"""
function get_integration_points(element::Element, change_order::Int)
order = get_integration_order(element.properties)
order += change_order
ips = get_integration_points(element.properties, Val{order})
return [IP(i, w, xi) for (i, (w, xi)) in enumerate(ips)]
end
function get_gdofs(element::Element)
return get_gdofs(element, 1)
end
@@ -141,11 +163,12 @@ function get_dualbasis(element::Element, time)
nnodes = length(element)
De = zeros(nnodes, nnodes)
Me = zeros(nnodes, nnodes)
for (w, xi) in get_integration_points(element, Val{3})
detJ = element(xi, time, Val{:detJ})
N = element(xi, time)
De += w*diagm(vec(N))*detJ
Me += w*N'*N*detJ
for ip in get_integration_points(element)
detJ = element(ip, time, Val{:detJ})
w = ip.weight*detJ
N = element(ip, time)
De += w*diagm(vec(N))
Me += w*N'*N
end
return De, Me, De*inv(Me)
end
+29
View File
@@ -270,6 +270,35 @@ function Base.done(f::DVTI, s)
return s > length(f.data)
end
""" Update time-dependent fields with new values.
Examples
--------
julia> f = Field(0.0 => 1.0)
julia> update!(f, 1.0 => 2.0)
Now field has two (time, value) pairs: (0.0, 1.0) and (1.0, 2.0)
Notes
-----
Time vector is assumed to be ordered t_i-1 < t_i < t_i+1. If updating
field with already existing time the old value is replaced with new one.
"""
function update!{T}(field::Union{DCTV, DVTV}, val::Pair{Float64, T})
time, data = val
if isapprox(last(field).time, time)
last(field).data = data
else
push!(field.data, Increment(val...))
end
end
function update!{T}(field::Union{DCTI, DVTI}, val::T)
field.data = val
end
### Accessing continuous fields
function Base.call(field::CVTI, xi::Vector)
+19 -17
View File
@@ -51,9 +51,9 @@ end
### 1d elements
typealias CartesianLineElement Union{Element{Seg2}, Element{Seg3}}
typealias CartesianSurfaceElement Union{Element{Quad4}}
typealias CartesianVolumeElement Union{Element{Hex8}}
typealias CartesianLineElement Union{Seg2, Seg3}
typealias CartesianSurfaceElement Union{Quad4}
typealias CartesianVolumeElement Union{Hex8}
function get_integration_points(element::CartesianLineElement, ::Type{Val{1}})
w, xi = get_integration_points(Val{1})
@@ -100,7 +100,7 @@ end
# http://math2.uncc.edu/~shaodeng/TEACHING/math5172/Lectures/Lect_15.PDF
# http://libmesh.github.io/doxygen/quadrature__gauss__2D_8C_source.html
typealias TriangularElement Union{Element{Tri3}, Element{Tri6}}
typealias TriangularElement Union{Tri3, Tri6}
function get_integration_points(element::TriangularElement, ::Type{Val{1}})
weights = [0.5]
@@ -152,7 +152,7 @@ end
### 3d elements
typealias TetrahedralElement Union{Element{Tet4}, Element{Tet10}}
typealias TetrahedralElement Union{Tet4, Tet10}
function get_integration_points(element::TetrahedralElement, ::Type{Val{1}})
weights = 1.0/6.0*[1.0]
@@ -191,23 +191,25 @@ end
### default number of integration points for each element
### 2 for linear elements, 3 for quadratic
typealias LinearElement Union{
Element{Seg2},
Element{Tri3},
Element{Quad4},
Element{Tet4},
Element{Hex8}}
typealias LinearElement Union{Seg2, Tri3, Quad4, Tet4, Hex8}
typealias QuadraticElement Union{
Element{Seg3},
Element{Tri6},
Element{Tet10}}
typealias QuadraticElement Union{Seg3, Tri6, Tet10}
function get_integration_points(element::LinearElement; order=2)
function get_integration_order(element::LinearElement)
return 2
end
function get_integration_order(element::QuadraticElement)
return 3
end
function get_integration_points(element::LinearElement)
order = get_integration_order(element)
get_integration_points(element, Val{order})
end
function get_integration_points(element::QuadraticElement; order=2)
function get_integration_points(element::QuadraticElement)
order= get_integration_order(element)
get_integration_points(element, Val{order})
end
+2 -2
View File
@@ -42,8 +42,8 @@ macro create_lagrange_element(element_name, element_description, X, P)
A = calculate_lagrange_basis_coefficients($P, $X)
#basis(xi) = C*$P(xi)
function get_basis(element::Element{$eltype}, xi::Vector, time)
return transpose($P(xi))*A
function get_basis(element::Element{$eltype}, ip, time)
return transpose($P(ip))*A
end
function size(element::Element{$eltype})
+3 -159
View File
@@ -1,167 +1,11 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
if VERSION >= v"0.5-"
if VERSION >= v"0.5-dev+7720"
using Base.Test
else
using BaseTestNext
const Test = BaseTestNext
end
abstract TestResult
type NormalTestResult <: TestResult
test_function :: Function
result
end
type CriticalTestResult <: TestResult
filename :: ASCIIString
message
end
function TestResult(test_function::Function, result)
NormalTestResult(test_function, result)
end
function TestResult(filename::ASCIIString, message)
CriticalTestResult(filename, message)
end
global test_results = []
function get_test_functions(func)
return Function[]
end
""" Return all functions from module with name starting test """
function get_test_functions(mod::Module)
test_function_names = filter((k) -> startswith(string(k), "test_"), names(mod, true))
if haskey(ENV, "JULIAFEM_TEST_SLOW")
info("JULIAFEM_TEST_SLOW set, testing also tests that are taking a long time")
slow_test_functions = filter((k) -> startswith(string(k), "slow_test_"), names(mod, true))
append!(test_function_names, slow_test_functions)
end
test_function_expressions = map((k) -> :($mod.$k), test_function_names)
test_functions = map(eval, test_function_expressions)
return test_functions
end
""" Run tests from some file. """
function run_test(filename::ASCIIString, test_function=nothing)
info("running tests from $filename")
test_module = nothing
try
test_module = include(filename)
catch error
warn("Unable to include file $filename for testing.")
err = Base.showerror(Base.STDOUT, error)
push!(test_results, TestResult(filename, "Unable to include file: $error"))
return
end
if isa(test_function, Void)
test_functions = get_test_functions(test_module)
else
test_functions = [eval( :($test_module.$test_function) )]
end
if length(test_functions) == 0
warn("Unable to get test functions for file $filename. Define test functions inside module, look for test_heat.jl for concrete example how to do that.")
push!(test_results, TestResult(filename, "Unable to find test functions"))
return
end
for test_function in test_functions
run_test(test_function)
end
end
""" Run single test set. """
function run_test(test_function::Function)
allok = true
function test_handler(r::Base.Test.Success)
print(".")
result = TestResult(test_function, r)
push!(test_results, result)
end
function test_handler(r::Base.Test.Failure)
allok = false
print(" [\x1b[31mFAIL\x1b[0m]")
push!(test_results, TestResult(test_function, r))
println()
println("Test failed: $(r.expr)")
#warn("partially evaluated expression: $(r.resultexpr)")
end
function test_handler(r::Base.Test.Error)
allok = false
println(" [\x1b[31mERROR\x1b[0m]")
push!(test_results, TestResult(test_function, r))
println("Error when testing: $(r.expr)")
end
Base.Test.with_handler(test_handler) do
print("TEST: $test_function() ")
# TODO: print docstring of test function if defined.
#@doc($test_function)
try
test_function()
catch error
allok = false
println("[\x1b[31mCRITICAL\x1b[0m]")
println("Testing $test_function() stopped for critical error:\n$error")
Base.showerror(Base.STDOUT, error)
println()
println("Cannot continue to test function $test_function()")
push!(test_results, TestResult("$test_function", error))
end
if allok
println(" [\x1b[32mPASS\x1b[0m]")
else
filename, linenum = Base.functionloc(test_function)
println("Test $test_function() failed: file $filename, line $linenum.")
println()
end
end
end
function print_test_statistics()
info("################# TEST RESULTS #####################")
passed = 0
failed = 0
errors = 0
critical = 0
for result in test_results
if isa(result, NormalTestResult)
if isa(result.result, Base.Test.Success)
passed += 1
continue
elseif isa(result.result, Base.Test.Failure)
failed += 1
filename, linenum = Base.functionloc(result.test_function)
functionname = Base.function_name(result.test_function)
warn("test failed: $(result.result.expr), partially evaluated expression: $(result.result.expr)")
warn("in function $functionname, file $filename, line $linenum")
continue
elseif isa(result.result, Base.Test.Error)
errors += 1
filename, linenum = Base.functionloc(result.test_function)
functionname = Base.function_name(result.test_function)
warn("error in function: $(result.result.expr)")
warn("in function $functionname, file $filename, line $linenum")
continue
end
elseif isa(result, CriticalTestResult)
critical += 1
warn("Critical error on $(result.filename): $(result.message)")
end
end
info("$passed test passed, $failed test failed, $errors errors, $critical critical failures")
return passed, failed, errors, critical
end
export @test, @testset, @test_throws, run_test, print_test_statistics
export @test, @testset, @test_throws
+37 -51
View File
@@ -3,77 +3,63 @@
typealias Node Vector{Float64}
abstract AbstractPoint
"""
Integration point
xi
(dimensionless) coordinates of integration point
weight
integration weight
fields
FieldSet what can be used to store internal variables, stress, strain, ...
"""
immutable IntegrationPoint
xi :: Vector
type Point{P<:AbstractPoint}
id :: Int
weight :: Float64
coords :: Vector{Float64}
fields :: Dict{ASCIIString, Field}
changed :: Bool
properties :: P
end
function IntegrationPoint(xi, weight)
return IntegrationPoint(xi, weight, FieldSet(), false)
function setindex!{T}(point::Point, val::Pair{Float64, T}, field_name::ASCIIString)
point.fields[field_name] = Field(val)
end
function setindex!{T<:ForwardDiff.ForwardDiffNumber}(ip::IntegrationPoint, data::Array{T,2}, field_name::ASCIIString)
data = ForwardDiff.get_value(data)
setindex!(ip, data, field_name)
end
function setindex!(ip::IntegrationPoint, data, field_name)
ip.fields[field_name] = Field(data)
ip.changed = true
function getindex(point::Point, field_name::ASCIIString)
return point.fields[field_name]
end
function getindex(ip::IntegrationPoint, field_name::ASCIIString)
ip.fields[field_name]
function getindex(point::Point, idx::Int)
return point.coords[idx]
end
function convert(::Type{Number}, ip::IntegrationPoint)
return ip.xi
function haskey(point::Point, field_name::ASCIIString)
return haskey(point.fields, field_name)
end
function call(field::CVTI, ip::IntegrationPoint)
return call(field, ip.xi)
function call(point::Point, field_name::ASCIIString, time::Float64=0.0)
point.fields[field_name](time).data
end
function call(basis::CVTI, field::DCTI, ip::IntegrationPoint)
call(basis, field, ip.xi)
function update!{T}(point::Point, field_name, val::Pair{Float64, T})
if haskey(point, field_name)
update!(point[field_name], val)
else
point[field_name] = val
end
end
function call(basis::CVTI, field::DVTI, ip::IntegrationPoint, ::Type{Val{:grad}})
call(basis, field, ip.xi, Val{:grad})
#= TODO: in future
type Node <: AbstractPoint
end
function call(basis::CVTI, field::DVTI, ip::IntegrationPoint)
call(basis, field, ip.xi)
type MaterialPoint <: AbstractPoint
end
=#
type IntegrationPoint <: AbstractPoint
end
function call(basis::CVTI, geometry::DVTI, field::Union{DCTI, DVTI}, ip::IntegrationPoint, ::Type{Val{:grad}})
call(basis, geometry, field, ip.xi, Val{:grad})
typealias IP Point{IntegrationPoint}
function IP(id, weight, coords)
return IP(id, weight, coords, Dict(), IntegrationPoint())
end
function convert(::Type{IP}, data::Tuple{Float64, Vector{Float64}})
weight, coords = data
return IP(-1, weight, coords)
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, field::Field, ip::IntegrationPoint, ::Type{Val{:grad}})
# return call(basis, field, 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
#function Base.call(basis::Basis, field::Field, ip::IntegrationPoint)
# return call(basis, field, ip.xi)
#end
+65
View File
@@ -0,0 +1,65 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using JuliaFEM
using JuliaFEM.Test
importall Base
import JuliaFEM: get_basis, get_dbasis, get_integration_points
type MyQuad4 <: AbstractElement
end
function get_basis(element::Element{MyQuad4}, xi, time)
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])]
end
function get_dbasis(element::Element{MyQuad4}, xi, time)
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])]
end
function get_integration_points(element::MyQuad4)
[
(1.0, 1.0/sqrt(3.0)*[-1, -1]),
(1.0, 1.0/sqrt(3.0)*[ 1, -1]),
(1.0, 1.0/sqrt(3.0)*[ 1, 1]),
(1.0, 1.0/sqrt(3.0)*[-1, 1])
]
end
function length(element::Element{MyQuad4})
return 4
end
function size(element::Element{MyQuad4})
return (2, 4)
end
@testset "test new element" begin
el = Element(MyQuad4)
el["geometry"] = Vector{Float64}[[0.0,0.0], [1.0,0.0], [1.0,1.0], [0.0,1.0]]
el["displacement"] = Vector{Float64}[[0.0,0.0], [0.0,0.0], [1.0,0.0], [0.0,0.0]]
@test isapprox(el("geometry", [0.0, 0.0]), [0.5, 0.5])
@test isapprox(el("displacement", [0.0, 0.0], 0.0), [0.25, 0.0])
el["temperature thermal conductivity"] = 6.0
dim = length(el)
K = zeros(dim, dim)
A = 0.0
time = 0.0
for ip in get_integration_points(el)
dN = el(ip, time, Val{:Grad})
detJ = el(ip, time, Val{:detJ})
w = ip.weight*detJ
c = el("temperature thermal conductivity", ip, time)
K += w*c*dN'*dN
A += w
end
@test isapprox(A, 1.0)
K_expected = [
4.0 -1.0 -2.0 -1.0
-1.0 4.0 -1.0 -2.0
-2.0 -1.0 4.0 -1.0
-1.0 -2.0 -1.0 4.0]
@test isapprox(K, K_expected)
end
+54 -15
View File
@@ -1,27 +1,66 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using JuliaFEM
using JuliaFEM.Test
using JuliaFEM.Core: Tri3, Seg2, Dirichlet, Assembly, assemble!, Node, Problem
#=
In [36]: C = Matrix([[0], [30], [15]]) # node coordinates
In [37]: A = Matrix([P.subs({x: C[i,0]}).T for i in range(len(P))])
In [38]: N = P.T*A.inv()
In [39]: Me = integrate(N.T*N, (x, 0, 30))
In [40]: De = diag(*integrate(N, (x, 0, 30)))
In [41]: Me
Out[41]:
Matrix([
[ 4, -1, 2],
[-1, 4, 2],
[ 2, 2, 16]])
In [42]: De
Out[42]:
Matrix([
[5, 0, 0],
[0, 5, 0],
[0, 0, 20]])
=#
@testset "dirichlet problem in 1 dimension" begin
element = Seg2([1, 2])
element["geometry"] = Node[[1.0, 1.0], [0.0, 1.0]]
element["temperature"] = 0.0
problem = Problem(Dirichlet, "test problem", 1, "temperature")
push!(problem, element)
assemble!(problem, 0.0)
C1 = full(problem.assembly.C1)
info("C1")
dump(C1)
C2 = full(problem.assembly.C2)
g = full(problem.assembly.g)
element = Element(Seg2, [1, 2])
element["geometry"] = Vector{Float64}[[0.0, 0.0], [6.0, 0.0]]
element["temperature 1"] = 0.0
p1 = Problem(Dirichlet, "test problem 1", 1, "temperature")
p1.properties.dual_basis = false
p2 = Problem(Dirichlet, "test problem 2", 1, "temperature")
assemble!(p1, element)
assemble!(p2, element)
C1 = full(p1.assembly.C1)
C2 = full(p1.assembly.C2)
@test isapprox(C1, C2)
@test isapprox(C1, 1/6*[2 1; 1 2])
@test isapprox(g, [0.0, 0.0])
@test isapprox(C1, [2.0 1.0; 1.0 2.0])
C1 = full(p2.assembly.C1)
C2 = full(p2.assembly.C2)
@test isapprox(C1, C2)
@test isapprox(C1, [3.0 0.0; 0.0 3.0])
element = Element(Seg3, [1, 2, 3])
element["geometry"] = Vector{Float64}[[0.0, 0.0], [30.0, 0.0], [15.0, 0.0]]
element["temperature 1"] = 0.0
p1 = Problem(Dirichlet, "quadratic 1", 1, "temperature")
p1.properties.dual_basis = false
p2 = Problem(Dirichlet, "quadratic 1", 1, "temperature")
assemble!(p1, element)
assemble!(p2, element)
C1 = full(p1.assembly.C1)
C2 = full(p1.assembly.C2)
@test isapprox(C1, C2)
@test isapprox(C1, [4.0 -1.0 2.0; -1.0 4.0 2.0; 2.0 2.0 16.0])
C1 = full(p2.assembly.C1)
C2 = full(p2.assembly.C2)
@test isapprox(C1, C2)
@test isapprox(C1, [5.0 0.0 0.0; 0.0 5.0 0.0; 0.0 0.0 20.0])
end
#=
@testset "dirichlet problem using tri3 surface element" begin
element = Tri3([1, 2, 3])
element["geometry"] = Node[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
@@ -34,7 +73,6 @@ end
@test isapprox(C1, C2)
@test isapprox(C1, 1/24*[2 1 1; 1 2 1; 1 1 2])
end
=#
@testset "dirichlet problem in 2 dimensions" begin
element = Seg2([1, 2])
@@ -72,4 +110,5 @@ end
@test isapprox(C1, C1_expected)
@test isapprox(g, [0.0, 0.0, 0.0, 0.0])
end
=#
@@ -35,6 +35,7 @@ using JuliaFEM.Test
solver = Solver("solve block problem")
push!(solver, block, bc_sym)
call(solver)
f = 288.0
g = 576.0
E = 288.0
@@ -43,4 +44,19 @@ using JuliaFEM.Test
u3 = reshape(block.assembly.u, 2, 4)[:,3]
info("u3 = $u3")
@test isapprox(u3, u3_expected)
info("strain")
for ip in get_integration_points(elements[1])
eps = ip("strain")
@printf "%i | %8.3f %8.3f | %8.3f %8.3f %8.3f\n" ip.id ip.coords[1] ip.coords[2] eps[1] eps[2] eps[3]
@test isapprox(eps, [u3; 0.0])
end
info("stress")
for ip in get_integration_points(elements[1])
sig = ip("stress")
@printf "%i | %8.3f %8.3f | %8.3f %8.3f %8.3f\n" ip.id ip.coords[1] ip.coords[2] sig[1] sig[2] sig[3]
@test isapprox(sig, [0.0; g; 0.0])
end
end
@@ -8,15 +8,21 @@ using JuliaFEM.Test
@testset "test 2d nonlinear elasticity with surface load" begin
meshfile = "/geometry/2d_block/BLOCK_1elem.med"
mesh = parse_aster_med_file(Pkg.dir("JuliaFEM")*meshfile)
# field problem
body = Problem(Elasticity, "BLOCK", 2)
body.properties.formulation = :plane_stress
body_elements = aster_create_elements(mesh, :BLOCK, :QU4)
update!(body_elements, "youngs modulus", 900.0)
update!(body_elements, "poissons ratio", 0.25)
trac_elements = aster_create_elements(mesh, :TOP, :SE2)
update!(trac_elements, "displacement traction force 2", -100.0)
push!(body, body_elements..., trac_elements...)
block = Problem(Elasticity, "BLOCK", 2)
block.properties.formulation = :plane_stress
elements = aster_create_elements(mesh, :BLOCK, :QU4)
update!(elements, "youngs modulus", 288.0)
update!(elements, "poissons ratio", 1/3)
update!(elements, "displacement load 2", 576.0)
push!(block, elements...)
traction = aster_create_elements(mesh, :TOP, :SE2)
update!(traction, "displacement traction force 2", 288.0)
push!(block, traction...)
# boundary conditions
bc_sym = Problem(Dirichlet, "symmetry bc", 2, "displacement")
bc_elements_left = aster_create_elements(mesh, :LEFT, :SE2)
@@ -24,11 +30,34 @@ using JuliaFEM.Test
update!(bc_elements_left, "displacement 1", 0.0)
update!(bc_elements_bottom, "displacement 2", 0.0)
push!(bc_sym, bc_elements_left..., bc_elements_bottom...)
solver = Solver("solve block problem")
push!(solver, body, bc_sym)
push!(solver, block, bc_sym)
call(solver)
# result is verified using code aster
u3_expected = [3.17431158889468E-02, -1.38591518927826E-01]
u3 = reshape(body.assembly.u, 2, 4)[:,3]
@test isapprox(u3, u3_expected)
# from code aster
u3_expected = [-4.92316106779943E-01, 7.96321884292103E-01]
eps_zz = -3.71128811855451E-01
eps_expected = [-3.71128532282463E-01, 1.11338615599337E+00, 0.0]
sig_expected = [ 3.36174888827909E-05, 2.23478729403118E+03, 0.0]
u3 = reshape(block.assembly.u, 2, 4)[:, 3]
info("u3 = $u3")
@test isapprox(u3, u3_expected, atol=1.0e-5)
info("strain")
for ip in get_integration_points(elements[1])
eps = ip("strain")
#eps = [eps[1,1]; eps[2,2]; eps[1,2]]
@printf "%i | %8.3f %8.3f | %8.3f %8.3f %8.3f\n" ip.id ip.coords[1] ip.coords[2] eps[1] eps[2] eps[3]
@test isapprox(eps, eps_expected)
end
info("stress")
for ip in get_integration_points(elements[1])
sig = ip("stress")
#sig = [sig[1,1]; sig[2,2]; sig[1,2]]
@printf "%i | %8.3f %8.3f | %8.3f %8.3f %8.3f\n" ip.id ip.coords[1] ip.coords[2] sig[1] sig[2] sig[3]
@test isapprox(sig, sig_expected)
end
end
@@ -7,7 +7,7 @@ using JuliaFEM
using JuliaFEM.Test
@testset "test 2d linear elasticity local matrices" begin
element = Element(Quad4)
element = Element(Quad4, [1, 2, 3, 4])
element["geometry"] = Vector{Float64}[
[0.0, 0.0],
[1.0, 0.0],
@@ -19,7 +19,7 @@ using JuliaFEM.Test
problem = Problem(Elasticity, "[0x1] x [0x1] block", 2)
problem.properties.formulation = :plane_stress
K, f = assemble(problem, element)
K, f = assemble!(problem, element)
K_expected = [
144 54 -90 0 -72 -54 18 0
+18 -7
View File
@@ -1,15 +1,26 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
module FieldTests
using JuliaFEM
using JuliaFEM.Test
using JuliaFEM.Core: Field
@testset "test updating time dependent fields" begin
f = Field(0.0 => 1.0)
@test last(f).time == 0.0
@test last(f).data == 1.0
update!(f, 0.0 => 2.0)
@test last(f).time == 0.0
@test last(f).data == 2.0
@test length(f) == 1
update!(f, 1.0 => 3.0)
@test last(f).time == 1.0
@test last(f).data == 3.0
@test length(f) == 2
end
function test_create_field()
@testset "test updating time invariant fields" begin
f = Field(1.0)
end
@test f.data == 1.0
update!(f, 2.0)
@test f.data == 2.0
end
+14
View File
@@ -0,0 +1,14 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
using JuliaFEM
using JuliaFEM.Test
@testset "test integration point" begin
ip = IP(1, 1.0, sqrt(1.0/3.0)*[-1.0, -1.0])
strain = [1.0 2.0; 3.0 4.0]
update!(ip, "strain", 0.0 => strain)
@test isapprox(ip("strain", 0.0), strain)
@test isapprox(ip("strain"), strain)
end