gensec.solver¶
Fiber integration, equilibrium solving, interaction diagram generation, and demand verification.
This is the computational core of GenSec. The solver chain is:
FiberSolver— evaluates the direct problem \((\varepsilon_0, \chi_x, \chi_y) \to (N, M_x, M_y)\) and solves the inverse problem via Newton–Raphson.NMDiagram— generates the \(N\text{-}M\) diagram (uniaxial) or \(N\text{-}M_x\text{-}M_y\) surface (biaxial) by scanning ultimate strain configurations.DemandChecker— verifies load demands against the resistance domain via convex-hull ray-casting.
Fiber integrator and equilibrium solver¶
Fiber integrator and equilibrium solver — biaxial bending.
Supports both single-material and multi-material sections. When
the section has mat_indices (from GenericSection with
bulk_materials zones), the integrator groups fibers by material
to call each constitutive law on its own subset. For single-material
sections, the fast vectorized path is used unchanged.
The strain plane has 3 parameters \((\varepsilon_0, \chi_x, \chi_y)\) and the internal forces are \((N, M_x, M_y)\).
Uniaxial bending is the special case \(\chi_y = 0\).
Performance features¶
Batch integration (
integrate_batch()): evaluate many strain configurations in a single vectorized call, eliminating Python-loop overhead in capacity-surface generators.Analytical tangent stiffness (
integrate_with_tangent()): compute internal forces and the 3×3 tangent matrix in one pass, halving the cost of each Newton-Raphson iteration compared to a finite-difference Jacobian.
- class FiberSolver(
- section,
- x_ref=None,
- y_ref=None,
Bases:
objectMaterial-agnostic fiber solver for biaxial bending + axial force.
Strain plane:
\[\varepsilon(x, y) = \varepsilon_0 + \chi_x \, (y - y_{\text{ref}}) + \chi_y \, (x - x_{\text{ref}})\]Internal forces:
\[\begin{split}N &= \sum_i \sigma_i \, A_i \\ M_x &= \sum_i \sigma_i \, A_i \, (y_i - y_{\text{ref}}) \\ M_y &= \sum_i \sigma_i \, A_i \, (x_i - x_{\text{ref}})\end{split}\]Convention:
\(M_x > 0\) compresses the bottom edge (\(y = 0\)), tensions the top edge. Right-hand rule: thumb along +x, fingers curl from +y toward observer.
\(M_y > 0\) compresses the right edge (\(x = x_{\max}\)), tensions the left edge. Right-hand rule: thumb along +y, fingers curl from +x toward observer.
- Parameters:
section (GenericSection or RectSection) – Any section exposing
x_fibers,y_fibers,A_fibers,x_rebars,y_rebars,A_rebars,embedded_rebars,bulk_material,rebars,x_centroid,y_centroid.x_ref (float or None, optional) – Reference x-coordinate [mm]. Default: x-centroid.
y_ref (float or None, optional) – Reference y-coordinate [mm]. Default: y-centroid.
- get_fiber_results(
- eps0,
- chi_x,
- chi_y=0.0,
Full strain/stress state at every fiber.
For multi-material sections, each bulk fiber is evaluated with its own constitutive law.
- Parameters:
eps0 (float)
chi_x (float)
chi_y (float, optional)
- Returns:
'bulk': sub-dict withx,y,eps,sigma,dA.'rebars': sub-dict withx,y,eps,sigma(gross),sigma_net(net after bulk subtraction),A,embedded.- Return type:
dict
- integrate(
- eps0,
- chi_x,
- chi_y=0.0,
Direct problem: strain parameters to internal forces.
For embedded rebars, the bulk material contribution at the rebar location is subtracted to avoid double-counting:
\[F_i = \bigl[\sigma_{\text{rebar},i}(\varepsilon_i) - \sigma_{\text{bulk}}(\varepsilon_i)\bigr] \, A_{s,i}\]For multi-material sections, each fiber group is evaluated with its own constitutive law.
- Parameters:
eps0 (float)
chi_x (float) – Curvature about x-axis [1/mm].
chi_y (float, optional) – Curvature about y-axis [1/mm]. Default 0.
- Returns:
N (float) – Axial force [N]. Positive = tension.
Mx (float) – Bending moment about x-axis [N*mm].
My (float) – Bending moment about y-axis [N*mm].
- integrate_batch(
- eps0,
- chi_x,
- chi_y,
Evaluate internal forces for many strain configurations at once.
All inputs are 1-D arrays of the same length n. The computation is fully vectorized: strain fields are built as 2-D arrays of shape
(n, n_fibers)and passed through the constitutive laws in one call, eliminating Python-loop overhead.- Parameters:
eps0 (numpy.ndarray) – Shape
(n,). Strains at the reference point.chi_x (numpy.ndarray) – Shape
(n,). Curvatures about the x-axis [1/mm].chi_y (numpy.ndarray) – Shape
(n,). Curvatures about the y-axis [1/mm].
- Returns:
N (numpy.ndarray) – Shape
(n,). Axial forces [N].Mx (numpy.ndarray) – Shape
(n,). Bending moments about x [N·mm].My (numpy.ndarray) – Shape
(n,). Bending moments about y [N·mm].
- integrate_with_tangent(
- eps0,
- chi_x,
- chi_y=0.0,
Internal forces and 3×3 tangent stiffness in one pass.
The tangent stiffness matrix relates infinitesimal changes in the strain-plane parameters to changes in internal forces:
\[\mathbf{K} = \frac{\partial (N,\,M_x,\,M_y)} {\partial (\varepsilon_0,\,\chi_x,\,\chi_y)} = \sum_i E_{t,i} \, A_i \; \boldsymbol{\varphi}_i \, \boldsymbol{\varphi}_i^T\]where the shape-function vector for fiber i is
\[\boldsymbol{\varphi}_i = \bigl[1,\; (y_i - y_{\text{ref}}),\; -(x_i - x_{\text{ref}})\bigr]^T\]and \(E_{t,i} = d\sigma_i / d\varepsilon_i\) is the tangent modulus at the current strain.
- Parameters:
eps0 (float)
chi_x (float)
chi_y (float, optional)
- Returns:
N, Mx, My (float) – Internal forces [N, N·mm].
K (numpy.ndarray) – 3×3 tangent stiffness matrix.
- jacobian(
- eps0,
- chi_x,
- chi_y=0.0,
- deps=1e-8,
Numerical 3×3 Jacobian via forward finite differences.
\[\mathbf{J} = \frac{\partial(N, M_x, M_y)} {\partial(\varepsilon_0, \chi_x, \chi_y)}\]- Parameters:
eps0 (float)
chi_x (float)
chi_y (float)
deps (float, optional) – Perturbation. Default 1e-8.
- Returns:
Shape (3, 3).
- Return type:
numpy.ndarray
- solve_equilibrium(
- N_target,
- Mx_target,
- My_target=0.0,
- eps0_init=0.0,
- chi_x_init=1e-6,
- chi_y_init=0.0,
- tol=1e-3,
- max_iter=50,
Inverse problem: find strain plane for target (N, Mx, My).
Newton-Raphson with analytical tangent stiffness and backtracking line search. Automatically reduces to a 2×2 system when the section is uniaxial, and further to a 1×1 bisection when both target moments are negligible (pure axial load).
The pure-axial branch exploits the monotonicity of \(N(\varepsilon_0)\) at \(\chi_x = \chi_y = 0\):
\[\frac{\partial N}{\partial \varepsilon_0}\bigg|_{\chi=0} = \sum_i \frac{\partial \sigma_i} {\partial \varepsilon}\,A_i \;\ge\; 0\]which guarantees unique bracketing and convergence of the bisection search.
- Parameters:
N_target (float) – Target axial force [N].
Mx_target (float) – Target moment about x-axis [N*mm].
My_target (float, optional) – Target moment about y-axis [N*mm]. Default 0.
eps0_init (float, optional) – Initial guesses.
chi_x_init (float, optional) – Initial guesses.
chi_y_init (float, optional) – Initial guesses.
tol (float, optional) – Force tolerance [N]; moment tolerance is
tol * 1000.max_iter (int, optional)
- Returns:
Keys:
eps0,chi_x,chi_y,N,Mx,My,converged,iterations.- Return type:
dict
- strain_field(
- eps0,
- chi_x,
- chi_y=0.0,
Compute strains at all fibers.
\[\varepsilon_i = \varepsilon_0 + \chi_x (y_i - y_{\text{ref}}) - \chi_y (x_i - x_{\text{ref}})\]The minus sign on \(\chi_y\) ensures that positive \(M_y\) compresses the right edge (\(x = x_{\max}\)), consistent with the right-hand rule around the y-axis.
- Parameters:
eps0 (float) – Strain at the reference point.
chi_x (float) – Curvature about the x-axis [1/mm].
chi_y (float, optional) – Curvature about the y-axis [1/mm]. Default 0.
- Returns:
eps_bulk (numpy.ndarray)
eps_rebars (numpy.ndarray)
Resistance domain generator¶
Resistance surface generator — N-Mx-My interaction domain.
Phase 2: generates a 3D point cloud in (N, Mx, My) space by scanning ultimate strain configurations across all curvature directions.
For uniaxial analysis (chi_y=0), produces the classic N-M diagram.
Performance notes¶
All capacity generators use FiberSolver.integrate_batch() to
evaluate hundreds or thousands of strain configurations in a single
vectorized NumPy call, eliminating Python-loop overhead. The
moment-curvature scanner uses
FiberSolver.integrate_with_tangent() for the inner Newton
iteration, halving the cost compared to a finite-difference Jacobian.
- class NMDiagram(
- solver,
Bases:
objectMaterial-agnostic resistance domain generator.
For uniaxial bending, generates the N-Mx interaction diagram (chi_y = 0). For biaxial bending, generates a 3D point cloud in (N, Mx, My) space by scanning curvature directions in the \((\chi_x, \chi_y)\) plane.
The strain plane at any point is:
\[\varepsilon(x, y) = \varepsilon_0 + \chi_x\,(y - y_{\text{ref}}) - \chi_y\,(x - x_{\text{ref}})\]For the 3D scan, the curvature direction angle \(\theta\) is:
\[\chi_x = \chi \cos\theta, \quad \chi_y = \chi \sin\theta\]and the curvature magnitude \(\chi\) is determined by the strain limits at the extreme fibers for that direction.
- Parameters:
solver (FiberSolver)
- generate(
- n_points=300,
- direction='x',
Generate the N-M interaction diagram for a single bending direction.
For
direction='x': N-Mx diagram (\(\chi_y = 0\)). Fordirection='y': N-My diagram (\(\chi_x = 0\)).All strain configurations are evaluated in a single batch call, avoiding per-configuration Python-loop overhead.
- Parameters:
n_points (int, optional) – Base resolution. Default 300.
direction (
'x'or'y', optional) – Bending direction. Default'x'.
- Returns:
N,M[N, N*mm],N_kN,M_kNm. AlsoMxorMyalias depending on direction.- Return type:
dict
- generate_biaxial(
- n_angles=72,
- n_points_per_angle=200,
Generate the 3D resistance surface (N, Mx, My).
Scans curvature directions \(\theta\) in \([0, 2\pi)\) and, for each direction, scans curvature magnitudes through ultimate strain configurations.
All directions are integrated in chunked mega-batches, reducing Python-loop overhead by an order of magnitude compared to per-angle batching.
- Parameters:
n_angles (int, optional) – Number of curvature direction angles. Default 72 (every 5°).
n_points_per_angle (int, optional) – Strain configurations per angle. Default 200.
- Returns:
N[N],Mx,My[N*mm], and_kN/_kNmvariants.- Return type:
dict
- generate_moment_curvature(
- N_fixed,
- chi_max=None,
- n_points=200,
- direction='x',
Generate the moment-curvature diagram at fixed axial force.
Scans curvature \(\chi\) from 0 to the ultimate value (where a material strain limit is reached), tracking the moment response. Key points (first yield, ultimate) are identified.
The inner Newton iteration for \(\varepsilon_0\) at each curvature step uses the analytical tangent from
FiberSolver.integrate_with_tangent(), halving the integrate calls compared to a finite-difference approach.- Parameters:
N_fixed (float) – Fixed axial force [N]. Negative = compression.
chi_max (float, optional) – Maximum curvature to scan [1/mm]. If
None, computed automatically from strain limits.n_points (int, optional) – Number of curvature steps. Default 200.
direction (str, optional) –
'x'for Mx-chi_x (default) or'y'for My-chi_y.
- Returns:
chi[1/mm],M[N*mm],M_kNm,chi_km[1/km],eps_min,eps_max(extreme strains at each step),yield_index(first yield step or None),ultimate_index(ultimate step or None),N_fixed_kN,direction.- Return type:
dict
- generate_mx_my(
- N_fixed,
- n_angles=72,
- n_points_per_angle=200,
Generate the Mx-My interaction contour at a fixed axial force.
For a given \(N\), scans curvature directions \(\theta \in [0, 2\pi)\). For each direction, sweeps through strain configurations, collects all (N, Mx, My) points, and interpolates at \(N = N_{\text{fixed}}\) to find the moment point on the contour.
All directions are integrated in chunked mega-batches for maximum throughput.
- Parameters:
N_fixed (float) – Fixed axial force [N].
n_angles (int, optional) – Number of curvature directions. Default 72 (every 5°).
n_points_per_angle (int, optional) – Strain scan resolution per direction. Default 200.
- Returns:
Mx[N*mm],My[N*mm],Mx_kNm,My_kNm,N_fixed_kN.- Return type:
dict
Demand checker¶
Demand verification against the resistance domain.
Implements the GenSec v2.1 demand architecture with four utilization ratio types, staged combinations, and envelopes.
Utilization ratios¶
All four \(\eta\) types share the same geometric primitive — a ray from base \(\mathbf{B}\) through target \(\mathbf{T}\) intersecting a boundary at \(\mathbf{R}\):
The four variants differ in what \(\mathbf{B}\), \(\mathbf{T}\) and the boundary represent:
Classes¶
- DomainChecker
3-D ConvexHull operations (
eta_3D,eta_path,is_inside).- MxMyContour
2-D contour at fixed N (
eta_2D).- VerificationEngine
Flag-driven orchestrator that resolves demands, combinations and envelopes, managing contour caches and producing structured results.
- class DomainChecker(
- nm_3d,
Bases:
object3-D resistance domain checker using a ConvexHull in \((N, M_x, M_y)\) space.
- Parameters:
nm_3d (dict) – Output of
NMDiagram.generate_biaxial()(keysN,Mx,Myin Newton / N·mm) or ofNMDiagram.generate()for uniaxial mode (keysN,M).- Variables:
ndim (int) – Dimensionality (2 for uniaxial, 3 for biaxial).
hull (scipy.spatial.ConvexHull)
N_range (float) – \(N_{Rd,\max} - N_{Rd,\min}\) [N]. Used by
eta_path_2Dto evaluate the \(\Delta N\) tolerance.
- eta_3D(
- N,
- Mx,
- My=0.0,
Utilization ratio via ray from the origin.
\[\eta_{\text{3D}} = \frac{|\mathbf{S}|}{|\mathbf{R}|}, \qquad \mathbf{S} = (N,\,M_x,\,M_y)\]- Parameters:
N (float) – Demand forces [N, N·mm].
Mx (float) – Demand forces [N, N·mm].
My (float) – Demand forces [N, N·mm].
- Return type:
float
- eta_path(
- N_base,
- Mx_base,
- My_base,
- N_target,
- Mx_target,
- My_target,
Utilization ratio via ray from an arbitrary base point.
\[\eta_{\text{path}} = \frac{|\mathbf{T} - \mathbf{B}|} {|\mathbf{R} - \mathbf{B}|}\]- Parameters:
N_base (float) – Base point [N, N·mm].
Mx_base (float) – Base point [N, N·mm].
My_base (float) – Base point [N, N·mm].
N_target (float) – Target point [N, N·mm].
Mx_target (float) – Target point [N, N·mm].
My_target (float) – Target point [N, N·mm].
- Return type:
float
- is_inside(
- N,
- Mx,
- My=0.0,
Check whether a point lies inside the resistance domain.
- Parameters:
N (float) – Axial force [N].
Mx (float) – Bending moment about x [N·mm].
My (float, optional) – Bending moment about y [N·mm]. Default 0.
- Return type:
bool
- class MxMyContour(
- mx_my_data,
Bases:
objectMx-My interaction contour at a fixed axial force.
Wraps a 2-D ConvexHull in \((M_x, M_y)\) space and provides a ray-cast method for \(\eta_{\text{2D}}\).
- Parameters:
mx_my_data (dict) – Output of
NMDiagram.generate_mx_my(). Must containMx[N·mm],My[N·mm].- Variables:
N_fixed (float) – The axial force [N] at which this contour was generated.
hull (scipy.spatial.ConvexHull)
- eta_2D(
- Mx,
- My,
\(\eta_{\text{2D}}\): ray from origin to demand in the \(M_x\)-\(M_y\) plane.
- Parameters:
Mx (float) – Demand moments [N·mm].
My (float) – Demand moments [N·mm].
- Return type:
float
- eta_path_2D(
- Mx_base,
- My_base,
- Mx_target,
- My_target,
\(\eta_{\text{path,2D}}\): ray from base to target in the \(M_x\)-\(M_y\) plane.
- Parameters:
Mx_base (float) – Base moments [N·mm].
My_base (float) – Base moments [N·mm].
Mx_target (float) – Target moments [N·mm].
My_target (float) – Target moments [N·mm].
- Return type:
float
- is_inside(
- Mx,
- My,
Check whether a point lies inside the Mx-My contour.
- Parameters:
Mx (float) – Moment components [N·mm].
My (float) – Moment components [N·mm].
- Return type:
bool
- class VerificationEngine(
- nm_3d,
- nm_gen,
- output_flags,
- n_points=200,
Bases:
objectTop-level orchestrator for demand verification.
Resolves demands, combinations and envelopes, computes all enabled \(\eta\) types, and manages a cache of
MxMyContourinstances for the N-levels actually needed.- Parameters:
nm_3d (dict) – 3-D point cloud from
NMDiagram.generate_biaxial()(or uniaxialgenerate()fallback).nm_gen (NMDiagram) – Generator instance for on-demand Mx-My contour production.
output_flags (dict) –
Parsed output block from YAML. Expected keys:
eta_3D(bool, defaultTrue)eta_2D(bool, defaultFalse)eta_path(bool, defaultTrue)eta_path_2D(bool, defaultFalse)delta_N_tol(float, default0.03)n_angles_mx_my(int, default144)
n_points (int, optional) – Resolution passed to
generate_mx_my. Default 200.
- check_combination(
- combo,
- demand_db,
Verify a combination (simple or staged).
For a simple combination (
componentsonly), computes the factored resultant and returns \(\eta_{\text{3D}}\) and \(\eta_{\text{2D}}\) from the origin.For a staged combination, accumulates stages sequentially. Stage 0 is verified from the origin. Stage \(k > 0\) is verified from the cumulative point of stage \(k-1\):
\[\eta_{\text{path},k} = \frac{|\mathbf{S}_k - \mathbf{S}_{k-1}|} {|\mathbf{R} - \mathbf{S}_{k-1}|}\]\(\eta_{\text{path,2D}}\) is computed only if the axial force jump satisfies:
\[\frac{|N_{S_k} - N_{S_{k-1}}|}{N_{Rd,\max} - N_{Rd,\min}} < \delta_N\]- Parameters:
combo (dict) – Parsed combination spec with
nameand eithercomponentsorstages.demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.
- Returns:
Structured result per §6 of the v2.1 spec.
- Return type:
dict
- check_demand(
- demand,
Verify a single demand point.
- Parameters:
demand (dict) –
{"name": str, "N": float, "Mx": float, "My": float}.- Returns:
Verification result with forces in kN/kN·m, enabled η values,
inside,verified.- Return type:
dict
- check_demands(
- demands,
Batch verification of demand points.
- Parameters:
demands (list of dict)
- Return type:
list of dict
- check_envelope(
- envelope,
- demand_db,
- combination_results=None,
Verify an envelope: report max \(\eta\) among members.
Each member can be a
refto a demand or combination, or an inline demand. An optionalfactorscales the resolved resultant.- Parameters:
envelope (dict) –
{"name": str, "members": [...]}.demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.combination_results (dict or None) –
{name: <combination result dict>}.
- Returns:
name,eta_max,governing_member,verified,members(list of per-member results).- Return type:
dict
- static resolve_components(
- components,
- demand_db,
Sum factored components into a single force triple.
\[\mathbf{S} = \sum_i f_i \, \mathbf{d}_i\]- Parameters:
components (list of dict) – Each dict has
ref(str) and optionallyfactor(float, default 1.0).demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.
- Returns:
{"N": float, "Mx": float, "My": float}in [N, N·mm, N·mm].- Return type:
dict
- static resolve_ref(
- ref_name,
- demand_db,
- combination_results=None,
Look up a demand name and return the resolved force triple.
Resolution order:
demand_dbfirst, thencombination_results(using the combination resultant).- Parameters:
ref_name (str)
demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.combination_results (dict or None) –
{name: <combination result dict>}.
- Returns:
(N, Mx, My)in [N, N·mm, N·mm].- Return type:
tuple of float
- Raises:
KeyError – If ref_name is not found in either database.
Package-level exports¶
Solver subpackage.
Provides the fiber integrator (FiberSolver), the N-M
interaction diagram generator (NMDiagram), and the demand
verification module (DemandChecker).
- class DomainChecker(
- nm_3d,
Bases:
object3-D resistance domain checker using a ConvexHull in \((N, M_x, M_y)\) space.
- Parameters:
nm_3d (dict) – Output of
NMDiagram.generate_biaxial()(keysN,Mx,Myin Newton / N·mm) or ofNMDiagram.generate()for uniaxial mode (keysN,M).- Variables:
ndim (int) – Dimensionality (2 for uniaxial, 3 for biaxial).
hull (scipy.spatial.ConvexHull)
N_range (float) – \(N_{Rd,\max} - N_{Rd,\min}\) [N]. Used by
eta_path_2Dto evaluate the \(\Delta N\) tolerance.
- eta_3D(
- N,
- Mx,
- My=0.0,
Utilization ratio via ray from the origin.
\[\eta_{\text{3D}} = \frac{|\mathbf{S}|}{|\mathbf{R}|}, \qquad \mathbf{S} = (N,\,M_x,\,M_y)\]- Parameters:
N (float) – Demand forces [N, N·mm].
Mx (float) – Demand forces [N, N·mm].
My (float) – Demand forces [N, N·mm].
- Return type:
float
- eta_path(
- N_base,
- Mx_base,
- My_base,
- N_target,
- Mx_target,
- My_target,
Utilization ratio via ray from an arbitrary base point.
\[\eta_{\text{path}} = \frac{|\mathbf{T} - \mathbf{B}|} {|\mathbf{R} - \mathbf{B}|}\]- Parameters:
N_base (float) – Base point [N, N·mm].
Mx_base (float) – Base point [N, N·mm].
My_base (float) – Base point [N, N·mm].
N_target (float) – Target point [N, N·mm].
Mx_target (float) – Target point [N, N·mm].
My_target (float) – Target point [N, N·mm].
- Return type:
float
- is_inside(
- N,
- Mx,
- My=0.0,
Check whether a point lies inside the resistance domain.
- Parameters:
N (float) – Axial force [N].
Mx (float) – Bending moment about x [N·mm].
My (float, optional) – Bending moment about y [N·mm]. Default 0.
- Return type:
bool
- class FiberSolver(
- section,
- x_ref=None,
- y_ref=None,
Bases:
objectMaterial-agnostic fiber solver for biaxial bending + axial force.
Strain plane:
\[\varepsilon(x, y) = \varepsilon_0 + \chi_x \, (y - y_{\text{ref}}) + \chi_y \, (x - x_{\text{ref}})\]Internal forces:
\[\begin{split}N &= \sum_i \sigma_i \, A_i \\ M_x &= \sum_i \sigma_i \, A_i \, (y_i - y_{\text{ref}}) \\ M_y &= \sum_i \sigma_i \, A_i \, (x_i - x_{\text{ref}})\end{split}\]Convention:
\(M_x > 0\) compresses the bottom edge (\(y = 0\)), tensions the top edge. Right-hand rule: thumb along +x, fingers curl from +y toward observer.
\(M_y > 0\) compresses the right edge (\(x = x_{\max}\)), tensions the left edge. Right-hand rule: thumb along +y, fingers curl from +x toward observer.
- Parameters:
section (GenericSection or RectSection) – Any section exposing
x_fibers,y_fibers,A_fibers,x_rebars,y_rebars,A_rebars,embedded_rebars,bulk_material,rebars,x_centroid,y_centroid.x_ref (float or None, optional) – Reference x-coordinate [mm]. Default: x-centroid.
y_ref (float or None, optional) – Reference y-coordinate [mm]. Default: y-centroid.
- get_fiber_results(
- eps0,
- chi_x,
- chi_y=0.0,
Full strain/stress state at every fiber.
For multi-material sections, each bulk fiber is evaluated with its own constitutive law.
- Parameters:
eps0 (float)
chi_x (float)
chi_y (float, optional)
- Returns:
'bulk': sub-dict withx,y,eps,sigma,dA.'rebars': sub-dict withx,y,eps,sigma(gross),sigma_net(net after bulk subtraction),A,embedded.- Return type:
dict
- integrate(
- eps0,
- chi_x,
- chi_y=0.0,
Direct problem: strain parameters to internal forces.
For embedded rebars, the bulk material contribution at the rebar location is subtracted to avoid double-counting:
\[F_i = \bigl[\sigma_{\text{rebar},i}(\varepsilon_i) - \sigma_{\text{bulk}}(\varepsilon_i)\bigr] \, A_{s,i}\]For multi-material sections, each fiber group is evaluated with its own constitutive law.
- Parameters:
eps0 (float)
chi_x (float) – Curvature about x-axis [1/mm].
chi_y (float, optional) – Curvature about y-axis [1/mm]. Default 0.
- Returns:
N (float) – Axial force [N]. Positive = tension.
Mx (float) – Bending moment about x-axis [N*mm].
My (float) – Bending moment about y-axis [N*mm].
- integrate_batch(
- eps0,
- chi_x,
- chi_y,
Evaluate internal forces for many strain configurations at once.
All inputs are 1-D arrays of the same length n. The computation is fully vectorized: strain fields are built as 2-D arrays of shape
(n, n_fibers)and passed through the constitutive laws in one call, eliminating Python-loop overhead.- Parameters:
eps0 (numpy.ndarray) – Shape
(n,). Strains at the reference point.chi_x (numpy.ndarray) – Shape
(n,). Curvatures about the x-axis [1/mm].chi_y (numpy.ndarray) – Shape
(n,). Curvatures about the y-axis [1/mm].
- Returns:
N (numpy.ndarray) – Shape
(n,). Axial forces [N].Mx (numpy.ndarray) – Shape
(n,). Bending moments about x [N·mm].My (numpy.ndarray) – Shape
(n,). Bending moments about y [N·mm].
- integrate_with_tangent(
- eps0,
- chi_x,
- chi_y=0.0,
Internal forces and 3×3 tangent stiffness in one pass.
The tangent stiffness matrix relates infinitesimal changes in the strain-plane parameters to changes in internal forces:
\[\mathbf{K} = \frac{\partial (N,\,M_x,\,M_y)} {\partial (\varepsilon_0,\,\chi_x,\,\chi_y)} = \sum_i E_{t,i} \, A_i \; \boldsymbol{\varphi}_i \, \boldsymbol{\varphi}_i^T\]where the shape-function vector for fiber i is
\[\boldsymbol{\varphi}_i = \bigl[1,\; (y_i - y_{\text{ref}}),\; -(x_i - x_{\text{ref}})\bigr]^T\]and \(E_{t,i} = d\sigma_i / d\varepsilon_i\) is the tangent modulus at the current strain.
- Parameters:
eps0 (float)
chi_x (float)
chi_y (float, optional)
- Returns:
N, Mx, My (float) – Internal forces [N, N·mm].
K (numpy.ndarray) – 3×3 tangent stiffness matrix.
- jacobian(
- eps0,
- chi_x,
- chi_y=0.0,
- deps=1e-8,
Numerical 3×3 Jacobian via forward finite differences.
\[\mathbf{J} = \frac{\partial(N, M_x, M_y)} {\partial(\varepsilon_0, \chi_x, \chi_y)}\]- Parameters:
eps0 (float)
chi_x (float)
chi_y (float)
deps (float, optional) – Perturbation. Default 1e-8.
- Returns:
Shape (3, 3).
- Return type:
numpy.ndarray
- solve_equilibrium(
- N_target,
- Mx_target,
- My_target=0.0,
- eps0_init=0.0,
- chi_x_init=1e-6,
- chi_y_init=0.0,
- tol=1e-3,
- max_iter=50,
Inverse problem: find strain plane for target (N, Mx, My).
Newton-Raphson with analytical tangent stiffness and backtracking line search. Automatically reduces to a 2×2 system when the section is uniaxial, and further to a 1×1 bisection when both target moments are negligible (pure axial load).
The pure-axial branch exploits the monotonicity of \(N(\varepsilon_0)\) at \(\chi_x = \chi_y = 0\):
\[\frac{\partial N}{\partial \varepsilon_0}\bigg|_{\chi=0} = \sum_i \frac{\partial \sigma_i} {\partial \varepsilon}\,A_i \;\ge\; 0\]which guarantees unique bracketing and convergence of the bisection search.
- Parameters:
N_target (float) – Target axial force [N].
Mx_target (float) – Target moment about x-axis [N*mm].
My_target (float, optional) – Target moment about y-axis [N*mm]. Default 0.
eps0_init (float, optional) – Initial guesses.
chi_x_init (float, optional) – Initial guesses.
chi_y_init (float, optional) – Initial guesses.
tol (float, optional) – Force tolerance [N]; moment tolerance is
tol * 1000.max_iter (int, optional)
- Returns:
Keys:
eps0,chi_x,chi_y,N,Mx,My,converged,iterations.- Return type:
dict
- strain_field(
- eps0,
- chi_x,
- chi_y=0.0,
Compute strains at all fibers.
\[\varepsilon_i = \varepsilon_0 + \chi_x (y_i - y_{\text{ref}}) - \chi_y (x_i - x_{\text{ref}})\]The minus sign on \(\chi_y\) ensures that positive \(M_y\) compresses the right edge (\(x = x_{\max}\)), consistent with the right-hand rule around the y-axis.
- Parameters:
eps0 (float) – Strain at the reference point.
chi_x (float) – Curvature about the x-axis [1/mm].
chi_y (float, optional) – Curvature about the y-axis [1/mm]. Default 0.
- Returns:
eps_bulk (numpy.ndarray)
eps_rebars (numpy.ndarray)
- class MxMyContour(
- mx_my_data,
Bases:
objectMx-My interaction contour at a fixed axial force.
Wraps a 2-D ConvexHull in \((M_x, M_y)\) space and provides a ray-cast method for \(\eta_{\text{2D}}\).
- Parameters:
mx_my_data (dict) – Output of
NMDiagram.generate_mx_my(). Must containMx[N·mm],My[N·mm].- Variables:
N_fixed (float) – The axial force [N] at which this contour was generated.
hull (scipy.spatial.ConvexHull)
- eta_2D(
- Mx,
- My,
\(\eta_{\text{2D}}\): ray from origin to demand in the \(M_x\)-\(M_y\) plane.
- Parameters:
Mx (float) – Demand moments [N·mm].
My (float) – Demand moments [N·mm].
- Return type:
float
- eta_path_2D(
- Mx_base,
- My_base,
- Mx_target,
- My_target,
\(\eta_{\text{path,2D}}\): ray from base to target in the \(M_x\)-\(M_y\) plane.
- Parameters:
Mx_base (float) – Base moments [N·mm].
My_base (float) – Base moments [N·mm].
Mx_target (float) – Target moments [N·mm].
My_target (float) – Target moments [N·mm].
- Return type:
float
- is_inside(
- Mx,
- My,
Check whether a point lies inside the Mx-My contour.
- Parameters:
Mx (float) – Moment components [N·mm].
My (float) – Moment components [N·mm].
- Return type:
bool
- class NMDiagram(
- solver,
Bases:
objectMaterial-agnostic resistance domain generator.
For uniaxial bending, generates the N-Mx interaction diagram (chi_y = 0). For biaxial bending, generates a 3D point cloud in (N, Mx, My) space by scanning curvature directions in the \((\chi_x, \chi_y)\) plane.
The strain plane at any point is:
\[\varepsilon(x, y) = \varepsilon_0 + \chi_x\,(y - y_{\text{ref}}) - \chi_y\,(x - x_{\text{ref}})\]For the 3D scan, the curvature direction angle \(\theta\) is:
\[\chi_x = \chi \cos\theta, \quad \chi_y = \chi \sin\theta\]and the curvature magnitude \(\chi\) is determined by the strain limits at the extreme fibers for that direction.
- Parameters:
solver (FiberSolver)
- generate(
- n_points=300,
- direction='x',
Generate the N-M interaction diagram for a single bending direction.
For
direction='x': N-Mx diagram (\(\chi_y = 0\)). Fordirection='y': N-My diagram (\(\chi_x = 0\)).All strain configurations are evaluated in a single batch call, avoiding per-configuration Python-loop overhead.
- Parameters:
n_points (int, optional) – Base resolution. Default 300.
direction (
'x'or'y', optional) – Bending direction. Default'x'.
- Returns:
N,M[N, N*mm],N_kN,M_kNm. AlsoMxorMyalias depending on direction.- Return type:
dict
- generate_biaxial(
- n_angles=72,
- n_points_per_angle=200,
Generate the 3D resistance surface (N, Mx, My).
Scans curvature directions \(\theta\) in \([0, 2\pi)\) and, for each direction, scans curvature magnitudes through ultimate strain configurations.
All directions are integrated in chunked mega-batches, reducing Python-loop overhead by an order of magnitude compared to per-angle batching.
- Parameters:
n_angles (int, optional) – Number of curvature direction angles. Default 72 (every 5°).
n_points_per_angle (int, optional) – Strain configurations per angle. Default 200.
- Returns:
N[N],Mx,My[N*mm], and_kN/_kNmvariants.- Return type:
dict
- generate_moment_curvature(
- N_fixed,
- chi_max=None,
- n_points=200,
- direction='x',
Generate the moment-curvature diagram at fixed axial force.
Scans curvature \(\chi\) from 0 to the ultimate value (where a material strain limit is reached), tracking the moment response. Key points (first yield, ultimate) are identified.
The inner Newton iteration for \(\varepsilon_0\) at each curvature step uses the analytical tangent from
FiberSolver.integrate_with_tangent(), halving the integrate calls compared to a finite-difference approach.- Parameters:
N_fixed (float) – Fixed axial force [N]. Negative = compression.
chi_max (float, optional) – Maximum curvature to scan [1/mm]. If
None, computed automatically from strain limits.n_points (int, optional) – Number of curvature steps. Default 200.
direction (str, optional) –
'x'for Mx-chi_x (default) or'y'for My-chi_y.
- Returns:
chi[1/mm],M[N*mm],M_kNm,chi_km[1/km],eps_min,eps_max(extreme strains at each step),yield_index(first yield step or None),ultimate_index(ultimate step or None),N_fixed_kN,direction.- Return type:
dict
- generate_mx_my(
- N_fixed,
- n_angles=72,
- n_points_per_angle=200,
Generate the Mx-My interaction contour at a fixed axial force.
For a given \(N\), scans curvature directions \(\theta \in [0, 2\pi)\). For each direction, sweeps through strain configurations, collects all (N, Mx, My) points, and interpolates at \(N = N_{\text{fixed}}\) to find the moment point on the contour.
All directions are integrated in chunked mega-batches for maximum throughput.
- Parameters:
N_fixed (float) – Fixed axial force [N].
n_angles (int, optional) – Number of curvature directions. Default 72 (every 5°).
n_points_per_angle (int, optional) – Strain scan resolution per direction. Default 200.
- Returns:
Mx[N*mm],My[N*mm],Mx_kNm,My_kNm,N_fixed_kN.- Return type:
dict
- class VerificationEngine(
- nm_3d,
- nm_gen,
- output_flags,
- n_points=200,
Bases:
objectTop-level orchestrator for demand verification.
Resolves demands, combinations and envelopes, computes all enabled \(\eta\) types, and manages a cache of
MxMyContourinstances for the N-levels actually needed.- Parameters:
nm_3d (dict) – 3-D point cloud from
NMDiagram.generate_biaxial()(or uniaxialgenerate()fallback).nm_gen (NMDiagram) – Generator instance for on-demand Mx-My contour production.
output_flags (dict) –
Parsed output block from YAML. Expected keys:
eta_3D(bool, defaultTrue)eta_2D(bool, defaultFalse)eta_path(bool, defaultTrue)eta_path_2D(bool, defaultFalse)delta_N_tol(float, default0.03)n_angles_mx_my(int, default144)
n_points (int, optional) – Resolution passed to
generate_mx_my. Default 200.
- check_combination(
- combo,
- demand_db,
Verify a combination (simple or staged).
For a simple combination (
componentsonly), computes the factored resultant and returns \(\eta_{\text{3D}}\) and \(\eta_{\text{2D}}\) from the origin.For a staged combination, accumulates stages sequentially. Stage 0 is verified from the origin. Stage \(k > 0\) is verified from the cumulative point of stage \(k-1\):
\[\eta_{\text{path},k} = \frac{|\mathbf{S}_k - \mathbf{S}_{k-1}|} {|\mathbf{R} - \mathbf{S}_{k-1}|}\]\(\eta_{\text{path,2D}}\) is computed only if the axial force jump satisfies:
\[\frac{|N_{S_k} - N_{S_{k-1}}|}{N_{Rd,\max} - N_{Rd,\min}} < \delta_N\]- Parameters:
combo (dict) – Parsed combination spec with
nameand eithercomponentsorstages.demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.
- Returns:
Structured result per §6 of the v2.1 spec.
- Return type:
dict
- check_demand(
- demand,
Verify a single demand point.
- Parameters:
demand (dict) –
{"name": str, "N": float, "Mx": float, "My": float}.- Returns:
Verification result with forces in kN/kN·m, enabled η values,
inside,verified.- Return type:
dict
- check_demands(
- demands,
Batch verification of demand points.
- Parameters:
demands (list of dict)
- Return type:
list of dict
- check_envelope(
- envelope,
- demand_db,
- combination_results=None,
Verify an envelope: report max \(\eta\) among members.
Each member can be a
refto a demand or combination, or an inline demand. An optionalfactorscales the resolved resultant.- Parameters:
envelope (dict) –
{"name": str, "members": [...]}.demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.combination_results (dict or None) –
{name: <combination result dict>}.
- Returns:
name,eta_max,governing_member,verified,members(list of per-member results).- Return type:
dict
- static resolve_components(
- components,
- demand_db,
Sum factored components into a single force triple.
\[\mathbf{S} = \sum_i f_i \, \mathbf{d}_i\]- Parameters:
components (list of dict) – Each dict has
ref(str) and optionallyfactor(float, default 1.0).demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.
- Returns:
{"N": float, "Mx": float, "My": float}in [N, N·mm, N·mm].- Return type:
dict
- static resolve_ref(
- ref_name,
- demand_db,
- combination_results=None,
Look up a demand name and return the resolved force triple.
Resolution order:
demand_dbfirst, thencombination_results(using the combination resultant).- Parameters:
ref_name (str)
demand_db (dict) –
{name: {"N": ..., "Mx": ..., "My": ...}}.combination_results (dict or None) –
{name: <combination result dict>}.
- Returns:
(N, Mx, My)in [N, N·mm, N·mm].- Return type:
tuple of float
- Raises:
KeyError – If ref_name is not found in either database.