You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Take this example and make it part of the documentation, ideally comparing its results with a trust made with beam elements and/or link elements.
Truss Example
Problem Description:
We are tasked with optimizing the cross-sectional areas of the members of a truss structure to minimize its total weight, while ensuring that the stress in each member does not exceed a predefined limit (i.e., yield stress). This involves solving a constrained optimization problem, where the constraints are based on structural safety criteria.
Model:
A truss is a structure made up of straight members connected at joints (nodes).
We minimize the total weight of the truss by adjusting the cross-sectional areas of the members.
The constraints ensure that stresses in the members due to applied loads are within safe limits.
The stress in each member is computed using the force-displacement relationship based on Hooke’s Law.
Assumptions:
We will use linear elasticity.
Small deformations, so linear analysis applies.
The truss is statically determinate (i.e., forces in members are calculated using equilibrium equations).
Steps:
Define the truss geometry (nodes and members).
Define the force-displacement equations.
Set the objective function: minimize total weight.
Set the stress constraints for each member.
Use SciPy’s optimization tools to solve the problem.
Code
importnumpyasnpfromscipy.optimizeimportminimize################################################################################# Problem definitionmax_displacement=0.001minimal_section=0.001# Density of the material (steel)density=7850# kg/m^3# Young's Modulus (material property)E=210e9# Pa (steel)# Stress constraint: stress in each member must be below yield stressyield_stress=250e6# Pa (steel yield stress)# Geometry of the truss bridge (nodes and members)nodes=np.array([
[0, 0], # Node 0: Pinned support
[2, 0], # Node 1
[4, 0], # Node 2 - Load -15 vertical
[6, 0], # Node 3
[8, 0], # Node 4 - Load -15 vertical
[10, 0], # Node 5: Roller support
[2, 3], # Node 6
[4, 3], # Node 7
[6, 3], # Node 8
[8, 3] # Node 9
])
members= [
(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), # Lower members
(0, 6), (1, 7), (2, 8), (3, 7), (4, 8), (5, 9), # Diagonal members
(1, 6), (2, 7), (3, 8), (4, 9), # vertical members
(6, 7), (7, 8), (8, 9), # Upper members
]
# Number of members and nodesn_members=len(members)
n_nodes=len(nodes)
# Applied loads at each node (Node 2 and Node 3 experience loads)loads=np.zeros((n_nodes, 2)) # No load on other nodesloads[2] = [0, -15] # Vertical load (-150 kN) on Node 2loads[3] = [10, -15] # Horizontal (5 kN) and vertical (-150 kN) loads on Node 3multiplier=1E4# KiloNewtonsloads=loads*multiplier# Displacementsfixed_dofs= [0, 1, 11] # Node 0 and 5 fixed in both directions################################################################################# Plotting functionimportmatplotlib.pyplotaspltimportnumpyasnpdefplot_truss(nodes, members, areas=None, loads=None, constraints=None, displacements=None, title="Truss Structure"):
""" Plots the truss structure using node coordinates and member connections. Parameters: nodes (np.array): An array of shape (n_nodes, 2) representing the (x, y) coordinates of the nodes. members (list of tuples): A list of tuples where each tuple contains two node indices representing a member. areas (np.array or None): An array of cross-sectional areas for each member. Default is None (uniform thickness). title (str): The title of the plot. """plt.figure(figsize=(8, 6))
font= {'weight': 'bold'}
plt.title(title, fontdict=font)
# Default line width if no areas providedifareasisNone:
areas=np.ones(len(members))
# Normalize areas for line width visualization (for better plotting)min_thickness=1max_thickness=8ifareasisnotNoneand (areas.max() -areas.min()) !=0:
norm_areas= (areas-areas.min()) / (areas.max() -areas.min())
line_widths=norm_areas* (max_thickness-min_thickness) +min_thicknesselse:
line_widths=np.ones(len(members)) *2# Default line width# Plot all the membersfori, (n1, n2) inenumerate(members):
x_coords= [nodes[n1][0], nodes[n2][0]]
y_coords= [nodes[n1][1], nodes[n2][1]]
plt.plot(x_coords, y_coords, 'b-', lw=line_widths[i])
# Plot the nodes as red circlesplt.scatter(nodes[:, 0], nodes[:, 1], color='r', zorder=5)
# Label the nodesfori, (x, y) inenumerate(nodes):
plt.text(x, y, f'{i}', fontsize=12, ha='right')
# Plot loads as arrowsifloadsisnotNone:
fori, (fx, fy) inenumerate(loads):
iffx!=0orfy!=0:
plt.arrow(nodes[i][0], nodes[i][1], fx*0.1/multiplier, fy*0.1/multiplier, head_width=0.2, head_length=0.3, fc='g', ec='g')
# Plot constraints (supports)ifconstraintsisnotNone:
fornodeinconstraints:
marker=6ifnode%2else4node=node//2plt.scatter(nodes[node][0], nodes[node][1], marker=marker, color="black", s=100, zorder=10)
# Set axis limits and labelsplt.xlabel('X-coordinate (m)')
plt.ylabel('Y-coordinate (m)')
plt.gca().set_aspect('equal', adjustable='box')
plt.grid(True)
plt.show()
plot_truss(nodes, members, loads=loads, constraints=fixed_dofs, title="Truss Bridge Structure")
################################################################################# Solving# Length and initial cross-sectional areas of the membersdefmember_length(n1, n2):
returnnp.linalg.norm(nodes[n1] -nodes[n2])
lengths=np.array([member_length(n1, n2) forn1, n2inmembers])
# Objective function: minimize weight of the trussdefweight(areas):
w=np.sum(areas*lengths*density)
# print(w)returnwdefweight_opt(areas):
# Normalizeareas=areas/np.abs(areas)
returnweight(areas)
# Force-displacement matrix (stiffness matrix) and stress calculationsdefstiffness_matrix(areas):
K=np.zeros((2*n_nodes, 2*n_nodes)) # Global stiffness matrixfori, (n1, n2) inenumerate(members):
L=lengths[i]
A=areas[i]
cos_theta= (nodes[n2][0] -nodes[n1][0]) /Lsin_theta= (nodes[n2][1] -nodes[n1][1]) /Lk_local=E*A/L*np.array([
[ cos_theta**2, cos_theta*sin_theta, -cos_theta**2, -cos_theta*sin_theta],
[ cos_theta*sin_theta, sin_theta**2, -cos_theta*sin_theta, -sin_theta**2],
[-cos_theta**2, -cos_theta*sin_theta, cos_theta**2, cos_theta*sin_theta],
[-cos_theta*sin_theta, -sin_theta**2, cos_theta*sin_theta, sin_theta**2]
])
# Assemble the local stiffness matrix into the global stiffness matrixdof_map= [2*n1, 2*n1+1, 2*n2, 2*n2+1]
forrowinrange(4):
forcolinrange(4):
K[dof_map[row], dof_map[col]] +=k_local[row, col]
returnK# Displacement constraint function (nodal equilibrium equations)defdisplacements(areas):
K=stiffness_matrix(areas)
# Boundary conditionsfree_dofs=np.setdiff1d(np.arange(2*n_nodes), fixed_dofs)
# Solve for displacements at free nodesK_reduced=K[np.ix_(free_dofs, free_dofs)]
load_reduced=loads.flatten()[free_dofs]
u_free=np.linalg.solve(K_reduced, load_reduced)
displacements_full=np.zeros(2*n_nodes)
displacements_full[free_dofs] =u_freereturndisplacements_full.reshape((n_nodes, 2))
# Stress constraintdefstress_constraint(areas):
# https://people.duke.edu/~hpgavin/cee421/truss-method.pdfu=displacements(areas)
stresses=np.zeros(n_members)
fori, (n1, n2) inenumerate(members):
L=lengths[i]
A=areas[i]
cos_theta= (nodes[n2][0] -nodes[n1][0]) /Lsin_theta= (nodes[n2][1] -nodes[n1][1]) /Ldelta=u[n2] -u[n1] # Displacement difference between nodesforce=E*A/L* (delta[0]*cos_theta+delta[1]*sin_theta)
stresses[i] =abs(force/A)
returnyield_stress-stresses# Positive means constraint satisfied# Add displacement constraintdefdisplacement_constraint(areas):
u=displacements(areas)
returnmax_displacement- (u[:,0]**2+u[:,1]**2)**0.5# Add positivity constraint for cross-sectional areas (they must be positive)defarea_positivity_constraint(areas):
return (areas-minimal_section) # Positive areas, minimal area# Initial guess for cross-sectional areas (0.01 m^2 for each member)initial_areas=np.ones(n_members) *minimal_section*2# Solve the optimization problem with constraintsconstraints= [
{'type': 'ineq', 'fun': stress_constraint},
{'type': 'ineq', 'fun': displacement_constraint},
{'type': 'ineq', 'fun': area_positivity_constraint}
]
# Solveresult=minimize(
weight_opt,
initial_areas,
constraints=constraints,
method="SLSQP",
options={"disp": True}
)
# Optimized areas and corresponding weightoptimized_areas=result.xoptimized_weight=weight(optimized_areas)
# Resultsprint("Optimized Cross-Sectional Areas (m^2):", optimized_areas)
print("Optimized Truss Weight (kg):", optimized_weight)
# Plotting resultplot_truss(nodes, members, areas=optimized_areas, title="Result Truss Bridge Structure")
Take this example and make it part of the documentation, ideally comparing its results with a trust made with beam elements and/or link elements.
Truss Example
Problem Description:
We are tasked with optimizing the cross-sectional areas of the members of a truss structure to minimize its total weight, while ensuring that the stress in each member does not exceed a predefined limit (i.e., yield stress). This involves solving a constrained optimization problem, where the constraints are based on structural safety criteria.
Model:
A truss is a structure made up of straight members connected at joints (nodes).
We minimize the total weight of the truss by adjusting the cross-sectional areas of the members.
The constraints ensure that stresses in the members due to applied loads are within safe limits.
The stress in each member is computed using the force-displacement relationship based on Hooke’s Law.
Assumptions:
Steps:
Code
The text was updated successfully, but these errors were encountered: