Skip to content

Commit b9153ae

Browse files
Merge pull request #227 from ccaprani/trapz_approximation
Added advanced example on trapezoid approximation for torsion constant
2 parents 023f7b5 + 3ba6d4e commit b9153ae

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
rectangular 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.
26+
import numpy as np
27+
import matplotlib.pyplot as plt
28+
from shapely.geometry import Polygon
29+
import sectionproperties.pre.geometry as geometry
30+
import sectionproperties.pre.pre as pre
31+
import sectionproperties.pre.library.primitive_sections as sections
32+
from sectionproperties.analysis.section import Section
33+
34+
#%%
35+
# Define the Calculation Engine
36+
# =============================
37+
# It's better to collect the relevant section property calculation in a
38+
# single function. We are only interested in the torsion constant, so this
39+
# is straightforward enough. `geom` is the Section Property Geometry object;
40+
# `ms` is the mesh size.
41+
def get_section_j(geom, ms, plot_geom=False):
42+
geom.create_mesh(mesh_sizes=[ms])
43+
section = Section(geom)
44+
if plot_geom:
45+
section.plot_mesh()
46+
section.calculate_geometric_properties()
47+
section.calculate_warping_properties()
48+
return section.get_j()
49+
50+
51+
#%%
52+
# Define the Mesh Density
53+
# =======================
54+
# The number of elements per unit area is an important input to the calculations
55+
# even though we are only examining ratios of the results. A nominal value of 100
56+
# is reasonable.
57+
n = 100 # mesh density
58+
59+
#%%
60+
# Create and Analyse the Section
61+
# ==============================
62+
# This function accepts the width `b` and a slope `S` to create the trapezoid.
63+
# Since we are only interested in relative results, the nominal dimensions are
64+
# immaterial. There are a few ways to parametrize the problem, but it has been
65+
# found that setting the middle height of trapezoid (i.e. the average height)
66+
# to a unit value works fine.
67+
def do_section(b, S, d_mid=1, plot_geom=False):
68+
delta = S * d_mid
69+
d1 = d_mid - delta
70+
d2 = d_mid + delta
71+
72+
# compute mesh size
73+
ms = d_mid * b / n
74+
75+
points = []
76+
points.append([0, 0])
77+
points.append([0, d1])
78+
points.append([b, d2])
79+
points.append([b, 0])
80+
if S < 1.0:
81+
trap_geom = geometry.Geometry(Polygon(points))
82+
else:
83+
trap_geom = sections.triangular_section(h=d2, b=b)
84+
jt = get_section_j(trap_geom, ms, plot_geom)
85+
86+
rect_geom = sections.rectangular_section(d=(d1 + d2) / 2, b=b)
87+
jr = get_section_j(rect_geom, ms, plot_geom)
88+
return jt, jr, d1, d2
89+
90+
91+
#%%
92+
# Example Section
93+
# ===============
94+
# The analysis for a particular section looks as follows:
95+
b, S = 4.0, 0.3
96+
jt, jr, d1, d2 = do_section(b, S, plot_geom=True)
97+
print(f"{b=}; {S=}; {jr=}; {jt=}; {jr/jt}")
98+
99+
#%%
100+
# Create Loop Variables
101+
# =====================
102+
# The slope `S` is 0 for a rectangle, and 1 for a triangle and is
103+
# defined per the plot below. A range of `S`, between 0.0 and 1.0 and
104+
# a range of `b` are considered, between 1 and 10 here (but can be extended)
105+
b_list = np.logspace(0, np.log10(10), 10)
106+
S_list = np.linspace(0.0, 1.0, 10)
107+
j_rect = np.zeros((len(b_list), len(S_list)))
108+
j_trap = np.zeros((len(b_list), len(S_list)))
109+
110+
#%%
111+
# The Main Loop
112+
# =============
113+
# Execute the double loop to get the ratios for combinations of `S` and `b`.
114+
#
115+
# An optional deugging line is left in for development but commented out.
116+
for i, b in enumerate(b_list):
117+
for j, S in enumerate(S_list):
118+
jt, jr, d1, d2 = do_section(b, S)
119+
j_trap[i][j] = jt
120+
j_rect[i][j] = jr
121+
122+
# 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}")
123+
#%%
124+
# Calculate the Ratios
125+
# ====================
126+
# Courtesy of numpy, this is easy:
127+
j_ratio = j_rect / j_trap
128+
#%%
129+
# Plot the Results
130+
# ================
131+
# Here we highlight a few of the contours to illustrate the accuracy and behaviour
132+
# of the approximation.
133+
#
134+
# As expected, when the section is rectangular, the error is small, but as it increases
135+
# towards a triangle the accuracy generally reduces. However, there is an interesting
136+
# line at an aspect ratio of about 2.7 where the rectangular approximation is always
137+
# equal to the trapezoid's torsion constant.
138+
levels = np.arange(start=0.5, stop=1.5, step=0.05)
139+
plt.figure(figsize=(12, 6))
140+
cs = plt.contour(
141+
S_list,
142+
b_list,
143+
j_ratio,
144+
levels=[0.95, 0.99, 1.00, 1.01, 1.05],
145+
colors=("k",),
146+
linestyles=(":",),
147+
linewidths=(1.2,),
148+
)
149+
plt.clabel(cs, colors="k", fontsize=10)
150+
plt.contourf(S_list, b_list, j_ratio, 25, cmap="Wistia", levels=levels)
151+
# plt.yscale('log')
152+
minor_x_ticks = np.linspace(min(S_list), max(S_list), 10)
153+
# plt.xticks(minor_x_ticks,minor=True)
154+
plt.minorticks_on()
155+
plt.grid(which="both", ls=":")
156+
plt.xlabel(r"Slope $S = (d_2-d_1)/(d_2+d_1); d_2\geq d_1, d_1\geq 0$")
157+
plt.ylabel("Aspect $b/d_{ave}; d_{ave} = (d_1 + d_2)/2$")
158+
plt.colorbar()
159+
plt.title(
160+
r"Accuracy of rectangular approximation to trapezoid torsion constant $J_{rect}\, /\, J_{trapz}$",
161+
multialignment="center",
162+
)
163+
plt.show()

0 commit comments

Comments
 (0)