Theoretical Basis#
PyCBA analyses continuous beams by the matrix (direct) stiffness
method — the standard displacement-based formulation of linear structural
analysis
(McGuire, Gallagher & Ziemian, 2000;
Przemieniecki, 1968;
Weaver & Gere, 1990).
The structure is idealised as an assembly of two-node Euler–Bernoulli beam
elements; the element stiffness relations are assembled into a global system,
the supports are imposed, the system is solved for the nodal displacements, and
the support reactions and member load effects are recovered. This page derives
each step, in the order the implementation performs it, so that every equation
corresponds directly to the code in
BeamAnalysis, Beam,
pycba.load and SectionEI.
The overall procedure is:
Elements — each span is a two-node Euler–Bernoulli beam element whose \(4\times4\) stiffness matrix depends on its end releases and, for a non-prismatic member, on the variation of \(EI\) along its length.
Global stiffness matrix — the element matrices are assembled by the direct stiffness method into the global matrix \(\mathbf{K}\), with elastic spring supports added on the diagonal.
Loads — span loads are converted to consistent (equivalent) nodal forces that assemble into the force vector \(\mathbf{f}\).
Stability check — the free-DOF partition of \(\mathbf{K}\) is tested for a mechanism before the system is solved.
Boundary conditions — fixed supports, prescribed settlements and spring supports are imposed.
Solution and recovery — the system \(\mathbf{K}\,\mathbf{d}=\mathbf{f}\) is solved for the nodal displacements \(\mathbf{d}\), from which the reactions and the member results (bending moment, shear, rotation and deflection along each span) are recovered.
Note
By default PyCBA uses Euler–Bernoulli (engineer’s) beam theory: plane
sections remain plane and normal to the neutral axis, so shear deformation is
neglected. A member may instead be made a Timoshenko (shear-deformable)
element by supplying a finite transverse shear rigidity GAv (see
Timoshenko elements below); this is opt-in per member and
the Euler–Bernoulli path is otherwise unchanged. Axial deformation is omitted in
both cases — the elements carry transverse (bending and, optionally, shear)
action only, which is the appropriate idealisation for a continuous beam.
Sign conventions and degrees of freedom#
PyCBA is unit-agnostic: the solver performs no unit conversions, and any
internally consistent set of units may be used (see the Defining Beams page).
The sign conventions used throughout are:
Vertical displacements \(v\) and vertical forces (reactions) are positive upward.
Rotations \(\theta\) and moments are positive counter-clockwise.
A downward applied load (UDL \(w\), point load \(P\)) is entered with a positive magnitude; a positive applied moment is counter-clockwise.
A support settlement is a prescribed (negative = downward) displacement.
Each node carries two degrees of freedom (DOF) — a transverse displacement \(v\) and a rotation \(\theta\) — so a single element with end nodes \(i\) and \(j\) has the four local DOF, ordered
and a conjugate force vector \(\mathbf{f}^{(e)} = [\,V_i,\;M_i,\;V_j,\;M_j\,]^{\mathsf T}\) of end shears and moments. This is the classical Hermitian beam element of the matrix stiffness method (McGuire, Gallagher & Ziemian, 2000; Weaver & Gere, 1990).
Elements#
The prismatic Euler–Bernoulli beam element#
For a prismatic member of constant flexural rigidity \(EI\) and length \(L\), Euler–Bernoulli theory relates the transverse displacement \(v(x)\) to the bending moment by \(EI\,v''(x) = M(x)\), with no distributed load on the element interior giving \(v(x)\) as a cubic. Using the cubic Hermite shape functions \(\mathbf{N}(x)\) that interpolate the four end DOF, the element stiffness matrix follows from the strain-energy (virtual-work) integral
which, for constant \(EI\), evaluates to the well-known \(4\times4\) matrix
(Przemieniecki, 1968;
Cook et al., 2001). In PyCBA’s DOF order and
sign convention (implemented in pycba.beam.Beam.k_FF()):
This is the fixed–fixed (Type 1, the default) element: both ends transmit moment. The matrix is symmetric and positive semi-definite — it has a two-dimensional null space corresponding to the rigid-body translation and rotation of the unconstrained element, which is removed once the element is assembled and supports are applied.
Element types and moment releases#
A continuous beam may contain internal hinges (moment releases). PyCBA
encodes this per span through the eletype index, which controls which end(s)
of the element carry moment:
|
Name |
Released end(s) |
Method |
|---|---|---|---|
1 |
Fixed–Fixed (FF) |
none (default) |
|
2 |
Fixed–Pinned (FP) |
right end (\(j\)) |
|
3 |
Pinned–Fixed (PF) |
left end (\(i\)) |
|
4 |
Pinned–Pinned (PP) |
both ends |
A moment release is a known zero internal moment at that end. The corresponding rotational DOF is therefore not connected to the element stiffness and is statically condensed out: setting the released end moment to zero and eliminating its rotation gives the reduced element stiffness. For the prismatic element these condensations have the closed forms below (with the released rotational rows and columns zeroed).
Type 2 — Fixed–Pinned (pin at the \(j\)-end), k_FP():
Type 3 — Pinned–Fixed (pin at the \(i\)-end), k_PF():
Type 4 — Pinned–Pinned (pin at both ends), k_PP():
with no moment transmitted at either end the element has no transverse bending
stiffness, so
Warning
A moment release models an internal hinge within the elements, not a support
condition. At any joint where a hinge is required, exactly one of the two
members meeting there should carry the pin; if both adjacent ends are released
the rotation at that node is unrestrained by either element and the assembled
stiffness becomes singular — a mechanism. PyCBA detects this before solving
(see Stability check).
Non-prismatic (variable-\(EI\)) elements#
A non-prismatic member has a flexural rigidity \(EI(x)\) that varies along its
length (a haunch, a taper, or a stepped section). PyCBA represents the
variation with a SectionEI object built from contiguous
segments (const, linear, pwl, poly) describing \(EI(x)\) in the
span-local coordinate \(x\), and analyses the member with a flexibility-integrated
element (pycba.beam.Beam.k_nonprismatic()). This follows the classical
force (flexibility) method for variable-rigidity members
(Ghali, Favre & Elbadry, 2002, Ch. 13;
Hulse & Mosley, 1986, §2.6; and the
idiom tabulated in the PCA
Handbook of Frame Constants).
Release the span to a simply-supported beam and apply unit moments at each end. The resulting (linear) unit-moment diagrams are
where the sign of \(m_j\) carries the counter-clockwise-positive nodal-moment convention used throughout (so that the formulation reproduces \(\mathbf{k}_{\text{FF}}\) exactly in the constant-\(EI\) limit). By the unit-load theorem the \(2\times2\) end-rotation flexibility about the two end DOF is
and the end moment–rotation stiffness is its inverse,
(implemented in pycba.beam.Beam.k_theta()). The diagonal terms of
\(\mathbf{K}_\theta\) are the rotational stiffness factors (end moment per unit
rotation, far end fixed), and the off-diagonal term yields the carry-over
factors — exactly the quantities tabulated for haunched members in the PCA
handbook.
The full \(4\times4\) element stiffness is recovered by expanding the two end rotations to the four nodal DOF, removing the rigid-body chord rotation \(\psi = (v_j - v_i)/L\). With the kinematic transformation \([\theta_i,\;\theta_j]^{\mathsf T} = \mathbf{T}\,\mathbf{d}^{(e)}\) where
the element stiffness is
The end shears emerge automatically as \((M_i + M_j)/L\) from this transformation,
matching PyCBA’s DOF order and sign convention. Moment releases (types 2–4)
are then imposed by the same static condensation of the released rotational
DOF used for the prismatic element (the Schur complement
\(\mathbf{k}_{kk} - \mathbf{k}_{kr}\,\mathbf{k}_{rr}^{-1}\,\mathbf{k}_{rk}\) over
the released rows/columns; pycba.beam.Beam._condense()).
The flexibility integrals are evaluated by breakpoint-aware Gauss–Legendre
quadrature (Stroud & Secrest, 1966;
numpy.polynomial.legendre.leggauss,
Harris et al., 2020), summed
piece-by-piece between consecutive breakpoints (segment joins and pwl
kinks). Splitting at the breakpoints makes a slope change (haunch → flat) or a
step (a discontinuous \(EI\)) exact rather than smeared by a single global
quadrature. On a constant piece the integrand \(m_p m_q / EI\) is a pure quadratic
polynomial and a 2-point Gauss rule is exact, so a single const segment (the
prismatic limit) reproduces the closed-form \(\mathbf{k}_{\text{FF}}\),
\(\mathbf{k}_{\text{FP}}\), \(\mathbf{k}_{\text{PF}}\) and \(\mathbf{k}_{\text{PP}}\)
above to machine precision. Scalar (prismatic) and SectionEI (non-prismatic)
members may be freely mixed in one beam.
Timoshenko (shear-deformable) elements#
A Timoshenko member augments the bending deformation with a transverse shear deformation (Timoshenko, 1921): the cross section rotates by \(\psi(x)\), which is no longer constrained to remain normal to the deflected axis, and the difference between the axis slope and the section rotation is the shear strain
where \(GA_v\) is the transverse shear rigidity (\(A_v = kA\) the shear area,
with \(k\) the cross-section shear coefficient,
Cowper, 1966). A member becomes a Timoshenko element purely
by being given a finite GAv (a scalar, or a SectionEI
for a variable \(GA_v(x)\)); the nodal DOF are unchanged — two per node,
\([v,\ \theta]\), with \(\theta\) now the section rotation \(\psi\) — so assembly,
supports, reactions and the plotting layer are all inherited unchanged.
For a prismatic member the element is parameterised by the dimensionless shear parameter
giving the locking-free two-node stiffness
(Friedman & Kosmatka, 1993;
Przemieniecki, 1968;
pycba.beam.Beam.k_FF_timo())
As \(GA_v \to \infty\) (slender member, \(\Phi \to 0\)) this reduces exactly to the Euler–Bernoulli \(\mathbf{k}_{\text{FF}}\) above, and member releases are imposed by the same static condensation used everywhere else.
The element follows the same flexibility derivation as the non-prismatic member, with one addition: applying unit end moments to the released span also produces a constant shear \(v = -1/L\) (the support reactions), so the shear strain energy adds the term
to every entry of the end-rotation flexibility \(\mathbf{F}\) before inverting
to \(\mathbf{K}_\theta\) (pycba.beam.Beam._timo_flexibility()). For a
constant \(EI\)/\(GA_v\) this reproduces the closed-form matrix above to machine
precision; the same single path
(pycba.beam.Beam.k_timoshenko()) therefore covers prismatic,
non-prismatic, and variable-shear members. Because this shares the non-prismatic
machinery, no per-load fixed-end-force formulae are rewritten: for a
prismatic member the shear contribution to the released-span end rotations
integrates to zero, so the Timoshenko fixed-end moments are the exact closed-form
transform of the Euler–Bernoulli values,
(pycba.beam.Beam._ref_timoshenko()); for a variable \(EI\)/\(GA_v\) the end
rotations (including the shear term) are obtained by the same breakpoint-aware
integration. A symmetric load on a prismatic member therefore keeps its
Euler–Bernoulli fixed-end moments, while an unsymmetric load — or a continuous
beam — redistributes according to \(\Phi\).
In the member-results recovery the reported rotation is the section rotation \(\psi\) (continuous with the nodal DOF), and the deflection integrates the axis slope \(\psi + \gamma\), i.e. the bending deflection plus the shear contribution \(\int V/GA_v\); for \(GA_v \to \infty\) the shear slope vanishes and the result is identical to Euler–Bernoulli.
Note
For slender members (span/depth \(\gtrsim 20\)) the shear deflection is typically under a couple of percent; the Timoshenko option matters for deep or short members (transfer beams, deep voided slabs, short spans). Shear-deformable elements are not currently combined with the nonlinear (plastic-hinge) engine, which remains Euler–Bernoulli.
Beam on an elastic (Winkler) foundation#
A member given a finite foundation modulus \(k_f\) (the modulus of subgrade reaction per unit length of beam) rests on a continuous Winkler foundation that resists deflection with a distributed reaction \(q(x) = -k_f\,v(x)\). (Throughout, member and span are used interchangeably for the element between two nodes.) The governing equation becomes
whose homogeneous solutions decay over the characteristic length \(\lambda = (4EI/k_f)^{1/4}\).
Rather than introduce the exact (hyperbolic) foundation element and re-derive the fixed-end forces for every load type, PyCBA models the foundation member as a statically-condensed super-element (the same internal-meshing idea used by the nonlinear analysis). The member is meshed into \(n\) ordinary Euler–Bernoulli sub-elements; each receives the standard consistent foundation stiffness
formed from the same cubic Hermite shape functions as the element stiffness (\(h = L/n\) the sub-element length). The internal nodes are removed by static condensation, so the member still presents a two-node \(4\times4\) stiffness and a condensed fixed-end-force vector to the global assembly — reactions, plotting and influence lines are inherited unchanged. Member results are recovered by reconstructing the internal sub-element displacements and concatenating each sub-element’s exact Euler–Bernoulli diagrams; accuracy improves with mesh refinement, and the mesh defaults to several sub-elements per characteristic length \(\lambda\). The implementation reproduces the analytic infinite-beam deflection \(P\beta/2k_f\) and moment \(P/4\beta\) (with \(\beta = 1/\lambda\)) under a point load from Hetényi (1946), and is used to validate it.
The Winkler model is linear and bidirectional: where a member lifts, the springs resist by pulling down, so the foundation can carry apparent tension. A real soil cannot, and a railway run-on slab in fact spans a settlement trough behind the abutment rather than a continuous bed (O’Brien, Keogh & O’Connor 2014, §4.5); the linear bed therefore over-estimates effects in any uplift zones. The foundation tutorial shows a worked railway-bridge example with ballasted approaches under a moving load.
Note
The foundation super-element currently supports prismatic, fixed-fixed members
without shear flexibility (GAv), carrying UDL, point and partial-UDL loads;
other combinations raise a clear error.
Global stiffness matrix#
For an \(N\)-span beam there are \(N+1\) nodes and hence \(2(N+1)\) degrees of freedom. The global DOF vector is ordered node by node:
The unrestricted global stiffness matrix \(\mathbf{K}\), of size
\(2(N+1)\times2(N+1)\), is assembled by the direct stiffness method
(pycba.analysis.BeamAnalysis._assemble()). For span \(m\) (zero-indexed),
the left node is global node \(m\) and the right node is global node \(m+1\), so the
element DOF map to the global indices \([\,2m,\;2m+1,\;2m+2,\;2m+3\,]\). The
\(4\times4\) element stiffness \(\mathbf{k}^{(e)}\) is overlapped (scatter-added)
into \(\mathbf{K}\):
Because adjacent spans share a node, the \(2\times2\) blocks of neighbouring elements overlap on the shared DOF; this overlap is precisely what enforces displacement compatibility (a single \(v\) and \(\theta\) per node) and nodal equilibrium between the spans. The assembled \(\mathbf{K}\) is symmetric, banded and — before supports are applied — positive semi-definite (it retains the global rigid-body modes).
Spring supports#
An elastic support at DOF \(i\) with stiffness \(k_s > 0\) (a vertical translational spring on a \(v\)-DOF, or a rotational spring on a \(\theta\)-DOF) contributes a restoring force \(k_s u_i\) at that DOF. It is added directly to the diagonal of the assembled matrix, before boundary conditions:
A spring DOF then remains a free unknown in the linear system — no row or
column elimination is performed for it, unlike a rigid support. Adding the
spring to the unrestricted matrix here is what later allows the spring force to
be recovered correctly during reaction recovery. This makes PyCBA usable as a
sub-frame analysis tool, where springs model the rotational/translational
restraint offered by members not explicitly modelled.
Loads#
Span loads are not applied at the nodes directly. Each load on a member is first converted to its consistent (equivalent) nodal forces — the fixed-end forces the load would produce with both member ends clamped — which then assemble into the global force vector \(\mathbf{f}\). This is the work-equivalent load lumping of the displacement method (Przemieniecki, 1968; Cook et al., 2001; Felippa, 2004).
Consistent nodal (fixed-end) forces#
For a load on a fixed–fixed span, the consistent nodal load vector is the set of end shears \(V_a, V_b\) and end moments \(M_a, M_b\) that hold both ends clamped. For a transverse distributed load \(w(x)\) these follow from the fixed-end influence integrals
with the end shears following from vertical and moment equilibrium of the
clamped span. PyCBA implements the closed-form results of these integrals (and
their point-load and applied-moment analogues) for each supported load type in
pycba.load — the standard fixed-end actions tabulated in, e.g.,
Roark’s Formulas and the
AISC beam diagrams. The supported types and the
fixed-end actions on a fixed–fixed span are:
Type |
Load |
Class |
Fixed-end actions (\(M_a\), \(M_b\)) |
|---|---|---|---|
1 |
UDL \(w\) |
\(M_a = \dfrac{wL^2}{12}\), \(\;M_b = -\dfrac{wL^2}{12}\) |
|
2 |
Point load \(P\) at \(a\) (with \(b=L-a\)) |
\(M_a = \dfrac{P a b^2}{L^2}\), \(\;M_b = -\dfrac{P a^2 b}{L^2}\) |
|
3 |
Partial UDL \(w\) over \([a, a+c]\) |
influence integrals over the loaded length |
|
4 |
Moment \(M\) at \(a\) (with \(b=L-a\)) |
\(M_a = \dfrac{Mb}{L^2}(2a - b)\), \(\;M_b = \dfrac{Ma}{L^2}(2b - a)\) |
|
5 |
Trapezoidal \(w_1 \to w_2\) over \([a, a+c]\) |
analytic evaluation of the influence integrals |
|
6 |
Imposed curvature \(\kappa(x)\) |
derived below |
The partial-UDL and trapezoidal loads are clipped to the span: any portion that extends beyond the member end is silently ignored. The full load-matrix format and column conventions are given on the Defining Beams page.
Released end forces (member releases)#
When the member is not fixed–fixed, the consistent nodal forces above must be
adjusted so that the moment at any released end is zero — otherwise the
clamped-end moment would be applied to a hinge. These adjusted forces are the
released end forces (get_ref). They are obtained by superimposing onto the
fixed–fixed consistent nodal loads a correction that exactly cancels the moment
at the released DOF, distributing its effect to the retained DOF — the
load-vector counterpart of the static condensation applied to the element
stiffness (pycba.load.Load.get_ref(); for a non-prismatic member the
equivalent flexibility-based reduction is
pycba.beam.Beam._ref_nonprismatic()). For example, releasing the \(j\)-end
(Type 2) carries the clamped moment \(M_b\) back to the \(i\)-end and into the end
shears via the \(3EI/L\) stiffness of the released element, giving the
simply-supported-equivalent fixed-end actions.
Assembly of the load vector#
The released end forces of every span are accumulated into the global force
vector \(\mathbf{f}\) (pycba.analysis.BeamAnalysis._forces()). The span
contribution is subtracted:
because the equivalent nodal loads applied to the structure are equal and opposite to the fixed-end reactions the clamped member exerts on the nodes. The resulting \(\mathbf{f}\) is the right-hand side of \(\mathbf{K}\,\mathbf{d} = \mathbf{f}\).
Imposed-curvature (initial-strain) loads#
An imposed-curvature (or initial-strain) load applies a free, stress-free curvature field along a member,
specified by its polynomial coefficients (load Type 6, added with
pycba.analysis.BeamAnalysis.add_ic()). It is the mechanism by which
PyCBA (and downstream time-dependent tools) apply creep, shrinkage and
thermal curvatures to a continuous beam
(Ghali, Favre & Elbadry, 2002, §13.7;
Elbadry & Ghali, 1989). The
implementation is pycba.load.LoadIC.
On a statically-determinate (simply-supported) span an imposed curvature induces no internal forces — only a free deflected shape (the curvature is taken up freely, e.g. a midspan deflection \(\kappa L^2/8\) for a uniform \(\kappa\)). On a restrained or continuous structure, however, the restraint of the free curvature generates real bending moments and reactions, exactly as for a temperature gradient or differential settlement.
The fixed-end forces use the same flexibility integration as the non-prismatic element. The primary (simply-supported) end rotations produced by the free curvature are
(the unit-moment diagrams), and the fixed-end moments follow from the moment–rotation stiffness,
with the balancing end shears \(V_a = (M_a + M_b)/L = -V_b\) from equilibrium of
the resulting couple (no transverse load is present). For a scalar \(EI\) the
closed-form \(\mathbf{K}_\theta = \dfrac{2EI}{L}\begin{bmatrix} 2 & 1 \\ 1 & 2
\end{bmatrix}\) is used; for a SectionEI member the
flexibility-integrated \(\mathbf{K}_\theta\) is used and the rotation integrals are
split at the section breakpoints, so an imposed curvature on a non-prismatic
member honours every \(EI\) kink/step exactly.
Two reference cases fix ideas (both reproduced by PyCBA to machine precision):
a uniform curvature \(\kappa\) on a prismatic fixed–fixed member gives a constant restraint moment \(M = EI\kappa\) throughout;
two equal continuous spans, each with uniform \(\kappa\), give an interior-support moment \(-1.5\,EI\kappa\), fully self-equilibrating (zero net reaction).
These are the classic imposed-deformation restraint results of Ghali, Favre & Elbadry (2002, §13.7).
Stability / mechanism detection#
If the restraints leave the structure under-supported, or an internal hinge is
over-released, the structure is a mechanism: the stiffness matrix is singular
and the solution is meaningless (enormous, spurious displacements). Before
solving, pycba.analysis.BeamAnalysis.analyze() performs a stability check
(_check_stability()) and raises a clear
ValueError rather than returning nonsense.
The check operates on the free-DOF partition of the unrestricted global stiffness matrix (including any spring terms). Let \(\mathcal{F}\) be the set of DOF that are neither fully fixed (\(R_i < 0\)) nor carry a prescribed displacement; spring DOF remain free and contribute their stiffness. The partition \(\mathbf{K}_{\mathcal{F}\mathcal{F}}\) governs the unknown displacements. (The free partition of the unrestricted matrix is used deliberately: direct elimination places \(1.0\) on the diagonal of constrained DOF, which would pollute the condition number of the reduced system.)
For a stable linear-elastic structure \(\mathbf{K}_{\mathcal{F}\mathcal{F}}\)
is symmetric positive-definite
(Bathe, 2014); a mechanism makes it singular —
its smallest eigenvalue collapses to zero relative to the largest. PyCBA
forms the symmetric eigenvalues \(\lambda\) of
\(\mathbf{K}_{\mathcal{F}\mathcal{F}}\) (numpy.linalg.eigvalsh) and compares the
reciprocal condition number
against a floor of \(10^{-12}\) (Golub & Van Loan, 2013). A value below the floor (or a zero maximum eigenvalue) signals a mechanism. The floor is chosen so that a genuine mechanism — whose null eigenvalue sits near machine epsilon (\(\sim 10^{-16}\)) — is separated with margin from a legitimately flexible but stable structure.
The check runs at most once per structure: its result is cached against the
beam’s structure_version and re-evaluated only if the geometry, rigidity,
restraints or prescribed displacements change. It therefore adds no cost to
looped analyses that vary only the loads (e.g. a moving-load run). The same logic
is exposed without solving via pycba.analysis.BeamAnalysis.is_stable()
(returns a bool), and can be skipped with analyze(check_stability=False) for
an intentionally near-singular model. As a final safeguard, the linear solve
itself (_solver()) traps a singular matrix and
raises the same instability error.
Boundary conditions#
With \(\mathbf{K}\) and \(\mathbf{f}\) assembled, the governing system is
Boundary conditions are imposed by the direct elimination method
(pycba.analysis.BeamAnalysis._apply_bc();
Cook et al., 2001). A DOF \(i\) is eliminated
whenever its displacement is known — either because an explicit value
\(\bar{d}_i\) has been prescribed in the displacement vector \(\mathbf{D}\), or
because the DOF is fully fixed (\(R_i = -1\)) with no override, in which case
\(\bar{d}_i = 0\). For each such DOF:
Transfer the constraint’s contribution to the right-hand side of every other equation, by subtracting the full \(i\)-th column scaled by \(\bar{d}_i\):
\[ f_j \;\leftarrow\; f_j - K_{ji}\,\bar{d}_i \qquad \forall\, j. \]Zero the \(i\)-th row and column:
\[ K_{ij} = K_{ji} = 0 \qquad \forall\, j. \]Set the diagonal to unity and the right-hand side to the prescribed value:
\[ K_{ii} = 1, \qquad f_i = \bar{d}_i. \]
This preserves symmetry and reproduces the prescribed displacement exactly in the solution (\(d_i = \bar{d}_i\)), while correctly transferring the constraint reaction into the remaining free equations.
A fixed support is the special case \(\bar{d}_i = 0\).
A support settlement is modelled simply by providing a non-zero \(\bar{d}_i\) at the support DOF (negative = downward). Prescribed displacements may also be applied to otherwise-free DOF.
Spring DOF (\(R_i > 0\)) without a prescribed displacement are not eliminated — their stiffness is already on the diagonal and they remain free unknowns.
Note
A DOF cannot simultaneously carry a spring (\(R_i > 0\)), a prescribed displacement
and a non-zero consistent nodal load: the elimination would set
\(f_i = \bar{d}_i\) and silently discard the load. PyCBA validates against this
inconsistent combination up front (pycba.analysis.BeamAnalysis._validate())
and raises a ValueError.
Solution and reaction recovery#
The restricted system is solved for the nodal displacements with a direct dense
solver, \(\mathbf{d} = \mathbf{K}^{-1}\mathbf{f}\) (numpy.linalg.solve,
Harris et al., 2020).
Support reactions are recovered using the unrestricted stiffness matrix
\(\mathbf{K}_U\) (assembled before boundary conditions, including spring terms) and
the original force vector \(\mathbf{f}_U\)
(pycba.analysis.BeamAnalysis._reactions()). The nodal residual is
At a fully-fixed DOF \(i\) (\(R_i = -1\)) the displacement is zero (or the prescribed settlement) and the residual equals the support reaction directly:
returned in beam_results.R, with the upward-positive sign convention.
For a spring DOF the residual contains structural coupling from neighbouring
DOF and any applied nodal load, so it is not the spring force alone. The spring
force is therefore computed explicitly from the spring displacement and reported
separately in beam_results.Rs:
with the sign chosen so that an upward spring reaction is positive when the displacement is downward (negative).
Member results: moment, shear, rotation and deflection#
Once the nodal displacements are known, the load effects are recovered along each
member at \(n_{\text{pts}}\) stations (default 100;
pycba.results.BeamResults._member_values()). The bending moment and
shear distributions are obtained from exact analytical expressions:
superposing the member-end-moment effect (from the solved nodal moments) with the
simply-supported member results of each applied load (the closed-form get_mbr_results
of each load class in pycba.load). These are exact because the
inter-nodal load distributions are known in closed form.
The rotation and deflection are then obtained by integrating the curvature, because the cubic Hermite shape functions are not valid in the presence of inter-nodal loading. The total curvature is the flexural curvature plus any free (imposed) curvature:
with \(EI(x)\) evaluated point-wise for a non-prismatic member (constant for the prismatic path). The rotation and deflection follow from successive integration:
evaluated numerically by the cumulative trapezoidal rule
using scipy.integrate.cumulative_trapezoid
(Virtanen et al., 2020) over the member stations.
The integration constants \(\theta_0\) and \(\delta_0\) are the rotation and
deflection at the \(i\)-node. The deflection constant is the nodal DOF,
\(\delta_0 = v_i\). For a fixed–fixed element the start rotation is also the
nodal DOF, \(\theta_0 = \theta_i\). For an element with a release (types 2–4)
the rotation at a pinned \(i\)-end is not a primary unknown and must be recovered.
In the prismatic case PyCBA uses the slope-deflection relation
where the \(\mathrm{FEM}\) are the consistent nodal-load moments due to the inter-nodal loads and the \(M_i, M_j\) are the solved member-end moments. For a non-prismatic member, or whenever an imposed curvature is present, \(\theta_0\) is instead recovered from the kinematic boundary condition \(\delta(L) = v_j\), which is valid for any \(EI(x)\) and curvature field.
For a Timoshenko member the integrated quantity \(\theta(x)\) is the section rotation \(\psi\) (from the bending curvature, as above), and the deflection integrates the axis slope \(\psi + \gamma\) with the shear strain \(\gamma = V/GA_v\), so \(\delta\) carries the additional shear deflection \(\int V/GA_v\). Because the closed-form release correction is Euler–Bernoulli only, a released Timoshenko member uses the same kinematic boundary-condition recovery as the non-prismatic case. For \(GA_v \to \infty\) the shear slope vanishes and the recovery is identical to Euler–Bernoulli.
Linear superposition, load cases and patterning#
Because the analysis is linear elastic, responses to different load
arrangements superpose. PyCBA exposes this directly: load matrices can be added
and factored (pycba.load.add_LM(), pycba.load.factor_LM()), and the
higher-level LoadCases / LoadCombination workflow assembles a response matrix
of basis cases and forms arbitrary weighted combinations and envelopes (see the
Defining Beams page).
Segmented load patterning#
For a distributed live load that may act on selected parts of the beam, a
continuous UDL is discretised into short partial-UDL segments, each analysed
as a basis LoadCase over the whole beam. For a chosen target effect (e.g.
bending moment at an internal support, at target coordinate \(x_t\)) the basis
analyses give a response vector
where \(r_i(x_t)\) is the contribution of segment \(i\). A loading arrangement is a
LoadCombination with \(\{0,1\}\) factors \(\alpha_i\) on the basis cases. When the
segments may be selected independently, the adverse arrangement is a simple
sign selection — include every segment whose contribution worsens the target
effect:
with the inequality reversed for a maximum effect. This produces one physical loading arrangement for that target.
If a loading rule instead requires a single contiguous loaded length, the ordered segment responses are searched for the contiguous block with the largest adverse sum — a Kadane maximum-subarray step, with recurrence
(sign-reversed for a minimum effect). The indices of the governing block define
the non-zero factors of the LoadCombination. A target combination such as
“hogging at support 1” is therefore one arrangement, whereas a response
envelope over many target stations is an envelope of (possibly different)
arrangements.
Free-vibration (modal) analysis#
Beyond static analysis, PyCBA provides the natural frequencies and mode shapes
of the beam. A consistent mass matrix is assembled alongside the stiffness
matrix; for a prismatic Euler–Bernoulli element of mass per unit length
\(\bar m\) it is, from the same cubic Hermite shape functions used for the
stiffness,
The natural circular frequencies \(\omega\) and mode shapes \(\boldsymbol\phi\) are the solutions of the generalized eigenproblem
on the free (unrestrained) degrees of freedom, with elastic spring supports contributing to \(\mathbf{K}\). Because a single element per span resolves only the first mode or two, each span is refined into several Euler–Bernoulli sub-elements for the eigenanalysis; the lowest frequencies then match the classical analytic results (simply-supported \(\omega_n = (n\pi/L)^2\sqrt{EI/\bar m}\), cantilever, fixed-fixed) to well under a percent.
Note
Modal analysis currently supports prismatic, fixed-fixed spans without shear
flexibility (GAv); other combinations raise a clear error.
Nonlinear analysis — Generalized Clough model#
Beyond linear elastic analysis, PyCBA provides an incremental nonlinear
(elasto-plastic) analysis that tracks plastic-hinge formation and moment
redistribution up to collapse. It uses concentrated plasticity: nonlinear
behaviour is localised at element ends (potential hinge locations), while the
element interior remains elastic
(Clough & Johnston, 1966;
Li & Li, 2007, Ch. 4;
Neal, 1977).
Concentrated plasticity via stiffness degradation#
The Generalized Clough model (Clough & Johnston, 1966) introduces a stiffness-reduction parameter \(R\) at each element end, varying between \(R = 1\) (fully elastic) and \(R = q\) (fully plastic hinge), where \(q\) is the strain-hardening ratio (\(q = 0\) for elastic–perfectly-plastic). The transition is governed by the normalised moment ratio \(\gamma = |M|/M_p\):
where \(\gamma_y = M_y/M_p\) is the normalised yield moment. This produces a bilinear moment–rotation response at the section. On unloading (\(\gamma\) decreasing below the historical peak), \(R\) resets to \(1\) — the Clough “origin-oriented” unloading rule.
Element stiffness with degradation#
For an element with left- and right-end parameters \(R_1\) and \(R_2\), the element stiffness is interpolated between the fixed–fixed (\(\mathbf{k}_e\)), pinned–fixed (\(\mathbf{k}_1\)) and fixed–pinned (\(\mathbf{k}_2\)) matrices (Li & Li, 2007, Ch. 4). If \(R_1 \ge R_2\),
and if \(R_1 < R_2\),
The stiffness degrades smoothly as either end yields, and reduces to the elastic matrix when \(R_1 = R_2 = 1\).
Hinge ownership#
At a node shared by two elements, each plastic hinge is owned by a single element end — the one that reached plasticity first — while the adjacent element retains \(R = 1\) at that node. If both ends at a node were simultaneously degraded the global stiffness would become singular prematurely, terminating the analysis before the true collapse mechanism forms. For an interior node \(j\) the hinge is assigned to element \(j-1\) (its right end), which keeps the global stiffness non-singular during the progressive formation of hinges.
Incremental analysis (static)#
The proportional-load analysis proceeds incrementally:
Initialise: all \(R = 1\), load factor \(\lambda = 0\), moments \(\mathbf{M} = \mathbf{0}\).
Increment: choose a step \(\Delta\lambda\) (adaptive — smaller when any \(R\) is low, i.e. near plasticity).
Solve the current tangent system \(\mathbf{K}\,\Delta\mathbf{u} = \Delta\lambda\,\mathbf{f}_{\text{ref}}\), where \(\mathbf{f}_{\text{ref}}\) is the reference load vector.
Update moments: \(\mathbf{M} \leftarrow \mathbf{M} + \Delta\mathbf{M}\).
Update \(R\): recompute \(\gamma\) at each node; degrade \(R\) if \(\gamma\) exceeds its historical peak, or reset \(R = 1\) on unloading.
Check for collapse when a new hinge forms (see below).
Repeat until collapse or \(\lambda_{\max}\).
Moving-load analysis#
For a vehicle traversing the beam the load position changes at each step, and the
load must be transferred from the old position to the new one without spurious
plastic deformation. PyCBA uses paired elastic-unload / nonlinear-reload
sub-increments at each position: unload the previous position’s load fraction
using the elastic stiffness (unloading is always elastic in the Clough model),
then reload the new position’s load fraction using the current (degraded)
stiffness. Each position step is divided into \(n_{\text{sub}}\) sub-increments for
accuracy. This correctly accumulates plastic damage as the vehicle traverses
(McCarthy, 2012;
Caprani, 2006).
Collapse detection#
A collapse mechanism requires enough plastic hinges to render the structure
kinematically unstable. PyCBA detects this with a rank test: nearby hinged
nodes are first clustered (within \(2 h_{\min}\), where \(h_{\min}\) is the smallest
element length) to absorb mesh discretisation; at each clustered hinge \(R = 0\) is
set at both element ends, fully releasing the node as in a true mechanism; the
test stiffness \(\mathbf{K}_{\text{test}}\) is assembled with the boundary
conditions applied; and if
\(\operatorname{rank}(\mathbf{K}_{\text{test}}) < n_{\text{dof}}\) the structure is
a mechanism and collapse has occurred. This direct kinematic test is more robust
than monitoring determinant magnitude or displacement growth.
Mesh considerations#
The continuous beam is internally meshed into short elements of a target length
(mesh_size). Point loads at mesh nodes are represented exactly; loads between
nodes are distributed to adjacent nodes by Hermite interpolation, and UDL is
lumped to nodal forces. Plastic hinges can form only at mesh nodes, so a hinge
whose theoretical location (e.g. \(x^{*} = 0.414\,L\) for a propped cantilever
under UDL) falls between nodes “snaps” to the nearest node — a discretisation
error that is not monotonic with refinement. Point-load problems whose load and
hinge locations coincide with nodes converge rapidly; UDL problems typically show
errors of a few percent, reducible by placing nodes near the expected hinge
locations.
The nonlinear analysis is demonstrated in the nonlinear tutorial; worked linear examples are in the Tutorials, and all the source texts cited above — for the matrix stiffness method, the consistent nodal loads, the non-prismatic and imposed-curvature formulations, the stability check, and the plastic-hinge model — are collected with hyperlinks on the References page.