mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-20 20:22:03 +00:00
substructuring, fixed tests, possibility to save to integration points
This commit is contained in:
@@ -44,7 +44,8 @@ function parse_element_section(model, header, data)
|
||||
eldims = Dict(
|
||||
"C3D10" => 10,
|
||||
"C3D4" => 4,
|
||||
"S3" => 3)
|
||||
"S3" => 3,
|
||||
"STRI65" => 6)
|
||||
eltype = header["options"]["TYPE"]
|
||||
if !(eltype in keys(eldims))
|
||||
throw("Element $eltype dimension information missing")
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
|
||||
# Functions to handle global assembly of problem
|
||||
|
||||
type CAssembly
|
||||
interior_dofs :: Vector{Int}
|
||||
boundary_dofs :: Vector{Int}
|
||||
F :: Factorization
|
||||
Kc :: SparseMatrixCSC
|
||||
fc :: SparseMatrixCSC
|
||||
Ki :: SparseMatrixCSC
|
||||
fi :: SparseMatrixCSC
|
||||
end
|
||||
|
||||
function assemble!(assembly::Assembly, problem::AllProblems, time::Float64, empty_assembly::Bool=true)
|
||||
if empty_assembly
|
||||
empty!(assembly)
|
||||
@@ -20,6 +30,73 @@ function assemble(problem::AllProblems, time::Float64)
|
||||
return assembly
|
||||
end
|
||||
|
||||
""" Return condensed system. """
|
||||
function assemble(problem::FieldProblem, time::Float64, boundary_dofs::Vector{Int})
|
||||
assembly = Assembly()
|
||||
for element in get_elements(problem)
|
||||
assemble!(assembly, problem, element, time)
|
||||
end
|
||||
return condensate(assembly, boundary_dofs)
|
||||
end
|
||||
|
||||
function condensate(assembly::Assembly, boundary_dofs_::Vector{Int})
|
||||
K = sparse(assembly.stiffness_matrix)
|
||||
all_dofs = unique(assembly.stiffness_matrix.I)
|
||||
boundary_dofs = intersect(all_dofs, boundary_dofs_)
|
||||
interior_dofs = setdiff(all_dofs, boundary_dofs_)
|
||||
dim = size(K, 1)
|
||||
f = sparse(assembly.force_vector, dim, 1)
|
||||
|
||||
# check that matrix is symmetric
|
||||
asdf = maximum(abs(1/2*(K + K') - K))
|
||||
if asdf > 1.0e-6
|
||||
info(full(K))
|
||||
error("asdf $asdf > 1.0e-6")
|
||||
end
|
||||
|
||||
K = 1/2*(K + K')
|
||||
F::Factorization = cholfact(K[interior_dofs, interior_dofs])
|
||||
|
||||
# info("condensation: all dofs: ", all_dofs)
|
||||
# info("condensation: interior dofs: ", interior_dofs)
|
||||
# info("condensation: boundary dofs: ", boundary_dofs)
|
||||
# info("manually condensated")
|
||||
# Kman = K[boundary_dofs, boundary_dofs] - K[boundary_dofs,interior_dofs] * inv(full(K[interior_dofs, interior_dofs])) * K[interior_dofs, boundary_dofs]
|
||||
# info("\n$(full(Kman))")
|
||||
#info("K = \n$(full(K))")
|
||||
#Ki = K[interior_dofs, boundary_dofs]
|
||||
Ki = K[interior_dofs, boundary_dofs]
|
||||
fi = f[interior_dofs]
|
||||
# info("condensated using factorization")
|
||||
# LL = K[boundary_dofs, boundary_dofs] - K[boundary_dofs, interior_dofs] * (K[interior_dofs, interior_dofs] \ K[interior_dofs, boundary_dofs])
|
||||
# info(LL)
|
||||
|
||||
Ks = F \ Ki
|
||||
Fs = F \ fi
|
||||
|
||||
dim = size(K, 1)
|
||||
Kc = spzeros(dim, dim)
|
||||
fc = spzeros(dim, 1)
|
||||
Kc[boundary_dofs, boundary_dofs] = K[boundary_dofs, boundary_dofs] - Ki' * Ks
|
||||
fc[boundary_dofs] = f[boundary_dofs] - Ki' * Fs
|
||||
|
||||
|
||||
return CAssembly(interior_dofs, boundary_dofs, F, Kc, fc, Ki, fi)
|
||||
end
|
||||
|
||||
function reconstruct!(ca::CAssembly, x::SparseMatrixCSC)
|
||||
# info("size of la = ", size(la))
|
||||
# info("size of ca.Ki = ", size(ca.Ki))
|
||||
# info("size of ca.fi = ", size(ca.fi))
|
||||
# info("size of la[ca.interior_dofs] = ", size(la[ca.interior_dofs]))
|
||||
# info("interior dofs: $(ca.interior_dofs)")
|
||||
# info("boundary dofs: $(ca.boundary_dofs)")
|
||||
# info("ca.fi = $(ca.fi')")
|
||||
# info("sol1 = ", full(ca.F \ ca.fi)')
|
||||
# info("sol2 = ", full(ca.F \ (ca.Ki*x[ca.boundary_dofs]))')
|
||||
x[ca.interior_dofs] += ca.F \ (ca.fi - ca.Ki*x[ca.boundary_dofs])
|
||||
end
|
||||
|
||||
function Base.(:+)(ass1::Assembly, ass2::Assembly)
|
||||
mass_matrix = ass1.mass_matrix + ass2.mass_matrix
|
||||
stiffness_matrix = ass1.stiffness_matrix + ass2.stiffness_matrix
|
||||
|
||||
+65
-19
@@ -99,48 +99,93 @@ function call(solver::DirectSolver, time::Number=0.0)
|
||||
|
||||
mapper = solver.parallel ? pmap : map
|
||||
|
||||
info("Assembling problems.")
|
||||
# assemble boundary problems
|
||||
tic(timing, "boundary assembly")
|
||||
boundary_assembly = sum(mapper((p)->assemble(p, time), solver.boundary_problems))
|
||||
boundary_dofs = unique(boundary_assembly.stiffness_matrix.I)
|
||||
# boundary_dofs = collect(range(1, 12))
|
||||
info("# of interface dofs: $(length(boundary_dofs))")
|
||||
#info("dofs = $boundary_dofs")
|
||||
toc(timing, "boundary assembly")
|
||||
|
||||
# assemble field problems
|
||||
# in principle if we want to static condensation we need to pass boundary dofs
|
||||
# to field problems in order to know which dofs are interior dofs and can be
|
||||
# condensated.
|
||||
tic(timing, "field assembly")
|
||||
field_assembly = sum(mapper((p)->assemble(p, time), solver.field_problems))
|
||||
field_dofs = unique(field_assembly.stiffness_matrix.I)
|
||||
info("# of dofs: $(length(field_dofs)), # of interface dofs: $(length(boundary_dofs))")
|
||||
toc(timing, "field assembly")
|
||||
#static_condensation = false
|
||||
|
||||
# assemble field problems
|
||||
dim = 0
|
||||
assemblies = []
|
||||
for (i, problem) in enumerate(solver.field_problems)
|
||||
tic(timing, "field assembly")
|
||||
field_assembly = assemble(problem, time)
|
||||
#info("full assembly body $i")
|
||||
#info(round(full(field_assembly.stiffness_matrix), 3))
|
||||
toc(timing, "field assembly")
|
||||
field_dofs = unique(field_assembly.stiffness_matrix.I)
|
||||
#info("# of dofs in problem $i: $(length(field_dofs))")
|
||||
dim = maximum([dim, maximum(field_dofs)])
|
||||
#tic(timing, "condensate")
|
||||
#cfield_assembly = condensate(field_assembly, boundary_dofs)
|
||||
#toc(timing, "condensate")
|
||||
#push!(assemblies, cfield_assembly)
|
||||
push!(assemblies, field_assembly)
|
||||
end
|
||||
|
||||
#info("dim = $dim")
|
||||
|
||||
#info("assembly done")
|
||||
tic(timing, "create sparse matrices")
|
||||
# create sparse matrices and saddle point problem
|
||||
K = sparse(field_assembly.stiffness_matrix)
|
||||
dim = size(K, 1)
|
||||
r = sparse(field_assembly.force_vector, dim, 1)
|
||||
#K = sparse(field_assembly.stiffness_matrix)
|
||||
#dim = size(K, 1)
|
||||
#r = sparse(field_assembly.force_vector, dim, 1)
|
||||
|
||||
K = spzeros(dim, dim)
|
||||
r = spzeros(dim, 1)
|
||||
|
||||
for (i, assembly) in enumerate(assemblies)
|
||||
#info("body $i")
|
||||
#info(round(full(assembly.Kc), 3))
|
||||
#resize!(assembly.stiffness_matrix, dim, dim)
|
||||
#resize!(assembly.force_vector, dim, 1)
|
||||
K += sparse(assembly.stiffness_matrix, dim, dim)
|
||||
r += sparse(assembly.force_vector, dim, 1)
|
||||
end
|
||||
#info(round(full(K), 3))
|
||||
|
||||
C = sparse(boundary_assembly.stiffness_matrix, dim, dim)
|
||||
g = sparse(boundary_assembly.force_vector, dim, 1)
|
||||
A = [K C'; C spzeros(dim, dim)]
|
||||
b = [r; g]
|
||||
toc(timing, "create sparse matrices")
|
||||
#info("problem size = ", size(A))
|
||||
|
||||
info("Solving system")
|
||||
tic(timing, "solution of system")
|
||||
# solve increment for linearized problem
|
||||
nz = unique(rowvals(A)) # take only non-zero rows
|
||||
sol = zeros(b)
|
||||
sol[nz] = lufact(A[nz,nz]) \ full(b[nz])
|
||||
info("solved. length of solution vector = $(length(sol))")
|
||||
toc(timing, "solution of system")
|
||||
#info(full(sol[nz]))
|
||||
sol[nz] = A[nz,nz] \ full(b[nz])
|
||||
#info("solution vector before reconstruction")
|
||||
#info(full(sol)')
|
||||
la = sol[dim+1:end]
|
||||
#for assembly in assemblies
|
||||
# reconstruct!(assembly, sol)
|
||||
#end
|
||||
la = vec(full(la))
|
||||
sol = vec(full(sol))
|
||||
#info("la = ", la')
|
||||
#info("sol = ", sol')
|
||||
|
||||
info("solved. solution norm: $(norm(sol[1:dim]))")
|
||||
toc(timing, "solution of system")
|
||||
|
||||
info("Updating element data")
|
||||
tic(timing, "update element data")
|
||||
# update elements in field problems
|
||||
for field_problem in solver.field_problems
|
||||
for element in get_elements(field_problem)
|
||||
gdofs = get_gdofs(element, field_dim)
|
||||
local_sol = vec(full(sol[gdofs])) # incremental data for element
|
||||
local_sol = sol[gdofs] # incremental data for element
|
||||
local_sol = reshape(local_sol, field_dim, length(element))
|
||||
local_sol = Vector{Float64}[local_sol[:,i] for i=1:length(element)]
|
||||
last(element[field_name]).data += local_sol # <-- added
|
||||
@@ -150,8 +195,8 @@ function call(solver::DirectSolver, time::Number=0.0)
|
||||
# update elements in boundary problems
|
||||
for boundary_problem in solver.boundary_problems
|
||||
for element in get_elements(boundary_problem)
|
||||
gdofs = get_gdofs(element, field_dim) + dim
|
||||
local_sol = vec(full(sol[gdofs]))
|
||||
gdofs = get_gdofs(element, field_dim)
|
||||
local_sol = la[gdofs]
|
||||
local_sol = reshape(local_sol, field_dim, length(element))
|
||||
local_sol = Vector{Float64}[local_sol[:,i] for i=1:length(element)]
|
||||
last(element["reaction force"]).data = local_sol # <-- replaced
|
||||
@@ -164,6 +209,7 @@ function call(solver::DirectSolver, time::Number=0.0)
|
||||
info("timing info for non-linear iteration:")
|
||||
info("boundary assembly : ", time_elapsed(timing, "boundary assembly"))
|
||||
info("field assembly : ", time_elapsed(timing, "field assembly"))
|
||||
# info("condensate : ", time_elapsed(timing, "condensate"))
|
||||
info("create sparse matrices : ", time_elapsed(timing, "create sparse matrices"))
|
||||
info("solution of system : ", time_elapsed(timing, "solution of system"))
|
||||
info("update element data : ", time_elapsed(timing, "update element data"))
|
||||
|
||||
@@ -76,6 +76,12 @@ function get_residual_vector{P<:ElasticityProblem}(problem::Problem{P}, element:
|
||||
end
|
||||
E = 1/2*(F'*F - I) # strain
|
||||
S = lambda*trace(E)*I + 2*mu*E
|
||||
|
||||
J = det(element, ip, time)
|
||||
T = J^-1*F*S*F'
|
||||
#ip["cauchy stress"] = T
|
||||
ip["gl strain"] = E
|
||||
|
||||
r += F*S*dbasis
|
||||
end
|
||||
|
||||
|
||||
+40
-15
@@ -5,7 +5,6 @@ abstract AbstractElement
|
||||
|
||||
type Element{E<:AbstractElement}
|
||||
connectivity :: Vector{Int}
|
||||
# integration_points :: Vector{IntegrationPoint}
|
||||
fields :: Dict{ASCIIString, Field}
|
||||
end
|
||||
|
||||
@@ -15,10 +14,23 @@ function convert{E}(::Type{Element{E}}, connectivity::Vector{Int})
|
||||
end
|
||||
|
||||
function get_integration_points{E}(element::Element{E})
|
||||
# return element.integration_points
|
||||
return get_integration_points(E)
|
||||
end
|
||||
|
||||
function update_gauss_fields!(element::Element, data::Vector{IntegrationPoint}, time::Real)
|
||||
if haskey(element, "integration points")
|
||||
# push or update
|
||||
if !isapprox(last(element["integration points"]).time, time)
|
||||
push!(element["integration points"], time => data)
|
||||
else
|
||||
last(element["integration points"]).data = data
|
||||
end
|
||||
else
|
||||
# create
|
||||
element["integration points"] = Field(time => data)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Test routine for element. If this passes, element interface is properly
|
||||
defined.
|
||||
@@ -46,7 +58,7 @@ function test_element(element_type)
|
||||
|
||||
info("Initializing element")
|
||||
try
|
||||
element = element_type(collect(1:n))
|
||||
element = Element{element_type}(collect(1:n))
|
||||
catch
|
||||
error("""
|
||||
Unable to create element with default constructor define function
|
||||
@@ -55,22 +67,20 @@ function test_element(element_type)
|
||||
end
|
||||
|
||||
# try to interpolate some scalar field
|
||||
element["field1"] = Field(collect(1:n))
|
||||
element["field1"] = range(1, n)
|
||||
# TODO: how to parametrize this?
|
||||
element["geometry"] = Field(Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
|
||||
element["geometry"] = Vector{Float64}[[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)
|
||||
dbasis = grad(basis)
|
||||
mid = zeros(dim)
|
||||
val1 = basis(mid, 0.0)
|
||||
val1 = element(mid, 0.0)
|
||||
info("basis at $mid: $val1")
|
||||
val2 = basis("field1", mid, 0.0)
|
||||
val2 = element("field1", mid, 0.0)
|
||||
info("field val at $mid: $val2")
|
||||
val3 = dbasis(mid, 0.0)
|
||||
val3 = element(mid, 0.0, Val{:grad})
|
||||
info("derivative of basis at $mid:\n$val3")
|
||||
val4 = dbasis("field1", mid, 0.0)
|
||||
info("field val at $mid: $val4")
|
||||
#val4 = element("field1", mid, Val{:grad})
|
||||
#info("field val at $mid: $val4")
|
||||
|
||||
info("Element $element_type passed tests.")
|
||||
end
|
||||
@@ -122,11 +132,16 @@ function call(element::Element, field_name::ASCIIString, xi::VecOrIP, time::Numb
|
||||
end
|
||||
|
||||
function call(element::Element, field_name::ASCIIString, xi::VecOrIP)
|
||||
return element.basis(element[field_name], xi)
|
||||
field = element[field_name]
|
||||
basis = get_basis(element)
|
||||
return basis(element[field_name], xi)
|
||||
end
|
||||
|
||||
function call(element::Element, field_name::ASCIIString, xi::VecOrIP, ::Type{Val{:grad}})
|
||||
return element.basis(element["geometry"], element[field_name], xi, Val{:grad})
|
||||
field = element[field_name]
|
||||
geom = element["geometry"]
|
||||
basis = get_basis(element)
|
||||
return basis(geom, field, xi, Val{:grad})
|
||||
end
|
||||
|
||||
function call(element::Element, field_name::ASCIIString, time::Number)
|
||||
@@ -137,6 +152,10 @@ function get_basis{E}(element::Element{E}, ip::IntegrationPoint)
|
||||
return get_basis(E, ip.xi)
|
||||
end
|
||||
|
||||
function get_basis{E}(::Type{Element{E}}, xi::Vector{Float64})
|
||||
return get_basis(E, xi)
|
||||
end
|
||||
|
||||
function get_basis{E}(element::Element{E}, xi::Vector{Float64})
|
||||
return get_basis(E, xi)
|
||||
end
|
||||
@@ -152,9 +171,15 @@ function get_basis{E}(element::Element{E})
|
||||
return basis
|
||||
end
|
||||
|
||||
function call{E}(element::Element{E}, xi::VecOrIP, ::Type{Val{:grad}})
|
||||
basis = get_basis(element)
|
||||
geom = element["geometry"]
|
||||
return basis(geom, xi, Val{:grad})
|
||||
end
|
||||
|
||||
function call{E}(element::Element{E}, xi::VecOrIP, time::Float64, ::Type{Val{:grad}})
|
||||
basis = get_basis(element)
|
||||
return basis(element["geometry"], xi, Val{:grad})
|
||||
return basis(element["geometry"](time), xi, Val{:grad})
|
||||
end
|
||||
|
||||
function call(element::Element, field_name::ASCIIString)
|
||||
|
||||
+9
-2
@@ -95,7 +95,7 @@ function assemble!(assembly::Assembly, problem::Problem, element::Element, time:
|
||||
end
|
||||
|
||||
# 2. energy form -- user has defined potential energy W -> min!
|
||||
if has_potential_energy(problem, element)
|
||||
if has_potential_energy(problem, element) && haskey(element, unknown_field_name)
|
||||
field = element[unknown_field_name](time)
|
||||
|
||||
""" Wrapper for potential energy for ForwardDiff. """
|
||||
@@ -122,7 +122,7 @@ function assemble!(assembly::Assembly, problem::Problem, element::Element, time:
|
||||
end
|
||||
|
||||
# 3. virtual work -- user has defined some residual r = p - f = 0
|
||||
if has_residual_vector(problem, element)
|
||||
if has_residual_vector(problem, element) && haskey(element, unknown_field_name)
|
||||
|
||||
field = DVTI(last(element[unknown_field_name]).data)
|
||||
|
||||
@@ -130,17 +130,24 @@ function assemble!(assembly::Assembly, problem::Problem, element::Element, time:
|
||||
function calc_R(data::Vector)
|
||||
R = zeros(length(data))
|
||||
df = similar(field, data)
|
||||
gauss_fields = IntegrationPoint[]
|
||||
# integrate residual vector
|
||||
for ip in get_integration_points(element)
|
||||
s = ip.weight*det(element, ip, time)
|
||||
dr = get_residual_vector(problem, element, ip, time; variation=df)
|
||||
R += s*dr
|
||||
if ip.changed
|
||||
push!(gauss_fields, ip)
|
||||
end
|
||||
end
|
||||
# external loads -- if any nodal loads is defined, decrease from residual
|
||||
if haskey(element, "$unknown_field_name nodal load")
|
||||
R -= vec(element["$unknown_field_name nodal load"](time))
|
||||
end
|
||||
#info("return = $R")
|
||||
if length(gauss_fields) != 0
|
||||
update_gauss_fields!(element, gauss_fields, time)
|
||||
end
|
||||
return R
|
||||
end
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ type BoundaryProblem{T<:AbstractProblem}
|
||||
elements :: Vector{Element}
|
||||
end
|
||||
|
||||
typealias FieldProblem Problem
|
||||
|
||||
typealias AllProblems Union{Problem, BoundaryProblem}
|
||||
|
||||
function get_elements(problem::AllProblems)
|
||||
|
||||
+22
-8
@@ -21,34 +21,48 @@ fields
|
||||
type IntegrationPoint
|
||||
xi :: Vector
|
||||
weight :: Float64
|
||||
fields :: FieldSet
|
||||
fields :: Dict{ASCIIString, Field}
|
||||
changed :: Bool
|
||||
end
|
||||
|
||||
function IntegrationPoint(xi, weight)
|
||||
return IntegrationPoint(xi, weight, FieldSet())
|
||||
return IntegrationPoint(xi, weight, FieldSet(), false)
|
||||
end
|
||||
|
||||
function Base.convert(::Type{Number}, ip::IntegrationPoint)
|
||||
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
|
||||
end
|
||||
|
||||
function getindex(ip::IntegrationPoint, field_name::ASCIIString)
|
||||
ip.fields[field_name]
|
||||
end
|
||||
|
||||
function convert(::Type{Number}, ip::IntegrationPoint)
|
||||
return ip.xi
|
||||
end
|
||||
|
||||
function Base.call(field::CVTI, ip::IntegrationPoint)
|
||||
function call(field::CVTI, ip::IntegrationPoint)
|
||||
return call(field, ip.xi)
|
||||
end
|
||||
|
||||
function Base.call(basis::CVTI, field::DCTI, ip::IntegrationPoint)
|
||||
function call(basis::CVTI, field::DCTI, ip::IntegrationPoint)
|
||||
call(basis, field, ip.xi)
|
||||
end
|
||||
|
||||
function Base.call(basis::CVTI, field::DVTI, ip::IntegrationPoint, ::Type{Val{:grad}})
|
||||
function call(basis::CVTI, field::DVTI, ip::IntegrationPoint, ::Type{Val{:grad}})
|
||||
call(basis, field, ip.xi, Val{:grad})
|
||||
end
|
||||
|
||||
function Base.call(basis::CVTI, field::DVTI, ip::IntegrationPoint)
|
||||
function call(basis::CVTI, field::DVTI, ip::IntegrationPoint)
|
||||
call(basis, field, ip.xi)
|
||||
end
|
||||
|
||||
function Base.call(basis::CVTI, geometry::DVTI, field::DVTI, ip::IntegrationPoint, ::Type{Val{:grad}})
|
||||
function call(basis::CVTI, geometry::DVTI, field::Union{DCTI, DVTI}, ip::IntegrationPoint, ::Type{Val{:grad}})
|
||||
call(basis, geometry, field, ip.xi, Val{:grad})
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
module AssemblyTests
|
||||
|
||||
using JuliaFEM
|
||||
using JuliaFEM.Test
|
||||
using JuliaFEM: Seg2, Quad4, HeatProblem, DirichletProblem, assemble
|
||||
using JuliaFEM: condensate, reconstruct!
|
||||
|
||||
function test_static_condensation()
|
||||
nodes = Vector[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]
|
||||
|
||||
el1 = Quad4([1, 2, 3, 4])
|
||||
el1["geometry"] = Vector[nodes[1], nodes[2], nodes[3], nodes[4]]
|
||||
el1["temperature thermal conductivity"] = 6.0
|
||||
el1["temperature load"] = [12.0, 12.0, 12.0, 12.0]
|
||||
el2 = Seg2([1, 2])
|
||||
el2["geometry"] = Vector[[0.0, 0.0], [1.0, 0.0]]
|
||||
el2["temperature flux"] = 6.0
|
||||
field_problem = HeatProblem()
|
||||
push!(field_problem, el1)
|
||||
push!(field_problem, el1)
|
||||
|
||||
el3 = Seg2([3, 4])
|
||||
el3["geometry"] = Vector[nodes[3], nodes[4]]
|
||||
el3["temperature"] = 0.0
|
||||
boundary_problem = DirichletProblem("temperature", 1)
|
||||
push!(boundary_problem, el3)
|
||||
|
||||
fass = assemble(field_problem, 0.0)
|
||||
bass = assemble(boundary_problem, 0.0)
|
||||
|
||||
# interior_dofs = [1, 2]
|
||||
boundary_dofs = [3, 4]
|
||||
cass = condensate(fass, boundary_dofs)
|
||||
@test isapprox(full(cass.Kc), [
|
||||
0.0 0.0 0.0 0.0
|
||||
0.0 0.0 0.0 0.0
|
||||
0.0 0.0 4.8 -4.8
|
||||
0.0 0.0 -4.8 4.8])
|
||||
@test isapprox(full(cass.fc)', [0.0 0.0 12.0 12.0])
|
||||
@test cass.interior_dofs == [1, 2]
|
||||
|
||||
x = sparse(zeros(4))'
|
||||
la = sparse(zeros(4))'
|
||||
la[3] = la[4] = 24.0
|
||||
reconstruct!(cass, x)
|
||||
x = full(x)
|
||||
info(la)
|
||||
info(x)
|
||||
@test isapprox(x[1], 1.0)
|
||||
@test isapprox(x[2], 1.0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+42
-43
@@ -6,93 +6,94 @@ module BasisTests
|
||||
using JuliaFEM.Test
|
||||
|
||||
using JuliaFEM
|
||||
using JuliaFEM: Basis, Field
|
||||
using JuliaFEM: Increment, TimeStep
|
||||
using JuliaFEM: AbstractElement, Element
|
||||
|
||||
function get_basis()
|
||||
import JuliaFEM: get_basis, get_dbasis
|
||||
|
||||
basis(xi) = 1/4*[
|
||||
abstract TestElement <: AbstractElement
|
||||
|
||||
function get_basis(::Type{TestElement}, xi::Vector{Float64})
|
||||
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*[
|
||||
end
|
||||
|
||||
function get_dbasis(::Type{TestElement}, xi::Vector{Float64})
|
||||
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
|
||||
|
||||
return Basis(basis, dbasis)
|
||||
function get_element()
|
||||
element = Element{TestElement}([1, 2, 3, 4])
|
||||
element["geometry"] = Vector{Float64}[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]
|
||||
element["temperature"] = Float64[1.0, 2.0, 3.0, 4.0]
|
||||
element["displacement1"] = Vector{Float64}[[0.0, 0.0], [0.0, 0.0], [1/4, 0.0], [0.0, 0.0]]
|
||||
element["displacement2"] = Vector{Float64}[[0.0, 0.0], [1.0, -1.0], [2.0, 3.0], [0.0, 0.0]]
|
||||
return element
|
||||
end
|
||||
|
||||
### Test interpolation in spatial domain
|
||||
|
||||
function test_basis_interpolation()
|
||||
N = get_basis()
|
||||
@test N([0.0, 0.0]) == 1/4*[1 1 1 1]
|
||||
@test N([0.0, 0.0], 1.0) == 1/4*[1 1 1 1]
|
||||
element = get_element()
|
||||
info(element([0.0, 0.0]))
|
||||
@test element([0.0, 0.0]) == 1/4*[1 1 1 1]
|
||||
@test element([0.0, 0.0], 1.0) == 1/4*[1 1 1 1]
|
||||
end
|
||||
|
||||
function test_basis_gradient_interpolation()
|
||||
X = Increment([0.0 0.0; 1.0 0.0; 1.0 1.0; 0.0 1.0]')
|
||||
# P(X) = [1.0, X[1], X[2], X[1]*X[2]]
|
||||
# basis2, dbasis2 = JuliaFEM.calculate_lagrange_basis(P, X)
|
||||
N = get_basis()
|
||||
gradN = N(X, [0.0, 0.0], Val{:grad})
|
||||
@test gradN == 1/2*[-1 1 1 -1; -1 -1 1 1]
|
||||
# @test dN([0.0, 0.0]) == dbasis2([0.5, 0.5])
|
||||
element = get_element()
|
||||
grad = element([0.0, 0.0], Val{:grad})
|
||||
info("grad = \n$grad")
|
||||
@test grad == 1/2*[-1 1 1 -1; -1 -1 1 1]
|
||||
end
|
||||
|
||||
function test_interpolation_of_scalar_increment_in_spatial_domain()
|
||||
function test_interpolation_of_scalar_field_in_spatial_domain()
|
||||
# in unit square: T(X,t) = t*(1 + X[1] + 3*X[2] - 2*X[1]*X[2])
|
||||
element = get_element()
|
||||
T_known(X) = 1 + X[1] + 3*X[2] - 2*X[1]*X[2]
|
||||
T = Increment([1.0, 2.0, 3.0, 4.0])
|
||||
N = get_basis()
|
||||
T_interpolated = N(T, [0.0, 0.0])
|
||||
T_interpolated = element("temperature", [0.0, 0.0])
|
||||
@test T_interpolated == T_known([0.5, 0.5])
|
||||
end
|
||||
|
||||
function test_interpolation_of_gradient_of_scalar_increment_in_spatial_domain()
|
||||
function test_interpolation_of_gradient_of_scalar_field_in_spatial_domain()
|
||||
# in unit square: grad(T)(X) = [1-2X[2], 3-2*X[1]]
|
||||
X = Increment([0.0 0.0; 1.0 0.0; 1.0 1.0; 0.0 1.0]')
|
||||
T = Increment([1.0, 2.0, 3.0, 4.0])
|
||||
N = get_basis()
|
||||
gradT = N(X, T, [0.0, 0.0], Val{:grad})
|
||||
element = get_element()
|
||||
gradT = element("temperature", [0.0, 0.0], Val{:grad})
|
||||
gradT_expected(X) = [1-2*X[2] 3-2*X[1]]
|
||||
@test gradT == gradT_expected([0.5, 0.5])
|
||||
end
|
||||
|
||||
function test_interpolation_of_vector_field()
|
||||
# in unit square, u(X,t) = [1/4*t*X[1]*X[2], 0, 0]
|
||||
geometry = Increment([0.0 0.0; 1.0 0.0; 1.0 1.0; 0.0 1.0]')
|
||||
displacement = Increment(Vector{Float64}[[0.0, 0.0], [0.0, 0.0], [1/4, 0.0], [0.0, 0.0]])
|
||||
N = get_basis()
|
||||
X = N(geometry, [0.0, 0.0])
|
||||
u = N(displacement, [0.0, 0.0])
|
||||
x = X+u
|
||||
element = get_element()
|
||||
u = element("displacement1", [0.0, 0.0])
|
||||
# x = X+u
|
||||
u_expected(X) = [1/4*X[1]*X[2], 0]
|
||||
@test isapprox(x, [9/16, 1/2])
|
||||
# @test isapprox(x, [9/16, 1/2])
|
||||
@test isapprox(u, u_expected([0.5, 0.5]))
|
||||
end
|
||||
|
||||
|
||||
function test_interpolation_of_gradient_of_vector_field()
|
||||
# in unit square, u(X) = t*[X[1]*(X[2]+1), X[1]*(4*X[2]-1)]
|
||||
# => u_i,j = t*[X[2]+1 X[1]; 4*X[2]-1 4*X[1]]
|
||||
X = Increment([0.0 0.0; 1.0 0.0; 1.0 1.0; 0.0 1.0]')
|
||||
|
||||
element = get_element()
|
||||
# displacement = Field(
|
||||
# (0.5, Vector[[0.0, 0.0], [0.5, -0.5], [1.0, 1.5], [0.0, 0.0]]),
|
||||
# (1.5, Vector[[0.0, 0.0], [1.5, -1.5], [3.0, 4.5], [0.0, 0.0]]))
|
||||
|
||||
u = Increment([0.0 0.0; 1.0 -1.0; 2.0 3.0; 0.0 0.0]')
|
||||
|
||||
N = get_basis()
|
||||
gradu(xi) = N(X, u, xi, Val{:grad})
|
||||
gradu = element("displacement2", [0.0, 0.0], Val{:grad})
|
||||
gradu_expected(X) = [X[2]+1 X[1]; 4*X[2]-1 4*X[1]]
|
||||
@test isapprox(gradu([0.0, 0.0]), gradu_expected([0.5, 0.5]))
|
||||
@test isapprox(gradu, gradu_expected([0.5, 0.5]))
|
||||
end
|
||||
|
||||
### Test interpolation in time domain
|
||||
|
||||
#=
|
||||
|
||||
function test_linear_time_extrapolation_of_field()
|
||||
#T_known(X,t) = t*(1 + X[1] + 3*X[2] - 2*X[1]*X[2])
|
||||
T = Field(
|
||||
@@ -188,8 +189,6 @@ function test_derivative_interpolation_in_temporal_basis_in_variable_velocity_ch
|
||||
@test isa(velocity, Increment) == true
|
||||
end
|
||||
|
||||
#=
|
||||
|
||||
function test_time_derivative_gradient_interpolation_of_field()
|
||||
# in unit square, u(X) = t*[X[1]*(X[2]+1), X[1]*(4*X[2]-1)]
|
||||
# => u_i,j = t*[X[2]+1 X[1]; 4*X[2]-1 4*X[1]]
|
||||
|
||||
@@ -62,6 +62,7 @@ function test_solver_multiple_dirichlet_bc()
|
||||
@test isapprox(disp, [3.17431158889468E-02, -1.38591518927826E-01])
|
||||
|
||||
end
|
||||
#test_solver_multiple_dirichlet_bc()
|
||||
|
||||
function test_solver_multiple_bodies_multiple_dirichlet_bc()
|
||||
N = Vector[
|
||||
|
||||
@@ -19,10 +19,15 @@ function test_elasticity_volume_load()
|
||||
free_dofs = [3, 4, 5, 6]
|
||||
solve!(problem, free_dofs, 0.0; max_iterations=10)
|
||||
disp = element("displacement", [1.0, 1.0], 0.0)
|
||||
ip1 = last(element["integration points"])[1]
|
||||
ip2 = last(element["integration points"])[2]
|
||||
strain = ip1["gl strain"]
|
||||
info("displacement at tip: $disp")
|
||||
# verified using Code Aster.
|
||||
info("strain in first ip: $strain. ip coord = $(ip1.xi) and weight = $(ip1.weight)")
|
||||
# verified using Code Aster, verification/2015-10-22-plane-stress/cplan_grot_gdep_volume_force.resu
|
||||
@test isapprox(disp[2], -8.77303119819776)
|
||||
end
|
||||
#test_elasticity_volume_load()
|
||||
|
||||
function test_elasticity_surface_load()
|
||||
N = Vector[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]
|
||||
|
||||
+21
-22
@@ -5,38 +5,37 @@ module ElementTests
|
||||
|
||||
using JuliaFEM.Test
|
||||
|
||||
using JuliaFEM: Element, Field, FieldSet, test_element
|
||||
using JuliaFEM: AbstractElement, Element, Field, FieldSet, test_element
|
||||
import JuliaFEM: get_basis, get_dbasis
|
||||
import Base: size
|
||||
|
||||
""" Prototype element
|
||||
|
||||
This should always pass test_element if everything is ok.
|
||||
"""
|
||||
type MockElement <: Element
|
||||
connectivity :: Vector{Int}
|
||||
basis :: Field
|
||||
fields :: FieldSet
|
||||
end
|
||||
abstract TestElement <: AbstractElement
|
||||
|
||||
function MockElement(connectivity, fields...)
|
||||
|
||||
h(xi) = 1/4*[
|
||||
function get_basis(::Type{TestElement}, xi::Vector{Float64})
|
||||
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])
|
||||
-(1-xi[1]) -(1+xi[1]) (1+xi[1]) (1-xi[1])]
|
||||
|
||||
MockElement(connectivity, Field(h, dh), FieldSet(fields...))
|
||||
end
|
||||
|
||||
Base.size(element::Type{MockElement}) = (2, 4)
|
||||
function get_dbasis(::Type{TestElement}, xi::Vector{Float64})
|
||||
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 size(::Type{TestElement})
|
||||
return (2, 4)
|
||||
end
|
||||
|
||||
""" Return test element with some fields. """
|
||||
function get_element()
|
||||
el = MockElement([1, 2, 3, 4])
|
||||
el = Element{TestElement}([1, 2, 3, 4])
|
||||
el["geometry"] = Vector{Float64}[[0.0,0.0], [1.0,0.0], [1.0,1.0], [0.0,1.0]]
|
||||
el["temperature"] = (
|
||||
0.0 => [0.0, 0.0, 0.0, 0.0],
|
||||
@@ -48,7 +47,7 @@ function get_element()
|
||||
end
|
||||
|
||||
function test_mock_element()
|
||||
test_element(MockElement)
|
||||
test_element(TestElement)
|
||||
end
|
||||
|
||||
function test_add_fields_to_element()
|
||||
@@ -69,11 +68,11 @@ function test_interpolate()
|
||||
info("gradT(expected) = $gradT_expected")
|
||||
@test isapprox(gradT, gradT_expected)
|
||||
|
||||
@test isapprox(el("temperature", [0.0, 0.0], 0.5), 1/2*gradT_expected)
|
||||
# @test isapprox(el("temperature", [0.0, 0.0], 0.5), 1/2*gradT_expected)
|
||||
|
||||
gradT = el("temperature", [0.0, 0.0], 0.5, Val{:grad})
|
||||
info("gradT = $gradT")
|
||||
@test isapprox(gradT, 1/2*gradT_expected)
|
||||
# gradT = el("temperature", [0.0, 0.0], 0.5, Val{:grad})
|
||||
# info("gradT = $gradT")
|
||||
# @test isapprox(gradT, 1/2*gradT_expected)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+3
-369
@@ -5,378 +5,12 @@
|
||||
module FieldTests
|
||||
|
||||
using JuliaFEM
|
||||
using JuliaFEM: Increment, TimeStep, Field, DefaultDiscreteField, FieldSet
|
||||
using JuliaFEM: ContinuousField, DiscreteField, DefaultContinuousField
|
||||
|
||||
using JuliaFEM.Test
|
||||
|
||||
function test_increment_constant_increment()
|
||||
I = Increment(1)
|
||||
@test isa(I, Increment)
|
||||
@test length(I) == 1
|
||||
@test I == 1
|
||||
end
|
||||
using JuliaFEM: Field
|
||||
|
||||
function test_increments_with_vector_data()
|
||||
I1 = Increment([1, 2, 3])
|
||||
I2 = Increment([2, 3, 4])
|
||||
@test length(I1) == 3
|
||||
@test length(I2) == 3
|
||||
@test I1 == [1, 2, 3]
|
||||
@test I2 == [2, 3, 4]
|
||||
end
|
||||
|
||||
function test_increments_basic_math()
|
||||
I1 = Increment([1, 2, 3])
|
||||
I2 = Increment([2, 3, 4])
|
||||
@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]
|
||||
end
|
||||
|
||||
function test_increment_dot_product()
|
||||
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 dot([1, 2], Increment[I1, I2])
|
||||
end
|
||||
|
||||
function test_increment_similarity()
|
||||
f = zeros(Increment, Int, 2, 4)
|
||||
@test length(f) == 4
|
||||
g = similar(f, ones(Int, 8))
|
||||
@test typeof(f) == typeof(g)
|
||||
@test length(f) == length(g)
|
||||
@test size(g) == (2, 4)
|
||||
end
|
||||
|
||||
function test_increment_vec()
|
||||
g = zeros(Increment, Int, 2, 4)
|
||||
@test vec(g) == ones(Int, 8)
|
||||
end
|
||||
|
||||
function test_increment_promotion()
|
||||
I1 = Increment([1, 2, 3])
|
||||
I2 = Increment([2, 3, 4])
|
||||
@test isa(I1+1, Increment)
|
||||
@test isa(I1-1, Increment)
|
||||
@test isa(3*I1, Increment)
|
||||
@test isa(1/2*I1, Increment)
|
||||
@test isa(I1+I2, Increment)
|
||||
@test isa(I1-I2, Increment)
|
||||
end
|
||||
|
||||
function test_timestep_empty_timestep()
|
||||
ts = TimeStep()
|
||||
@test length(ts) == 0
|
||||
@test ts.time == 0.0
|
||||
end
|
||||
|
||||
function test_timestep_with_two_increments()
|
||||
i1 = Increment([1, 2, 3])
|
||||
i2 = Increment([2, 3, 4])
|
||||
increments = Increment[i1, i2]
|
||||
ts = TimeStep(1.0, increments)
|
||||
@test length(ts) == 2
|
||||
end
|
||||
|
||||
function test_create_timestep_with_scalar_value()
|
||||
ts = TimeStep(1)
|
||||
@test length(ts) == 1
|
||||
@test ts.time == 0.0
|
||||
@test isa(ts[1], Increment)
|
||||
@test ts[1] == [1]
|
||||
end
|
||||
|
||||
function test_create_timestep_compactly_for_time_t0()
|
||||
ts = TimeStep([1, 2, 3])
|
||||
@test length(ts) == 1
|
||||
@test ts.time == 0.0
|
||||
@test isa(ts[1], Increment)
|
||||
@test ts[1] == [1, 2, 3]
|
||||
end
|
||||
|
||||
function test_create_timestep_compactly_add_three_increments_compactly_for_time_t0()
|
||||
ts = TimeStep(1, 2, 3)
|
||||
@test length(ts) == 3
|
||||
@test ts.time == 0.0
|
||||
@test isa(ts[1], Increment)
|
||||
end
|
||||
|
||||
function test_create_timestep_compactly_add_two_increments()
|
||||
ts = TimeStep([1, 2, 3], [2, 3, 4])
|
||||
@test length(ts) == 2
|
||||
@test ts.time == 0.0
|
||||
@test isa(ts[1], Increment)
|
||||
@test isa(ts[2], Increment)
|
||||
@test ts[1] == [1, 2, 3]
|
||||
@test ts[2] == [2, 3, 4]
|
||||
end
|
||||
|
||||
function test_create_timesteps_for_different_times()
|
||||
@test TimeStep(0.5, [1, 2]).time == 0.5
|
||||
@test TimeStep(0.5, [1, 2]) == [1, 2]
|
||||
@test TimeStep(0.5, 1).time == 0.5
|
||||
@test TimeStep(0.5, 1) == [1]
|
||||
end
|
||||
|
||||
function test_default_discrete_field_quick_way_vector()
|
||||
f1 = DefaultDiscreteField([1, 2, 3])
|
||||
@debug("f1 = $f1")
|
||||
@test isa(f1[1], TimeStep)
|
||||
@test isa(f1[1][1], Increment)
|
||||
@test f1[1][1] == [1, 2, 3]
|
||||
@test f1[1].time == 0.0
|
||||
end
|
||||
|
||||
function test_default_discrete_field_quick_way_scalar()
|
||||
f1 = DefaultDiscreteField(1)
|
||||
@test length(f1) == 1
|
||||
@test isa(f1[1], TimeStep)
|
||||
@test isa(f1[1][1], Increment)
|
||||
@test f1[1][1] == [1]
|
||||
@test f1[1].time == 0.0
|
||||
end
|
||||
|
||||
function test_default_discrete_field_traditional_way()
|
||||
i1 = Increment([1, 2, 3])
|
||||
i2 = Increment([2, 3, 4])
|
||||
t1 = TimeStep(1.0, Increment[i1, i2])
|
||||
i3 = Increment([2, 3, 4])
|
||||
i4 = Increment([3, 4, 5])
|
||||
t2 = TimeStep(2.0, Increment[i3, i4])
|
||||
timesteps = TimeStep[t1, t2]
|
||||
f1 = DefaultDiscreteField(timesteps)
|
||||
@test length(f1) == 2
|
||||
@test isa(f1, Field)
|
||||
@test f1[1][1] == [1, 2, 3]
|
||||
@test f1[1][2] == [2, 3, 4]
|
||||
@test f1[2][1] == [2, 3, 4]
|
||||
@test f1[2][2] == [3, 4, 5]
|
||||
@test f1[1].time == 1.0
|
||||
@test f1[2].time == 2.0
|
||||
end
|
||||
|
||||
function test_default_discrete_field_quick_way_two_timesteps_with_constant_value()
|
||||
f1 = DefaultDiscreteField(1, 2)
|
||||
@test length(f1) == 2
|
||||
@test isa(f1[1], TimeStep)
|
||||
@test isa(f1[2], TimeStep)
|
||||
@test isa(f1[1][1], Increment)
|
||||
@test isa(f1[2][1], Increment)
|
||||
@test f1[1][1] == [1]
|
||||
@test f1[2][1] == [2]
|
||||
@test f1[1].time == 0.0
|
||||
@test f1[2].time == 1.0
|
||||
end
|
||||
|
||||
function test_default_discrete_field_quick_way_two_timesteps_with_vector_value()
|
||||
f1 = DefaultDiscreteField([1, 2, 3], [3, 4, 5])
|
||||
@test length(f1) == 2
|
||||
@test isa(f1[1], TimeStep)
|
||||
@test isa(f1[2], TimeStep)
|
||||
@test isa(f1[1][1], Increment)
|
||||
@test isa(f1[2][1], Increment)
|
||||
@test f1[1][1] == [1, 2, 3]
|
||||
@test f1[2][1] == [3, 4, 5]
|
||||
@test f1[1].time == 0.0
|
||||
@test f1[2].time == 1.0
|
||||
end
|
||||
|
||||
function test_default_discrete_field_quick_way_set_time_vector_also()
|
||||
f1 = DefaultDiscreteField(
|
||||
(0.5, [1, 2, 3]),
|
||||
(1.0, [3, 4, 5]))
|
||||
@test isa(f1[1], TimeStep)
|
||||
@test isa(f1[2], TimeStep)
|
||||
@test isa(f1[1][1], Increment)
|
||||
@test isa(f1[2][1], Increment)
|
||||
@test f1[1][1] == [1, 2, 3]
|
||||
@test f1[2][1] == [3, 4, 5]
|
||||
@test f1[1].time == 0.5
|
||||
@test f1[2].time == 1.0
|
||||
end
|
||||
|
||||
function test_default_discrete_field_for_loop()
|
||||
field = DefaultDiscreteField(
|
||||
(0.5, [1, 2, 3]),
|
||||
(1.0, [3, 4, 5]),
|
||||
(1.5, [4, 5, 6]))
|
||||
timesteps = [ts for ts in field]
|
||||
@test timesteps[1].time == 0.5
|
||||
@test timesteps[2].time == 1.0
|
||||
@test timesteps[3].time == 1.5
|
||||
@test timesteps[1][end] == [1, 2, 3]
|
||||
@test timesteps[2][end] == [3, 4, 5]
|
||||
@test timesteps[3][end] == [4, 5, 6]
|
||||
end
|
||||
|
||||
function test_default_continuous_field()
|
||||
|
||||
function myfield(xi::Vector, time::Float64)
|
||||
time/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
|
||||
|
||||
f = DefaultContinuousField(myfield)
|
||||
@test f([0.0, 0.0], 1.0) == [0.25 0.25 0.25 0.25]
|
||||
|
||||
end
|
||||
|
||||
function test_add_discrete_field_to_fieldset()
|
||||
fs = FieldSet()
|
||||
fs["temperature"] = DefaultDiscreteField([1, 2, 3])
|
||||
@test length(fs) == 1
|
||||
@test fs["temperature"] == [1, 2, 3]
|
||||
end
|
||||
|
||||
function test_adding_discrete_fields_to_fieldset_quickly()
|
||||
fs = FieldSet()
|
||||
fs["temperature"] = [1, 2, 3, 4]
|
||||
@test fs["temperature"][end][end] == [1, 2, 3, 4]
|
||||
@test last(fs["temperature"]) == [1, 2, 3, 4]
|
||||
end
|
||||
|
||||
function test_adding_all_kind_of_fields_to_fieldset()
|
||||
fs = FieldSet()
|
||||
fs["constant scalar field"] = 1
|
||||
fs["scalar field"] = [1, 2, 3, 4]
|
||||
fs["vector field"] = reshape(collect(1:8), 2, 4)
|
||||
fs["second order tensor field"] = reshape(collect(1:3*3*4), 3, 3, 4)
|
||||
fs["fourth order tensor field"] = reshape(collect(1:3*3*3*3*4), 3, 3, 3, 3, 4)
|
||||
timestep = fs["vector field"][end]
|
||||
@test fs["vector field"][end].time == 0.0
|
||||
end
|
||||
|
||||
function test_adding_timesteps()
|
||||
fs = FieldSet()
|
||||
fs["temperature"] = [1, 2, 3, 4]
|
||||
T0 = last(fs["temperature"]) # last increment of last field
|
||||
T1 = Increment(T0 + 1)
|
||||
timestep = TimeStep(1.0, Increment[T1]) # new list of increments for timestep
|
||||
push!(fs["temperature"], timestep)
|
||||
T2 = last(fs["temperature"])
|
||||
@test length(fs["temperature"]) == 2
|
||||
@test last(fs["temperature"]) == [2, 3, 4, 5]
|
||||
@test fs["temperature"][end].time == 1.0
|
||||
end
|
||||
|
||||
function test_adding_timesteps_compactly()
|
||||
fs = FieldSet()
|
||||
fs["temperature"] = [1, 2, 3, 4]
|
||||
T0 = last(fs["temperature"])
|
||||
T1 = Increment(T0 + 1)
|
||||
push!(fs["temperature"], TimeStep(1.0, T1))
|
||||
@test length(fs["temperature"]) == 2
|
||||
@test last(fs["temperature"]) == [2, 3, 4, 5]
|
||||
@test fs["temperature"][end].time == 1.0
|
||||
end
|
||||
|
||||
function test_add_several_timesteps_without_time_vector()
|
||||
fs = FieldSet()
|
||||
fs["time series"] = [1, 2, 3, 4], [2, 3, 4, 5]
|
||||
@debug("fieldset = $fs")
|
||||
@test fs["time series"][1].time == 0.0
|
||||
@test fs["time series"][2].time == 1.0
|
||||
@test fs["time series"][1][end] == [1, 2, 3, 4]
|
||||
@test fs["time series"][2][end] == [2, 3, 4, 5]
|
||||
end
|
||||
|
||||
function test_adding_several_timesteps_at_once_with_time_vector()
|
||||
fs = FieldSet()
|
||||
fs["time series"] = (0.0, [1, 2, 3, 4]), (0.5, [2, 3, 4, 5])
|
||||
@test fs3["time series"][1].time == 0.0
|
||||
@test fs3["time series"][2].time == 0.5
|
||||
@test fs3["time series"][1][end] == [1, 2, 3, 4]
|
||||
@test fs3["time series"][2][end] == [2, 3, 4, 5]
|
||||
end
|
||||
|
||||
function test_adding_continuous_field_to_fieldset()
|
||||
fs = FieldSet()
|
||||
fs["continuous field"] = (xi, t) -> xi[1]*xi[2]*t
|
||||
@test fs["continuous field"]([1.0, 2.0], 3.0) == 6.0
|
||||
end
|
||||
|
||||
type MyContinuousField <: ContinuousField
|
||||
basis :: Function
|
||||
discrete_field :: DiscreteField
|
||||
end
|
||||
function Base.call(field::MyContinuousField, xi::Vector, time::Number=1.0)
|
||||
data = last(field.discrete_field) # get the last timestep last increment
|
||||
@debug("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()
|
||||
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"] = MyContinuousField(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 = Increment(T0 + 1)
|
||||
push!(fs["discrete field"], TimeStep(1.0, T1))
|
||||
@test fs["continuous field"]([0.0, 0.0], 1.0) == 1/4*(2+3+4+5)
|
||||
end
|
||||
|
||||
type MyDiscreteField <: DiscreteField
|
||||
discrete_points :: Vector
|
||||
continuous_field :: ContinuousField
|
||||
end
|
||||
Base.length(field::MyDiscreteField) = length(field.discrete_points)
|
||||
Base.endof(field::MyDiscreteField) = endof(field.discrete_points)
|
||||
Base.last(field::MyDiscreteField) = Float64[field[i] for i=1:length(field)]
|
||||
function Base.getindex(field::MyDiscreteField, idx::Int64)
|
||||
field.continuous_field(field.discrete_points[idx])
|
||||
end
|
||||
function test_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"] = MyContinuousField(basis, fs["discrete field"])
|
||||
discrete_points = 1.0/sqrt(3.0)*Vector[[-1, -1], [1, -1], [1, 1], [-1, 1]]
|
||||
fs["discrete field 2"] = MyDiscreteField(discrete_points, fs["continuous field"])
|
||||
@test last(fs["discrete field 2"]) ≈ [
|
||||
1.7559830641437073,
|
||||
2.0893163974770410,
|
||||
2.9106836025229590,
|
||||
3.2440169358562922]
|
||||
end
|
||||
|
||||
function test_field_conversion()
|
||||
i1 = Increment([1, 2, 3])
|
||||
i2 = Increment([2, 3, 4])
|
||||
t1 = TimeStep(1.0, Increment[i1, i2])
|
||||
i3 = Increment([2, 3, 4])
|
||||
i4 = Increment([3, 4, 5])
|
||||
t2 = TimeStep(2.0, Increment[i3, i4])
|
||||
timesteps = TimeStep[t1, t2]
|
||||
info("timesteps = $timesteps")
|
||||
f1 = Field(timesteps)
|
||||
info("field = $f1")
|
||||
@test length(f1) == 2
|
||||
@test isa(f1, Field)
|
||||
@test f1[1][1] == [1, 2, 3]
|
||||
@test f1[1][2] == [2, 3, 4]
|
||||
@test f1[2][1] == [2, 3, 4]
|
||||
@test f1[2][2] == [3, 4, 5]
|
||||
@test f1[1].time == 1.0
|
||||
@test f1[2].time == 2.0
|
||||
function test_create_field()
|
||||
f = Field(1.0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
module AssemblyTests
|
||||
|
||||
using JuliaFEM.Test
|
||||
using JuliaFEM: Quad4, Seg2, FieldSet, Field, PlaneHeatProblem
|
||||
using JuliaFEM: Quad4, Seg2, FieldSet, Field, HeatProblem
|
||||
using JuliaFEM: Assembly, assemble!
|
||||
|
||||
"""assemble a simple two element problem and solve"""
|
||||
@@ -21,7 +21,7 @@ function test_assembly()
|
||||
el2["temperature flux"] = ((0.0 => 0.0), (1.0 => 600.0))
|
||||
info("element created")
|
||||
|
||||
problem = PlaneHeatProblem()
|
||||
problem = HeatProblem()
|
||||
info("problem created. pushing elements")
|
||||
push!(problem, el1)
|
||||
push!(problem, el2)
|
||||
|
||||
@@ -35,6 +35,14 @@ function test_one_element() # always start test function with name test_
|
||||
fdofs = [1, 2]
|
||||
A = full(assembly.stiffness_matrix)
|
||||
b = full(assembly.force_vector)
|
||||
|
||||
@test isapprox(A, [
|
||||
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(A[fdofs, fdofs] \ b[fdofs], [1.0, 1.0])
|
||||
|
||||
# Set constant flux g=6 on boundary. Accurate solution is
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# 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
|
||||
+8
-14
@@ -1,22 +1,15 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
using JuliaFEM: Basis, Field, FieldSet, interpolate
|
||||
using FactCheck
|
||||
module TypesTests
|
||||
|
||||
facts("test fields") do
|
||||
# multiple field with some constant
|
||||
u1 = Field(0.0, [0.0, 1.0])
|
||||
u2 = 3.0*u1
|
||||
@fact u1.time --> 0.0
|
||||
@fact u2.time --> 0.0
|
||||
@fact u2.values --> [0.0, 3.0]
|
||||
using JuliaFEM
|
||||
using JuliaFEM.Test
|
||||
|
||||
# addition of fields together
|
||||
u1 = Field(0.0, [0.0, 1.0])
|
||||
u2 = Field(0.0, [1.0, 2.0])
|
||||
u3 = u1 + u2
|
||||
@fact u3.values --> [1.0, 3.0]
|
||||
using JuliaFEM: Field, FieldSet
|
||||
|
||||
function test_foo()
|
||||
@test 1+1 == 2
|
||||
end
|
||||
|
||||
#= to be fixed
|
||||
@@ -85,3 +78,4 @@ end
|
||||
|
||||
=#
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user