Pilkey - Circular Arc#

This example re-creates the numerical example “B.7 Circular Arc” on page 451 of “Analysis and Design of Elastic Beams” by Walter D. Pilkey.

BibTeX reference:

@book{Pilkey,
    author = {Pilkey, Walter D},
    booktitle = {Analysis and Design of Elastic Beams},
    edition = {First},
    isbn = {0471381527},
    language = {eng},
    publisher = {Wiley},
    title = {Analysis and Design of Elastic Beams: Computational Methods},
    year = {2002},
}

Problem Description#

A circular arc of 120\(^{\circ}\) and radius of 16 in. is analysed, see the figure below. The material properties of the section are taken to be E=210000000 and nu=0.33333. Note that the elastic modulus plays no role in this analysis as the geometry is homogenous, however its value is included for completeness. Also note that the value for E used by Pilkey is in kPa (steel), whereas the problem is defined in inches - this mixing of units is not an issue due to the elastic modulus not affecting the geometric results.

Note that sectionproperties uses an x-y coordinate system rather than the y-z system used by Pilkey.

[1]:
from IPython.display import Image


display(Image(filename="images/arc-geom.png"))
../../_images/examples_validation_pilkey_arc_3_0.png

We can model the above geometry by generating a shapely LineString from a list of points defining the centreline of the above arc. The LineString can be buffered (extruded) orthogonal to the line to create a Polygon object. This Polygon object can then be passed to the sectionproperties Geometry object.

[2]:
import numpy as np
from shapely import LineString, Polygon, buffer

from sectionproperties.pre import Geometry, Material


r = 16  # radius
t = 0.5  # thickness
alpha = 2 * np.pi / 3  # arc angle
n = 128  # number of points definining LineString
# steel material
mat = Material(
    name="Steel",
    elastic_modulus=2.1e8,
    poissons_ratio=0.33333,
    yield_strength=1.0,
    density=1.0,
    color="lightgrey",
)

# define points on the LineString
pts = []
for idx in range(n):
    theta = -alpha / 2 + idx / (n - 1) * alpha
    x = r * np.sin(theta)
    y = r * np.cos(theta)
    pts.append((x, y))

# create LineString
line = LineString(coordinates=pts)

# create Polygon by buffering the LineString
poly = buffer(geometry=line, distance=0.5 * t, cap_style="flat", join_style="mitre")

# create sectionproperties Geometry object
geom = Geometry(geom=poly, material=mat)

# plot geometry
geom.plot_geometry()
../../_images/examples_validation_pilkey_arc_5_0.svg
[2]:
<Axes: title={'center': 'Cross-Section Geometry'}>

Create mesh and Section object#

The numerical analysis by Pilkey uses 9-noded quadraliteral elements. The mesh used by Pilkey for this problem can be seen below.

[3]:
display(Image(filename="images/arc-mesh.png"))
../../_images/examples_validation_pilkey_arc_7_0.png

We can create a mesh in sectionproperties using 6-noded triangular elements by defining a maximum triangular element area. In this case we choose mesh_sizes=0.1 and create the resulting Section object.

[4]:
from sectionproperties.analysis import Section


geom.create_mesh(mesh_sizes=0.1)
sec = Section(geometry=geom)
sec.plot_mesh()
../../_images/examples_validation_pilkey_arc_9_0.svg
[4]:
<Axes: title={'center': 'Finite Element Mesh'}>

Calculate Cross-Section Properties#

Pilkey reports both geometric and warping properties, as such we conduct both analyses.

[5]:
sec.calculate_geometric_properties()
sec.calculate_warping_properties()

Comparison of Results#

The numerical results obtained by Pilkey is listed in the dictionary below.

[6]:
pilkey = {
    "area": 16.75516,
    "qx": 221.72054,
    "qy": 0.0,
    "cx": 0.0,
    "cy": 13.23297,
    "x_sc": 0.0,
    "y_sc": 17.83662,
    "ixx_g": 3032.21070,
    "iyy_g": 1258.15764,
    "ixy_g": 0.0,
    "ixx_c": 98.18931,
    "iyy_c": 1258.15764,
    "ixy_c": 0.0,
    "zxx": 18.32584,
    "zyy": 89.40279,
    "rx": 2.42079,
    "ry": 8.66549,
    "phi": -90.0,
    "alpha_x": 1.50823,
    "alpha_y": 4.60034,
    "alpha_xy": 0.0,
    "j": 1.38355,
    "gamma": 1046.49221,
}

Most of these results can be directly obtained from sectionproperties, the only properties that require calculation are the shear coefficients, alpha. The shear coefficient can be obtained from the shear area as follows:

\(\alpha = \frac{A}{A_s}\)

where \(A\) is the cross-section area and \(A_s\) is the shear area.

We create a similar dictionary for the sectionproperties results.

[7]:
sectionproperties = {
    "area": sec.get_area(),
    "qx": sec.get_eq(e_ref=mat)[0],
    "qy": sec.get_eq(e_ref=mat)[1],
    "cx": sec.get_c()[0],
    "cy": sec.get_c()[1],
    "x_sc": sec.get_sc()[0],
    "y_sc": sec.get_sc()[1],
    "ixx_g": sec.get_eig(e_ref=mat)[0],
    "iyy_g": sec.get_eig(e_ref=mat)[1],
    "ixy_g": sec.get_eig(e_ref=mat)[2],
    "ixx_c": sec.get_eic(e_ref=mat)[0],
    "iyy_c": sec.get_eic(e_ref=mat)[1],
    "ixy_c": sec.get_eic(e_ref=mat)[2],
    "zxx": min(sec.get_ez(e_ref=mat)[:2]),
    "zyy": min(sec.get_ez(e_ref=mat)[2:]),
    "rx": sec.get_rc()[0],
    "ry": sec.get_rc()[1],
    "phi": sec.get_phi(),
    "alpha_x": sec.get_area() / sec.get_eas(e_ref=mat)[0],
    "alpha_y": sec.get_area() / sec.get_eas(e_ref=mat)[1],
    "alpha_xy": sec.get_area() / sec.section_props.a_sxy,
    "j": sec.get_ej(e_ref=mat),
    "gamma": sec.get_egamma(e_ref=mat),
}

The comparison of results is summarised in the table below. Relative error is reported in all cases, except where a value is zero, in which the absolute error is reported.

[8]:
from rich.console import Console
from rich.table import Table
from rich.text import Text


# setup table
table = Table(title="Comparison of Results")
table.add_column("Property", justify="left", style="cyan", no_wrap=True)
table.add_column(Text("Pilkey", justify="center"), justify="right", style="green")
table.add_column(Text("sectionproperties", style="i"), justify="right", style="green")
table.add_column(Text("Error", justify="center"), justify="right", style="green")

# create a row for each property
for key in pilkey:
    # get results
    p_res = pilkey[key]
    sp_res = sectionproperties[key]

    # calculate relative error
    if p_res != 0:
        rel_error = (sp_res - p_res) / p_res
    else:
        rel_error = sp_res

    # print row
    table.add_row(key, f"{p_res:.4e}", f"{sp_res:.4e}", f"{rel_error:.2e}")

console = Console()
console.print(table)
                  Comparison of Results                   
┏━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Property    Pilkey     sectionproperties    Error   ┃
┡━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ area       1.6755e+01         1.6755e+01  -1.13e-05 │
│ qx         2.2172e+02         2.2171e+02  -3.44e-05 │
│ qy         0.0000e+00         1.0424e-12   1.04e-12 │
│ cx         0.0000e+00         6.2217e-14   6.22e-14 │
│ cy         1.3233e+01         1.3233e+01  -2.30e-05 │
│ x_sc       0.0000e+00         1.8959e-13   1.90e-13 │
│ y_sc       1.7837e+01         1.7836e+01  -2.11e-05 │
│ ixx_g      3.0322e+03         3.0320e+03  -5.71e-05 │
│ iyy_g      1.2582e+03         1.2581e+03  -5.99e-05 │
│ ixy_g      0.0000e+00         1.4695e-11   1.47e-11 │
│ ixx_c      9.8189e+01         9.8185e+01  -4.73e-05 │
│ iyy_c      1.2582e+03         1.2581e+03  -5.99e-05 │
│ ixy_c      0.0000e+00         9.0024e-13   9.00e-13 │
│ zxx        1.8326e+01         1.8320e+01  -3.23e-04 │
│ zyy        8.9403e+01         8.9404e+01   1.39e-05 │
│ rx         2.4208e+00         2.4208e+00  -1.64e-05 │
│ ry         8.6655e+00         8.6653e+00  -2.40e-05 │
│ phi       -9.0000e+01        -9.0000e+01  -3.16e-16 │
│ alpha_x    1.5082e+00         1.5082e+00   1.92e-06 │
│ alpha_y    4.6003e+00         4.6003e+00  -1.25e-05 │
│ alpha_xy   0.0000e+00         9.4762e-21   9.48e-21 │
│ j          1.3836e+00         1.3836e+00   5.87e-05 │
│ gamma      1.0465e+03         1.0465e+03  -3.18e-05 │
└──────────┴─────────────┴───────────────────┴───────────┘

All results are within acceptable limits. Out of the warping properties, the torsion constant had the largest relative error, however this value is relatively small and acceptable given the differences in element type and mesh.

[9]:
err = (sectionproperties["j"] - pilkey["j"]) / pilkey["j"]
print(f"Torsion Constant Relative Error: {err:.6f}")
Torsion Constant Relative Error: 0.000059