← AI-pair numerics

Truss bridges

Pick a bridge style, span it, load it. The solver propagates the forces through every member — tension blue, compression red. Doing this by hand is an hour of method-of-joints; here it's instant.

1. Setup

A truss is a frame of straight two-force members pinned at the joints. Each member carries only an axial force — pure tension or pure compression, no bending. Given a geometry, a set of supports, and a load, you want the axial force in every member and the deflected shape.

By hand, the tool is the method of joints: isolate a joint, write ΣFx = 0 and ΣFy = 0, solve for the two unknown member forces, march to the next joint. It works, but the bookkeeping explodes — an n-bay bridge has roughly 4n members and you're hand-propagating dependencies across all of them. Three bays and you've lost an afternoon to arithmetic.

2. Why this is an afternoon, not a week

The direct stiffness method turns the whole truss into one linear system K u = f. Each member contributes a 4×4 element stiffness (a one-liner in terms of its direction cosines); you scatter them into a global matrix, fix the support DOFs, solve once, and read every member force off the displacement field. The machinery is identical whether the truss has 5 members or 500 — that's the leverage. Your brain picks the geometry and the load; the solver propagates.

3. Build it

Start with a simple parallel-chord Pratt truss, 4 bays, pin support at one end and roller at the other, a single point load at midspan. With your AI pair: write the bar-element stiffness in global coordinates, assemble, apply the supports, solve K u = f, recover member forces from F = (EA/L)(uj − ui)·(c, s). Then let the geometry generator loose — cycle styles and bay counts and watch the force distribution reorganize.

Spec the node/member topology, the DOF numbering convention, and the support handling with your AI pair. Have it draft. Your job is to specify, audit, verify.

4. Verification

Live below. Pick a style, set the span and depth, choose a load. Members color by sign of axial force; thickness scales with magnitude. Toggle the deflected shape to see how it sags.

Style:
Load:
tension compression ~zero force
max tension: max compression: max deflection:
Loading WASM…

Switch Pratt ↔ Howe under the center load and watch the colors flip: in a Pratt truss the diagonals run in tension (blue), in a Howe truss they run in compression (red). That's the whole reason Pratt trusses dominate steel construction — slender steel members are happy in tension, prone to buckling in compression. The solver reproduces a design rule you can find in any structures text.

Rung 1 — analytical anchor. For a symmetric parallel-chord truss under a center load, method of joints gives the end-diagonal and chord forces by hand in a couple of minutes. Check one or two joints against the solver.
Pass: hand-computed joint forces match to your arithmetic precision.
Fail diagnosis: your element stiffness direction cosines or your DOF mapping is wrong.
Rung 2 — global equilibrium & symmetry. Total upward reaction equals total downward load. A symmetric truss under symmetric load gives mirror-symmetric member forces.
Pass: reactions sum to the load; symmetric pairs match.
Fail diagnosis: assembly or load-vector error.
Rung 3 — the singular case is informative. Remove enough members and the stiffness matrix goes singular — the truss is a mechanism, it can move without straining. A good solver should refuse to "solve" a mechanism rather than return garbage. (Try mentally removing a diagonal: which joint floats free?)
Pass: the solver flags the singular system instead of returning numbers.

5. Hints

Hint 1 — frame it

Two DOFs per joint (horizontal + vertical displacement). Each member is a spring along its own axis with stiffness EA/L. The trick is rotating that 1D spring into global x–y coordinates using the member's direction cosines c = cosθ, s = sinθ.

Hint 2 — the element matrix

The 4×4 global-coordinate bar stiffness is

k_e = (EA/L) · ⎡  c²   cs  -c²  -cs ⎤
               ⎢  cs   s²  -cs  -s² ⎥
               ⎢ -c²  -cs   c²   cs ⎥
               ⎣ -cs  -s²   cs   s² ⎦

acting on [uix, uiy, ujx, ujy]. Scatter into the global matrix by the four DOF indices of the member's two end nodes.

Hint 3 — the gotcha (statical determinacy)

A 2D truss is stable and statically determinate when m + r = 2j (members + support reactions = twice the joints). Too few members and your stiffness matrix is singular — a mechanism. Don't fight a failed solve; it's telling you the structure can't carry load. Standard support set: a pin (2 reactions) at one end, a roller (1 reaction) at the other.

Hint 4 — skeleton
def bar_k(xi, yi, xj, yj):     # 4x4 global stiffness
    L = hypot(xj-xi, yj-yi); c=(xj-xi)/L; s=(yj-yi)/L
    ...
def assemble(nodes, members): ...   # scatter into 2N x 2N
def apply_supports(K, f, fixed): ...
def solve(K, f): return np.linalg.solve(K, f)
def member_force(u, member): ...    # (EA/L)(u_j - u_i)·(c,s)

6. Where to draw the line

Method of joints is exact and needs no computer for a handful of members — and it's the right tool for checking your solver on a small case. But the moment you want to sweep (every bay count from 4 to 12, three load patterns, four styles) the hand method is hopeless and the matrix solver is trivial. The judgement: hand-verify the small case, then trust the solver for the sweep. Don't hand-roll the linear solver either — numpy.linalg.solve (or the SPD-aware scipy.linalg.cho_solve) is the right call.

7. One worked solution

Reveal the reference solver

The reference solver is the same Rust crate driving the panel above — function solve_truss in solver/src/lib.rs. The bridge geometry generators (Pratt / Howe / Warren / K) live in this page's JavaScript, since topology bookkeeping is easier to tweak there; the Rust side is the pure linear-algebra core.

Note: the reference uses EA = 1 for every member, so the deflection numbers are dimensionless and the force magnitudes are relative. Real design plugs in actual section areas; the machinery is unchanged.

8. Going further

For the dynamic cousin of all this: Beam vibration →