Architecture overview¶
This document describes the internal architecture of GenSec at a level of detail that allows a civil engineer to understand what each software component does and how data flows through the program.
No prior software engineering knowledge is required beyond a basic familiarity with the concept of functions and objects (containers that group data and operations together).
What GenSec does¶
GenSec receives a description of a cross-section (geometry, materials, reinforcement) and a set of load demands, and answers two questions:
What is the section’s resistance domain? The set of all \((N, M_x, M_y)\) combinations the section can carry at the ultimate limit state.
Are the demands inside that domain? For each demand point, GenSec computes one or more utilization ratios \(\eta\) that measure how close the demand is to the boundary.
Everything else — moment-curvature diagrams, stress maps, CSV/JSON exports, plots — is built on top of these two capabilities.
Package structure¶
GenSec is organized into four subpackages (folders), each with a well-defined responsibility:
graph TB
subgraph materials["materials/"]
base["base.py<br/>Abstract Material"]
concrete["concrete.py<br/>Parabola-rectangle"]
steel["steel.py<br/>Elastic-plastic"]
tabulated["tabulated.py<br/>Arbitrary σ-ε"]
ec2props["ec2_properties.py<br/>EC2 Table 3.1"]
ec2bridge["ec2_bridge.py<br/>EC2 → GenSec factory"]
end
subgraph geometry["geometry/"]
fiber["fiber.py<br/>RebarLayer"]
primitives["primitives.py<br/>Shape factories"]
geom["geometry.py<br/>GenericSection + mesher"]
section["section.py<br/>RectSection factory"]
end
subgraph solver["solver/"]
integrator["integrator.py<br/>FiberSolver"]
capacity["capacity.py<br/>NMDiagram"]
check["check.py<br/>VerificationEngine"]
end
subgraph output["output/"]
plots["plots.py<br/>matplotlib"]
export["export.py<br/>CSV / JSON"]
report["report.py<br/>Terminal"]
end
io_yaml["io_yaml.py<br/>YAML loader"]
cli["cli.py<br/>CLI: run / plot"]
cli --> io_yaml
io_yaml --> materials
io_yaml --> geometry
cli --> solver
cli --> output
solver --> materials
solver --> geometry
Think of these subpackages as departments in a design office:
materials/ — the material laboratory: knows how to convert a strain value into a stress value for any material (concrete, steel, CFRP, etc.).
geometry/ — the drafter’s desk: defines section shapes, places rebars, and discretizes the section into fibers.
solver/ — the calculation team: takes a section and its materials, computes internal forces, builds resistance domains, verifies demands.
output/ — the reporting team: produces plots, data files, and terminal summaries.
io_yaml.py — the secretary: reads the YAML input file and hands the right objects to each department.
cli.py — the project manager: coordinates the entire workflow.
Main workflow¶
When you run gensec run input.yaml, the following happens:
flowchart TD
A["Read YAML file<br/>(io_yaml.py)"] --> B["Build Material objects<br/>(concrete, steel, ...)"]
B --> C["Build Section object<br/>(meshed polygon + rebars)"]
C --> D["Create FiberSolver<br/>(strain → forces integrator)"]
D --> E["Generate N-M diagrams<br/>(NMDiagram)"]
D --> F["Generate 3D surface<br/>(NMDiagram.generate_biaxial)"]
D --> G["Generate M-χ curves<br/>(NMDiagram.generate_moment_curvature)"]
E --> H["Build VerificationEngine"]
F --> H
H --> I["Check demands<br/>(η_3D, η_2D)"]
H --> J["Check combinations<br/>(η_path, staged)"]
H --> K["Check envelopes<br/>(max η among members)"]
I --> L["Export JSON + CSV"]
J --> L
K --> L
L --> M["Generate plots<br/>(plots.py)"]
M --> N["Done: all outputs<br/>in output directory"]
style A fill:#e1f5fe
style N fill:#c8e6c9
Class hierarchy¶
The key classes and their relationships:
classDiagram
class Material {
<<abstract>>
+stress(eps) float
+stress_array(eps) ndarray
+tangent(eps) float
+tangent_array(eps) ndarray
+eps_min float
+eps_max float
}
class Concrete {
+fck: float
+fcd: float
+eps_c2: float
+eps_cu2: float
+n_parabola: float
+fct: float
+Ec: float
+tension_enabled: bool
}
class Steel {
+fyk: float
+fyd: float
+Es: float
+eps_yd: float
}
class TabulatedMaterial {
+strains: ndarray
+stresses: ndarray
}
Material <|-- Concrete
Material <|-- Steel
Material <|-- TabulatedMaterial
class RebarLayer {
+x: float
+y: float
+As: float
+material: Material
+embedded: bool
}
class GenericSection {
+polygon: Polygon
+bulk_material: Material
+rebars: list~RebarLayer~
+x_fibers: ndarray
+y_fibers: ndarray
+A_fibers: ndarray
+n_grid_x: int or None
+n_grid_y: int or None
+dx: float
+dy: float
}
GenericSection o-- Material : bulk
GenericSection o-- RebarLayer : rebars
note for GenericSection "RectSection() is a factory function\nthat returns a GenericSection\nwith a rectangular polygon."
class FiberSolver {
+integrate(eps0, chi_x, chi_y)
+integrate_batch(eps0[], chi_x[], chi_y[])
+integrate_with_tangent(eps0, chi_x, chi_y)
+solve_equilibrium(N, Mx, My)
+get_fiber_results(...)
}
class NMDiagram {
+generate()
+generate_biaxial()
+generate_mx_my()
+generate_moment_curvature()
}
class VerificationEngine {
+check_demand()
+check_combination()
+check_envelope()
}
class DomainChecker {
+eta_3D()
+eta_path()
+is_inside()
}
class MxMyContour {
+eta_2D()
+eta_path_2D()
}
FiberSolver --> GenericSection : reads
NMDiagram --> FiberSolver : uses
VerificationEngine --> DomainChecker : 3D checks
VerificationEngine --> MxMyContour : 2D checks
VerificationEngine --> NMDiagram : generates contours
Units and sign conventions¶
All internal computations use basic SI units:
Quantity |
Unit |
Sign convention |
|---|---|---|
Length |
mm |
|
Force |
N |
Positive = tension |
Moment |
N·mm |
Right-hand rule around each axis |
Stress |
MPa |
Positive = tension |
Strain |
— |
Positive = tension, negative = compression |
Curvature |
1/mm |
YAML input uses engineering units (kN, kN·m) for convenience. The parser converts automatically. All output files provide values in both unit systems.
The coordinate origin is at the bottom-left corner of the section’s bounding box:
\(x\) axis: horizontal (width direction).
\(y\) axis: vertical (height direction, upward).
Reference point for moments: the geometric centroid of the gross section (computed from the polygon, not from the bounding box).