Skip to content

feat: Capacity Manager: reconstruct DNV-RP-C201 stiffened-panel + girder capacity models from a Sesam SIN#233

Open
oleandor wants to merge 57 commits into
Krande:mainfrom
oleandor:feat/capacity_man
Open

feat: Capacity Manager: reconstruct DNV-RP-C201 stiffened-panel + girder capacity models from a Sesam SIN#233
oleandor wants to merge 57 commits into
Krande:mainfrom
oleandor:feat/capacity_man

Conversation

@oleandor

@oleandor oleandor commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a Capacity Manager (ada.fem.capacity) that reconstructs Genie-style buckling capacity models straight from a Sesam .SIN result file and resolves their DNV-RP-C201 design loads, plus the viewer integration to inspect the results:

  • Stiffened-panel capacity models built SIN-natively (geometric grouping, no naming dependence): plate fields grouped per panel, secondary stiffeners classified geometrically, tributary plate partition per stiffener, GLSEC/GIORH/GBOX section parsing (incl. bulb flats), set scoping via GSETMEMB.
  • Girder capacity models (Section 7): one model per girder bay with flanking-panel geometry (l/s/t/A_s/I_s), coplanar-plane strip partition, and Section-7 load resolve (N_G/M_G/tau at the three [5.3] stations).
  • Stress resolve: [5.3] three-station sampling on RDPOINTS coordinates, Section-5 axial/moment resultant preservation, RVFORCES beam/line forces, shell membrane-stress recovery from displacements for stress-less SINs, load-combination resolve by FE-field superposition, and performance work (single SIN read across cases, element-id indexed field blocks, RVFORCES element filter).
  • Viewer integration (React frontend + embedded dist): capacity result sidecar (versioned bundle: spine + per-case detail + AFEL colour fields), UF colouring with Genie bands, per-stiffener tributary strips, girder line UF + strip selection, Input panel showing the as-checked geometry/loads, case export to any companion cc UI, wireframe/isolation toggles, worst-over-cases.
  • Robustness fixes along the way: OpenBLAS pin for the Windows MKL svd/gemm crash, LAPACK-free stress recovery, viewer-api PYTHONPATH on win-64, and a Genie T-girder convention fix (unsymmetrical-I dummy flange: width == tw with token thickness — the real flange is now picked from either slot).

The branch already contains upstream main (0.27.0) as its merge base and is 0 behind — it merges clean.

Testing

  • tests/core/fem/capacity: 36 tests (grouping/geometry vs a reference Genie model.json, SIN-native model reconstruction, stress-resolve values vs Genie criteria results, section parsing, flange-pick rules).
  • Frontend: tsc --noEmit clean, npm run build regenerates the embedded bundle.
  • Exercised end-to-end on production-size SIN models (100+ capacity models, multi-set scoping, combination cases).

🤖 Generated with Claude Code

oleandor and others added 30 commits June 18, 2026 13:15
…m a SIN

New ada.fem.capacity subpackage: reads a Sesam .SIN, assembles panel groups
(grouping seeded from a Genie model.json source for now), extracts plate/
stiffener geometry, and resolves FE membrane stresses into DNV-RP-C201 design
variables. Validated against the Mini-topside reference: plate geometry 354/354
vs model.json; transverse-membrane + shear stresses exact (median rel 1e-6 over
540 cases); axial reproduces Genie's AxialLoads decomposition (~8% residual).
Emits a neutral CapacityModel and a Genie-compatible model.json mirror. Abstract
CapacityModelBuilder leaves a girder seam. 5 tests (skip-if-no-reference).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A Sesam SIN set's GSETMEMB block can carry an uninitialised cap/dim slot
(~1e8 on the Ruben 399 MB topside) that tripped the _MAX_DIM_VALUE guard;
the PTAB decode loop then swallowed the ValueError and dropped the whole
block, so mesh.sets came back without those sets and named-group scoping
failed. The pointer table is authoritative — _read_pointer_table already
clamps the read to the file size and truncates at the first invalid
pointer — so cap only the requested slot count instead of rejecting the
block. All 104 Ruben sets now resolve (allPlates=58809 shells,
Pl_T100=2372 plates, etc.). Also give a clear error when a SIN genuinely
has no set membership.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capacity reconstruction scanned every element block (np.where per call,
O(n_elements)) for each node/coords/tributary lookup, making panel
assembly O(n^2) — ~23 h on a 128k-element topside. Build a node->element /
element->node index once (extract._ensure_index, cached on the mesh): the
same lookups are now O(1) and tributary is O(node degree). Tributary for
all 69k beams drops from a ~23 h extrapolation to ~0.4 s; the Mini suite
runs ~2x faster with identical results.

Also:
- scope a plate-only set (e.g. Pl_T100) to the stiffeners bordering its
  plates so it still yields stiffened panels (_beams_bordering_plates);
- when concept names carry no _sbm role suffix, classify the secondary
  stiffener as the dominant plate-bordering profile rather than the
  shallowest — real models have tiny stub beams below the real stiffeners.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scope-aware + cached so a scoped code-check on a 128k-element topside is
seconds, not infeasible (Pl_T100: ~87 s -> ~13 s):
- restrict the plate side to the scoped region (its plate members + the
  stiffeners' tributary plates) so plate-field bounding and the merge do
  not scan every shell in the model;
- cache each plate's (frame-invariant) area on the node index and sum the
  cache in the rectangularity test instead of re-shoelacing every plate on
  every merge attempt;
- prune merge candidate pairs by bounding-box adjacency before the
  expensive geometry test;
- use elementwise dot, not @, in the rectangularity bbox (the local BLAS
  faults on batched matmuls of that shape).

Classification is now purely geometric and naming-independent: the
secondary stiffener is the dominant plate-bordering profile, not the
_sbm-tagged concept. (Stiffener/cell *labels* still use concept names
where present, but no grouping decision depends on the naming.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_concept_role / _CONCEPT_ROLE_RE were only used by the secondary-stiffener
classification, which is now geometric (dominant plate-bordering profile).
Remove the dead _sbm/_gbm role convention; the remaining _BEAM_SUFFIX_RE is
used only to derive readable labels from concept names, never for grouping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the concept-name-based atomic grouping (_concept_groups grouped
beams by concept base / _stiffener_specs_for_beams grouped by concept
name) with a geometric one: each secondary beam is a stiffener run, its
bounded rectangular plate strip is an atomic panel, and _merge_panels
fuses adjacent strips into Genie's panels. Concept names are now used only
to LABEL panels/stiffeners — verified the grouping (plate + stiffener
element sets) is identical with names suppressed.

Mini stays exact: Mini_grid_x100 reproduces Genie's 10 panels with the
same names, stiffener tuples and plate tuples; codecheck2 still matches 49
panels; stress-resolution tests unchanged. Full-Mini panel count rises
(per-beam merges slightly less than the old per-cell pre-clustering in
unvalidated regions) but every panel stays unique and rectangular.

Multi-element stiffener chaining is intentionally not done here: a physical
stiffener is split by Genie at every support (girders AND perpendicular-
plate floors the FE mesh doesn't expose as beams), so a purely geometric
chain over-merges across floor lines; per-beam keeps the panels correct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SinSource.group now accepts several set names (their union via
_scoped_set_members), so a code-check can be scoped to a few selected sets
at once; a single name still works unchanged.

get_field_shell_data dropped the whole result read with a KeyError when an
RVSTRESS shell record referenced an element with no RDPOINTS entry (seen on
the 399 MB Ruben topside). Skip those rows — without a result-point
definition there is no integration-point field to build — so the viewer
bake of real models does not fail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The solver pulled the MKL 2026 preview (mkl_avx10.3) as numpy's BLAS/LAPACK
backend, which SEH-crashes (0xc06d007f) on np.linalg.svd / gemm on Windows
here — taking down the FEA-artefact bake (test_fea_artefacts) and any svd
path. Force the OpenBLAS-backed libblas in the prod solve-group. svd/gemm
now work; test_fea_artefacts goes from a hard crash to 86/87 (the remaining
failure is an unrelated freetype poster-rendering dep).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
capacity_models() and resolve_cases() (and stress_resolve.resolve_cases)
now accept a keyword-only on_progress(completed, total) callback, ticked
once per built model / per resolved result case. This lets a caller drive
a progress bar without adapy taking any UI dependency. Backward
compatible: the parameter defaults to None.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New toggle next to "Show definitions"/"Results" that hides every FEA
draw range not belonging to a capacity model, leaving only the capacity
panels (their faces plus the boundary/colour overlays when enabled). The
pre-isolation hidden-range set is snapshotted per mesh so toggling it off
restores exactly what was visible before. Driven by a new
isolateDefinitions store flag and applyCapacityIsolation/
clearCapacityIsolation handlers built on CustomBatchedMesh's per-range
hide. Frontend rebuilt (embedded index.zip).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The capacity result colour overlay was a transparent (opacity 0.96)
layer coplanar with the lit base shell mesh and only a -2 polygon
offset. On large models the base z-fights through and the <1 opacity
lets the lit base bleed, so the UF colour read differently on each face
(or appeared on one side only). Make the overlay fully opaque to the UF
colour and give it a robust polygon offset (factor -4, units -16) so it
reliably wins against the coplanar base on both faces. depthWrite stays
off so the capacity-boundary lines still layer on top. Frontend rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
resolve_cases now raises a clear ValueError listing the available result
cases when a requested case is not present, instead of silently resolving
to all-zero (the Ruben example asked for cases 14/15 on a SIN with only
1-13).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a SIN has no element stresses (SESTRA ISEL4=-1), resolve_cases now
reconstructs shell membrane stresses from nodal displacements instead of
resolving to all-zero. New stress_recovery module computes per-corner
plane-stress membrane tensors (sigma = D*B*u) for TRI3/QUAD4 in the
element local frame, emitted as a synthetic STRESS block (corners as
bottom/top pairs, with result-point coords) so the existing resolver
pipeline -- including the Section-5 start/mid/end station logic -- works
unchanged.

Verified against the Mini model (which has real RVSTRESS): per-element
membrane tensors match exactly (median rel err 0.000); governing UFs
match to median abs 0.0009 / p95 0.025, residual only in the start/end
peaks (slightly conservative). Closed-form unit tests added. The
stress-bearing path is untouched (recovery only triggers when STRESS is
absent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
get_field_line_data bailed when the RDPOINTS map was empty, so SINs that
ship beam forces but no RDPOINTS (e.g. SESTRA smart-load-combination
runs) lost all element forces and resolved stiffener axial/bending to
zero. RDPOINTS is only needed for the shell-stress path; for line
elements the element type comes from the converted mesh and the
result-point count from the record length (component count from
RDFORCES). The shell path and the RDPOINTS-present (e.g. Mini) path are
unchanged.

Verified on Ruben: FORCES now materialises (69564 beams x 3 stations),
stiffener Nsd is non-zero, governing UF ~0.21 (was 0). Sesam reader
regression tests and the Mini capacity integration test still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
resolve_cases re-opened and re-decoded the SIN and rebuilt the full
(step-invariant) mesh for every result case, which dominates on
multi-hundred-MB models. Route it through a single SinStreamReader via a
new iter_sin_step_results generator: the file is opened and decoded once,
the static blocks and mesh are built once, and only each step's RV*
tables are re-read. Results are unchanged (Mini capacity integration
still passes); the win scales with the number of cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The resolve loop looked up element stresses/forces via
ElementFieldData.get_by_element_id, which scans the whole block; calling
it once per element across hundreds of capacity models made resolution
quadratic and dominated runtime on large SINs. Index each field block's
rows by element id once (cached on the block, which is rebuilt per step)
and look up in O(1).

Results unchanged (Mini capacity integration still passes); the Mini
integration test dropped from ~23s to ~9s, and the win grows with model
count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The membrane recovery called np.linalg.inv/det per shell element
(thousands of LAPACK calls), which crashes with a native SEH exception
(0xC06D007F) on some Sesam-env BLAS/LAPACK backends -- the same failure
class as the earlier svd crash. The operands are tiny (2x2 Jacobian,
3-vectors), so replace inv/det and all @/np.dot/np.linalg.norm with
closed-form scalar + elementwise arithmetic. Crash-safe regardless of
BLAS backend, and faster. Results unchanged (recovery unit tests + Mini
capacity integration still pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The capacity side panel showed usage factors and the governing checks for
the selected stiffener; add a per-row "Input" toggle that reveals the
structured numeric input the DNV-RP-C201 stiffened-plate check actually
used, grouped: Geometry, Stiffener section, Material, Design loads,
Resolved design (Section 6), Options. Values are read from the existing
sidecar (model geometry/section/material + per-row loads/resolved
variables) and shown in engineering units (mm, MPa, kN, kN·m, kPa). No
figures, numbers only; default view is unchanged until the button is
clicked. Frontend rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
oleandor and others added 27 commits June 22, 2026 10:22
The inline Input details overflowed the (cramped, unscrollable) capacity
side panel. Render it instead as a floating, scrollable panel pinned to
the top-right of the 3D view via a portal (max-h 80vh, overflow-y-auto),
with a header (stiffener name + close button). The "Input" button still
toggles it. Frontend rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add stiffener_stations() (start/mid/end coords along the span) and render
them in the viewer: a "Points" toggle in the floating Input panel marks
positions 1/2/3 with numbered, colour-coded sprites on the selected
capacity model in the 3D view. The per-position load rows (sigma_y1/2/3,
tau, N, M) carry a matching colour dot so it's clear which point each
component is taken from. Markers are screen-space sprites (always
visible), children of the FEA mesh (share its transform). Frontend
rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stale v1 sidecars (no station coords) were being reused, so the Points
markers had nothing to place. Bump the sidecar schema to v2 (adds the
Section-5 stations) so the reuse matcher refreshes old sidecars
automatically; sync the frontend validator + test. Also enlarge the
station marker sprites (0.03 -> 0.05) for visibility. Frontend rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Input panel now shows axial as membrane stress σ_x1/2/3 (from
  AverageLongitudinalMembraneStresses) instead of force, aligning with the
  FE stiffened-panel (Section 5) inputs in aibel_dnv_rp_c201_ui.
- Tag resolved/extracted values with DNV-RP-C201 refs: σ_ySd (6.17),
  τ_Sd (6.16), N_Sd (6.2); per-position [5.3.x] clauses.
- [6.4.3] mesh-discretization check per stiffener (extract.stiffener_
  discretization): a warning banner on the main panel counts under-meshed
  models (e.g. Mini's single first-order element stiffeners; need >=4),
  plus a "Modelling [6.4.3]" group in the input panel.
- "Individual UF" toggle on the main panel colours the selected panel by
  the selected stiffener's UF instead of the panel-governing maximum.
- Sidecar schema -> v3; test tracks the constant. Frontend rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous "Individual UF" coloured the whole selected panel by one
(governing) stiffener's UF. Rework it to a per-stiffener view: each
stiffener's own line + tributary plate strip is coloured by that
stiffener's UF (max where strips overlap), revealing the within-panel UF
variation. Uses the new per-stiffener tributary_plate_ids from the
sidecar; the toggle is global (all panels). Sidecar -> v4. Frontend
rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tributary_plate_ids only returned plates sharing a stiffener's full edge,
so triangles and mid-bay elements were left uncoloured in the individual
UF view. Add partition_plates_by_nearest_stiffener: assign every panel
plate element (quad or triangle) to its nearest stiffener segment, giving
gap-free per-stiffener coverage. Sidecar -> v5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SESTRA "smart load combinations" (e.g. Ruben case 14) are defined in the
SIN's RDRESCMB records but not stored as basic RV* result cases, so the
capacity check could not request them.

Read the combination definitions (combination IRES -> {basic IRES: factor})
and resolve a combination by superposing its basic cases' STRESS/FORCES/
RVNODDIS field blocks before the Section-6 resolve. Resolution is linear in
the FE field up to the per-station min/max, so superposing fields (and
running stress recovery on the combined displacement field) is exact —
unlike linearly combining the resolved scalars.

resolve_cases now splits requests into directly-stored basic cases (streamed
and resolved one at a time, memory-bounded as before) and combinations (their
basic blocks accumulated, then resolved once via the extracted _resolve_step).

SinMetadata gains combinations/result_names plus combination_ids and
selectable_cases. Verified on Mini: superposing lcc1/lcc2 reproduces the
fields SESTRA also stored for them to the float32 noise floor (< 1e-4 rel)
across every resolved design variable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…etail)

Matches the dnv-rp-c201 v6 sidecar. The frontend no longer expects every
per-element UF value and case row inline:

- applyCapacityVisualField is now async; for storage === "afel" it
  Range-fetches one (field, case) step from the capacity AFEL blob
  (reusing fetchElemFieldStep) and maps it onto the run's shared
  element_axis, instead of reading inline field.cases values.
- loadCapacityCaseDetail lazy-loads capacity.detail/<case>.json into a new
  caseDetail map in the store when a case becomes active; CapacityControls
  sources its rows (list, selection, individual-UF, metric scores) from it.
- active.capacityFetch retains the manifest-relative fetchers so the
  overlay can fetch blobs/detail after the initial load.

Legacy (<=v5) inline sidecars still load via the json fallback path.
CAPACITY_RESULTS_VERSION -> 6 in lockstep. index.zip rebuilt from source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cases

Three capacity-panel UX changes (sidecar v7):

"Only definitions" now shows *only* the capacity models:
- hides the whole-model element-edge wireframe (was drawn over the isolated
  scene as "rest as wireframe"), and
- collapses every non-capacity triangle in the colour overlay to zero area so
  the rest no longer shows as grey shells when results are on.
- New "Show rest as wireframe" toggle brings the wireframe back for context.

Worst over selected cases:
- "Worst (over selected cases)" is the first Case option; per-case checkboxes
  (All/None, default all) choose the subset.
- Colours by the max UF per element over the subset for the active check
  (Range-fetching each case's AFEL step and reducing element-wise).
- The list shows worst UF per (model, stiffener) and the case it came from
  (new Case column), backed by the compact capacity.summary.json.
- Clicking a worst row drills into that governing case.

Also: rename the "Metric" dropdown to "Check"; lazy per-case detail and AFEL
fetch retained. CAPACITY_RESULTS_VERSION -> 7 in lockstep. index.zip rebuilt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Equal-height button row (items-stretch); "Only definitions" and
  "Show rest as wireframe" now default on.
- "Individual UF" colouring now also works in the worst-over-cases view.
- Input/Results sidecars get an "Input"/"Results" heading with the Case
  and capacity-model name beneath.
- Input sidecar "Export" button downloads the inputs as an
  aibel_dnv_rp_c201_ui case file (fe_stiffened) for import there.
- Failed capacity checks (runs[].errors) are flagged in red.
- Design-loads inputs regrouped under Axial/Transverse/Shear/Moment
  sub-headings so the long names fit the sidecar width.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Paint the definition edges of capacity models whose check raised (for
  the active case, or any selected case in the worst view) red in the 3D
  scene, via per-vertex colours on the boundary overlay — matching the red
  error banner in the sidecar.
- Move the under-meshed warning up next to the error banner at the top of
  the Capacity panel and present both as the same full-width collapsible
  notices (error red, warning amber), instead of the warning sitting below
  the run/case selectors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The input export wrote full float precision (e.g. "hw": 180.00000715255737)
from the metre->mm unit conversions. Round each numeric field to the same
precision the Input sidebar shows (1 dp >=100, 2 dp >=1, else 3 sig figs)
so the exported case reads like the panel (180.0, 5.94, ...).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read the new per-case check_inputs (the exact geometry/material the engine
consumed) and prefer it over the display capacity_model dict when showing
the Input sidebar and building the fe_stiffened export. This makes the
exported case reproduce the viewer's UF in aibel_dnv_rp_c201_ui — the
stiffener span feeding sigma_y,R (eq. 4.6) is now the value the check used
(e.g. 2.6 m, not the dict's 3.9 m), so C_ys [6.21] no longer flips negative
on import. Pin CAPACITY_RESULTS_VERSION to 8 in lockstep; falls back to the
display dict for pre-v8 bundles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Input panel "Resolved design (Section 6)" now reads the engine's
  resolved_design (sigma_y,Sd eq 6.17, tau_Sd eq 6.16, N_Sd eq 6.2) instead
  of the Genie reference SigmaYSd, so the figures are DNV-RP-C201-correct
  (e.g. sigma_y,Sd 63.6 MPa, not the mid-point 27.0). Falls back to
  resolved_variables for pre-v8 bundles.
- Carry optimize_z_star from check_inputs into the Options line and the
  fe_stiffened export, so an exported case imports with the same z*
  optimisation the viewer used.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Export loads.N_1..3 (kN) in the fe_stiffened case so the UI uses the FE
axial-force resultants directly instead of re-deriving N from the membrane
sigma_x — the two differ several-fold for stiffeners carrying global
bending, which made the imported beam-column UF far too low. Pin
CAPACITY_RESULTS_VERSION to 9 in lockstep (negative interaction UFs are now
floored at 0 engine-side).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- UF colour coding is now the Genie "UfTot" discrete 6-band scheme (no
  fading): <0.2 light-blue, >=0.2 cyan, >=0.4 green, >=0.6 yellow, >=0.8
  orange, >=1 red. Applied to the 3D overlay (capacityUfColor), the legend
  gradient (hard colour stops), and the result-table text colours. Sampled
  from .local/reference/genie_uf_color_scheme/genie_uf_color_scheme.png.
- Drop loads.N_1..3 from the fe_stiffened export — that check is stress-only
  now (Stipla Section-5); the membrane sigma_x is exported as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ve-stresses perf

Two faults made the stiffener axial force (and beam-column UF) several-fold too
high on some panels:

- The panel merge bucketed only by (beam section, plane, offset), so one "panel"
  could span different plate thicknesses (e.g. 8/20/40 mm). A plate-thickness
  change is a buckling-panel boundary, so the merge now keys on plate thickness
  too (_merge_panels takes aux for the GELTH lookup) and never fuses across it.
- _build_stiff_geom picked an arbitrary bordering plate via next() for the axial
  tributary (t, s_spacing) — it could grab a multi-spacing / wrong-thickness
  plate, so AxialLoadsPlate diverged from sigma_x*t*s by that factor. It now uses
  the panel's own plate (model.plates[0]) — the same one the capacity check
  consumes — so the integral matches the check exactly.

Verified on DBSW OPE / Pl_L500: the bogus governing UF 1.385 becomes 0.226/0.450,
and the invariant AxialLoadsPlate == sigma_x*t*s holds for all 1923 resolved
stiffener-cases (worst ratio 1.0000). Adds
test_plate_axial_force_matches_membrane_stress_times_tributary.

This commit also lands the pre-existing in-tree "resolve design stresses" perf
refactor (per-element rotation cache, geom_cache step-invariant geometry) that the
tributary fix is built on, and updates its _resolve_step test for the geom_cache
argument.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump CAPACITY_RESULTS_VERSION to 10 in lockstep with viewer_export.py so cached
v9 bundles regenerate with the corrected stiffener axial forces (topology fix).
Rebuilt the embedded frontend bundle (index.zip).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
iter_sin_step_results / SinStreamReader / _records_for take an optional
`forces_elements` set that narrows the per-step RVFORCES decode to the given
IELNO (stiffener beam elements). The capacity resolve only needs line forces for
those elements, so this skips decoding the whole model's forces every step.

Required by the already-committed stress_resolve.py, which calls
iter_sin_step_results(..., forces_elements=...) — committing it completes the
in-tree resolve-stresses perf refactor (the rest landed with the topology fix).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…presentation

- ada.fem.capacity.girder: girder bays from primary plate-bordering beam runs
  (coplanar, perpendicular-stiffener flanking with line-contact coverage);
  rectangular per-side tributary strips partitioned nearest-girder-wins
  (parallel girders never overlap; perpendicular crossings share corners)
- CapacityManager.girder_models()/resolve_girder_cases()
- frontend: capacity sidecar v11 (second run, scope 'girder', g-prefixed case
  ids), girder line drawn amber / UF-coloured, tributary plates shown for the
  selected girder only, panel stiffeners as dashed lines, Individual UF always
  on (button removed), per-run store reset

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	src/frontend/package-lock.json
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ride)

The ':'-joined /opt/adacpp-overlay prefix from the Linux deploy makes the
whole PYTHONPATH one bogus entry on win-64 (';' separator), so
'python -m ada.comms.rest' failed with ModuleNotFoundError: ada. The overlay
path is a Linux-image concern only; the win-64 override drops it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ection

- GirderCapacityModel.stiffener_stations: supported-stiffener 2-point lines
  clipped to the tributary strip (drawn dashed in the viewer, like panels)
- CapacityManager.panel_groups(): grouping exposed as its own step so callers
  can report the slow SIN grouping as a separate progress phase
- capacity colour overlay is RGBA: elements without a result are transparent
  (shaded base mesh shows through) instead of flat grey
- capacityResultsStore: per-run UI memory - switching Run (panel <-> girder)
  stashes and restores case/metric/selection/worst subset
- click inside an already-selected panel selects the individual stiffener
  strip under the cursor (repeat clicks re-trigger via clickCoordinate);
  amber outline highlights the selected strip; Points/Input/Results follow
- Export from a girder row emits an aibel fe_girder case (sigma_y,i =
  N_Gi/(A_G+l*t) inversion of eq. 5.2); girder-specific Input panel groups
- rebuild embedded frontend bundle

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…le for Stipla

- dominant_flange(): Genie models T-girders as unsymmetrical I-sections
  with a dummy flange (width == tw, token thickness); the GIORH parser and
  _section_from_mesh took the top slot literally, checking e.g.
  TG850x300x16x20 with a 16 x 0.1 mm flange (UF 8.13 vs 1.52 corrected).
  Both paths now read both flange slots and pick the governing real one.
- Girder case export (buildUiGirderValues) emits the stiffener profile
  (stiffener_type + st_hw/st_tw/st_bf/st_tf, from sidecar v11+
  check_inputs) so the dnv-rp-c201 UI can export the case to Stipla 3
  DNV-G without manual profile entry; viewer Input panel lists the
  profile dims under Supported stiffeners. Frontend dist rebuilt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oleandor oleandor changed the title Capacity Manager: reconstruct DNV-RP-C201 stiffened-panel + girder capacity models from a Sesam SIN feat: Capacity Manager: reconstruct DNV-RP-C201 stiffened-panel + girder capacity models from a Sesam SIN Jul 2, 2026
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

👋 Hi there! I have checked your PR and found the following:

PR Review:

I found no pr-related issues.

  • ✅ PR title is ok
  • ✅ Release label is ok
  • ✅ SOURCE_KEY is set as a secret
  • ✅ Skipping release

Python Review:

I found some python-related issues:

Python Linting results:

  • ❌ Isort
  • ❌ Black
  • ❌ Ruff

Python Packaging results:

  • ✅ I found the PYPI_API_TOKEN secret.
Packaging Type Package Name Version
pyproject.toml ada-py 0.27.0
pypi ada-py 0.28.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant