RasmahRasmah

Structural Sections: An I-Beam Column Base

println("Julia ", VERSION)
Julia 1.12.6

Overview

Structural steelwork is drawn from standard sections — I-beams, channels, and angles that come from the mill in published shapes with published section properties. A steel detailer never draws a beam from scratch; they call up a UB 203×133×25 from the section table and read off its area (32.3 cm²) and mass per metre (25.4 kg/m).

Rasmah’s standard sections are the same idea as parametric functions: give the four cross-section dimensions and a length, and the shape — plus its exact area, volume, and mass — follows. This tutorial draws three steel parts the way a workshop would see them:

  1. an I-beam column — i_beam, checked against the section table;
  2. an angle cleat — angle_bar, the classic gusset/connection piece;
  3. a column base assembly — a base plate with anchor-bolt holes and stiffening gussets, sized from the plate and welded to the column.

Every volume is verified against the handbook formula — cross-section area times length — and finished with a mass in kilograms.

Units and density

Lengths are interpreted as millimetres, volumes as mm³, and mass uses steel at 7.85 g/cm³:

using Rasmah

ρ_steel = 7.85e-3   # g/mm³  (7.85 g/cm³ = 7850 kg/m³)
0.00785

Mass per metre then follows the rule steel detailers use: a section of area A (mm²) weighs A \times 7.85\times10^{-3} kg per metre.

Part 1 — an I-beam column

An I-beam (universal beam) is a web of thickness tw joining two flanges of width b and thickness tf, over an overall depth h:

# 1. UB 203×133×25: h = 203.2, b = 133.4, tf = 7.8, tw = 5.8 (mm)
h, b, tf, tw = 203.2, 133.4, 7.8, 5.8
column = i_beam(h, b, tf, tw, 600.0)     # 600 mm long
FeatureUnion{FeatureUnion{Box{Float64, Float64, Float64}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}(FeatureUnion{Box{Float64, Float64, Float64}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}(Box{Float64, Float64, Float64}(5.8, 203.2, 600.0), Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}(Box{Float64, Float64, Float64}(133.4, 7.8, 600.0), 0, 97.69999999999999, 0)), Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}(Box{Float64, Float64, Float64}(133.4, 7.8, 600.0), 0, -97.69999999999999, 0))
vtk(surface_mesh(column; n = 48))

The section table

The beam is three boxes — the web and the two flanges — so its volume is exact from the box volumes, and the cross-section area is the section-table formula A = 2\,b\,t_f + (h - 2 t_f)\,t_w (the web height excludes the flanges):

V_web    = ustrip(volume(box(tw, h - 2 * tf, 600.0)))   # web, clear of the flanges
V_flange = ustrip(volume(box(b, tf, 600.0)))             # one flange

V = V_web + 2 * V_flange
A = 2 * b * tf + (h - 2 * tf) * tw

println("volume          = ", round(V; digits = 0), " mm³")
println("cross-section A = ", round(A; digits = 0), " mm²")
println("mass per metre  = ", round(A * ρ_steel; digits = 1), " kg/m")
println("column mass     = ", round(V * ρ_steel / 1000; digits = 2), " kg")
volume          = 1.901472e6 mm³
cross-section A = 3169.0 mm²
mass per metre  = 24.9 kg/m
column mass     = 14.93 kg

The numbers land next to the published section: a UB 203×133×25 carries A = 32.3 cm² and 25.4 kg/m. A meshed measurement of the same solid agrees to a fraction of a percent — the thin web is the only part the mesh struggles to resolve:

sdf = column
lo, hi = bounds(sdf)
vm = ustrip(volume(marching_tetrahedra(sdf, lo, hi, 96)))

println("meshed volume  = ", round(vm; digits = 0), " mm³  (formula ", round(V; digits = 0), ")")
meshed volume  = 1.895702e6 mm³  (formula 1.901472e6)

Part 2 — an angle cleat

The second workhorse of steel detailing is the equal-leg angle. Its area formula counts the two legs minus the corner counted twice — (a + b - t)t:

# 2. L90×90×8 cleat, 120 mm long
cleat = angle_bar(90.0, 90.0, 8.0, 120.0)

A_cleat = (90.0 + 90.0 - 8.0) * 8.0
println("cleat area       = ", round(A_cleat; digits = 0), " mm²  (L90×90×8 table: 13.9 cm²)")
println("mass per metre   = ", round(A_cleat * ρ_steel; digits = 1), " kg/m")
println("cleat mass       = ", round(A_cleat * 120.0 * ρ_steel / 1000; digits = 2), " kg")
cleat area       = 1376.0 mm²  (L90×90×8 table: 13.9 cm²)
mass per metre   = 10.8 kg/m
cleat mass       = 1.3 kg
vtk(surface_mesh(cleat; n = 48))

Part 3 — a column base assembly

The column sits on a 300×300×20 base plate, anchored by four M24 bolts (Ø26 holes at a 220 mm square pitch), and stiffened by two triangular gussets welded to the web and the plate. Each feature is drawn separately and united — exactly how the fabrication drawing enumerates the plates.

3.1 The base plate

# 3. base plate with four Ø26 anchor-bolt holes at (±110, ±110)
h1 = translate(cylinder(13.0, 22.0),  110.0, 0.0,  110.0)
h2 = translate(cylinder(13.0, 22.0), -110.0, 0.0,  110.0)
h3 = translate(cylinder(13.0, 22.0),  110.0, 0.0, -110.0)
h4 = translate(cylinder(13.0, 22.0), -110.0, 0.0, -110.0)

plate = translate(
    difference(box(300.0, 20.0, 300.0), union(union(h1, h2), union(h3, h4))),
    0.0, -111.6, 0.0,    # plate top face at y = −101.6, the column's bottom face
)
Translate{Difference{Box{Float64, Float64, Float64}, FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}}, Float64, Float64, Float64}(Difference{Box{Float64, Float64, Float64}, FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}}(Box{Float64, Float64, Float64}(300.0, 20.0, 300.0), FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}(FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}(Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), 110.0, 0.0, 110.0), Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), -110.0, 0.0, 110.0)), FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}(Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), 110.0, 0.0, -110.0), Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), -110.0, 0.0, -110.0)))), 0.0, -111.6, 0.0)
vtk(surface_mesh(plate; n = 48))

The edge distance of the holes is 150 - 110 = 40 mm — at least 1.5d, as the connection design rules require.

3.2 The gusset plates

A gusset is a right triangle, 240 mm along the plate and 80 mm up the web. It is sketched as a triangle polygon in the (z, y) plane, extruded 6 mm, then rotated into the column’s web plane and offset so the pair of gussets sits flush against the two faces of the 5.8 mm web:

# 4. one triangular gusset, then a copy on each web face
tri = polygon_2d([[-120.0, -101.6], [120.0, -101.6], [0.0, -21.6]])
gusset0 = rotate(extrude(tri, 6.0), rotation_y(-π / 2))   # (z, y) plane, 6 thick
g_right = translate(gusset0,  3.0 + tw / 2, 0.0, 0.0)
g_left  = translate(gusset0, -(3.0 + tw / 2), 0.0, 0.0)

V_gusset = 0.5 * 240.0 * 80.0 * 6.0
println("gusset volume = ", ustrip(volume(gusset0)), " mm³  (½·240·80·6 = ", V_gusset, ")")
gusset volume = 57600.0 mm³  (½·240·80·6 = 57600.0)

3.3 The assembly

# 5. weld it all together: column + gussets + base plate
assembly = union(union(column, g_right), union(g_left, plate))
FeatureUnion{FeatureUnion{FeatureUnion{FeatureUnion{Box{Float64, Float64, Float64}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}, Translate{Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}, Float64, Float64, Float64}}, FeatureUnion{Translate{Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}, Float64, Float64, Float64}, Translate{Difference{Box{Float64, Float64, Float64}, FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}}, Float64, Float64, Float64}}}(FeatureUnion{FeatureUnion{FeatureUnion{Box{Float64, Float64, Float64}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}, Translate{Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}, Float64, Float64, Float64}}(FeatureUnion{FeatureUnion{Box{Float64, Float64, Float64}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}(FeatureUnion{Box{Float64, Float64, Float64}, Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}}(Box{Float64, Float64, Float64}(5.8, 203.2, 600.0), Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}(Box{Float64, Float64, Float64}(133.4, 7.8, 600.0), 0, 97.69999999999999, 0)), Translate{Box{Float64, Float64, Float64}, Int64, Float64, Int64}(Box{Float64, Float64, Float64}(133.4, 7.8, 600.0), 0, -97.69999999999999, 0)), Translate{Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}, Float64, Float64, Float64}(Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}(Extrude{Polygon2D{Float64}, Float64}(Polygon2D{Float64}([[-120.0, -101.6], [120.0, -101.6], [0.0, -21.6]]), 6.0), [6.123233995736766e-17 0.0 -1.0; 0.0 1.0 0.0; 1.0 0.0 6.123233995736766e-17]), 5.9, 0.0, 0.0)), FeatureUnion{Translate{Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}, Float64, Float64, Float64}, Translate{Difference{Box{Float64, Float64, Float64}, FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}}, Float64, Float64, Float64}}(Translate{Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}, Float64, Float64, Float64}(Rotate{Extrude{Polygon2D{Float64}, Float64}, Matrix{Float64}}(Extrude{Polygon2D{Float64}, Float64}(Polygon2D{Float64}([[-120.0, -101.6], [120.0, -101.6], [0.0, -21.6]]), 6.0), [6.123233995736766e-17 0.0 -1.0; 0.0 1.0 0.0; 1.0 0.0 6.123233995736766e-17]), -5.9, 0.0, 0.0), Translate{Difference{Box{Float64, Float64, Float64}, FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}}, Float64, Float64, Float64}(Difference{Box{Float64, Float64, Float64}, FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}}(Box{Float64, Float64, Float64}(300.0, 20.0, 300.0), FeatureUnion{FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}, FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}}(FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}(Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), 110.0, 0.0, 110.0), Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), -110.0, 0.0, 110.0)), FeatureUnion{Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}, Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}}(Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), 110.0, 0.0, -110.0), Translate{Cylinder{Float64, Float64}, Float64, Float64, Float64}(Cylinder{Float64, Float64}(13.0, 22.0), -110.0, 0.0, -110.0)))), 0.0, -111.6, 0.0)))
vtk(surface_mesh(assembly; n = 48))

3.4 Mass from the drawing

The fabricated mass is the exact sum of the parts (the weld metal adds a few percent, which a fabrication shop adds on its own):

V_plate  = 300.0 * 300.0 * 20.0 - 4 * π * 13.0^2 * 20.0
V_total  = V + V_plate + 2 * V_gusset

println("column    = ", round(V / 1000; digits = 0), " cm³")
println("plate     = ", round(V_plate / 1000; digits = 0), " cm³")
println("gussets   = ", round(2 * V_gusset / 1000; digits = 0), " cm³")
println("total     = ", round(V_total / 1000; digits = 0), " cm³")
println("mass      = ", round(V_total * ρ_steel / 1000; digits = 1), " kg")
column    = 1901.0 cm³
plate     = 1758.0 cm³
gussets   = 115.0 cm³
total     = 3774.0 cm³
mass      = 29.6 kg

The meshed volume of the whole assembly brackets the formula — slightly light because the 5.8 mm web is barely resolved by the grid, and converging as the grid refines:

sdf = assembly
lo, hi = bounds(sdf)

for n in (96, 160, 256)
    vmesh = ustrip(volume(marching_tetrahedra(sdf, lo, hi, n)))
    println("meshed (n = ", n, ") = ", round(vmesh / 1000; digits = 0), " cm³")
end
println("formula            = ", round(V_total / 1000; digits = 0), " cm³")
meshed (n = 96) = 3743.0 cm³
meshed (n = 160) = 3750.0 cm³
meshed (n = 256) = 3753.0 cm³
formula            = 3774.0 cm³

What the section approach buys you

Every dimension above is a parameter: change h, b, tf, tw to the next size up in the section table and the column, the gussets, and the mass all follow — which is exactly how a steel detailer re-sizes a frame. Because the sections are ordinary features, the same column can go straight into a structural simulation or a fixture for one.

Next steps