| name | apesteel |
| description | apeSteel is a composition-based, strictly-typed Python library implementing AISC 360-22, AISC 341-22 (seismic), and AISC 358-22 member-level steel design (plus EN 10365 IPE/HE shapes). Use this skill whenever the user is writing, reading, debugging, or reviewing code that imports apeSteel: building Element composites (section + SteelMaterial + Bracing + construction), running AISC checks via the Element facade (classify_flexural, classify_axial_compression, classify_seismic, flexural_strength_F2/F3/F4/F5, shear_strength_G2, compression_strength, combined_strength_H1, torsion §H3), the BeamCheck facade (run_full_beam_check), pulling shapes from AISCv16Catalog / EuropeanIPECatalog, working with frozen Report dataclasses and AISCClauseReference citations, the canonical N-mm-tonne-s baseUnits convention, the geometry classes (DoublySymmetricISection, SinglySymmetricISection, ChannelSection, RectangularHSS, RoundHSS, TeeSection, DoubleAngleSection, SingleAngleSection, RectangularBar, RoundBar), and BeamColumnConnection seismic checks (panel-zone shear, column-flange tension, doubler/continuity plates per AISC 341 §E3.6e and 358). Covers apeSteel's own idioms — composition spine, pure-function vs Element facade, AISC clause-cited Reports. For AISC code theory and worked examples see the aisc-steel-design skill; for FEM modeling that consumes these checks see apegmsh-helper.
|
apeSteel helper
apeSteel is a composition-based, strictly-typed Python library that
implements AISC 360-22, AISC 341-22 (seismic), and AISC 358-22
member-level design. Every calculator returns a frozen, AISC-cited
Report; every Element check is a method on a composite of
(section, material, bracing, construction).
This skill teaches apeSteel's vocabulary and idioms — the composition
spine, the units rule, the report contract, and which method to call for
which AISC chapter. It does not re-explain AISC equations; for code
theory and worked examples reach for the aisc-steel-design skill.
Repo: C:\Users\nmora\Github\apeSteel.
Public surface: everything is re-exported from the top-level apeSteel
package — from apeSteel import … is always correct.
1. Mental model
1.1 The one immutable rule: units
apeSteel runs in a single canonical base:
N-mm-tonne-s (force = N, length = mm, stress = MPa, moment = N·mm,
mass = tonne).
Every dataclass attribute, every function argument, every return value
inside the library is a float already in this base. Multiply on the
way in, divide on the way out — baseUnits (the companion package)
supplies the multipliers.
import baseUnits as u
assert u.BASE == "N-mm-tonne-s"
300 * u.mm
4.0 * u.m
345 * u.MPa
1.2 * u.kN * u.m
There is no compound kN_m symbol in baseUnits — build a moment
unit by multiplying (u.kN * u.m) and divide by the same product to
display.
There is no Quantity wrapper. The float you store is the canonical
value. To display a result, divide by the matching constant:
phi_Mn_kNm = report.phi_strength_LRFD / (u.kN * u.m)
baseUnits lives at https://github.com/nmorabowen/baseUnits and is
pulled by the [dev] extra of apeSteel. If anyone switches the base
system, every apeSteel module asserts on import and fails fast.
1.2 Three primary domain objects + a composite
Every analysis is the same chain:
SectionGeometry → SteelMaterial → Bracing → Element → Report
(or catalog) ← .method() ←
| Primary object | Role | Notable instances |
|---|
SectionGeometry | pure shape (no Fy) | DoublySymmetricISection, SinglySymmetricISection, ChannelSection, RectangularHSS, RoundHSS, TeeSection, DoubleAngleSection, SingleAngleSection, RectangularBar, RoundBar |
SteelMaterial | grade & expected-strength factors | A36, A992, A572_Gr50, S275, S355, S460 (or build your own) |
Bracing | Lb_top, Lb_bot, Cb | per-element |
Element | the composite that owns every check | built via section.element(...) |
Element is @dataclass(frozen=True). Rebind a single field with the
with_* builders — section properties are cached and don't get
recomputed:
e2 = element.with_material(S355)
e3 = element.with_bracing(Bracing(0.001*u.m, 1.5*u.m, lateral_torsional_buckling_modification_factor_Cb=1.2))
1.3 Every calculator returns a Report
Each calculator returns a @dataclass(frozen=True, slots=True) subclass
of Report. Universal attributes:
cited_clauses: tuple[AISCClauseReference, ...] — every spec/section/equation/page touched
governing_limit_state: str — e.g. "yielding", "inelastic_LTB", "web_elastic_buckling"
phi_LRFD, omega_ASD — both factors, always
nominal_strength — in base units
phi_strength_LRFD, omega_strength_ASD — design strengths in base units
Plus the AISC-symbol-suffixed intermediate quantities (limiting_length_plastic_Lp,
web_shear_strength_coefficient_Cv1, …) so every step is introspectable
without rerunning the math. Never print inside the engine;
Report.format() is the only renderer.
1.4 Pure-function form and Element-facade form are both first-class
For every AISC equation, there is a free function (compute_flexural_strength_F2_compact_doubly_symmetric,
classify_flexural_compactness_B4_1b, compute_shear_strength_G2_doubly_symmetric,
compute_combined_strength_H1_1, …). The Element method is a one-line
wrapper over it. They stay in sync; pick whichever fits the workflow:
- Element facade — interactive design, member-level reports.
- Free functions — parametric studies, FEM post-processing, porting
from procedural spreadsheets.
from apeSteel import (
classify_flexural_compactness_B4_1b,
compute_flexural_strength_F2_compact_doubly_symmetric,
)
props = section.compute_section_properties()
flex_class = classify_flexural_compactness_B4_1b(props, A992, construction="welded")
F2 = compute_flexural_strength_F2_compact_doubly_symmetric(
section_properties=props, material=A992,
unbraced_length_Lb=4.0*u.m,
lateral_torsional_buckling_modification_factor_Cb=1.0,
)
1.5 Verbose names + AISC symbol suffix
Public attributes are spelled out, followed by the AISC symbol so the
spec link is unmistakable:
section.flange_width_bf
section.web_thickness_tw
bracing.unbraced_length_top_flange_Lb_top
bracing.lateral_torsional_buckling_modification_factor_Cb
report.limiting_length_plastic_Lp
report.nominal_flexural_strength_Mn
Inside short calculator bodies the bare AISC symbol (Lp, Mp, Fy)
is allowed — that's what the code citation says.
2. Building an Element — the canonical example
import baseUnits as u
from apeSteel import (
A992,
Bracing,
DoublySymmetricISection,
)
section = DoublySymmetricISection(
flange_width_bf = 300 * u.mm,
flange_thickness_tf = 22 * u.mm,
web_clear_height_hw = 350 * u.mm,
web_thickness_tw = 12 * u.mm,
)
material = A992
bracing = Bracing(
unbraced_length_top_flange_Lb_top = 0.001 * u.m,
unbraced_length_bot_flange_Lb_bot = 4.0 * u.m,
lateral_torsional_buckling_modification_factor_Cb = 1.0,
)
element = section.element(material=material, construction="welded", bracing=bracing)
flex_class = element.classify_flexural()
axial = element.classify_axial_compression()
seismic = element.classify_seismic("highly_ductile")
F2_both = element.flexural_strength_F2_both_flanges()
print(F2_both.governing_flange)
print(F2_both.governing_report.phi_strength_LRFD / (u.kN * u.m), "kN·m")
print(F2_both.governing_report.governing_limit_state)
Gotchas
- Never pass
Lb = 0 for a slab-braced flange — use a small positive
value (0.001 * u.m). The F2-5 regime check expects Lb > 0.
construction is "welded" (default) or "rolled". It drives the
plate-element compactness limits in §B4.1b and the residual-stress
factor in §F3.
Element.bracing may be None at construction. Classification methods
(classify_flexural, classify_axial_compression, classify_seismic)
do not require bracing. LTB methods do; calling flexural_strength_F2_*
with bracing=None raises ValueError.
3. The Element check methods (cheatsheet)
The full ISO-mapped surface — Element.<method>() → AISC clause →
free-function equivalent. Doubly-symmetric I is the default; SS I has
F4/F5 (DS-only methods raise NotImplementedError on SS).
| Element method | AISC | Free function |
|---|
classify_flexural() | 360 §B4.1b | classify_flexural_compactness_B4_1b |
classify_axial_compression() | 360 §B4.1a | classify_axial_compression_B4_1a |
classify_seismic(ductility, Ca=0.0) | 341 §D1.1 | classify_seismic_compactness_341_D1 |
flexural_strength_F2_top_flange() / _bot_flange() / _both_flanges() | 360 §F2 (compact DS) | compute_flexural_strength_F2_compact_doubly_symmetric |
flexural_strength_F3_*_flange[s]() | 360 §F3 (non-compact / slender flange) | compute_flexural_strength_F3_noncompact_or_slender_flange |
flexural_strength_F4_*_flange[s]() | 360 §F4 (DS noncompact-web, SS compact/noncompact web) | compute_flexural_strength_F4* |
flexural_strength_F5_*_flange[s]() | 360 §F5 (slender-web plate girder, DS & SS) | compute_flexural_strength_F5_slender_web_plate_girder |
shear_strength_G2(transverse_stiffener_spacing_a=None) | 360 §G2 | compute_shear_strength_G2_doubly_symmetric |
compression_strength(Kx, Lx, Ky, Ly, Kz, Lz) | 360 §E2–§E4 (DS I) | compute_compression_strength_from_K_L |
phi_Pn_vs_length(lengths_L, Kx=1, Ky=1, Kz=1) | E capacity curve | compute_phi_Pn_vs_length |
combined_strength_H1(Pr, Mrx, …) | 360 §H1.1 | compute_combined_strength_H1_1 |
connected_to(column) | builds BeamColumnConnection | — |
run_full_check() | dispatches B4.1b → F2/F3/F4/F5 + G2 | run_full_beam_check |
compute_deflection_* | serviceability | compute_deflection_* |
For non-I shapes, use the free-function facade under
apeSteel.checks — it routes channels, HSS, tees, angles, bars,
unsymmetric shapes to the right §F chapter automatically:
from apeSteel import (
compute_flexural_strength_channel,
compute_flexural_strength_rectangular_hss,
compute_flexural_strength_round_hss,
compute_flexural_strength_tee_or_double_angle,
compute_flexural_strength_single_angle,
compute_flexural_strength_bar,
compute_flexural_strength_unsymmetric_F12,
)
4. The BeamCheck facade
run_full_beam_check(element) (also Element.run_full_check()) is the
"one call, full member" entry point. It:
- Classifies via B4.1b.
- Routes to F2 / F3 / F4 / F5 based on web + flange compactness and
section symmetry.
- Runs both flanges through the routed engine and picks the
governing one (lowest
phi*Mn).
- Runs G2 web shear (DS only at present).
- Returns a
BeamCheckReport aggregating everything.
It does not run seismic compactness or axial / combined — those need
extra inputs the caller must supply explicitly.
from apeSteel import run_full_beam_check
report = run_full_beam_check(element)
report.routed_flexure_chapter
report.governing_flexural_flange
report.governing_flexural_phi_Mn / (u.kN * u.m)
report.shear.phi_strength_LRFD / u.kN
5. Catalogs — when not to build plate-by-plate
Two catalogs ship in apeSteel.sections:
from apeSteel import AISCv16Catalog, EuropeanIPECatalog
aisc = AISCv16Catalog()
ipe = EuropeanIPECatalog()
Three adapters per catalog:
get_row("W24X94") — the raw CatalogRowAISCv16 (or …EuropeanIPE)
for any of the 2 299 shapes (W/M/S/HP/C/MC/L/WT/MT/ST/HSS/Pipe).
get_section_properties("W24X94") — universal SectionProperties for
downstream calculators. Today only W/M/S/HP adapt cleanly;
channels/angles/HSS raise SectionTypeNotAdaptableError.
get_doubly_symmetric_i_geometry("W24X94") — a plate-built
DoublySymmetricISection reconstructed from (bf, tf, h, tw). Useful
for verification (compare plate-built Ag, Ix, J, Cw against the
AISC published values) or anywhere a calculator needs raw plate
dimensions.
Fuzzy matching. Typos trigger a RapidFuzz fallback at similarity ≥
80/100. The match is returned with a logging warning; below 80 a
SectionNotFoundError is raised with the best candidate attached for
debugging.
props = aisc.get_section_properties("W24X94")
element = section_from_plates.element(material=A992, construction="rolled", bracing=bracing)
6. Singly-symmetric I-sections — the sharp edges
SinglySymmetricISection is a full citizen of the ISection union, but
F2 / F3 / G2 assume doubly-symmetric geometry and will raise
NotImplementedError on it. The canonical SS flexure interface is F4
(compact / non-compact web) or F5 (slender web) — AISC §F5 covers
DS and SS. Use flexural_strength_F4_* / flexural_strength_F5_*.
Section properties for SS sections depend on which flange is in
compression. The Element exposes both views:
props_top_comp = element.section_properties_for("top")
props_bot_comp = element.section_properties_for("bot")
element.section_properties is the cached "top"-compression case.
7. Combined forces & torsion — Chapter H
Full Chapter H is shipped (§H1.1, §H1.2 with Cb amplification and
Pey, §H1.3 single-axis, §H2 unsymmetric, §H3.1/3.2 HSS torsion, §H3.3
non-HSS torsion limit).
from apeSteel import (
compute_combined_strength_H1_1,
compute_combined_strength_H1_2,
compute_combined_strength_H1_3,
compute_combined_strength_H2,
compute_combined_strength_H3_2,
compute_Cb_amplification_factor_H1_2,
compute_Pey_H1_2,
compute_torsional_strength_rect_HSS_H3_1,
compute_torsional_strength_round_HSS_H3_1,
compute_nonHSS_torsion_limit_H3_3,
ensure_h1_3_applicable,
)
Pr/Mrx/Mry passed to §H1 are the required second-order strengths —
already amplified by the Direct Analysis Method. No Appendix-8 B1/B2
machinery happens inside apeSteel (see design note 09).
8. Seismic and joint checks
BeamColumnConnection aggregates a (beam, column) pair of Elements and
exposes joint-level seismic checks. Build it via either:
joint = beam_element.connected_to(column_element)
from apeSteel import BeamColumnConnection
joint = BeamColumnConnection(beam=beam_element, column=column_element)
Free-function checks (use directly or via the joint):
| Function | AISC clause |
|---|
check_column_flange_tension_341(...) | 341 §E3.6e — column-flange tension at moment connection |
check_panel_zone_shear_341(...) | 360 §J10.6 (capacity-amplified by Ry,col) |
recommend_doubler_plate_thickness_341(...) | shop-practical doubler sizing |
check_continuity_plates_required_358(...) | 358 stiffener need + minimum dims |
Out of scope for now (the architecture accommodates them — they're
roadmap items): D2-2 tension rupture / §J4 block shear, composite
members (I), connections (J/K), AISC 358 prequalified moment connections
beyond the continuity-plate slice, system-level 341 checks.
9. Serviceability
Closed-form elastic deflection helpers (no LTB / second-order — pure
elastic beam theory):
from apeSteel import (
compute_deflection_simply_supported_udl,
compute_deflection_simply_supported_point_load_midspan,
compute_deflection_simply_supported_point_load_arbitrary,
compute_deflection_cantilever_udl_and_tip_load,
recommend_camber_from_dead_load_deflection,
)
Each returns a typed report carrying the deflection limit denominator
defaults (DEFAULT_LIVE_LOAD_DEFLECTION_LIMIT_DENOMINATOR,
DEFAULT_TOTAL_LOAD_DEFLECTION_LIMIT_DENOMINATOR — i.e. L/360, L/240).
10. Materials
Pre-built grades carrying AISC 341 Table A3.1 Ry / Rt:
| Name | Spec | Notes |
|---|
A36 | ASTM A36 | structural carbon |
A992 | ASTM A992 | wide-flange default for new US construction |
A572_Gr50 | ASTM A572 Gr 50 | plates, channels |
S275, S355, S460 | EN 10025 | European grades for IPE/HE pairings |
SteelMaterial is a frozen dataclass — build your own:
from apeSteel import SteelMaterial
custom = SteelMaterial(
name="Custom",
yield_stress_Fy = 420 * u.MPa,
tensile_stress_Fu = 540 * u.MPa,
elastic_modulus_E = 200e3 * u.MPa,
shear_modulus_G = 77.2e3 * u.MPa,
density_rho = 7.85e-9,
expected_yield_ratio_Ry = 1.1,
expected_tensile_ratio_Rt = 1.1,
)
11. Architecture (one-screen summary)
apeSteel.checks ← BeamCheck facade (orchestrates only, no math)
▲
apeSteel.flexure, .shear, .compression, .combined, .seismic, .serviceability, .classification
▲ (each returns a typed Report)
apeSteel.sections (.geometry → .properties → .catalog)
▲
apeSteel.core (.units → .materials → .result_types)
Each layer depends only on the layers strictly below; tests enforce this
by inspecting imports. Polymorphism happens at the SectionProperties
boundary — downstream calculators talk only to that frozen dataclass,
never to a concrete shape class. No SteelSection ABC.
12. Citation discipline
Every Report carries cited_clauses: tuple[AISCClauseReference, ...].
An AISCClauseReference is (specification, section, equation, page)
and pretty-prints itself:
report.cited_clauses[0].short()
If you add new calculators, every equation you touch must be cited.
Tests enforce this.
13. Project conventions in one paragraph
Python ≥ 3.11. pyright runs in strict mode with zero errors; the
package is Any-free and uses no implicit Optional. Every result type
is @dataclass(frozen=True, slots=True). ruff + ruff format gate CI,
plus pytest with coverage ≥ 90 %. The 1 300+ regression tests
(-m regression) are snapshots of apeSteel's own output and catch
drift, not formula errors; numerical correctness of Chapter F is
anchored independently by
tests/golden/test_chapterF_independent.py, which re-derives every Mn
from AISC 360-22 with no apeSteel.flexure import.
When adding code: keep the layering, keep the citations, write the
free function first and the Element wrapper second, keep names verbose
with the AISC symbol suffix, and never print inside the engine.
14. Cross-references
- AISC code theory, worked examples, derivations —
aisc-steel-design
skill. This skill assumes the spec is already understood; if the user
asks "why §F4-2 has that form" or "what's the basis for c in §F2-8b",
reach for aisc-steel-design.
- FEM modelling that feeds demands into apeSteel checks —
apegmsh-helper. Typical handoff: apeGmsh produces nodal/element
results, the user pulls (Pr, Mrx, Mry, V) and passes them into
Element.combined_strength_H1(...).
- OpenSees post-processing —
mpco-recorder / stko-to-python for
pulling MPCO data, then back to this skill for the code check.
15. Worktree gotcha
When testing from a .claude/worktrees/<branch>/ tree on this machine,
set PYTHONPATH=<worktree>\src before running tests, otherwise
import apeSteel resolves to the install on main and silently runs
the wrong code. (Recorded in user memory as worktree-pythonpath-gating.)
16. Web-tapered members — partial (DG 25, axial only so far)
The prismatic core (Element, every SectionGeometry) is constant
(bf, tf, h, tw) and does not handle tapers — the Chapter E orchestrator
still raises NotImplementedError on a variable-section route. Web-tapered
doubly-symmetric I-members are instead handled by a separate
subpackage, apeSteel.tapered, built on the DG 25 γ (elastic-
buckling-ratio) stress-format method.
Not re-exported at top level. Import from the subpackage:
from apeSteel.tapered import TaperedMember, TaperedISection, ….
Status (design note 11 / _DG25_citation_reference.md): T-0 geometry
- demand and T-1 axial (§5.3) are complete; flexure (T-2) and
shear + combined (T-3) are NOT yet built. So: web-tapered DS I-member
compression is supported today; tapered flexure/shear/combined are
still roadmap — tell users exactly that, don't claim tapers are wholly
unsupported (that was true in v1, not now).
Shipped apeSteel.tapered surface (all in its __all__):
TaperedISection / StationSection — variable-section geometry, sampled
station-by-station; each station is polymorphic at the SectionProperties
boundary so the prismatic classify_flexural_compactness_B4_1b is reused
per station unchanged.
BucklingProvider / ClosedFormProvider — the member-buckling provider
supplying γ (DG 25 App. A.1 closed-form estimator, no solver). The seam
is designed to also ingest an eigenvalue λ from apeGmsh/OpenSees.
compute_compression_tapered — the stress-format §E7 engine; wraps the
prismatic curves in stress (first-yield) format fed by γ.
TaperedMember — the member-level facade (the taper analogue of
Element; strength depends on a member eigenvalue, not just local
geometry, so it is a member-level object, not a SectionGeometry).
TaperedCompressionReport / CompressionStationResult — the frozen,
AISC/DG-25-cited Reports; the AISCClauseReference contract carries
over verbatim with DG 25 clause refs added to the citation source.
The design method (for explaining it): AISC 360 has no tapered
chapter; the reference is AISC Design Guide 25, Frame Design Using
Web-Tapered Members. For the full γ-based methodology — equation
numbers, pages, the stress-format mapping onto §E7/§F4/F5 — read the
aisc-steel-design skill's references/dg25_tapered_members.md. One-
paragraph version: reuse the prismatic strength curves in stress (first-
yield) format, fed by a member buckling ratio
γ = (elastic buckling load)/(required strength) from an eigenvalue
analysis; Cb scales Fe.LTB (not Mn); classify B4 and check the
unity ratio station-by-station along the length.
Still to come (T-2 / T-3), following the same seams: tapered flexure
reuses the §F4/F5 engines in stress format fed by γ (Cb scales Fe.LTB,
not Mn); shear (§G2) and combined (§H1) close out the member check.
They land as more stress-format wrappers + TaperedMember methods, with
the frozen-Report + DG 25 AISCClauseReference contract unchanged.
Until then, calling for tapered flexure/shear/combined has no engine —
route the user to prismatic per-station estimates or say it's pending.