Typestate analysis composition: each stage exposes only the next
piece that must be wired, and only AnalysisBuilder<Ready> exposes
.build(). Illegal sequencing (e.g. calling .build() before an
algorithm is set) is a compile error, not a runtime setLinks ordering
bug like Xara’s BasicAnalysisBuilder.
Arc-length continuation settings (Integrator::ArcLength). Build with
ArcLength::fixed or ArcLength::adaptive and adjust fields from
there; every field is validated when the first step runs.
A user-supplied first direction, in physical node/DOF terms plus a load
component. Normalized in the metric and used to orient the first
bordered tangent (not as the predictor itself).
Optional stop criteria (docs/arclength.md §7). Any one that is met
ends the phase after the step that met it is accepted; later step()
calls return AnalysisError::ContinuationComplete.
Stop when node’s dof reaches value (in either direction). With
exact, the bracketing step is retaken with an interpolated radius so
the phase ends on the target.
Uniform ground-motion excitation in one direction — Xara/OpenSees’s
UniformExcitation. TransientAnalysis uses this to add the effective
inertial force -M·ι·ag(t) (standard formulation, e.g. Chopra,
“Dynamics of Structures”) to each Newmark step’s load, where ι is the
unit influence vector selecting every free DOF in direction
(Domain::direction_incidence) and ag(t) is this motion’s
acceleration at time t. An accelerogram is exactly a LoadSeries:: Path (piecewise-linear interpolation over recorded points) — no
separate series type needed; this reuses the same LoadSeries a static
LoadPattern would.
One computed mode: natural circular frequency (rad/time) and the
corresponding free-DOF mode shape, M-normalized (shape^T * M * shape = 1)
— the normalization modal damping needs to apply a per-mode
damping ratio consistently.
Rayleigh (mass- and stiffness-proportional) damping: C = alpha_m*M + beta_k*K.
A concrete struct, not an enum — Rayleigh damping is the only form provided; other forms (e.g. explicit modal damping ratios) would be a
distinct, separate mechanism, not another variant of this one.
An LU factorization held independently of the matrix it was formed
from — cached across Newton iterations by TangentStrategy:: ReuseAtStepStart/Initial so re-solving against the same tangent
doesn’t re-factor it (docs/algorithms.md §3.1’s real prerequisite for
those strategies to actually save anything).
Time-history (transient) analysis via Newmark-beta integration — a
distinct type from Analysis, not a third Integrator/Algorithm
variant squeezed into it: static analysis is about a scalar load factor
and (optionally) Newton-iterating displacement at a fixed time, dynamic
analysis is about propagating a very different state (displacement,
velocity, acceleration) forward through actual time. Trying to unify
them into one Analysis/AnalysisBuilder would blur two genuinely
different physical processes for no real benefit — same reasoning as
modal_analysis being a free function rather than an Analysis mode.
Iteration strategy for resolving each step’s equilibrium. Closed enum
. docs/algorithms.md designs Newton/KrylovNewton as three
composable axes (tangent strategy, line search, Krylov acceleration)
rather than a combinatorial pile of variants — see that doc for the
full design and Xara reference map.
Static/pseudo-static integration strategy. Closed enum.
LoadControl/DisplacementControl only change how this step’s load
factor is chosen (the “predictor”) — the iteration that follows
(see Algorithm) proceeds identically regardless of which one picked it.
ArcLength is different in kind: it predicts displacement and load
factor and corrects both together through a bordered system, so
Analysis::step hands it to its own continuation driver instead of
predict/correct (see arclength.rs and docs/arclength.md).
Post-processes a raw Newton correction to actually reduce the residual,
rather than accepting it outright. Closed enum. Ported from
Xara’s SRC/analysis/algorithm/equiSolnAlgo/search/ (docs/ algorithms.md §4): both variants bracket a sign change in the 1-D
“energy” s(eta) = du . residual(eta) by geometric expansion of eta,
then refine within the bracket — bisection, or false-position
(RegulaFalsi, usually fewer iterations than bisection once bracketed).
Only these two for now — Secant/InitialInterpolatedLineSearch can
diverge without a bracket and aren’t needed until a specific model
demands the extra speed (docs/algorithms.md §4).
Which tangent stiffness an Algorithm::Newton/KrylovNewton iteration
solves against. Closed enum — mirrors Xara’s TangentFlagType
(CURRENT_TANGENT / INITIAL_TANGENT / PREDICTOR_TANGENT), which
every Newton-family algorithm class there takes as a constructor
argument rather than hard-coding (docs/algorithms.md §3).
A shift-invert Lanczos eigensolver (shift = 0, so the operator is simply
K^-1 * M), not a dense full-spectrum nalgebra::SymmetricEigen and
not an ARPACK FFI binding either. Two things made the earlier dense
choice wrong beyond just performance, once SparseSolver itself went
sparse: