Torsion Constant of Trapezoidal Sections#

Trapezoidal elements or components of a cross-section are quite common in bridge structures, either concrete or steel composite construction. However, it’s common to determine the torsion constant of the trapezoidal section by using a rectangular approximation. For example, this is done in the Autodesk Structural Bridge Design software when there is a haunch in a Steel Composite Beam.

The question then arises, when is it appropriate to make the rectangular approximation to a trapezoidal section, and what might the expected error be?

Import Modules#

Here we bring in the rectangular and triangular primitive section shapes, and also the more generic shapely Polygon object.

[1]:
import matplotlib.pyplot as plt
import numpy as np
from shapely import Polygon

from sectionproperties.analysis import Section
from sectionproperties.pre import Geometry
from sectionproperties.pre.library import rectangular_section, triangular_section

Define the Calculation Engine#

It’s better to collect the relevant section property calculation in a single function. We are only interested in the torsion constant, so this is straightforward enough.

[2]:
def get_section_j(
    geom: Geometry,
    ms: float,
    plot_geom: bool = False,
) -> float:
    """Retrieve the torsion constant given a geometry (geom) and mesh size (ms)."""
    geom.create_mesh(mesh_sizes=[ms])
    sec = Section(geometry=geom)

    if plot_geom:
        sec.plot_mesh(materials=False)

    sec.calculate_frame_properties()

    return sec.get_j()

Define the Mesh Density#

The number of elements per unit area is an important input to the calculations even though we are only examining ratios of the results. A nominal value of 100 is reasonable.

[3]:
n = 100  # mesh density

Create and Analyse the Section#

This function accepts the width b and a slope s to create the trapezoid. Since we are only interested in relative results, the nominal dimensions are immaterial. There are a few ways to parametrize the problem, but it has been found that setting the middle height of trapezoid (i.e. the average height) to a unit value works fine.

[4]:
def do_section(
    b: float,
    s: float,
    d_mid: float = 1.0,
    plot_geom=False,
) -> tuple[float, float, float, float]:
    """Calculates the torsion constant for a trapezoid and rectangle."""
    delta = s * d_mid
    d1 = d_mid - delta
    d2 = d_mid + delta

    # compute mesh size
    ms = d_mid * b / n

    # define the points of the trapezoid
    points = [
        (0, 0),
        (0, d1),
        (b, d2),
        (b, 0),
    ]

    # create geometry
    if s < 1.0:
        trap_geom = Geometry(geom=Polygon(points))
    else:
        trap_geom = triangular_section(h=d2, b=b)

    # calculate torsion constant (trapezoid)
    jt = get_section_j(geom=trap_geom, ms=ms, plot_geom=plot_geom)

    # calculate torsion constant (rectangle)
    rect_geom = rectangular_section(d=(d1 + d2) / 2, b=b)
    jr = get_section_j(geom=rect_geom, ms=ms, plot_geom=plot_geom)

    return jt, jr, d1, d2

Example Section#

The analysis for a particular section looks as follows:

[5]:
b, s = 4.0, 0.3
jt, jr, d1, d2 = do_section(b=b, s=s, plot_geom=True)
print(f"{b=:.1f}; {s=:.1f}; {jr=:.3f}; {jt=:.3f}; {jr/jt=:.3f}")
../../_images/examples_advanced_trapezoidal_torsion_10_0.svg
../../_images/examples_advanced_trapezoidal_torsion_10_1.svg
b=4.0; s=0.3; jr=1.124; jt=1.153; jr/jt=0.975

Create Loop Variables#

The slope s is 0 for a rectangle, and 1 for a triangle. A range of s, between 0.0 and 1.0, and a range of b, between 1.0 and 10.0, are considered here (but can be extended).

[6]:
b_list = np.logspace(0, np.log10(10.0), 10)
s_list = np.linspace(0.0, 1.0, 10)
j_rect = np.zeros((len(b_list), len(s_list)))
j_trap = np.zeros((len(b_list), len(s_list)))

The Main Loop#

Execute the double loop to get the ratios for combinations of s and b.

[7]:
for i, b in enumerate(b_list):
    for j, s in enumerate(s_list):
        jt, jr, d1, d2 = do_section(b=b, s=s)
        j_trap[i][j] = jt
        j_rect[i][j] = jr

Calculate the Ratios#

Courtesy of numpy, this is easy:

[8]:
j_ratio = j_rect / j_trap

Plot the Results#

Here we highlight a few of the contours to illustrate the accuracy and behaviour of the approximation.

[9]:
# setup plot
plt.figure(figsize=(12, 6))

# colorbar levels
levels = np.arange(start=0.5, stop=1.5, step=0.05)

# contour line plot
cs = plt.contour(
    s_list,
    b_list,
    j_ratio,
    levels=[0.95, 0.99, 1.00, 1.01, 1.05],
    colors=("k",),
    linestyles=(":",),
    linewidths=(1.2,),
)
plt.clabel(cs, colors="k", fontsize=10)

# filled contour plot
plt.contourf(s_list, b_list, j_ratio, 25, cmap="Wistia", levels=levels)

# plot settings
plt.minorticks_on()
plt.grid(which="both", ls=":")
plt.xlabel(r"Slope $s = (d_2-d_1)/(d_2+d_1); d_2\geq d_1, d_1\geq 0$")
plt.ylabel("Aspect $b/d_{ave}; d_{ave} = (d_1 + d_2)/2$")
plt.colorbar()
plt.title(
    r"Accuracy of rectangular approximation to trapezoid torsion constant $J_{rect}\, /\, J_{trapz}$",
    multialignment="center",
)
plt.show()
../../_images/examples_advanced_trapezoidal_torsion_18_0.svg

As expected, when the section is rectangular s=0, the error is small, but as it increases towards a triangle s=1, the accuracy generally reduces. However, there is an interesting line at an aspect ratio of about 2.7 where the rectangular approximation is always equal to the trapezoid’s torsion constant.