Skip to content

PRO — 11/18: calculation fidelity - #233

Merged
diegokingston merged 13 commits into
mainfrom
feat/pro-11-fidelity
Sep 30, 2026
Merged

diegokingston merged 13 commits into
mainfrom
feat/pro-11-fidelity

Conversation

@Batuis

@Batuis Batuis commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Part 11 of 18. Stacked on #232.

This part is about calculation fidelity: loads that go where they are stated, one-way members solved in JS, and one explicit rule for how combinations are formed. The engine is not touched.

What it brings

Self-weight as a load of a case (analysis.selfWeight).

  • Each entry names its case, a global direction, a factor (−1 along Z is gravity) and what it covers: the whole model, a list of members or a named group.
  • On a member it is ρ·A along the member, applied as a member load, so a beam takes its own wL²/8 (wL²/12 at fixed ends). Before, the weight was lumped at the end nodes and gave the beam no bending. On shells it is ρ·t over the area, as before.
  • It goes in once, in its case, and each combination takes it with that case's factor.
  • A project without the rule keeps the old one (the whole model in every D case) until it is migrated. The PRO panel migrates it once to the first D case, or to a new "Self-weight" D case if there is none. A notice says so, and if there were several D cases it recalls that the weight used to be counted in each of them. An example opened from the menu is migrated without a notice.
  • The loads panel gets a table of these entries and hides the old checkbox and the per-combination self-weight factor once the rule exists.

Distributed 3D loads with axes and an axial part.

  • frame: local (qx along the member, which is the axial part, and qy, qz along its local axes), global (per metre of member) or projected (per metre of the member's projection, so a rafter's snow can be stated per metre of plan).
  • Every member load now goes through one representation, a global force per metre at the ends of the loaded stretch (member-loads.ts). The solve, the statics check and the viewport all read it from there.
  • The axial part goes to the end nodes by statics, with the arm × F couple when an offset puts the member end away from its node.
  • Split, merge, flip and transform-in-place carry frame and the axial part correctly: a global load is rotated with the model, and only a local axial load changes sign on a flip.

Transverse loads on axial-only members. The engine's truss assembly drops a transverse member load, so a load on a truss member used to vanish. It now goes to the two end nodes as the reactions of a simply supported span. The model diagnostic says so instead of warning that the load is lost.

One-way members solved in JS (the active set).

  • solveNonlinear3D is now a loop over the ordinary linear solve: a tension-only or compression-only member working the wrong way, or a lifting support that pulls, leaves the input and the model is solved again, until nothing changes. The engine's contact solve is no longer used.
  • While active, a one-way member is an axial member, and loads along it go to its nodes as above.
  • A member that left the input reports exactly zero, and its row is present in the results.
  • A settlement is its own case: it goes into each combination once, and into no load case.
  • Each result carries a NonlinearReport: converged, iterations, slack members, lifted supports, and members that keep switching. The Results panel shows it when there is one.

How combinations are formed (analysis.combinationMethod).

  • solveEach (default): each combination is solved on its own factored loads, with its own active set.
  • superpose: the cases are solved one by one, each with its own active set, and summed. The report then lists the one-way members and supports whose state in the sum contradicts their behaviour.
  • Without one-way behaviour both give the same, and the panel says so.

P-Delta per combination (analysis.perCombination: 'pdelta'). Cases stay linear; each combination is solved with P-Delta on its own factored loads, with the corrected member forces from #232.

Found on the way

  • The engine's contact solve and its truss assembly have five defects that this part works around in JS (M7 to M11 in the engine's pending list): the contact solve returns no reactions; a member it switches off reports the forces of the full structure; a tension-only frame member keeps its stiffness; its iterations, convergence and diagnostics were discarded; and the 3D assembly drops transverse loads on truss members.

  • The flagship 408-member frame designs nine more beams. Self-weight now bends every beam about its strong axis and adds nothing lateral, so nine BEAM-Y members fall under the 10 % biaxial threshold (0.104–0.125 before, 0.080–0.097 after). 395 verified and 13 proposals, from 386 and 22. Utilisation moved on 100 members, 65 up and 35 down, and the worst is still 1.000.

  • The 7-storey example has no proposal left. Its slabs meet the beams only at the columns, so a beam's primary moment used to be little more than frame action; with its own weight (6 kN/m on a 30×80 over 6 m) the five former proposals drop to ratios of 0.031–0.055. The tests that exercise the proposal on every projection now run on the 408-member frame, which still has 13.

  • The RC QA frame stopped closing its detailing. With 22 kN/m stated on top of the weight, the 300×550 beams got Ø16 bottom bars, and the 135° tails of the crossing beams' stirrups then reached a second-layer bar at every corner. The frame was sized for a dead line load of 22 kN/m, so its stated line load is now 22 − ρ·A = 17.875 kN/m with the weight as a case load. It designs exactly as before, and the CAD handoff golden reproduces its bytes. The detailing limitation is real and is recorded for later: the design loop does not try another layout when every stirrup corner is blocked.

  • A uniform load on a quad was split a quarter to each corner. That puts the resultant at the average of the corners, which is the centroid only for a parallelogram, so on an irregular mesh every slab pressure and every shell's self-weight had its moment shifted (3·10⁻⁵ on a published building with an irregular slab mesh). Each corner now takes ∫Nᵢ dA, its consistent share, in the solve and in the statics check alike. No test needed re-recording.

  • An anchor whose guys all went slack lost its support. The active set took out every node only slack members reached, supported ones included, and with them the share of the guys' own loads already moved to that node. A supported node now stays in the solve.

  • The quantities card's coverage line was under its contrast bar (3.62 against 4.5 at 9.6 px), which failed the documents-stage legibility check in all three languages on the base branch as well. It now uses the secondary text colour.

Numbers re-recorded, with the reason

Test Before After Why
rc-baseline-digest, autodesign-regression, coverage-outcomes, e2e rc-design B9 386 / 22 395 / 13 nine beams under the biaxial threshold (above)
rc-baseline-digest fingerprint 792b6f88… f4681de2… checked member by member: 9 outcomes, 0 limiting, 100 utilisations
beam-reinforcement-audit 5 proposals 0 the 7-storey example (above)
top-steel-projections 74 hanger members 28 a beam's own weight gives most supports a hogging moment and its own top steel
beam-emptiness-diagnostic 10 reported beams bottom-only 7 163, 146 and 89 now carry 7Ø10 over each support
e2e rebar-3d 4 proposals on the diagnostic fixture 3 one beam under the threshold
CIRSOC 201 blog post and its e2e shear row at D/C 0.86 0.88 lumped at the nodes, the weight at the supports went into the reactions and not through the beam's shear; flexure stays at 0.89
e2e project-restore, ded-roundtrip the 7-storey building shows proposals it shows none the 7-storey example (above); the notices spec now prepares the 408-member frame
CAD fixture checksum 15ce4e15… 1637ce2d… the restated line load; the V2 manifest is unchanged

Not changed

  • The Rust/WASM engine and its bindings. The defects above are worked around at the JS boundary and listed for the engine part.
  • ProDiagnosticsTab.svelte.
  • The design and detailing engines. No threshold, verifier or tolerance was touched to make a number pass.
  • The linear solve of a model without one-way behaviour, apart from where self-weight and axial loads now act.
  • 2D and Basic: the self-weight rule and the load axes are 3D and PRO.

Tests

  • Unit:
    • fidelity-loads: wL²/8 and wL²/12 from self-weight; its own case, factor, direction and group; the legacy rule; global against projected on a 3-4-5 rafter (10 against 8); an axial column load; a truss equal to the pin-ended frame; P-Delta per combination against the exact solution.
    • active-set: tension-only frame diagonals, exact zeros, reactions, statics in six components, a swap of the active diagonal, solveEach against superpose with the sign violations, a settlement in each combination once and in no case, and a slack guy at an anchor nothing else reaches.
    • quad-load-shares: a rectangle in four equal shares, a trapezoid with its resultant at the centroid, and the surface load and shell self-weight handed to the engine with those shares.
    • split and merge carrying the axial part and semi-rigid ends; self-weight as a member load in the Z-up field test.
  • E2E: pro-analysis-rules (self-weight rows, factor and new entry on the model, combination method and P-Delta, load axes from the table, an older project migrated once with its notice, the active-set report in Results), plus the loads, results, project-file, round-trip, design and rebar suites that these numbers reach.
  • The full e2e suite ran locally: 1385 passed. Three failures reproduce identically on the base branch and are left alone here: basic-demos (drawing a beam), landing (the deck's pricing table) and section-analysis (closing the panel). A @perf orbit case is flaky under load, and the visual baselines are non-blocking. m2-steel-workflow's Lb cell locator also matched the Lb editor's card; it is narrowed to the table's cells.
  • Gates: typecheck at its baseline, both test passes, build, check:gate.

QA

  1. Open the RC QA frame. Loads › the self-weight table has one row in "Dead Load". Change its factor to −1.1, solve, and see the beam moments grow by the weight's share.
  2. Add a second self-weight row in another case, scope "The selected members", with two beams selected.
  3. Open a project saved before this version (any .ded with members and loads): the notice names the case the weight went into.
  4. Add a distributed load on an inclined member, first "Global", then "Projected", and compare the reactions (the projected one is smaller by the cosine).
  5. Put an axial qx on a column and check the axial diagram.
  6. Load a truss and put a transverse load on one member: the reactions include it and the member has no bending.
  7. Make a bracing diagonal tension-only and solve: the Results panel says how many iterations, and the compressed diagonal shows zero.
  8. Loads › Combinations › "Superpose the cases": solve again and read the sign-violation list if any.
  9. Each combination › P-Delta, solve, and compare a combination's sway with the linear one.

…d an axial part, and the active set in JS

Self-weight is a load of a case (`analysis.selfWeight`): case, global direction, factor and
scope (whole model, a list or a group), applied along each member as rho*A, so a beam takes its
own wL^2/8. A project without the rule keeps the older one until the PRO panel migrates it to
its first D case, with a notice; a shipped example is given the rule quietly when it loads.

Every member load goes through one representation, a global force per metre at the ends of the
loaded stretch (`member-loads.ts`), read by the solve, the statics check and the viewport.
Distributed 3D loads take `frame` (local, global, projected) and an axial part, which goes to
the end nodes with its couple when an offset separates them. Transverse loads on axial-only
members go to their nodes as simply supported reactions, since the truss assembly drops them.

One-way members and lifting supports are solved by an active-set loop over the linear solve,
replacing the contact solve: members out of the input carry exact zeros, reactions stay, a
settlement is its own case, and each result carries a NonlinearReport that Results shows.
Combinations are solved each on its own loads or superposed from their cases
(`analysis.combinationMethod`), and optionally with P-Delta per combination.

Numbers re-recorded with their reason: the 408-member frame designs nine more beams (their
lateral ratio falls under 10 %), the 7-storey example has no proposal left, fewer beams need a
hanger pair, and the CIRSOC 201 post's shear row reads 0.88. The RC QA frame states its dead
line load as 22 - rho*A so its beams carry the 22 kN/m they were sized for; its CAD handoff
golden is byte-identical. The quantities card's coverage line meets its contrast bar.
…cal, global or projected axes, one-way members and how combinations are formed
@Batuis
Batuis marked this pull request as ready for review September 28, 2026 03:33
… loads

A quarter of q·A (or rho·t·A) to each corner puts the resultant at the average of the corners,
which is the centroid only for a parallelogram; on an irregular mesh it moved the moment of every
slab pressure and of every shell's self-weight. Each corner now takes its share of the integral
of its bilinear shape function, in the solve and in the statics check alike.
…s load

The active set took out, with the slack members, every node only they reached, and a supported
node went with its support and with the share of the guys' loads already moved to it. A
supported node now stays in the solve whatever is slack around it.
@diegokingston
diegokingston changed the base branch from feat/pro-10-steel-sections to main September 30, 2026 16:30
@diegokingston
diegokingston merged commit 7329f97 into main Sep 30, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants