Overview

analyzeFrame takes nodes, members, supports, the load cases (dead, superimposed dead, live, wind…) and the load combinations built from them. It analyses every load case, forms every combination by superposition, and returns displacements, reactions, member end forces and force diagrams for each, plus an envelope over the combinations. Section properties come from rectBeamToSectionProperties or are supplied directly, so the module itself does not depend on the RC code.

ℹ️ This is a first-principles structural analysis, not a code provision, so its exports carry a Matrix analysis badge. Only the stiffness modifiers in rectBeamToSectionProperties come from ACI 318-19. Load factors are entered by the user; the module does not generate code combinations.
Basis
QuantityBasisCode or adaptation
Element stiffnessEuler–Bernoulli prismatic beam, 12 DOF; no shear deformationAdaptation (standard matrix analysis)
Member loadsExact fixed-end forces from the beam shape functionsAdaptation
SolutionLinear, small displacements; no P-Δ, no crackingAdaptation
Load combinationsLinear superposition of load-case results, factors entered by the userAdaptation; valid because the analysis is linear
Stiffness modifiers (0.35 Ig beams, 0.70 Ig columns)ACI 318-19, 6.6.3.1.1Code, applied in rectBeamToSectionProperties
Concrete unit weight (24 kN/m³ default)Common design practiceNot a code value; override with unitWeight
Model
FrameModel
Nodes, members, restraints, load cases, the loads tagged with their case, and combinations.
LoadCase
One independently analysed set of loads: an id, a label and a self-weight multiplier.
LoadCombination
Factored sum of load cases and other combinations.
SectionProperties
E, G, A, I22, I33, J and optional unitWeight of a prismatic member.
FrameResult
Per-case and per-combination results keyed by id, and the envelope.
MemberDemandEnvelope
Mu⁺, Mu⁻, Vu and Nu for design checks, with the governing combination.
Axes & conventions
TopicConvention
Global axesRight-handed X, Y, Z with Z up. DOFs: UX, UY, UZ, RX, RY, RZ.
Local axesETABS-style 1, 2, 3. Local 1 runs from the start node to the end node. DOFs: U1, U2, U3, R1, R2, R3.
Non-vertical memberLocal 3 = (1 × Z) normalised, horizontal; local 2 = 3 × 1, in the vertical plane pointing up. A member along +X has local 2 = +Z and local 3 = −Y.
Vertical memberLocal 2 = +X, local 3 = 1 × 2. A column along +Z has local 3 = +Y.
Member angleRotates local 2 and 3 about local 1 by the right-hand rule, in rad.
Section axesI33 is about local 3 and governs bending in the 1–2 plane (U2–R3, the major axis); I22 is about local 2 and governs the 1–3 plane (U3–R2). A rectangle with depth h along local 2: I33 = b·h³/12.
RotationsAll results use the right-hand rule. The reference stiffness matrix measures R2 and R3 the other way; analyzeFrame converts internally.
UnitsCoordinates, lengths and positions in mm; E, G in MPa; A in mm²; I, J in mm⁴; loads in kN, kN·m and kN/m; springs in kN/mm and kN·m/rad; unit weight in kN/m³. Results in kN, kN·m, mm and rad.
Self-weightActs along global −Z (gravity, with Z up), as selfWeight · unitWeight · A per unit length of member.
Internal forcesForces and moments acting on the positive (+1) face of a cut, along the positive local axes. N is positive in tension; M3 is positive for sagging when local 2 points up; V2 = −dM3/dx.
Member displacementsu1, u2, u3 along the local axes and ux, uy, uz along the global axes, in mm, including the member's rigid-body motion. Bending follows u2″ = M3/EI33 and u3″ = −M2/EI22.
ℹ️ Member loads in a global direction ("X", "Y", "Z") are per unit length of member, not per unit projected length.
Load cases & combinations

Every load sits in a load case. A case is analysed on its own, so its results show what that load alone does. A load combination is a factored sum of cases (and of other combinations), for example 1.2D + 1.6L. Load factors are always entered by the user.

1
Define the load cases with a string id such as "D", "SD", "L". The optional type ("dead", "superDead", "live", "roofLive", "snow", "wind", "seismic", "other") is a label only and does not change the analysis.
2
List the loads in the model's top-level nodalLoads and memberLoads, each tagged with the loadCase it belongs to. Keeping them out of loadCases keeps the case list short in a large model, and the loads stay flat tables that can come straight from a spreadsheet or database. Their order does not matter.
3
Add self-weight to a case with selfWeight: 1. Every member then carries selfWeight · unitWeight · A per unit length along global −Z, so each member's section needs a unitWeight (rectBeamToSectionProperties sets 24 kN/m³). Use unitWeight: 0 for a weightless member.
4
Define the combinations as lists of terms, each { case, factor } or { combination, factor }. A combination may reference combinations listed after it; loops are rejected (310). Factors may be negative, e.g. 0.9D − 1.0W for reversed wind.
5
Read the results by id: result.cases.D, result.combinations["1.2D+1.6L"], and result.envelope for the max/min over all combinations. Case and combination ids share one namespace and must be unique across both lists.
ℹ️ A combination's force diagram is rebuilt from the combined end forces and the factored member loads of all its cases, so its extremes are exact. They are not sums of case extremes, which would overestimate a peak whose position differs between cases.
ℹ️ Pattern live loading (checkerboard spans) needs no special input: define one live case per span and combine them.
Loads

Three kinds of load can be applied. Nodal and member loads are listed in the model's nodalLoads and memberLoads, each tagged with its loadCase; self-weight is switched on per load case. Several loads on the same node or member in the same case simply add up.

LoadWhereUnitApplied as
Nodal force / momentnodalLoadskN, kN·mfx, fy, fz along and mx, my, mz about the global axes, at any node (supported nodes included). Omitted components are 0
Uniform member loadmemberLoads, type: "udl"kN/mw per unit length of member, over the whole member or from start to end (mm from the member start) for a partial load
Point member loadmemberLoads, type: "point"kNP at distance a (mm) from the member start
Self-weightloadCases[].selfWeight—A uniform load selfWeight · unitWeight · A along global −Z on every member; see Load cases
Member load direction and sign
directionMeaning
"1"Along the member axis (local 1, from start to end node) — an axial load
"2", "3"Along local 2 or local 3 — a transverse load in that member's own axes. For a beam along +X, local 2 is +Z, so direction: "2", w: -10 acts downward
"X", "Y", "Z"Along a global axis. The load is resolved onto the member's local axes, so on an inclined member a vertical load produces both axial and transverse components

A positive w or P acts in the positive direction of the chosen axis. With Z up, gravity loads are negative "Z" (e.g. w: -10).

ℹ️ A global-direction load is taken per unit length of member, not per unit horizontal (projected) length. On a member inclined at θ to the horizontal, a load given per projected length (snow on a rafter, for example) must be entered as w · cos θ.
Example
TypeScript
nodalLoads: [
  // 25 kN horizontal and 3 kN·m about Z at node B, wind case
  { loadCase: "W", nodeId: "B", fx: 25, mz: 3 },  // kN, kN·m
],
memberLoads: [
  // full-length gravity load
  { loadCase: "SD", type: "udl", memberId: "AB", direction: "Z", w: -10 },  // kN/m
  // partial load from 1.0 m to 3.0 m along the member
  { loadCase: "L", type: "udl", memberId: "AB", direction: "Z", w: -15,
    start: 1000, end: 3000 },  // mm
  // 40 kN point load 2.2 m from the start, in the member's local 2 direction
  { loadCase: "L", type: "point", memberId: "BC", direction: "2", P: -40, a: 2200 },  // kN, mm
],
How member loads enter the analysis

Each member load is turned into the exact fixed-end forces of a prismatic member (from the beam shape functions), adjusted for any end releases, and applied to the end nodes as equivalent nodal loads. The member's force diagrams are then built from its end forces plus the loads themselves, so a point load shows a shear jump (both sides are reported) and a partial load changes slope exactly where it starts and ends.

⚠️ Throws FrameAnalysisError 308 if a point load lies outside 0 ≤ a ≤ L, or a uniform load does not satisfy 0 ≤ start < end ≤ L; 302 if a load names a node or member that is not in the model; 309 if its loadCase is not in loadCases.
Not supported yet
LoadWorkaround
Concentrated moment inside a memberSplit the member with a node at that point and apply a nodal moment
Triangular / trapezoidal (varying) loadApproximate with several partial uniform loads (start / end)
Load per projected lengthEnter w · cos θ per member length (see above)
Temperature load—
Imposed support settlement—
Member diagrams member-diagram.ts

Every load case and combination result has a memberDiagrams entry per member, with values along the member, not only at its ends, ready for plotting axial force, shear, torsion and bending-moment diagrams and the deflected shape.

Stations
StationWhy it is there
Evenly spaced pointsoptions.stations of them (default 21), ends included. Raise it for smoother curves
Load breakpointsEvery point load and every start and end of a partial uniform load, where a diagram changes slope or steps
Zero-shear pointsWhere V2 or V3 crosses zero, so the peak bending moment is always a station
Both sides of a point loadTwo stations share the same x: the value just before and just after the load, so a shear step plots vertically
Station fields
FieldTypeUnitDescription
xnumbermmDistance from the member's start node
N, V2, V3numberkNAxial force (tension positive) and shears along local 2 and 3
T, M2, M3numberkN·mTorsion and bending moments about local 2 and 3
u1, u2, u3numbermmDisplacement along local 1, 2, 3
ux, uy, uznumbermmThe same displacement in global axes, for drawing the deflected shape on the model

extremes gives the max and min of N, V2, V3, T, M2, M3, u1, u2 and u3, each as { value, x }, for example the largest deflection between the nodes and where it occurs.

Example

A single 6 m simply supported member along X, 10 kN/m downward, E = 200 000 MPa, I33 = 8·10⁷ mm⁴ (no node at midspan):

Result (excerpt)
memberDiagrams[0].stations // 21 stations, x = 0, 300, … 6000 mm
  { x: 0,    V2: -30, M3: 0,  u2: 0,         uz: 0,         … }
  { x: 3000, V2: 0,   M3: 45, u2: -10.54687, uz: -10.54687, … }  // kN, kN·m, mm

memberDiagrams[0].extremes.u2  // { max: { value: 0, x: 0 }, min: { value: -10.54687, x: 3000 } }

That is wL²/8 = 45 kN·m and 5wL⁴/384EI = 10.5469 mm at midspan, while both nodal displacements are 0.

Plotting on the model
1
Place each station at start + (x / length) · (end − start) along the member.
2
Offset it perpendicular to the member by the diagram value times a drawing scale: M3 and V2 along local 2, M2 and V3 along local 3. memberAxes returns those directions.
3
Choose the sign convention to draw. M3 is positive for sagging when local 2 points up, so it plots upward on a beam along X; to draw moments on the tension side, flip the sign. V2 is the force on the positive face, negative at the left support of a gravity-loaded beam; flip it for the textbook shear sign.
4
Deflected shape: add (ux, uy, uz) times a magnification factor to each station's position.
ℹ️ Values between stations follow the exact curves: under a uniform load the moment is a parabola and the deflection a quartic. Straight lines through the default 21 stations are close; the peak moment is always a station, and the deflection extremes are taken from a 200-interval grid. At a hinged member end the member's own rotation is used, so the curve kinks there correctly even though the node does not rotate.
Using in a frontend

The module is plain TypeScript with no runtime dependencies or Node.js APIs, and both the model and the result are plain JSON data, so it runs in a browser, in a Web Worker, or on a server.

1
Import from @theerapat-s28/ts-structural-eng-tools/fea. The package ships an ES-module build (import) for bundlers such as Vite, with "sideEffects": false so unused code is dropped, and a CommonJS build (require) for Node.js.
2
Validate while editing with validateFrameModel and mark each issue's details (node, member, load case, combination, field) in the UI.
3
Analyse in a Web Worker so the page stays responsive: post the model in, post the result out (structured-clone copies plain data).
4
Keep results small for large models with diagrams: "extremes", and keep the createFrameAnalysis object in the worker to answer "show this member's diagram" requests.
5
Show errors in place: a thrown FrameAnalysisError has code, details and issues; a 304 (unstable) names a node and DOF of the mechanism.
TypeScript — worker.ts
import { createFrameAnalysis, type FrameAnalysis } from "@theerapat-s28/ts-structural-eng-tools/fea";

let analysis: FrameAnalysis | undefined;
self.onmessage = ({ data }) => {
  try {
    if (data.type === "analyse") {
      analysis = createFrameAnalysis(data.model, { diagrams: "extremes" });
      self.postMessage({ type: "result", result: analysis.result });
    } else if (data.type === "diagram" && analysis) {
      self.postMessage({ type: "diagram", diagram: analysis.memberDiagram(data.resultId, data.memberId) });
    }
  } catch (e) {
    self.postMessage({ type: "error", code: e.code, message: e.message, issues: e.issues });
  }
};
Model size and run time

Measured on regular building grids (columns and two-way beams, 3 load cases including self-weight, 3 combinations) with Node.js on a development machine; browsers run the same code at a similar speed. Time includes validation, all cases, combinations, diagrams and the envelope.

NodesMembersDOFFactorisationFull runResult ("full")Run / result ("extremes")
601113608 ms0.1 s2.9 MB0.06 s / 0.8 MB
4861 1762 9160.1 s0.8 s31 MB0.6 s / 8 MB
8472 1305 0820.26 s1.5 s56 MB1.2 s / 15 MB
1 8724 93511 2321.5 s4.3 s131 MB4.0 s / 34 MB
3 92710 66023 5628.0 s15 s285 MB14 s / 73 MB
ℹ️ The skyline solver's work grows with the number of DOFs times the square of the band, which reverse Cuthill–McKee keeps near one floor of nodes. Interactive use is comfortable up to a few thousand nodes; beyond that, run analyses on demand rather than on every edit.
Analysis flow
1
Validate the model, load cases, loads and combinations with validateFrameModel (errors 301–303 and 305–314); every problem is collected, and the first is thrown with all of them in issues.
2
Build the system once: member local stiffness, end-release condensation and transformation T; number the free DOFs with the nodes in reverse Cuthill–McKee order (which narrows the band), assemble the stiffness in skyline (variable-band) storage, add spring stiffnesses and factorise by Cholesky once. A singular matrix throws 304, naming a node and DOF of the mechanism.
3
Solve each load case against that factorisation: group the loads by their loadCase, resolve member loads and self-weight into local fixed-end forces, form F = nodal loads − fixed-end forces, and recover displacements, reactions R = K·U − F and member end forces.
4
Combine: expand nested combinations into net case factors and superpose the unrounded case results; rebuild each combination's diagrams exactly.
5
Check and present: global force and moment equilibrium of each case and combination, rounding of presented values, and the envelope over all combinations.
Analysis analyze-frame.ts
FN analyzeFrame(model, options?) Public API Matrix analysis

Analyses a 3D frame for every load case, forms every load combination, and returns the results of each with an envelope over the combinations.

Parameters
ParamTypeUnitRequired?Description
modelFrameModel—RequiredNodes, members, restraints, at least one load case, and optional combinations; see FrameModel
options.stationsnumber—OptionalEvenly spaced stations per member diagram (default 21); load breakpoints and zero-shear points are added
options.diagrams"full" | "extremes"—Optional"full" (default) returns every member diagram with its stations. "extremes" keeps each diagram's extremes — so the envelope and memberDemandEnvelope still work — but leaves stations empty, about 4× smaller for large models. Get full diagrams on demand from createFrameAnalysis
Example
TypeScript
const { sectionProperties } = rectBeamToSectionProperties({ fc_: 28, b: 300, h: 600 });
// unitWeight defaults to 24 kN/m³ → self-weight 24 · 0.18 m² = 4.32 kN/m

const udl = (loadCase, memberId, w) =>
  ({ loadCase, type: "udl", memberId, direction: "Z", w });  // w in kN/m

const result = analyzeFrame({
  nodes: [
    { id: "A", x: 0,     y: 0, z: 0 },  // mm
    { id: "B", x: 5000,  y: 0, z: 0 },
    { id: "C", x: 10000, y: 0, z: 0 },
  ],
  members: [
    { id: "AB", startNodeId: "A", endNodeId: "B", section: sectionProperties },
    { id: "BC", startNodeId: "B", endNodeId: "C", section: sectionProperties },
  ],
  restraints: [
    { nodeId: "A", ux: true, rx: true, uy: true, uz: true },
    { nodeId: "B", uy: true, uz: true },
    { nodeId: "C", uy: true, uz: true },
  ],
  loadCases: [
    { id: "D",  type: "dead", selfWeight: 1 },
    { id: "SD", type: "superDead" },
    { id: "L",  type: "live" },
  ],
  memberLoads: [  // one flat list for every load case; each load names its case
    udl("SD", "AB", -10), udl("SD", "BC", -10),
    udl("L", "AB", -15), udl("L", "BC", -15),
  ],
  combinations: [
    { id: "DEAD", terms: [{ case: "D", factor: 1 }, { case: "SD", factor: 1 }] },
    { id: "1.4D", terms: [{ combination: "DEAD", factor: 1.4 }] },
    { id: "1.2D+1.6L", terms: [{ combination: "DEAD", factor: 1.2 }, { case: "L", factor: 1.6 }] },
  ],
});
Returns
Result (excerpt)
{
  cases: {
    D:  { reactions: [{ nodeId: "A", fz: 8.1 }, { nodeId: "B", fz: 27 }, { nodeId: "C", fz: 8.1 }], … },  // kN
    SD: { … },
    L:  { … },
  },
  combinations: {
    "1.2D+1.6L": {
      id: "1.2D+1.6L", kind: "combination",
      reactions: [{ nodeId: "A", fz: 77.22 }, { nodeId: "B", fz: 257.4 }, { nodeId: "C", fz: 77.22 }],
      nodalDisplacements: [{ nodeId: "A", ry: 0.0022817, /* rad */ … }, …],
      memberEndForces: [{ memberId: "AB",
        start: { f2: 77.22, m3: 0 }, end: { f2: 128.7, m3: -128.7 } }, …],  // kN, kN·m
      memberDiagrams: [{ memberId: "AB",
        extremes: { M3: { max: { value: 72.3938, x: 1875 }, min: { value: -128.7, x: 5000 } }, … },
        stations: […] }, …],
      equilibriumResidual: { force: 0, moment: 0 },
      warnings: []
    },
    "DEAD": { … }, "1.4D": { … }
  },
  envelope: {
    combinationIds: ["DEAD", "1.4D", "1.2D+1.6L"],
    reactions: […, { nodeId: "B", fz: { max: { value: 257.4, combinationId: "1.2D+1.6L" },
                                   min: { value: 89.5,  combinationId: "DEAD" } }, … }, …],
    members: [{ memberId: "AB", extremes: { M3: {
      max: { value: 72.3938, x: 1875, combinationId: "1.2D+1.6L" },
      min: { value: -128.7,  x: 5000, combinationId: "1.2D+1.6L" } }, … } }, …],
    nodalDisplacements: […]
  },
  calculationDetails: { nodeCount: 3, memberCount: 2, dofCount: 18, freeDofCount: 10, profileSize: 47,
                         loadCaseCount: 3, combinationCount: 3 },
  units: { force: "kN", moment: "kN·m", length: "mm", rotation: "rad" },
  warnings: []
}

The factored load is 1.2·(4.32 + 10) + 1.6·15 = 41.184 kN/m, and the results are the textbook two-span values: reactions 3wL/8, 10wL/8, 3wL/8, support moment wL²/8 = 128.7 kN·m and span moment 9wL²/128 = 72.3938 kN·m.

FieldTypeUnitDescription
casesRecord<string, FrameCaseResult>—Result of each load case, keyed by its id; see FrameCaseResult
combinationsRecord<string, FrameCaseResult>—Result of each load combination, keyed by its id; empty when none are defined
envelopeFrameEnvelope | undefined—Max/min over all combinations with the governing combination; absent when there are no combinations. See FrameEnvelope
calculationDetailsobject—Node, member, DOF, load case and combination counts, and profileSize — the stored entries of the banded stiffness factor, a measure of memory and solve work
unitsobject—Units of every result field
warningsWarnings—Every case and combination warning, each message prefixed with its load case or combination id
⚠️ Pushes a warning, with reference Frame analysis, global equilibrium check, for a load case or combination whose equilibrium residual exceeds 10⁻⁶ of its applied load — a sign of an ill-conditioned stiffness matrix. This is a solver check, not a code clause.
⚠️ Throws FrameAnalysisError 301–314 for an invalid model, load case or combination, or an unstable structure (304). The model is checked by validateFrameModel first; the error is the first problem found, its details say where it is, and issues lists every problem. See the error table.
FN createFrameAnalysis(model, options?) Public API Matrix analysis

Runs the same analysis as analyzeFrame and keeps the solution, so a member's full diagram can be computed later on demand without re-analysing. Analyse with diagrams: "extremes" for a small result, then draw a member's diagram when the user selects it. Keep the returned object inside the Web Worker that ran the analysis and post plain results to the UI.

Parameters

Same as analyzeFrame.

Example
TypeScript
const analysis = createFrameAnalysis(model, { diagrams: "extremes" });
analysis.result.cases.D.memberDiagrams[0].stations;  // [] — extremes only

// the user clicks member "m" while viewing load case "D"
const diagram = analysis.memberDiagram("D", "m");  // 21 stations, same as "full"
const fine = analysis.memberDiagram("D", "m", 51);  // more stations
Returns
FieldTypeUnitDescription
resultFrameResult—What analyzeFrame returns for the same model and options
memberDiagram(resultId, memberId, stations?)MemberDiagramkN, kN·m, mmThe full diagram (stations and extremes) of one member for one load case or combination, identical to the one a "full" analysis returns
⚠️ Throws like analyzeFrame (FrameAnalysisError 301–314). memberDiagram throws a plain Error for an unknown load case, combination or member id.
FN validateFrameModel(model) Public API

Checks a model without analysing it and returns every problem, each with the node, member, load case, combination and field it concerns — for marking all invalid inputs in a form at once. An empty array means the model can be analysed; the only problem it cannot see is an unstable structure (304), which the solve detects. analyzeFrame runs it first.

Parameters
ParamTypeUnitRequired?Description
modelFrameModel—RequiredThe model to check, including untyped input from a UI
Example
TypeScript
const issues = validateFrameModel({
  …
  loadCases: [{ id: "D" }],
  nodalLoads:  [{ loadCase: "D", nodeId: 2, fz: NaN }],                              // empty input box
  memberLoads: [{ loadCase: "L", type: "udl", memberId: "m", direction: "z", w: -5 }], // no case "L"; lower-case "z"
});
Returns
Result
[
  { code: 314, details: { loadCaseId: "D", nodeId: 2, field: "fz" },
    message: "An input value is invalid: … [node \"2\", load case \"D\", field \"fz\"]" },
  { code: 309, details: { loadCaseId: "L", memberId: "m", field: "loadCase" }, message: "…" },
  { code: 314, details: { loadCaseId: "L", memberId: "m", field: "direction" }, message: "…" }
]
FieldTypeUnitDescription
codenumber—The error code
messagestring—The code's message followed by its location in brackets
detailsFrameErrorDetails—{ nodeId?, memberId?, loadCaseId?, combinationId?, field?, dof? } — only the fields that apply
ℹ️ Numbers must be real, finite numbers: NaN, Infinity and numeric strings such as "10" are rejected (314), so convert form input with Number(...) and check it before analysing.
FN memberDemandEnvelope(result, memberId, options?) Public API Matrix analysis

Reduces a member's force diagrams to the design demands Mu⁺, Mu⁻, Vu and Nu, enveloped over load combinations (and optionally load cases), and names the combination that governs each. The demands are ready for rectBeamMomentCapacity and the shear functions; the function takes plain numbers and does not import the RC module.

Parameters
ParamTypeUnitRequired?Description
resultFrameResult—RequiredOutput of analyzeFrame
memberIdstring | number—RequiredMember to reduce
options.combinationsstring[]—OptionalCombination ids to envelope. Default: every combination, unless cases is given
options.casesstring[]—OptionalLoad case ids to envelope as well (or instead). Default: none
options.shearCriticalDistancenumbermmOptionalTake Vu only between this distance from each end, e.g. the effective depth d (default 0)
Example
TypeScript
const demand = memberDemandEnvelope(result, "AB", {
  shearCriticalDistance: 540,  // mm, d
});
Returns
Result
{
  memberId: "AB",
  MuPositive: 72.3938,  // kN·m
  MuNegative: 128.7,    // kN·m, magnitude
  Vu: 106.4606,         // kN, at d from the supports
  Nu: 0,                // kN, tension positive
  governing: { MuPositive: "1.2D+1.6L", MuNegative: "1.2D+1.6L", Vu: "1.2D+1.6L", Nu: null },
  VuRange: { from: 540, to: 4460 }  // mm
}
ℹ️ governing names the combination (or case) producing each demand, and is null when that demand is zero in every selected result. The sign of M3 depends on the member's local 2 axis: for a beam along X, local 2 is +Z, so sagging is positive and MuPositive is the span moment.
⚠️ Throws a plain Error if memberId or a requested combination or case id is not in the result, if nothing is selected (a result without combinations needs cases), or if shearCriticalDistance is used on a result analysed with diagrams: "extremes" (which has no stations to read Vu between). Without a critical distance it works on such a result, taking Vu from the shear extremes.
Element beam-3d-stiffness.ts · coordinate-transform.ts
FN beam3dLocalStiffness(E, G, A, I22, I33, J, L) Public API Matrix analysis

The 12×12 local stiffness matrix of a prismatic beam, exactly as in the reference "Complete Beam Stiffness Matrix" (DOF order [U1, U2, U3, R1, R2, R3] at node i, then j). Returned in N and mm.

Parameters
ParamTypeUnitRequired?Description
EnumberMPaRequiredYoung's modulus
GnumberMPaRequiredShear modulus
Anumbermm²RequiredCross-section area
I22numbermm⁴RequiredUsed by the U3–R2 terms
I33numbermm⁴RequiredUsed by the U2–R3 terms
Jnumbermm⁴RequiredTorsion constant
LnumbermmRequiredMember length
Returns

A symmetric number[][]. Its nonzero terms are ±EA/L, ±GJ/L, 12EI/L³, ±6EI/L², 4EI/L and 2EI/L as laid out in the reference; with I22 = I33 it equals the reference's single-EI matrix.

ℹ️ The reference matrix measures the bending rotations R2 and R3 opposite to the right-hand rule, so it is free of force for rigid-body motion only with that sign. Use beam3dLocalStiffnessRightHanded when combining with right-handed quantities. analyzeFrame does this internally.
FN beam3dLocalStiffnessRightHanded(E, G, A, I22, I33, J, L) Public API Matrix analysis

The same element as beam3dLocalStiffness with every rotation following the right-hand rule: D·k·D, where D flips the sign of R2 and R3 at both nodes. This is the standard textbook matrix (for example F2,i = +6EI33/L²·R3,i).

Parameters and units are identical to beam3dLocalStiffness.

FN memberAxes(start, end, angle?) Public API Matrix analysis

Returns the member length and the ETABS-style local axes (see Axes & conventions) as the rows of a 3×3 direction-cosine matrix.

Parameters
ParamTypeUnitRequired?Description
start[number, number, number]mmRequiredStart node X, Y, Z
end[number, number, number]mmRequiredEnd node X, Y, Z
anglenumberradOptionalRotation of local 2 and 3 about local 1 (default 0)
Example
TypeScript
const { length, lambda } = memberAxes([0, 0, 0], [5000, 0, 0]);  // mm
// length = 5000
// lambda = [[1, 0, 0],    local 1 = +X
//           [0, 0, 1],    local 2 = +Z
//           [0, -1, 0]]   local 3 = −Y
⚠️ Throws FrameAnalysisError 301 if the two nodes coincide.
Types
TYPE FrameModel Public API

Input of analyzeFrame. Node and member ids may be strings or numbers and must be unique within nodes, members and restraints.

FieldTypeUnitRequired?Description
nodesFrameNode[]mmRequired{ id, x, y, z }
membersMember[]—Required{ id, startNodeId, endNodeId, section, angle?, releases? }. angle in rad. releases: { start?, end? } hold flags u1, u2, u3, r1, r2, r3 for local end DOFs that carry no force (hinges, rollers)
restraintsRestraint[]kN/mm, kN·m/radRequired{ nodeId, ux, uy, uz, rx, ry, rz } in global axes. true fixes the DOF, a positive number is a spring stiffness, omitted is free. At least one DOF must be restrained
loadCasesLoadCase[]—RequiredAt least one; see LoadCase
nodalLoadsNodalLoad[]kN, kN·mOptional{ loadCase, nodeId, fx, fy, fz, mx, my, mz } along and about global axes; see NodalLoad
memberLoadsMemberLoad[]kN/m, kN, mmOptionalUniform: { loadCase, type: "udl", memberId, direction, w, start?, end? }. Point: { loadCase, type: "point", memberId, direction, P, a }. direction is "1" | "2" | "3" (local) or "X" | "Y" | "Z" (global); positions are measured from the member start in mm; see MemberLoad
combinationsLoadCombination[]—OptionalSee LoadCombination
TYPE LoadCase Public API

One independently analysed set of loads. The loads themselves are listed in the model's nodalLoads and memberLoads, each with loadCase set to this case's id.

FieldTypeUnitRequired?Description
idstring—RequiredUnique across load cases and combinations, e.g. "D"
namestring—OptionalDescription, copied to the result
typeLoadCaseType—Optional"dead" | "superDead" | "live" | "roofLive" | "snow" | "wind" | "seismic" | "other"; a label only
selfWeightnumber—OptionalSelf-weight multiplier (1 = full self-weight) along global −Z; default 0. Needs unitWeight on every member section
TYPE LoadCombination Public API

A linear combination: the sum of factored load cases and other combinations.

FieldTypeUnitRequired?Description
idstring—RequiredUnique across load cases and combinations, e.g. "1.2D+1.6L"
namestring—OptionalDescription, copied to the result
termsCombinationTerm[]—RequiredAt least one. Each term is { case: id, factor } or { combination: id, factor }; factors may be negative
TYPE NodalLoad Public API

A force and/or moment applied at a node, in global axes. See Loads.

FieldTypeUnitRequired?Description
loadCasestring—RequiredId of the load case this load belongs to
nodeIdstring | number—RequiredLoaded node
fx, fy, fznumberkNOptionalForce along global X, Y, Z (default 0)
mx, my, mznumberkN·mOptionalMoment about global X, Y, Z, right-hand rule (default 0)
TYPE MemberLoad = UniformMemberLoad | PointMemberLoad Public API

A load on a member, chosen by type. Positions are measured from the member's start node. See Loads.

FieldTypeUnitRequired?Description
loadCasestring—RequiredId of the load case this load belongs to
type"udl" | "point"—RequiredUniform or point load
memberIdstring | number—RequiredLoaded member
directionLoadDirection—Required"1" | "2" | "3" (local) or "X" | "Y" | "Z" (global)
wnumberkN/mRequired (udl)Load per unit member length, positive along the direction axis
start, endnumbermmOptional (udl)Loaded length for a partial load (default 0 and the member length)
PnumberkNRequired (point)Load, positive along the direction axis
anumbermmRequired (point)Distance of the load from the member start
TYPE FrameResult Public API

Output of analyzeFrame: cases, combinations, envelope, calculationDetails, units and warnings. The fields are described under analyzeFrame.

TYPE FrameCaseResult Public API

Results of one load case or one load combination; both have the same shape.

FieldTypeUnitDescription
id, kindstring, "case" | "combination"—Which load case or combination this is; name and (cases only) type are copied from the input
nodalDisplacementsNodalDisplacement[]mm, radux, uy, uz, rx, ry, rz per node (global axes)
reactionsNodalReaction[]kN, kN·mfx…mz per node; zero at unrestrained DOFs. A spring DOF reports the force the spring exerts on the node
memberEndForcesMemberEndForces[]kN, kN·mf1, f2, f3, m1, m2, m3 that the node exerts on each member end, local axes, right-hand
memberDiagramsMemberDiagram[]kN, kN·m, mm{ memberId, length, stations, extremes }: forces N, V2, V3, T, M2, M3 and displacements u1, u2, u3 (local) and ux, uy, uz (global) at stations along the member, and each component's { max, min } as { value, x }; see Member diagrams
equilibriumResidual{ force, moment }kN, kN·mGlobal equilibrium residual of applied loads plus reactions
warningsWarnings—Warnings of this case or combination
TYPE FrameEnvelope Public API

Max and min of every presented result over all combinations, each as { value, combinationId } (member extremes also carry x). When two combinations tie, the one listed first governs.

FieldTypeUnitDescription
combinationIdsstring[]—The enveloped combinations, in model order
nodalDisplacementsarraymm, radPer node: ux … rz, each { max, min }
reactionsarraykN, kN·mPer node: fx … mz, each { max, min }
membersarraykN, kN·m, mmPer member: extremes of N, V2, V3, T, M2, M3, u1, u2, u3, each { max, min } with position x
TYPE FrameAnalysis Public API

Returned by createFrameAnalysis: { result: FrameResult; memberDiagram(resultId, memberId, stations?): MemberDiagram }. Unlike the result it holds a function, so it stays in the thread (or worker) that created it.

TYPE SectionProperties Public API

Prismatic member properties. Produced for rectangular RC beams by rectBeamToSectionProperties, or supplied directly for any other section.

FieldTypeUnitRequired?Description
EnumberMPaRequiredYoung's modulus
GnumberMPaRequiredShear modulus
Anumbermm²RequiredArea
I22numbermm⁴RequiredAbout local 2 (bending in the 1–3 plane)
I33numbermm⁴RequiredAbout local 3 (bending in the 1–2 plane, major axis)
Jnumbermm⁴RequiredTorsion constant
unitWeightnumberkN/m³OptionalMaterial unit weight for load-case self-weight; required only when a case sets selfWeight. Must not be negative
FrameAnalysisError Codes

Thrown errors are instances of FrameAnalysisError (exported from @theerapat-s28/ts-structural-eng-tools/core, with the coded entries in FrameErrors). It mirrors RCDesignError and uses the 3XX range.

FieldTypeUnitDescription
codenumber—The code below
messagestring—Description, followed by the location in brackets when known, e.g. [member "m", load case "L", field "w"]
detailsFrameErrorDetails—{ nodeId?, memberId?, loadCaseId?, combinationId?, field?, dof? } — where the problem is, for highlighting it
issuesFrameModelIssue[]—Every problem validateFrameModel found; the error itself is the first
CodeNameMeaning
301ZERO_LENGTH_MEMBERA member's start and end nodes coincide.
302UNKNOWN_NODE_REFERENCEA member, restraint or load refers to a node or member id that is not in the model.
303DUPLICATE_IDNode ids, member ids or restrained node ids are not unique, or an id is used by more than one load case or combination.
304STRUCTURE_UNSTABLEThe restrained structure is a mechanism (singular stiffness matrix): add supports or members. details names a node and DOF involved in the mechanism.
305INVALID_SECTION_PROPERTIESE, G, A, I22, I33 or J is not a positive finite number, or unitWeight is negative.
306NO_RESTRAINTSNo DOF is fixed or sprung.
307INVALID_RELEASEThe end releases let the member move as a rigid body, for example axial or torsion released at both ends.
308LOAD_OUTSIDE_MEMBERA point load or a uniform load lies outside the member, or a uniform load's start is not before its end.
309UNKNOWN_LOAD_CASEA load's loadCase, or a combination term, refers to a load case or combination that is not in the model, or a term names neither.
310CIRCULAR_COMBINATIONLoad combinations reference each other in a loop.
311INVALID_LOAD_FACTORA combination factor or self-weight multiplier is not a finite number, or a combination has no terms.
312NO_LOAD_CASESThe model has no load cases.
313MISSING_UNIT_WEIGHTA load case requests self-weight but a member section has no unitWeight.
314INVALID_INPUT_VALUEA number is not finite (NaN, Infinity, or not a number at all, e.g. the string "10"), a spring stiffness is negative, or a value is not one of its allowed options (load direction, member load type, load case type).
Export Summary
KindExportReturnsDescription
FNanalyzeFrameFrameResult3D frame analysis for every load case and combination
FNcreateFrameAnalysisFrameAnalysisSame analysis, keeping the solution for on-demand member diagrams
FNvalidateFrameModelFrameModelIssue[]Every problem in a model, with its location, without analysing
FNmemberDemandEnvelope{ MuPositive, MuNegative, Vu, Nu, governing, … }Design demands of a member over combinations, with the governing one
FNbeam3dLocalStiffnessnumber[][]Reference 12×12 local stiffness
FNbeam3dLocalStiffnessRightHandednumber[][]Same matrix with right-hand rotations
FNmemberAxes{ length, lambda }ETABS-style local axes of a member
TYPEFrameModel—Analysis input (nodes, members, restraints, load cases, combinations)
TYPELoadCase—One set of loads, with optional self-weight
TYPELoadCombination—Factored sum of load cases and combinations
TYPENodalLoad—Force/moment at a node, global axes
TYPEMemberLoad—Uniform (full or partial) or point load on a member
TYPEFrameResult—Analysis output
TYPEFrameCaseResult—Results of one load case or combination
TYPEFrameEnvelope—Max/min over combinations with the governing combination
TYPEFrameAnalysis—Result plus on-demand memberDiagram
TYPESectionProperties—E, G, A, I22, I33, J, unitWeight (core)
ERRFrameAnalysisError, FrameErrors—Error class (code, message, details, issues) and coded entries 301–314 (core)