Skip to content

Commit

Permalink
- Compute global displacement vector and global reaction force vector
Browse files Browse the repository at this point in the history
- Fix a bug with concentrated loads
  • Loading branch information
AkchurinDA committed Sep 20, 2024
1 parent ecce5ca commit a2ab580
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 49 deletions.
70 changes: 48 additions & 22 deletions src/Analyses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ struct O1EAnalysis <: AbstractAnalysis
end

struct O1ECache

K_e
F
P
R
U
end

struct O2EAnalysis <: AbstractAnalysis
Expand All @@ -23,29 +27,46 @@ function solve(model::Model, analysis::O1EAnalysis)
n_ID_mapping[node.ID] = i
end

# Assign internal IDs to elements:
e_ID_mapping = Dict{Int, Int}()
for (i, element) in enumerate(values(model.elements))
e_ID_mapping[element.ID] = i
end
# Get the partition indices:
indices_f, indices_s = _get_partition_indices(model, n_ID_mapping)

# Assemble the global elastic stiffness matrix:
K_e = _assemble_K_e(model, n_ID_mapping)
K_e_ff = K_e[indices_f, indices_f]
K_e_fs = K_e[indices_f, indices_s]
K_e_sf = K_e[indices_s, indices_f]
K_e_ss = K_e[indices_s, indices_s]

# Assemble the global force vector:
F = _assemble_F(model, n_ID_mapping)
F_f = F[indices_f]
F_s = F[indices_s]

# Assemble the global fixed-end force vector:
P = _assemble_P(model, n_ID_mapping)
P_f = P[indices_f]
P_s = P[indices_s]

# Get the partition indices:
indices_f, indices_s = _get_partition_indices(model, n_ID_mapping)
# Solve for the displacements at the unsupported DOFs:
U_f = K_e_ff \ (F_f - P_f)

# Solve for the displacements:
U_f = K_e[indices_f, indices_f] \ (F[indices_f] - P[indices_f])
# Assemble the global displacement vector:
U = zeros(eltype(U_f), 6 * length(model.nodes))
for (i, index) in enumerate(indices_f)
U[index] = U_f[i]
end

# Compute the reaction forces at the supported DOFs:
R_s = K_e_sf * U_f + P_s

# Assemble the global reaction force vector:
R = zeros(eltype(R_s), 6 * length(model.nodes))
for (i, index) in enumerate(indices_s)
R[index] = R_s[i]
end

# Return the displacements:
return U_f
return O1ECache(K_e, F, P, R, U)
end

function _assemble_K_e(model::Model, n_ID_mapping::Dict{Int, Int})
Expand Down Expand Up @@ -104,14 +125,14 @@ function _assemble_F(model::Model, n_ID_mapping::Dict{Int, Int})
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]
for node in values(model.nodes)
if haskey(model.concentrated_loads, node.ID)
node_i_ID = n_ID_mapping[node.ID]

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

@inbounds F[range_i] += concentrated_load.second
@inbounds F[range] += model.concentrated_loads[node.ID]
end
end
end

Expand All @@ -128,13 +149,18 @@ function _assemble_P(model::Model, n_ID_mapping::Dict{Int, Int})
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]
for element in values(model.elements)
if haskey(model.distributed_loads, element.ID)
# Extract the internal node IDs of the nodes of an element:
node_i_ID_i = n_ID_mapping[element.node_i_ID]
node_j_ID_i = n_ID_mapping[element.node_j_ID]

range_i = (6 * (node_ID_i - 1) + 1):(6 * node_ID_i)
range_i = (6 * (node_i_ID_i - 1) + 1):(6 * node_i_ID_i)
range_j = (6 * (node_j_ID_i - 1) + 1):(6 * node_j_ID_i)

@inbounds P[range_i] += p_g.second
@inbounds P[range_i] += model.p_g[element.ID][1:6 ]
@inbounds P[range_j] += model.p_g[element.ID][7:12]
end
end
end

Expand Down
67 changes: 42 additions & 25 deletions src/Models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ function Base.show(io::IO, model::Model)
println(io, styled"{yellow, bold: Empty model.}")
else
println(io, styled"{cyan, bold: Model with:}")
!isempty(model.nodes ) && println(io, styled"{cyan: \t $(length(model.nodes )) \t Nodes }")
!isempty(model.materials ) && println(io, styled"{cyan: \t $(length(model.materials )) \t Materials }")
!isempty(model.sections ) && println(io, styled"{cyan: \t $(length(model.sections )) \t Sections }")
!isempty(model.elements ) && println(io, styled"{cyan: \t $(length(model.elements )) \t Elements }")
!isempty(model.supports ) && println(io, styled"{cyan: \t $(length(model.supports )) \t Supported nodes}")
!isempty(model.concetrated_loads) && println(io, styled"{cyan: \t $(length(model.concetrated_loads)) \t Loaded nodes }")
!isempty(model.distributed_loads) && println(io, styled"{cyan: \t $(length(model.distributed_loads)) \t Loaded elements}")
!isempty(model.nodes ) && println(io, styled"{cyan: \t $(length(model.nodes )) \t Nodes }")
!isempty(model.materials ) && println(io, styled"{cyan: \t $(length(model.materials )) \t Materials }")
!isempty(model.sections ) && println(io, styled"{cyan: \t $(length(model.sections )) \t Sections }")
!isempty(model.elements ) && println(io, styled"{cyan: \t $(length(model.elements )) \t Elements }")
!isempty(model.supports ) && println(io, styled"{cyan: \t $(length(model.supports )) \t Supported nodes}")
!isempty(model.concentrated_loads) && println(io, styled"{cyan: \t $(length(model.concentrated_loads)) \t Loaded nodes }")
!isempty(model.distributed_loads ) && println(io, styled"{cyan: \t $(length(model.distributed_loads )) \t Loaded elements}")
end
end

Expand Down Expand Up @@ -141,6 +141,7 @@ function add_element!(model::Model, ID::Int, node_i_ID::Int, node_j_ID::Int, mat

# Compute the element's elastic stiffness matrix in the global coordinate system:
k_e_g = T' * k_e_l * T
map!(x -> abs(x) < 1E-12 ? 0 : x, k_e_g, k_e_g)

# 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 @@ -152,6 +153,7 @@ function add_element!(model::Model, ID::Int, node_i_ID::Int, node_j_ID::Int, mat

# Compute the element's geometric stiffness matrix in the global coordinate system:
k_g_g = T' * k_g_l * T
map!(x -> abs(x) < 1E-12 ? 0 : x, k_g_g, k_g_g)

# 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 Down Expand Up @@ -206,7 +208,7 @@ function add_concentrated_load!(model::Model, ID::Int, F_x::Real, F_y::Real, F_z
return model
end

function add_distributed_load!(model::Model, ID::Int, w_x::Real, w_y::Real, w_z::Real; CS::Symbol = :local)
function add_distributed_load!(model::Model, ID::Int, w_x::Real, w_y::Real, w_z::Real; cs::Symbol = :local)
# Check if the element exists in the model:
if !haskey(model.elements, ID)
throw(ArgumentError("Element with ID = $(ID) does not exist in the model."))
Expand All @@ -218,14 +220,29 @@ function add_distributed_load!(model::Model, ID::Int, w_x::Real, w_y::Real, w_z:
end

# Add the distributed loads to the model:
if CS == :local # If the distributed loads are provided in the local coordinate system of the element
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

# 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
elseif cs == :global # If the distributed loads are provided in the global coordinate system
# Extract the information of the element:
x_i, y_i, z_i = model.elements[ID].x_i, model.elements[ID].y_i, model.elements[ID].z_i
x_j, y_j, z_j = model.elements[ID].x_j, model.elements[ID].y_j, model.elements[ID].z_j
Expand All @@ -246,28 +263,28 @@ function add_distributed_load!(model::Model, ID::Int, w_x::Real, w_y::Real, w_z:
r = γ * R

# Resolve the resultants in the local coordinate system of the element into distributed loads:
w_x, w_y, w_z = r / L
w_x_l, w_y_l, w_z_l = r / L

# Add the distributed loads to the model:
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)
# Compute the element fixed-end forces in the element's local coordinate system:
p_l = _compute_p_l(
w_x_l, w_y_l, w_z_l,
L)

# Transform the element fixed-end forces to the global coordinate system:
p_g = T * p_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)
# 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
# Add the element fixed-end forces to the model:
model.p_l[ID] = p_l
model.p_g[ID] = p_g
else
throw(ArgumentError("Coordinate system must be either `:local` or `:global`."))
end

# Return the updated model:
return model
Expand Down
8 changes: 6 additions & 2 deletions test/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ 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)
add_distributed_load!(M, 1, 0, -100, 0, cs = :global)
add_distributed_load!(M, 2, 0, -100, 0, cs = :global)
add_distributed_load!(M, 3, 0, -100, 0, cs = :global)
add_distributed_load!(M, 4, 0, -100, 0, cs = :global)
add_distributed_load!(M, 5, 0, -100, 0, cs = :global)

U_f = solve(M, O1EAnalysis())
Solution = solve(M, O1EAnalysis())

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

3 comments on commit a2ab580

@AkchurinDA
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AkchurinDA
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register(branch = "main")

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/115601

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.1.0 -m "<description of version>" a2ab580d5ed865162a96e22388f3e86eac6d171e
git push origin v0.1.0

Please sign in to comment.