Skip to content

Commit d77f744

Browse files
committed
Added advanced example on trapezoid approximation for torsion constant
1 parent cb0109c commit d77f744

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
r"""
2+
.. _ref-ex-trapz-approx:
3+
4+
Approximation of Torsion Constant for Trapezoidal Sections
5+
----------------------------------------------------------
6+
7+
Trapezoidal elements or components of a cross-section are
8+
quite common in bridge structures, either concrete or steel
9+
composite construction. However, it's common to determine
10+
the torsion constant of the trapezoidal section by using a
11+
recatangular approximation. For example, this is done in
12+
the Autodesk Structural Bridge Design software when there
13+
is a haunch in a `Steel Composite Beam
14+
<https://knowledge.autodesk.com/support/structural-bridge-design/learn-explore/caas/CloudHelp/cloudhelp/ENU/ASBD-InProdAU/files/structure/tech-info/ti-torsion/ASBD-InProdAU-structure-tech-info-ti-torsion-Torsion-property-for-SAM-composite-beams-html-html.html>`_
15+
16+
The question then arises, when is it appropriate to make
17+
the rectangular approximation to a trapezoidal section,
18+
and what might the expected error be?
19+
"""
20+
21+
#%%
22+
# Define the Imports
23+
# ==================
24+
# Here we bring in the primative section shapes and also the more generic
25+
# Shapely `Polygon` object. We also use the nifty `tqdm` package to provide
26+
# a progress bar on computations.
27+
from tqdm import tqdm
28+
import numpy as np
29+
import matplotlib.pyplot as plt
30+
from shapely.geometry import Polygon
31+
import sectionproperties.pre.geometry as geometry
32+
import sectionproperties.pre.pre as pre
33+
import sectionproperties.pre.library.primitive_sections as sections
34+
from sectionproperties.analysis.section import Section
35+
36+
#%%
37+
# Define the Calculation Engine
38+
# =============================
39+
# It's better to collect the relevant section property calculation in a
40+
# single function. We are only interested in the torsion constant, so this
41+
# is straightforward enough. `geom` is the Section Property Gemoetry object;
42+
# `ms` is the mesh size. The "cgs" solver helps avoid singular matrices due
43+
# to small angles.
44+
def get_section_j(geom, ms):
45+
geom.create_mesh(mesh_sizes=[ms])
46+
section = Section(geom)
47+
section.calculate_geometric_properties()
48+
section.calculate_warping_properties(solver_type="cgs")
49+
return section.get_j()
50+
51+
52+
#%%
53+
# Define the Mesh Density
54+
# =======================
55+
# The number of elements per unit area is an important input to the calculations
56+
# even though we are only examining ratios of the results. A nominal value of 100
57+
# is reasonable.
58+
n = 110 # mesh density
59+
60+
#%%
61+
# Create and Analyze the Section
62+
# ==============================
63+
# This function accepts the width `b` and a slope `S` to create the trapezoid.
64+
# Since we are only interested in relative results, the nominal dimensions are
65+
# immaterial. There are a few ways to parametrize the problem, but it has been
66+
# found that setting the middle height of trapezoid (i.e. the average height)
67+
# to a unit value works fine.
68+
def do_section(b, S, d_mid=1):
69+
delta = S * d_mid
70+
d1 = d_mid - delta
71+
d2 = d_mid + delta
72+
73+
# compute mesh size
74+
ms = d_mid * b / n
75+
76+
points = []
77+
points.append([0, 0])
78+
points.append([0, d1])
79+
points.append([b, d2])
80+
points.append([b, 0])
81+
if S < 1.0:
82+
trap_geom = geometry.Geometry(Polygon(points))
83+
else:
84+
trap_geom = sections.triangular_section(h=d2, b=b)
85+
jt = get_section_j(trap_geom, ms)
86+
87+
rect_geom = sections.rectangular_section(d=(d1 + d2) / 2, b=b)
88+
jr = get_section_j(rect_geom, ms)
89+
return jt, jr, d1, d2
90+
91+
92+
#%%
93+
# Create Loop Variables
94+
# =====================
95+
# # The slope `S` is 0 for a rectangle, and 1 for a triangle and is
96+
# defined per the plot below. A range of `S`, between 0.0 and 1.0 and
97+
# a range of `b` are considered, between 1 and 10 here (but can be extended)
98+
b_list = np.logspace(0, np.log10(10), 10)
99+
S_list = np.linspace(0.0, 1.0, 10)
100+
j_rect = np.zeros((len(b_list), len(S_list)))
101+
j_trap = np.zeros((len(b_list), len(S_list)))
102+
103+
#%%
104+
# The Main Loop
105+
# =============
106+
# Execute the double loop to get the ratios for combinations of `S` and `b`.
107+
# We also use `tqdm` to provide progress bars.
108+
#
109+
# An optional deugging line is left in for development but commented out.
110+
for i, b in enumerate(tqdm(b_list)):
111+
for j, S in enumerate(tqdm(S_list, leave=False)):
112+
jt, jr, d1, d2 = do_section(b, S)
113+
j_trap[i][j] = jt
114+
j_rect[i][j] = jr
115+
116+
# print(f"{b=:.3}; {S=:.3}; {d1=:.5}; {d2=:.5}; J rect = {j_rect[i][j]:.5e}; J trap = {j_trap[i][j]:.5e}; J ratio = {j_rect[i][j]/j_trap[i][j]:.5}")
117+
#%%
118+
# Calculate the Ratios
119+
# ====================
120+
# Courtesy of numpy, this is easy:
121+
j_ratio = j_rect / j_trap
122+
#%%
123+
# Plot the Results
124+
# ================
125+
# Here we highlight a few of the contours to illustrate the accuracy and behaviour
126+
# of the approximation.
127+
#
128+
# As expected, when the section is rectangular, the error is small, but as it increases
129+
# towards a triangle the accuracy genearlly reduces. However, there is an interesting
130+
# line at an aspect ratio of about 2.7 where the rectangular approximation is always
131+
# equal to the trapezoid's torsion constant.
132+
levels = np.arange(start=0.5, stop=1.5, step=0.05)
133+
plt.figure(figsize=(12, 6))
134+
cs = plt.contour(
135+
S_list,
136+
b_list,
137+
j_ratio,
138+
levels=[0.95, 0.99, 1.00, 1.01, 1.05],
139+
colors=("k",),
140+
linestyles=(":",),
141+
linewidths=(1.2,),
142+
)
143+
plt.clabel(cs, colors="k", fontsize=10)
144+
plt.contourf(S_list, b_list, j_ratio, 25, cmap="Wistia", levels=levels)
145+
# plt.yscale('log')
146+
minor_x_ticks = np.linspace(min(S_list), max(S_list), 10)
147+
# plt.xticks(minor_x_ticks,minor=True)
148+
plt.minorticks_on()
149+
plt.grid(which="both", ls=":")
150+
plt.xlabel(r"Slope $S = (d_2-d_1)/(d_2+d_1); d_2\geq d_1, d_1\geq 0$")
151+
plt.ylabel("Aspect $b/d_{ave}; d_{ave} = (d_1 + d_2)/2$")
152+
plt.colorbar()
153+
plt.title(
154+
r"Accuracy of rectangular approximation to trapezoid torsion constant $J_{rect}\, /\, J_{trapz}$",
155+
multialignment="center",
156+
)
157+
plt.show()

0 commit comments

Comments
 (0)