mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-20 10:08:31 +00:00
tutorial notebook working again
This commit is contained in:
+1
-1
@@ -36,12 +36,12 @@ include("interpolate.jl") # interpolation routines
|
||||
include("elements.jl")
|
||||
include("lagrange.jl") # Lagrange elements
|
||||
#include("hierarchical.jl") # P-elements
|
||||
include("integrate.jl") # integration points
|
||||
|
||||
include("equations.jl")
|
||||
include("problems.jl")
|
||||
include("solvers.jl")
|
||||
|
||||
#include("math.jl") # basic mathematical operations -- obsolete ..?
|
||||
# pre- and postprocess
|
||||
include("xdmf.jl")
|
||||
include("abaqus_reader.jl")
|
||||
|
||||
+36
-28
@@ -5,48 +5,56 @@
|
||||
|
||||
abstract DirichletEquation <: Equation
|
||||
|
||||
get_unknown_field_name(eq::DirichletEquation) = symbol("reaction force")
|
||||
|
||||
### Dirichlet problem + equations
|
||||
|
||||
type DirichletProblem <: BoundaryProblem
|
||||
unknown_field_name :: ASCIIString
|
||||
unknown_field_dimension :: Int
|
||||
equations :: Array{DirichletEquation, 1}
|
||||
element_mapping :: Dict{DataType, DataType}
|
||||
field_value :: Function
|
||||
end
|
||||
function DirichletProblem()
|
||||
DirichletProblem([])
|
||||
end
|
||||
get_dimension(pr::Type{DirichletProblem}) = 1 # ..?
|
||||
get_equation(pr::Type{DirichletProblem}, el::Type{Seg2}) = DBC2D2
|
||||
|
||||
"""
|
||||
Dirichlet boundary condition element for 2 node line segment
|
||||
"""
|
||||
function DirichletProblem(dimension::Int, field_value::Function=(X)->[0.0,0.0,0.0])
|
||||
element_mapping = nothing
|
||||
if dimension == 1
|
||||
element_mapping = Dict(
|
||||
Seg2 => DBC2D2
|
||||
)
|
||||
end
|
||||
DirichletProblem("reaction force", dimension, [], element_mapping, field_value)
|
||||
end
|
||||
|
||||
""" Dirichlet boundary condition element for 2 node line segment """
|
||||
type DBC2D2 <: DirichletEquation
|
||||
element :: Seg2
|
||||
integration_points :: Array{IntegrationPoint, 1}
|
||||
global_dofs :: Array{Int64, 1}
|
||||
fieldval :: Function
|
||||
end
|
||||
function DBC2D2(element::Seg2)
|
||||
integration_points = [
|
||||
IntegrationPoint([-sqrt(1/3)], 1.0),
|
||||
IntegrationPoint([+sqrt(1/3)], 1.0)]
|
||||
push!(element, FieldSet("reaction force"))
|
||||
fieldval(X, t) = 0.0
|
||||
DBC2D2(element, integration_points, [], fieldval)
|
||||
DBC2D2(element, integration_points)
|
||||
end
|
||||
Base.size(equation::DBC2D2) = (1, 2)
|
||||
|
||||
function calculate_local_assembly!(assembly::LocalAssembly, equation::DirichletEquation,
|
||||
unknown_field_name::ASCIIString, time::Number=Inf,
|
||||
problem=nothing)
|
||||
initialize_local_assembly!(assembly, equation)
|
||||
element = get_element(equation)
|
||||
basis = get_basis(element)
|
||||
detJ = det(basis)
|
||||
for ip in get_integration_points(equation)
|
||||
w = ip.weight * detJ(ip)
|
||||
N = basis(ip, time)
|
||||
assembly.stiffness_matrix += w * N'*N
|
||||
if !isa(problem, Void)
|
||||
X = basis("geometry", ip, time)
|
||||
u = problem.field_value(X)
|
||||
assembly.force_vector += w * N'*u
|
||||
end
|
||||
end
|
||||
end
|
||||
function get_lhs(eq::DBC2D2, ip, t)
|
||||
el = get_element(eq)
|
||||
h = get_basis(el)(ip.xi)
|
||||
return h*h'
|
||||
end
|
||||
function get_rhs(eq::DBC2D2, ip, t)
|
||||
el = get_element(eq)
|
||||
h = get_basis(el, ip.xi)
|
||||
f = eq.fieldval
|
||||
X = interpolate(el, "geometry", ip.xi, t)
|
||||
return h*f(X, t)
|
||||
end
|
||||
has_lhs(eq::DBC2D2) = true
|
||||
has_rhs(eq::DBC2D2) = true
|
||||
|
||||
|
||||
+55
-79
@@ -15,12 +15,12 @@ abstract Element
|
||||
|
||||
""" Get FieldSet from element. """
|
||||
function Base.getindex(element::Element, field_name)
|
||||
element.fields[symbol(field_name)]
|
||||
element.fields[field_name]
|
||||
end
|
||||
|
||||
""" Add new FieldSet to element. """
|
||||
function Base.setindex!(element::Element, fieldset::FieldSet, fieldset_name)
|
||||
fieldset.name = symbol(fieldset_name)
|
||||
fieldset.name = fieldset_name
|
||||
element.fields[fieldset.name] = fieldset
|
||||
end
|
||||
function Base.push!(element::Element, fieldset::FieldSet)
|
||||
@@ -73,9 +73,8 @@ End of example.
|
||||
|
||||
=#
|
||||
|
||||
# These must be implemented for your own element
|
||||
get_number_of_basis_functions(el::Type{Element}) = nothing
|
||||
get_element_dimension(el::Type{Element}) = nothing
|
||||
# define size of your element as (dim, nbasis) tuple where first integer is spatial dimension and second is number of basis functions.
|
||||
# Base.size(element::Type{Element}) = nothing
|
||||
|
||||
### COMMON ELEMENT ROUTINES ###
|
||||
|
||||
@@ -95,12 +94,14 @@ This uses FactCheck and throws exceptions if element is not passing all tests.
|
||||
function test_element(element_type)
|
||||
Logging.info("Testing element $element_type")
|
||||
local element
|
||||
n = get_number_of_basis_functions(element_type)
|
||||
Logging.info("number of basis functions in this element: $n")
|
||||
@fact n --> not(nothing) """
|
||||
Unable to determine number of nodes for $eltype define a function
|
||||
'get_number_of_basis_functions' which returns the number of nodes
|
||||
for this element."""
|
||||
dim = nothing
|
||||
n = nothing
|
||||
try
|
||||
dim, n = size(element_type)
|
||||
catch
|
||||
Logging.error("Unable to determine element dimensions. Define Base.size(element::Type{$elementtype}) = (dim, nbasis) where dim is spatial dimension of element and nbasis is number of basis functions of element.")
|
||||
end
|
||||
Logging.info("element dimension: $dim x $n")
|
||||
|
||||
Logging.info("Initializing element")
|
||||
try
|
||||
@@ -112,45 +113,23 @@ function test_element(element_type)
|
||||
return false
|
||||
end
|
||||
|
||||
dim = get_element_dimension(element_type)
|
||||
Logging.info("Element dimension: $dim")
|
||||
@fact dim --> not(nothing) """
|
||||
Unable to get element dimension define function 'get_element_dimension'
|
||||
which return the dimension of this element (1, 2, 3)"""
|
||||
|
||||
# try to interpolate some scalar field
|
||||
field = Field(0.0, collect(1:n))
|
||||
Logging.info("Creating new scalar field $field")
|
||||
fieldset = FieldSet("field1")
|
||||
push!(fieldset, field)
|
||||
push!(element, fieldset)
|
||||
push!(element, FieldSet("field1", [Field(0.0, collect(1:n))]))
|
||||
# TODO: how to parametrize this?
|
||||
push!(element, FieldSet("geometry", [Field(0.0, 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)
|
||||
dbasis = grad(basis)
|
||||
mid = zeros(dim)
|
||||
try
|
||||
basis = get_basis(element)
|
||||
val1 = basis(mid, 0.0)
|
||||
Logging.info("basis at $mid: $val1")
|
||||
val2 = basis("field1", mid, 0.0)
|
||||
Logging.info("field val at $mid: $val2")
|
||||
catch
|
||||
Logging.error("""
|
||||
Unable to evaluate basis, define function 'get_basis' for
|
||||
this element.""")
|
||||
end
|
||||
try
|
||||
basis = get_basis(element)
|
||||
dbasis = grad(basis)
|
||||
val3 = dbasis(mid, 0.0)
|
||||
Logging.info("derivative of basis at $mid: $val3")
|
||||
val4 = dbasis("field1", mid, 0.0)
|
||||
Logging.info("field val at $mid: $val4")
|
||||
catch
|
||||
Logging.error("""
|
||||
Unable to evaluate partial derivatives of basis,
|
||||
define function 'get_dbasisdxi' for this element.""")
|
||||
end
|
||||
val1 = basis(mid, 0.0)
|
||||
Logging.info("basis at $mid: $val1")
|
||||
val2 = basis("field1", mid, 0.0)
|
||||
Logging.info("field val at $mid: $val2")
|
||||
val3 = dbasis(mid, 0.0)
|
||||
Logging.info("derivative of basis at $mid: $val3")
|
||||
val4 = dbasis("field1", mid, 0.0)
|
||||
Logging.info("field val at $mid: $val4")
|
||||
|
||||
Logging.info("Element $element_type passed tests.")
|
||||
end
|
||||
@@ -184,73 +163,70 @@ function call(u::FunctionSpace, field_name, xi::Vector, t::Number=Inf, variation
|
||||
return f.values
|
||||
end
|
||||
h = u.element.basis.basis(xi)
|
||||
return h*f
|
||||
return dot(vec(h), f)
|
||||
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)'
|
||||
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)
|
||||
b = gradu.element.basis.dbasisdxi(xi)
|
||||
return b*f*inv(b*X)
|
||||
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
|
||||
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)
|
||||
b = gradu.element.basis.dbasisdxi(xi)
|
||||
return (b*inv(b*X))'
|
||||
dN = gradu.element.basis.dbasisdxi(xi)
|
||||
J = sum([dN[:,i]*X[i]' for i=1:length(X)])
|
||||
grad = inv(J)*dN
|
||||
return grad
|
||||
end
|
||||
|
||||
# on-line functions to get api more easy to use, ip -> xi.ip
|
||||
call(u::FunctionSpace, ip::IntegrationPoint, t::Number) = call(u, ip.xi, t)
|
||||
call(u::FunctionSpace, ip::IntegrationPoint) = call(u, ip.xi)
|
||||
call(u::GradientFunctionSpace, ip::IntegrationPoint, t::Number) = call(u, ip.xi, t)
|
||||
call(u::GradientFunctionSpace, ip::IntegrationPoint) = call(u, ip.xi)
|
||||
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, variation=nothing) = call(u, field_name, ip.xi, t, variation)
|
||||
call(u::GradientFunctionSpace, field_name, ip::IntegrationPoint, t::Number, 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 field from function space. """
|
||||
""" Return a field from function space. """
|
||||
function get_field(u::FunctionSpace, field_name, time=Inf)
|
||||
return u.element[field_name](time)
|
||||
end
|
||||
|
||||
""" Return field from function space. """
|
||||
""" 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)
|
||||
end
|
||||
|
||||
""" Return fieldset from function space. """
|
||||
""" Return a fieldset from function space. """
|
||||
function get_fieldset(u::FunctionSpace, field_name)
|
||||
return u.element[field_name]
|
||||
end
|
||||
|
||||
# i think these will be the most called functions.
|
||||
call(u::FunctionSpace, field_name, ip::IntegrationPoint, t::Number, variation=nothing) = call(u, field_name, ip.xi, t, variation)
|
||||
call(u::GradientFunctionSpace, field_name, ip::IntegrationPoint, t::Number, variation=nothing) = call(u, field_name, ip.xi, t, variation)
|
||||
|
||||
function jacobian(u::FunctionSpace, xi, t)
|
||||
u.element.basis.dbasisdxi(xi)*u.element["geometry"](t)
|
||||
function LinAlg.det(u::FunctionSpace, xi::Vector, t::Number=Inf)
|
||||
X = u.element["geometry"](t)
|
||||
dN = u.element.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 jacobian(u::FunctionSpace, ip::IntegrationPoint, t::Number)
|
||||
jacobian(u, ip.xi, t)
|
||||
function LinAlg.det(u::FunctionSpace, ip::IntegrationPoint, t::Number=Inf)
|
||||
LinAlg.det(u, ip.xi, t)
|
||||
end
|
||||
|
||||
function jacobian(u::FunctionSpace, xi)
|
||||
jacobian(u, xi, Inf)
|
||||
end
|
||||
|
||||
function LinAlg.det(u::FunctionSpace)
|
||||
function detJ(args...)
|
||||
J = jacobian(u, args...)
|
||||
m, n = size(J)
|
||||
return m == n ? det(J) : norm(J)
|
||||
end
|
||||
return detJ
|
||||
return (args...) -> det(u, args...)
|
||||
end
|
||||
|
||||
function get_basis(element::Element)
|
||||
@@ -265,7 +241,7 @@ Base.(:-)(u::GradientFunctionSpace, v::GradientFunctionSpace) = (args...) -> u(a
|
||||
|
||||
""" Check does fieldset exist. """
|
||||
function Base.haskey(element::Element, what)
|
||||
haskey(element.fields, symbol(what))
|
||||
haskey(element.fields, what)
|
||||
end
|
||||
|
||||
|
||||
|
||||
+30
-49
@@ -11,17 +11,12 @@ type LocalAssembly <: Assembly
|
||||
mass_matrix :: Matrix
|
||||
stiffness_matrix :: Matrix
|
||||
force_vector :: Matrix
|
||||
potential_energy# :: Union{Array, Float64}
|
||||
potential_energy
|
||||
residual_vector :: Vector
|
||||
end
|
||||
|
||||
function LocalAssembly(ndofs, mass_matrix, stiffness_matrix, force_vector::Matrix)
|
||||
LocalAssembly(ndofs, mass_matrix, stiffness_matrix, force_vector[:])
|
||||
end
|
||||
|
||||
""" Initialize workspace for local assembly. """
|
||||
function LocalAssembly(equation::Equation)
|
||||
ndofs = size(equation)
|
||||
""" Initialize workspace for local matrices for dimension ndofs. """
|
||||
function initialize_local_assembly(ndofs::Int=1)
|
||||
mass_matrix = zeros(ndofs, ndofs)
|
||||
stiffness_matrix = zeros(ndofs, ndofs)
|
||||
force_vector = zeros(ndofs, 1)
|
||||
@@ -31,14 +26,24 @@ function LocalAssembly(equation::Equation)
|
||||
potential_energy, residual_vector)
|
||||
end
|
||||
|
||||
""" Initialize workspace for local matrices, get dimension from equation. """
|
||||
function initialize_local_assembly(equation::Equation)
|
||||
LocalAssembly(equation)
|
||||
ndofs = prod(size(equation))
|
||||
return initialize_local_assembly(ndofs)
|
||||
end
|
||||
|
||||
function initialize_local_assembly(equation::Equation, assembly::LocalAssembly)
|
||||
if size(equation) != assembly.ndofs
|
||||
""" Initialize or zero workspace. """
|
||||
function initialize_local_assembly!(assembly::LocalAssembly, equation::Equation)
|
||||
ndofs = prod(size(equation))
|
||||
if ndofs != assembly.ndofs
|
||||
# if problem size changes, automatically initialize new work space
|
||||
return initialize_local_assembly(equation)
|
||||
assembly.ndofs = ndofs
|
||||
assembly.mass_matrix = zeros(ndofs, ndofs)
|
||||
assembly.stiffness_matrix = zeros(ndofs, ndofs)
|
||||
assembly.force_vector = zeros(ndofs, 1)
|
||||
assembly.potential_energy = 0.0
|
||||
assembly.residual_vector = zeros(ndofs)
|
||||
return
|
||||
end
|
||||
# otherwise, empty workspace ready for next iteration
|
||||
fill!(assembly.mass_matrix, 0.0)
|
||||
@@ -46,15 +51,7 @@ function initialize_local_assembly(equation::Equation, assembly::LocalAssembly)
|
||||
fill!(assembly.force_vector, 0.0)
|
||||
assembly.potential_energy = 0.0
|
||||
fill!(assembly.residual_vector, 0.0)
|
||||
return assembly
|
||||
end
|
||||
function initialize_local_assembly(assembly::LocalAssembly, equation::Equation)
|
||||
initialize_local_assembly(equation, assembly)
|
||||
end
|
||||
|
||||
function get_unknown_field_name(equation::Equation)
|
||||
eqtype = typeof(equation)
|
||||
error("define get_unknown_field_name for this equation type $eqtype")
|
||||
return
|
||||
end
|
||||
|
||||
has_mass_matrix(equation::Equation) = false
|
||||
@@ -73,14 +70,15 @@ get_integration_points(equation::Equation) = equation.integration_points
|
||||
|
||||
|
||||
""" Return a local assembly for element. """
|
||||
function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation, time::Number=Inf)
|
||||
function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
|
||||
unknown_field_name::ASCIIString, time::Number=Inf,
|
||||
problem=nothing)
|
||||
|
||||
initialize_local_assembly(assembly, equation) # zero all
|
||||
initialize_local_assembly!(assembly, equation) # zero all
|
||||
|
||||
element = get_element(equation)
|
||||
basis = get_basis(element)
|
||||
detJ = det(basis)
|
||||
field_name = get_unknown_field_name(equation)
|
||||
|
||||
# 1. if equations are defined we just integrate them
|
||||
if has_mass_matrix(equation) || has_stiffness_matrix(equation) || has_force_vector(equation)
|
||||
@@ -96,17 +94,16 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
|
||||
assembly.force_vector += s*get_force_vector(equation, ip, time)[:]
|
||||
end
|
||||
# external loads -- if any nodal loads is defined add to force vector
|
||||
if haskey(element, "$field_name nodal load")
|
||||
assembly.force_vector += element["$field_name nodal load"](time)[:]
|
||||
if haskey(element, "$unknown_field_name nodal load")
|
||||
assembly.force_vector += element["$unknown_field_name nodal load"](time)[:]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# 2. variational / energy form - user has defined some potential energy / variational form
|
||||
if has_potential_energy(equation)
|
||||
field_name = get_unknown_field_name(equation)
|
||||
element = get_element(equation)
|
||||
field = element[field_name](time)
|
||||
field = element[unknown_field_name](time)
|
||||
function potential_energy(data::Vector)
|
||||
# calculate potential energy for some setting. this is needed by forwarddiff
|
||||
assembly.potential_energy = 0.0
|
||||
@@ -117,8 +114,8 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
|
||||
assembly.potential_energy += ip.weight * dw * detJ(ip)
|
||||
end
|
||||
# external energy -- if any nodal loads is defined, decrease from potential energy
|
||||
if haskey(element, "$field_name nodal load")
|
||||
P = element["$field_name nodal load"](time)
|
||||
if haskey(element, "$unknown_field_name nodal load")
|
||||
P = element["$unknown_field_name nodal load"](time)
|
||||
assembly.potential_energy -= dot(P[:], df[:])
|
||||
end
|
||||
if isa(assembly.potential_energy, Array)
|
||||
@@ -135,9 +132,8 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
|
||||
|
||||
# 3. virtual work form - user has defined residual vector δW_int(u,δu) + δW_ext(u,δu) = 0 ∀ v
|
||||
if has_residual_vector(equation)
|
||||
field_name = get_unknown_field_name(equation)
|
||||
element = get_element(equation)
|
||||
field = element[field_name](time)
|
||||
field = element[unknown_field_name](time)
|
||||
function residual_vector(data::Vector)
|
||||
fill!(assembly.residual_vector, 0.0)
|
||||
df = similar(field, data)
|
||||
@@ -147,8 +143,8 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
|
||||
assembly.residual_vector += ip.weight*dr*detJ(ip)
|
||||
end
|
||||
# external loads -- if any nodal loads is defined, remove from residual
|
||||
if haskey(element, "$field_name nodal load")
|
||||
assembly.residual_vector -= element["$field_name nodal load"](time)[:]
|
||||
if haskey(element, "$unknown_field_name nodal load")
|
||||
assembly.residual_vector -= element["$unknown_field_name nodal load"](time)[:]
|
||||
end
|
||||
return assembly.residual_vector
|
||||
end
|
||||
@@ -160,18 +156,3 @@ function calculate_local_assembly!(assembly::LocalAssembly, equation::Equation,
|
||||
|
||||
end
|
||||
|
||||
function calculate_local_assembly!(equation::Equation, assembly::LocalAssembly, time::Number=Inf)
|
||||
calculate_local_assembly!(assembly, equation)
|
||||
end
|
||||
|
||||
|
||||
""" Get global degrees of freedom for this element. """
|
||||
function get_global_dofs(eq::Equation)
|
||||
eq.global_dofs
|
||||
end
|
||||
|
||||
""" Set global degrees of freedom for this element. """
|
||||
function set_global_dofs!(eq::Equation, dofs)
|
||||
eq.global_dofs = dofs
|
||||
end
|
||||
|
||||
|
||||
+38
-34
@@ -6,68 +6,72 @@
|
||||
abstract HeatProblem <: Problem
|
||||
abstract HeatEquation <: Equation
|
||||
|
||||
get_unknown_field_name(eq::HeatEquation) = symbol("temperature")
|
||||
|
||||
|
||||
### Plane heat problem + equations ###
|
||||
|
||||
type PlaneHeatProblem <: HeatProblem
|
||||
unknown_field_name :: ASCIIString
|
||||
unknown_field_dimension :: Int
|
||||
equations :: Array{HeatEquation, 1}
|
||||
element_mapping :: Dict{DataType, DataType}
|
||||
end
|
||||
|
||||
""" Default constructor for problem takes no arguments. """
|
||||
function PlaneHeatProblem()
|
||||
return PlaneHeatProblem([])
|
||||
element_mapping = Dict(
|
||||
Quad4 => DC2D4,
|
||||
Seg2 => DC2D2)
|
||||
return PlaneHeatProblem("temperature", 1, [], element_mapping)
|
||||
end
|
||||
|
||||
""" Return dimension of unknown field variable, temperature is scalar field. """
|
||||
get_dimension(pr::Type{PlaneHeatProblem}) = 1
|
||||
|
||||
""" Map Lagrange element Quad4 to equation DC2D4 """
|
||||
get_equation(pr::Type{PlaneHeatProblem}, el::Type{Quad4}) = DC2D4
|
||||
|
||||
""" Map Lagrange element Seg2 to equation DC2D2 """
|
||||
get_equation(pr::Type{PlaneHeatProblem}, el::Type{Seg2}) = DC2D2
|
||||
|
||||
|
||||
""" Diffusive heat transfer for 4-node bilinear element. """
|
||||
type DC2D4 <: HeatEquation
|
||||
element :: Quad4
|
||||
integration_points :: Array{IntegrationPoint, 1}
|
||||
global_dofs :: Array{Int64, 1}
|
||||
end
|
||||
function DC2D4(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)]
|
||||
integration_points = get_default_integration_points(element)
|
||||
push!(element, FieldSet("temperature"))
|
||||
DC2D4(element, integration_points, [])
|
||||
DC2D4(element, integration_points)
|
||||
end
|
||||
function get_lhs(equation::DC2D4, ip, time)
|
||||
element = get_element(equation)
|
||||
dNdX = get_dbasisdX(element, ip.xi, time)
|
||||
k = interpolate(element, "temperature thermal conductivity", ip.xi, time)
|
||||
return dNdX*k*dNdX'
|
||||
end
|
||||
JuliaFEM.has_lhs(eq::DC2D4) = true
|
||||
Base.size(equation::DC2D4) = (1, 4)
|
||||
|
||||
""" Diffusive heat transfer for 2-node linear segment. """
|
||||
type DC2D2 <: HeatEquation
|
||||
element :: Seg2
|
||||
integration_points :: Array{IntegrationPoint, 1}
|
||||
global_dofs :: Array{Int64, 1}
|
||||
end
|
||||
function DC2D2(element::Seg2)
|
||||
integration_points = [IntegrationPoint([0.0], 2.0)]
|
||||
integration_points = get_default_integration_points(element)
|
||||
push!(element, FieldSet("temperature"))
|
||||
DC2D2(element, integration_points, [])
|
||||
DC2D2(element, integration_points)
|
||||
end
|
||||
function get_rhs(equation::DC2D2, ip, time)
|
||||
Base.size(equation::DC2D2) = (1, 2)
|
||||
|
||||
function calculate_local_assembly!(assembly::LocalAssembly, equation::HeatEquation,
|
||||
unknown_field_name::ASCIIString, time::Number=Inf,
|
||||
problem=nothing)
|
||||
|
||||
initialize_local_assembly!(assembly, equation)
|
||||
|
||||
element = get_element(equation)
|
||||
h = get_basis(element, ip.xi)
|
||||
f = interpolate(element, "temperature flux", ip.xi, time)
|
||||
return h*f
|
||||
basis = get_basis(element)
|
||||
dbasis = grad(basis)
|
||||
detJ = det(basis)
|
||||
for ip in get_integration_points(equation)
|
||||
w = ip.weight * detJ(ip)
|
||||
# evaluate fields in integration point
|
||||
ρ = basis("density", ip, time)
|
||||
k = basis("temperature thermal conductivity", ip, time)
|
||||
f = basis("temperature load", ip, time)
|
||||
# evaluate basis functions and gradient in integration point
|
||||
N = basis(ip, time)
|
||||
dN = dbasis(ip, time)
|
||||
# do assembly
|
||||
assembly.mass_matrix += w * ρ*N'*N
|
||||
assembly.stiffness_matrix += w * k*dN'*dN
|
||||
assembly.force_vector += w * N'*f
|
||||
end
|
||||
end
|
||||
JuliaFEM.has_rhs(eq::DC2D2) = true
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
function get_default_integration_points(element::Quad4)
|
||||
[
|
||||
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)
|
||||
]
|
||||
end
|
||||
|
||||
function get_default_integration_points(element::Seg2)
|
||||
[
|
||||
IntegrationPoint([0.0], 2.0)
|
||||
]
|
||||
end
|
||||
+3
-3
@@ -59,7 +59,7 @@ function interpolate(basis::Basis, field::Field, ip::IntegrationPoint)
|
||||
interpolate(basis, field, ip.xi)
|
||||
end
|
||||
|
||||
function dinterpolate(basis::Basis, u::Field, xi::Array{Float64, 1})
|
||||
basis.dbasisdxi(xi)*u
|
||||
end
|
||||
#function dinterpolate(basis::Basis, u::Field, xi::Array{Float64, 1})
|
||||
# basis.dbasisdxi(xi)*u
|
||||
#end
|
||||
|
||||
|
||||
+12
-10
@@ -17,8 +17,9 @@ function calculate_lagrange_basis(P, X)
|
||||
end
|
||||
# Logging.debug("Calculating inverse of A")
|
||||
invA = inv(A)'
|
||||
basis(xi) = invA*P(xi)
|
||||
basis
|
||||
basis(xi) = (invA*P(xi))'
|
||||
dbasisdxi(xi) = (ForwardDiff.jacobian((xi) -> invA*P(xi), xi, cache=autodiffcache))'
|
||||
basis, dbasisdxi
|
||||
end
|
||||
|
||||
"""
|
||||
@@ -33,21 +34,22 @@ macro create_lagrange_element(element_name, element_description, X, P)
|
||||
eltype = esc(element_name)
|
||||
quote
|
||||
global get_element_description
|
||||
global get_number_of_basis_functions, get_element_dimension
|
||||
dim = size($X, 1)
|
||||
nbasis = size($X, 2)
|
||||
basis = calculate_lagrange_basis($P, $X)
|
||||
#global get_number_of_basis_functions, get_element_dimension
|
||||
#dim = size($X, 1)
|
||||
#nbasis = size($X, 2)
|
||||
basis, dbasisdxi = calculate_lagrange_basis($P, $X)
|
||||
type $eltype <: CG
|
||||
connectivity :: Array{Int, 1}
|
||||
basis :: Basis
|
||||
fields :: Dict{Symbol, FieldSet}
|
||||
fields :: Dict{ASCIIString, FieldSet}
|
||||
end
|
||||
function $eltype(connectivity, args...)
|
||||
$eltype(connectivity, Basis(basis), Dict())
|
||||
$eltype(connectivity, Basis(basis, dbasisdxi), Dict())
|
||||
end
|
||||
get_element_description(el::Type{$eltype}) = $element_description
|
||||
get_number_of_basis_functions(el::Type{$eltype}) = nbasis
|
||||
get_element_dimension(el::Type{$eltype}) = dim
|
||||
#get_number_of_basis_functions(el::Type{$eltype}) = nbasis
|
||||
#get_element_dimension(el::Type{$eltype}) = dim
|
||||
Base.size(el::Type{$eltype}) = Base.size($X)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
using ForwardDiff
|
||||
|
||||
"""
|
||||
Linearize function f w.r.t some given field, i.e. calculate dR/du
|
||||
|
||||
Parameters
|
||||
----------
|
||||
f::Function
|
||||
(possibly) nonlinear function to linearize
|
||||
field::ASCIIString
|
||||
field variable
|
||||
|
||||
Returns
|
||||
-------
|
||||
Array{Float64, 2}
|
||||
jacobian / "tangent stiffness matrix"
|
||||
"""
|
||||
function linearize(f::Function, el::Element, field::ASCIIString)
|
||||
dim, nnodes = size(el.attributes[field])
|
||||
function helper!(x, y)
|
||||
orig = copy(el.attributes[field])
|
||||
el.attributes[field] = reshape(x, dim, nnodes)
|
||||
y[:] = f(el)
|
||||
el.attributes[field] = copy(orig)
|
||||
end
|
||||
jac = ForwardDiff.forwarddiff_jacobian(helper!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
|
||||
return jac(el.attributes[field][:])
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
This version returns another function which can be then evaluated against field
|
||||
"""
|
||||
function linearize(f::Function, field::ASCIIString)
|
||||
function jacobian(el::Element, args...)
|
||||
fld = get_field(el, field)
|
||||
dim, nnodes = size(fld)
|
||||
function helper!(x, y)
|
||||
orig = copy(fld)
|
||||
set_field(el, field, reshape(x, dim, nnodes))
|
||||
y[:] = f(el, args...)
|
||||
set_field(el, field, copy(orig))
|
||||
end
|
||||
jac = ForwardDiff.forwarddiff_jacobian(helper!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
|
||||
return jac(fld[:])
|
||||
end
|
||||
return jacobian
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
In-place version, no additional garbage collection.
|
||||
"""
|
||||
function linearize!(f::Function, el::Element, field::ASCIIString, target::ASCIIString)
|
||||
el.attributes[target][:] = 0.0
|
||||
dim, nnodes = size(el.attributes[field])
|
||||
function helper!(x, y)
|
||||
orig = copy(el.attributes[field])
|
||||
el.attributes[field] = reshape(x, dim, nnodes)
|
||||
y[:] = f(el)
|
||||
el.attributes[field] = copy(orig)
|
||||
end
|
||||
jac! = ForwardDiff.forwarddiff_jacobian!(helper!, Float64, fadtype=:dual, n=dim*nnodes, m=dim*nnodes)
|
||||
jac!(el.attributes[field][:], el.attributes[target])
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
This version returns a function which must be operated with element e
|
||||
"""
|
||||
function integrate(f::Function)
|
||||
function integrate(el::Element)
|
||||
target = []
|
||||
for ip in el.integration_points
|
||||
J = interpolate(el, :geometry, ip.xi; derivative=true)
|
||||
push!(target, ip.weight*f(el, ip)*det(J))
|
||||
end
|
||||
return sum(target)
|
||||
end
|
||||
return integrate
|
||||
end
|
||||
|
||||
"""
|
||||
This version saves results inplace to target, garbage collection free
|
||||
"""
|
||||
function integrate!(f::Function, el::Element, target)
|
||||
# set target to zero
|
||||
el.attributes[target][:] = 0.0
|
||||
for ip in el.integration_points
|
||||
J = interpolate(el, :geometry, ip.xi; derivative=true)
|
||||
el.attributes[target][:,:] += ip.weight*f(el, ip)*det(J)
|
||||
end
|
||||
end
|
||||
|
||||
function linearize(eq::Equation, f::Function, field::ASCIIString)
|
||||
function jacobian(eq::Equation, args...)
|
||||
el = get_element(eq)
|
||||
fld = get_field(el, field)
|
||||
dim, nnodes = size(fld)
|
||||
function helper(x::Vector)
|
||||
orig = copy(fld)
|
||||
set_field(el, field, reshape(x, dim, nnodes))
|
||||
y = f(eq, args...)
|
||||
set_field(el, field, orig)
|
||||
return y[:]
|
||||
end
|
||||
jac = ForwardDiff.jacobian(helper)
|
||||
return jac(fld[:])
|
||||
end
|
||||
return jacobian
|
||||
end
|
||||
|
||||
+11
-130
@@ -5,141 +5,22 @@ abstract Problem
|
||||
abstract BoundaryProblem <: Problem
|
||||
abstract FieldProblem <: Problem
|
||||
|
||||
get_equations(pr::Problem) = pr.equations
|
||||
|
||||
function get_dimension(pr::Type{Problem})
|
||||
throw("Unable to determine problem dimension for problem $pr")
|
||||
function get_equations(problem::Problem)
|
||||
problem.equations
|
||||
end
|
||||
|
||||
function get_equation(pr::Type{Problem}, el::Type{Element})
|
||||
throw("Could not find corresponding equation for element $el in problem $pr")
|
||||
function get_unknown_field_dimension(problem::Problem)
|
||||
problem.unknown_field_dimension
|
||||
end
|
||||
|
||||
"""
|
||||
Add new element to problem
|
||||
"""
|
||||
function add_element!(problem::Problem, element::Element)
|
||||
equation = get_equation(typeof(problem), typeof(element))
|
||||
push!(problem.equations, equation(element))
|
||||
function get_unknown_field_name(problem::Problem)
|
||||
problem.unknown_field_name
|
||||
end
|
||||
|
||||
""" Add new element to problem. """
|
||||
function Base.push!(problem::Problem, element::Element)
|
||||
equation = get_equation(typeof(problem), typeof(element))
|
||||
push!(problem.equations, equation(element))
|
||||
end
|
||||
|
||||
"""
|
||||
Return total number of basis functions in problem
|
||||
"""
|
||||
function get_number_of_basis_functions(pr::Problem)
|
||||
conn = Int[]
|
||||
for eq in get_equations(pr)
|
||||
append!(conn, get_connectivity(eq))
|
||||
end
|
||||
length(unique(conn))
|
||||
end
|
||||
|
||||
"""
|
||||
Problem matrix size dimension
|
||||
"""
|
||||
function get_matrix_dimension(pr::Problem)
|
||||
get_dimension(typeof(pr))*get_number_of_basis_functions(pr)
|
||||
end
|
||||
|
||||
"""
|
||||
Assign global dofs for element. This doesn't do any reordering.
|
||||
"""
|
||||
function set_global_dofs!(pr::Problem)
|
||||
#ndim = get_dimension(pr)*get_number_of_basis_functions(pr)
|
||||
#ndim = get_matrix_dimension(pr)
|
||||
dim = get_dimension(typeof(pr))
|
||||
nconn = get_number_of_basis_functions(pr)
|
||||
ndim = dim*nconn
|
||||
Logging.debug("Problem (matrix) dimension: $ndim")
|
||||
gdofs = reshape(collect(1:ndim), dim, nconn)
|
||||
for eq in get_equations(pr)
|
||||
lconn = get_connectivity(eq)
|
||||
gconn = gdofs[:, lconn][:]
|
||||
set_global_dofs!(eq, gconn)
|
||||
end
|
||||
end
|
||||
|
||||
""" Return unique list of connectivity (i.e. node ids). """
|
||||
function get_connectivity(problem::Problem)
|
||||
connectivity = Int[]
|
||||
for equation in get_equations(problem)
|
||||
element = get_element(equation)
|
||||
append!(connectivity, get_connectivity(element))
|
||||
end
|
||||
connectivity = unique(connectivity)
|
||||
return connectivity
|
||||
end
|
||||
|
||||
"""
|
||||
Calculate global dofs for equations, maybe using some bandwidth
|
||||
minimizing or fill reducing algorithm
|
||||
"""
|
||||
function calculate_global_dofs(pr::Problem)
|
||||
conn = get_connectivity(pr)
|
||||
dim = get_dimension(typeof(pr))
|
||||
ndofs = dim*length(conn)
|
||||
Logging.debug("total dofs: $ndofs")
|
||||
|
||||
mconn = maximum(conn)
|
||||
gdofs = reshape(collect(1:mconn), dim, mconn)
|
||||
dofmap = Dict{Int64, Array{Int64, 1}}()
|
||||
for (i, c) in enumerate(conn)
|
||||
dofmap[c] = gdofs[:, i]
|
||||
end
|
||||
return dofmap
|
||||
end
|
||||
|
||||
"""
|
||||
Assign global dofs for equations.
|
||||
"""
|
||||
function assign_global_dofs!(pr::Problem, dofmap)
|
||||
for eq in get_equations(pr)
|
||||
el = get_element(eq)
|
||||
c = get_connectivity(el)
|
||||
#gdofs = [dofmap[ci] for ci in c]
|
||||
gdofs = Int64[]
|
||||
for ci in c
|
||||
append!(gdofs, dofmap[ci])
|
||||
end
|
||||
set_global_dofs!(eq, gdofs)
|
||||
end
|
||||
end
|
||||
|
||||
function get_lhs(pr::Problem, t::Float64)
|
||||
I = Int64[]
|
||||
J = Int64[]
|
||||
V = Float64[]
|
||||
dim = get_dimension(typeof(pr))
|
||||
for eq in filter(has_lhs, get_equations(pr))
|
||||
dofs = get_global_dofs(eq)
|
||||
lhs = integrate_lhs(eq, t)
|
||||
for (li, i) in enumerate(dofs)
|
||||
for (lj, j) in enumerate(dofs)
|
||||
push!(I, i)
|
||||
push!(J, j)
|
||||
push!(V, lhs[li, lj])
|
||||
end
|
||||
end
|
||||
end
|
||||
return I, J, V
|
||||
end
|
||||
|
||||
function get_rhs(pr::Problem, t::Float64)
|
||||
I = Int64[]
|
||||
V = Float64[]
|
||||
dim = get_dimension(typeof(pr))
|
||||
for eq in filter(has_rhs, get_equations(pr))
|
||||
dofs = get_global_dofs(eq)
|
||||
rhs = integrate_rhs(eq, t)
|
||||
for (li, i) in enumerate(dofs)
|
||||
push!(I, i)
|
||||
push!(V, rhs[li])
|
||||
end
|
||||
end
|
||||
return I, V
|
||||
element_type = typeof(element)
|
||||
equation_type = problem.element_mapping[element_type]
|
||||
push!(problem.equations, equation_type(element))
|
||||
end
|
||||
|
||||
|
||||
+27
-22
@@ -15,6 +15,9 @@ end
|
||||
function Field(time, values)
|
||||
Field(time, 0, values)
|
||||
end
|
||||
function Field(values)
|
||||
Field(0.0, 0, values)
|
||||
end
|
||||
""" Get length of a field (number of basis functions in practice). """
|
||||
function Base.length(f::Field)
|
||||
length(f.values)
|
||||
@@ -31,16 +34,23 @@ end
|
||||
function Base.(:*)(k::Number, f::Field)
|
||||
Field(f.time, k*f.values)
|
||||
end
|
||||
""" Multiply field with some vector x. """
|
||||
function Base.(:*)(x::Vector, f::Field)
|
||||
|
||||
""" Inner product of field and vector x. """
|
||||
function Base.dot(x::Vector, f::Field)
|
||||
@assert length(x) == length(f)
|
||||
sum([f[i]*x[i] for i in 1:length(f)])
|
||||
end
|
||||
""" Multiply field with some matrix x. """
|
||||
# function Base.(:*){T}(x::Matrix, f::Field{Vector{T}})
|
||||
function Base.(:*)(x::Matrix, f::Field)
|
||||
sum([f[i]*x[i,:] for i in 1:length(f)])
|
||||
|
||||
function Base.size(field::Field)
|
||||
(length(field.values[1]), length(field.values))
|
||||
end
|
||||
|
||||
#""" Multiply field with some matrix x. """
|
||||
# function Base.(:*){T}(x::Matrix, f::Field{Vector{T}})
|
||||
#function Base.(:*)(x::Matrix, f::Field)
|
||||
# sum([f[i]*x[:,i]' for i in 1:length(f)])
|
||||
#end
|
||||
|
||||
""" Sum two fields. """
|
||||
function Base.(:+)(f1::Field, f2::Field)
|
||||
@assert(f1.time == f2.time, "Cannot add fields: time mismatch, $(f1.time) != $(f2.time)")
|
||||
@@ -59,6 +69,9 @@ Examples
|
||||
function Base.getindex(field::Field, c::Colon)
|
||||
[field.values...;]
|
||||
end
|
||||
function Base.vec(field::Field)
|
||||
[field.values...;]
|
||||
end
|
||||
|
||||
""" Return field similar to input but with new data in it.
|
||||
|
||||
@@ -87,20 +100,17 @@ end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
""" FieldSet is set of fields, each field can have different time and/or increment. """
|
||||
type FieldSet
|
||||
name :: Symbol
|
||||
name :: ASCIIString
|
||||
fields :: Array{Field, 1}
|
||||
end
|
||||
""" Initializer for FieldSet. """
|
||||
function FieldSet(field_name)
|
||||
FieldSet(Symbol(field_name), [])
|
||||
FieldSet(field_name, [])
|
||||
end
|
||||
function FieldSet()
|
||||
FieldSet(Symbol("unknown field"), [])
|
||||
FieldSet("unknown field", [])
|
||||
end
|
||||
""" Add new field to fieldset. """
|
||||
function Base.push!(fs::FieldSet, field::Field)
|
||||
@@ -127,15 +137,10 @@ type Basis
|
||||
basis :: Function
|
||||
dbasisdxi :: Function
|
||||
end
|
||||
""" Constructor of basis function. """
|
||||
function Basis(basis)
|
||||
Basis(basis, ForwardDiff.jacobian(basis))
|
||||
end
|
||||
""" Get partial derivative of basis function. """
|
||||
function grad(basis::Basis)
|
||||
(ip) -> basis.dbasisdxi(ip.xi)
|
||||
end
|
||||
|
||||
#""" Constructor of basis function. """
|
||||
#function Basis(basis)
|
||||
# Basis(basis, ForwardDiff.jacobian(basis))
|
||||
#end
|
||||
|
||||
"""
|
||||
Integration point
|
||||
@@ -151,7 +156,7 @@ attributes :: Dict{Any, Any}
|
||||
type IntegrationPoint
|
||||
xi :: Array{Float64, 1}
|
||||
weight :: Float64
|
||||
fields :: Dict{Symbol, FieldSet}
|
||||
fields :: Dict{ASCIIString, FieldSet}
|
||||
end
|
||||
function IntegrationPoint(xi, weight)
|
||||
IntegrationPoint(xi, weight, Dict())
|
||||
|
||||
Reference in New Issue
Block a user