Skip to content

Commit

Permalink
- Added support for distributed loads applied on elements (either in …
Browse files Browse the repository at this point in the history
…global or local coordinate systems)
  • Loading branch information
AkchurinDA committed Sep 20, 2024
1 parent 894b27f commit ecce5ca
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 31 deletions.
88 changes: 88 additions & 0 deletions src/Analyses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ function solve(model::Model, analysis::O1EAnalysis)
for (i, element) in enumerate(values(model.elements))
e_ID_mapping[element.ID] = i
end

# Assemble the global elastic stiffness matrix:
K_e = _assemble_K_e(model, n_ID_mapping)

# Assemble the global force vector:
F = _assemble_F(model, n_ID_mapping)

# Assemble the global fixed-end force vector:
P = _assemble_P(model, n_ID_mapping)

# Get the partition indices:
indices_f, indices_s = _get_partition_indices(model, n_ID_mapping)

# Solve for the displacements:
U_f = K_e[indices_f, indices_f] \ (F[indices_f] - P[indices_f])

# Return the displacements:
return U_f
end

function _assemble_K_e(model::Model, n_ID_mapping::Dict{Int, Int})
Expand Down Expand Up @@ -76,4 +94,74 @@ function _assemble_K_g(model::Model, n_ID_mapping::Dict{Int, Int})

# Return the global geometric stiffness matrix:
return K_g
end

function _assemble_F(model::Model, n_ID_mapping::Dict{Int, Int})
if isempty(model.concentrated_loads)
F = zeros(6 * length(model.nodes))
else
# Preallocate the global force vector:
T = promote_type([eltype(concentrated_load) for concentrated_load in values(model.concentrated_loads)]...)
F = zeros(T, 6 * length(model.nodes))

# Assemble the global force vector:
for concentrated_load in model.concentrated_loads
# Extract the internal node ID of a node:
node_ID_i = n_ID_mapping[concentrated_load.first]

range_i = (6 * (node_ID_i - 1) + 1):(6 * node_ID_i)

@inbounds F[range_i] += concentrated_load.second
end
end

# Return the global force vector:
return F
end

function _assemble_P(model::Model, n_ID_mapping::Dict{Int, Int})
if isempty(model.p_g)
P = zeros(6 * length(model.nodes))
else
# Preallocate the global fixed-end force vector:
T = promote_type([eltype(p_g) for p_g in values(model.p_g)]...)
P = zeros(T, 6 * length(model.nodes))

# Assemble the global fixed-end force vector:
for p_g in model.p_g
# Extract the internal node ID of a node:
node_ID_i = n_ID_mapping[p_g.first]

range_i = (6 * (node_ID_i - 1) + 1):(6 * node_ID_i)

@inbounds P[range_i] += p_g.second
end
end

# Return the global fixed-end force vector:
return P
end

function _get_partition_indices(model::Model, n_ID_mapping::Dict{Int, Int})
# Preallocate:
indices_f = Int[] # List of free DOFs
indices_s = Int[] # List of supported DOFs

# Get the partition indices:
for node in values(model.nodes)
node_tag_i = n_ID_mapping[node.ID]

if haskey(model.supports, node.ID) # Check if the node's DOFs are fixed
for i in 1:6
model.supports[node.ID][i] ? push!(indices_s, 6 * (node_tag_i - 1) + i) : push!(indices_f, 6 * (node_tag_i - 1) + i)
end
else
for i in 1:6
push!(indices_f, 6 * (node_tag_i - 1) + i)
end
end
end

# Return the partition indices:
return indices_f, indices_s
end
33 changes: 31 additions & 2 deletions src/Elements.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,18 @@ struct Element{CTI<:Real, CTJ<:Real, MPT<:Real, SPT<:Real}
# ADDITIONAL ELEMENT INFORMATION THAT CAN BE PRECOMPUTED:
"Length of the element, ``L``"
L ::Real
"Local-to-global sub-transformation matrix of the element, ``[\\gamma]``"
"Global-to-local sub-transformation matrix of the element, ``[\\gamma]``"
γ ::Matrix{<:Real}
"Local-to-global transformation matrix of the element, ``[T]``"
"Global-to-local transformation matrix of the element, ``[T]``"
T ::Matrix{<:Real}
"Element's elastic stiffness matrix in its local coordinate system, ``[k_{e, l}]``"
k_e_l ::Matrix{<:Real}
"Element's geometric stiffness matrix in its local coordinate system, ``[k_{g, l}]``"
k_g_l ::Matrix{<:Real}
"Element's elastic stiffness matrix in its global coordinate system, ``[k_{e, g}]``"
k_e_g ::Matrix{<:Real}
"Element's geometric stiffness matrix in its global coordinate system, ``[k_{g, g}]``"
k_g_g ::Matrix{<:Real}
# "Condensed element's elastic stiffness matrix in its local coordinate system, ``[k_{e, l, c}]``"
# k_e_l_c ::Matrix{<:Real}
# "Condensed element's geometric stiffness matrix in its local coordinate system, ``[k_{g, l, c}]``"
Expand Down Expand Up @@ -203,4 +207,29 @@ function _compute_k_g_l(

# Return the element geometric stiffness matrix:
return k_g_l
end

function _compute_p_l(
w_x::DLTX, w_y::DLTY, w_z::DLTZ,
L::ELT) where {DLTX<:Real, DLTY<:Real, DLTZ<:Real, ELT<:Real}
# Compute the element fixed-end forces:
p_l = [
-w_x * L / 2 ; # F_x_i
-w_y * L / 2 ; # F_y_i
-w_z * L / 2 ; # F_z_i
0 ; # M_x_i
-w_z * L ^ 2 / 12; # M_y_i
-w_y * L ^ 2 / 12; # M_z_i
+w_x * L / 2 ; # F_x_j
-w_y * L / 2 ; # F_y_j
-w_z * L / 2 ; # F_z_j
0 ; # M_x_j
+w_z * L ^ 2 / 12; # M_y_j
+w_y * L ^ 2 / 12] # M_z_j

# Remove small values if any:
map!(x -> abs(x) < 1E-12 ? 0 : x, p_l, p_l)

# Return the element fixed-end forces:
return p_l
end
7 changes: 6 additions & 1 deletion src/Hephaestus.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module Hephaestus
using LinearAlgebra
using StyledStrings
using OrderedCollections
using DocStringExtensions
Expand All @@ -8,6 +9,10 @@ include("Materials.jl")
include("Sections.jl")
include("Elements.jl")
include("Models.jl")
include("Analyses.jl")
export Node, Material, Section, Element, Model
export add_node!, add_material!, add_section!, add_element!, add_support!, add_concetrated_load!, add_distributed_load!
export add_node!, add_material!, add_section!, add_element!, add_support!, add_concentrated_load!, add_distributed_load!
export O1EAnalysis, O1ECache
export O2EAnalysis, O2ECache
export solve
end
69 changes: 49 additions & 20 deletions src/Models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ To create a model, use the [`Model()`](@ref) constructor.
# Fields
$(FIELDS)
"""
@kwdef mutable struct Model
@kwdef struct Model
"Dictionary of nodes in the model."
nodes ::OrderedDict{Int, Node } = OrderedDict{Int, Node }()
"Dictionary of materials in the model."
Expand All @@ -21,10 +21,15 @@ $(FIELDS)
"Dictionary of supports in the model."
supports ::OrderedDict{Int, Vector{Bool}} = OrderedDict{Int, Vector{Bool}}()

"Dictionary of concetrated loads (acting on nodes) in the model."
concetrated_loads ::OrderedDict{Int, Vector{<:Real}} = OrderedDict{Int, Vector{<:Real}}()
"Dictionary of concentrated loads (acting on nodes) in the model."
concentrated_loads ::OrderedDict{Int, Vector{<:Real}} = OrderedDict{Int, Vector{<:Real}}()
"Dictionary of distributed loads (acting on elements) in the model."
distributed_loads ::OrderedDict{Int, Vector{<:Real}} = OrderedDict{Int, Vector{<:Real}}()

"Dictionary of element fixed-end forces in the element's local coordinate system."
p_l ::OrderedDict{Int, Vector{<:Real}} = OrderedDict{Int, Vector{<:Real}}()
"Dictionary of element fixed-end forces in the global coordinate system."
p_g ::OrderedDict{Int, Vector{<:Real}} = OrderedDict{Int, Vector{<:Real}}()
end

function Base.show(io::IO, model::Model)
Expand Down Expand Up @@ -134,6 +139,9 @@ function add_element!(model::Model, ID::Int, node_i_ID::Int, node_j_ID::Int, mat
A, I_zz, I_yy, J,
L)

# Compute the element's elastic stiffness matrix in the global coordinate system:
k_e_g = T' * k_e_l * T

# Compute the condensed element's elastic stiffness matrix in its local coordinate system:
# k_e_l_c = _compute_k_e_l_c(k_e_l)

Expand All @@ -142,6 +150,9 @@ function add_element!(model::Model, ID::Int, node_i_ID::Int, node_j_ID::Int, mat
A, I_zz, I_yy,
L)

# Compute the element's geometric stiffness matrix in the global coordinate system:
k_g_g = T' * k_g_l * T

# Compute the condensed element's geometric stiffness matrix in its local coordinate system:
# k_g_l_c = _compute_k_g_l_c(k_g_l)

Expand All @@ -153,7 +164,7 @@ function add_element!(model::Model, ID::Int, node_i_ID::Int, node_j_ID::Int, mat
E, ν, ρ,
A, I_zz, I_yy, J,
ω, releases,
L, γ, T, k_e_l, k_g_l)
L, γ, T, k_e_l, k_g_l, k_e_g, k_g_g)

# Return the updated model:
return model
Expand All @@ -177,19 +188,19 @@ function add_support!(model::Model, ID::Int, u_x::Bool, u_y::Bool, u_z::Bool, θ
return model
end

function add_concetrated_load!(model::Model, ID::Int, F_x::Real, F_y::Real, F_z::Real, M_x::Real, M_y::Real, M_z::Real)
function add_concentrated_load!(model::Model, ID::Int, F_x::Real, F_y::Real, F_z::Real, M_x::Real, M_y::Real, M_z::Real)
# Check if the node exists in the model:
if !haskey(model.nodes, ID)
throw(ArgumentError("Node with ID = $(ID) does not exist in the model."))
end

# Check if the concetrated load already exists in the model:
if haskey(model.concetrated_loads, ID)
@warn "Concetrated loads at node with ID = $(ID) already exist in the model. Overwriting them."
# Check if the concentrated load already exists in the model:
if haskey(model.concentrated_loads, ID)
@warn "Concentrated loads at node with ID = $(ID) already exist in the model. Overwriting them."
end

# Add the concetrated load to the model:
model.concetrated_loads[ID] = [F_x, F_y, F_z, M_x, M_y, M_z]
model.concentrated_loads[ID] = [F_x, F_y, F_z, M_x, M_y, M_z]

# Return the updated model:
return model
Expand All @@ -208,6 +219,10 @@ function add_distributed_load!(model::Model, ID::Int, w_x::Real, w_y::Real, w_z:

# Add the distributed loads to the model:
if CS == :local # If the distributed loads are provided in the local coordinate system of the element
# Extract the information of the element:
L = model.elements[ID].L
T = model.elements[ID].T

# Add the distributed loads to the model:
model.distributed_loads[ID] = [w_x, w_y, w_z]
elseif CS == :global # If the distributed loads are provided in the global coordinate system
Expand All @@ -216,30 +231,44 @@ function add_distributed_load!(model::Model, ID::Int, w_x::Real, w_y::Real, w_z:
x_j, y_j, z_j = model.elements[ID].x_j, model.elements[ID].y_j, model.elements[ID].z_j
L = model.elements[ID].L
γ = model.elements[ID].γ

# Compute the length of the element along each axis in the global coordinate system:
L_x = abs(x_j - x_i)
L_y = abs(y_j - y_i)
L_z = abs(z_j - z_i)
T = model.elements[ID].T

# Compute the resultants of the distributed loads:
R_x = w_x * L_x
R_y = w_y * L_y
R_z = w_z * L_z
Δ_x = x_j - x_i
Δ_y = y_j - y_i
Δ_z = z_j - z_i
R_x = w_x * norm([0, Δ_y, Δ_z])
R_y = w_y * norm([Δ_x, 0, Δ_z])
R_z = w_z * norm([Δ_x, Δ_y, 0])
R = [R_x, R_y, R_z]

# Transform the resultants to the local coordinate system of the element:
r = γ \ R
r = γ * R

# Resolve the resultants in the local coordinate system of the element into distributed loads:
w_xl, w_yl, w_zl = r / L
w_x, w_y, w_z = r / L

# Add the distributed loads to the model:
model.distributed_loads[ID] = [w_xl, w_yl, w_zl]
model.distributed_loads[ID] = [w_x, w_y, w_z]
else
throw(ArgumentError("Coordinate system must be either `:local` or `:global`."))
end

# Compute the element fixed-end forces in the element's local coordinate system:
p_l = _compute_p_l(
w_x, w_y, w_z,
L)

# Transform the element fixed-end forces to the global coordinate system:
p_g = T * p_l

# Remove small values if any:
map!(x -> abs(x) < 1E-12 ? 0 : x, p_g, p_g)

# Add the element fixed-end forces to the model:
model.p_l[ID] = p_l
model.p_g[ID] = p_g

# Return the updated model:
return model
end
18 changes: 10 additions & 8 deletions test/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ add_element!(M, 3, 3, 4, 1, 1)
add_element!(M, 4, 4, 5, 1, 1)
add_element!(M, 5, 5, 6, 1, 1)

add_support!(M, 1, true , true, true, true, true, true )
add_support!(M, 2, false, true, true, true, true, false)
add_support!(M, 3, false, true, true, true, true, false)
add_support!(M, 4, false, true, true, true, true, false)
add_support!(M, 5, false, true, true, true, true, false)
add_support!(M, 6, false, true, true, true, true, false)

add_concetrated_load!(M, 6, 0, -1000, 0, 0, 0, 0)
add_support!(M, 1, true , true , true, true, true, true )
add_support!(M, 2, false, false, true, true, true, false)
add_support!(M, 3, false, false, true, true, true, false)
add_support!(M, 4, false, false, true, true, true, false)
add_support!(M, 5, false, false, true, true, true, false)
add_support!(M, 6, false, false, true, true, true, false)

add_concentrated_load!(M, 6, 0, -1000, 0, 0, 0, 0)

U_f = solve(M, O1EAnalysis())

# @benchmark FiniteDiff.finite_difference_derivative(f, -1000.0)
# @benchmark ForwardDiff.derivative(f, -1000.0)

0 comments on commit ecce5ca

Please sign in to comment.