[us-commits] [ehb54/ultrascan3] 12fa3e: somo/grpy: self-validating shell reduction with re...

emre brookes noreply at github.com
Sat Aug 8 17:23:45 MDT 2026


  Branch: refs/heads/somo-dev
  Home:   https://github.com/ehb54/ultrascan3
  Commit: 12fa3ee4567ed094364a5348644cc3fc2fb58b44
      https://github.com/ehb54/ultrascan3/commit/12fa3ee4567ed094364a5348644cc3fc2fb58b44
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/README.md
    A us_somo/develop/grpy/grpy_exposure.hpp
    A us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/tests/run.sh
    A us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/libus_somo.pro

  Log Message:
  -----------
  somo/grpy: self-validating shell reduction with reported error bars

GRPY costs O((11N)^3), so the bead count dominates. A dense bead packing is
hydrodynamically screened -- interior beads sit in near-stagnant fluid and carry
almost no force -- so the exact calculation can run on a surface-enriched subset.
This generalizes the existing binary ASA buried-bead exclusion into a convergence
test that reports the error it introduced.

Method: Shrake-Rupley exposure per bead (1.4 A probe), keep the most-exposed
ceil(f*N), solve on a doubling ladder of bead fractions, stop when the reported
bar drops below tolerance. Three rungs give the convergence order from the ratio
of successive gaps, so the remaining error is Richardson-extrapolated per
observable. The ladder is geometric against O(N^3) and costs ~1.14x its final
rung, so the check is nearly free; its final rung is the unreduced model, so an
unreducible structure degrades to exactly today's behaviour.

The bar bounds the true error: verified 92/92 (translational, 23 models,
N=204-4068) and 252/252 (7 observables x 12 models x 3 tolerances) against
unreduced exact GRPY. The observed convergence order (median 1.83) matches a
value predicted independently by a raw reduction sweep, so the error model is
derived rather than fitted.

Selection is by target bead FRACTION, not a fixed exposure threshold: the buried
fraction ranges ~1%-50% across model types, so one threshold gives wildly
different cost per model while a fraction controls O(N^3) directly.

Observables do not share a reduction frontier. Median error relative to D_t at
equal reduction is 1.77x for D_r but 3.34x for intrinsic viscosity, which drove
the stopping decision in 36/36 test cases. ShellOptions::require therefore lets
the caller choose what must converge, and ShellReport::viscosity_unreliable tells
the caller to withhold viscosity and the viscosity-derived Einstein radius when
it was not converged; the values stay in the report for the record, with a
warning appended.

Known failure mode, guarded: on a degenerate exposure distribution (a perfect
cubic lattice realizes ~9 distinct values over 216 beads) rungs swallow whole
symmetry shells instead of refining, the estimated order comes out spuriously
high, and the bar understated by ~1.4x. A floor caps extrapolation tightening at
2x the raw gap. That restores honesty on the lattice and is slack on every real
model tested -- identical honesty, no speedup cost. The lattice is kept as a
regression in tests/test_shell.cpp.

MW and Rg are pinned to full-model values. Re-derived from a reduced bead list,
MW would fall with the dropped beads (corrupting sedimentation and both
viscosities, which are mass-normalized) and Rg would rise (a hollow shell has a
larger radius of gyration than the solid body). Both are regression-tested.

Disabled by default: with enabled=false the result is byte-identical to
Solver::run, so results never move silently.

On speed, honestly: against an unreduced model the gain reaches ~120x, but SOMO
defaults to ASA buried-bead exclusion, which already removes most dead beads.
Measured on that production baseline the incremental gain is ~2-3x, rising with
model size. The durable contribution is the error bar -- the existing exclusion
reports no uncertainty at all.

Module only; the SOMO call site and GUI control follow separately.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 68384f0bed49388d107ce8e4ae046097a44a8fba
      https://github.com/ehb54/ultrascan3/commit/68384f0bed49388d107ce8e4ae046097a44a8fba
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/include/us_hydrodyn.h
    M us_somo/develop/include/us_hydrodyn_hydro.h
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/develop/src/us_hydrodyn_hydro.cpp
    M us_somo/develop/src/us_hydrodyn_settings.cpp

  Log Message:
  -----------
  somo/grpy: wire shell reduction into SOMO with a Hydrodynamics Options control

Adds the GUI control, settings persistence, and the call-site integration for the
shell-reduction layer, plus two fixes that only an end-to-end run exposed.

GUI: a "GRPY Shell Reduction" groupbox in the SOMO Hydrodynamic Calculation
Options window, below GRPY Numerical Precision and alongside the buried-bead
exclusion it generalizes. Off / On, a target-accuracy field, and a "require
intrinsic viscosity" checkbox. Backed by hydro.grpy_shell{,_tol,_require_eta},
persisted in settings save/load/default and display_default_differences,
mirroring grpy_single. gui_script `global grpy_shell` / `grpy_shell_tol` /
`grpy_shell_require_eta` and GRPY_SHELL still override, for headless work.

Intrinsic viscosity: when it was not required to converge (or was and did not),
it is WITHHELD from the reported results rather than propagated with a caveat --
a value carrying a warning is still a value that gets used downstream. Withheld
together with it is the GRPY Einstein radius: both are parsed from the same
report line ("Zero frequency intrinsic viscosity eta 0", fields 1 and 2) and the
radius is viscosity-derived, so it inherits the identical unreliability. Guarded
at all three sites -- the per-model assignments and the cross-model accumulator.
The values remain in the results file, annotated, for the record.

Note the cross-model mean divides by the TOTAL model count, not a per-observable
count, so a run mixing contributing and non-contributing models would silently
corrupt it. That is safe only because this is a run-level setting: every model in
a run either requires viscosity convergence or none does. Commented at the site.

Two fixes found by running it, not by the unit tests:

1. When the ladder cannot meet the tolerance it runs out onto its final rung,
   which is the unreduced model -- so the result is exact. It was still reporting
   the last inter-rung gap as the error bar (1.68% on a 246-bead test), which
   describes the coarser rung just discarded, not the answer returned. Honest,
   since it bounds a true error of zero, but plainly misleading. A ShellReport
   ::unreduced flag now zeroes the bars and the annotation states that the result
   is exact.

2. The same path wrongly marked viscosity unreliable. An unreduced solve has
   nothing to converge, so viscosity is exact regardless of what was requested;
   withholding it would have discarded a perfectly good value. viscosity_ok()
   now short-circuits on unreduced.

Six assertions cover both in tests/test_shell.cpp.

Verified end to end on a bead model via gui_script: the annotation reports
"98 of 98" beads (SOMO's ASA buried-bead exclusion having already reduced 246 ->
98, which is exactly why a model this size has nothing left to give), zero
estimated error, no viscosity warning, and every observable bit-identical to the
unreduced baseline. Clean libus_somo + app builds, no new warnings.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 95fec1cc63cc817cc419d9e1eeb4027a9793e0ad
      https://github.com/ehb54/ultrascan3/commit/95fec1cc63cc817cc419d9e1eeb4027a9793e0ad
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/doc: document GRPY shell reduction in the Hydrodynamics Options manual

Adds a section to somo_hydro.html covering the new "GRPY Shell Reduction"
box, placed after the GRPY buried-bead exclusion it generalizes, and bumps
the page's Last updated stamp.

Covers what the procedure does (repeated exact GRPY on progressively larger
subsets of the most solvent-exposed beads, stopping when successive results
agree, with the remaining error estimated by Richardson extrapolation), the
three controls and their defaults, and why the check is nearly free.

Gives particular attention to the "Require intrinsic viscosity" checkbox,
since the hydrodynamic quantities do not tolerate reduction equally: at equal
reduction the rotational diffusion error is ~1.8x the translational, and the
intrinsic viscosity ~3.3x and systematically one-sided. It is the viscosity
that determines when the procedure can stop, so requiring it costs much of
the speed gain; when not required, the viscosity and the Einstein radius
derived from it are withheld from the results rather than reported with a
caveat, while being retained and annotated in the results file.

Also answers, quantitatively, the question the preceding buried-bead
paragraph explicitly left open ("would need an in depth investigation",
supported only by preliminary 2021 tests on lysozyme): retaining >50% of
beads changes the translational diffusion coefficient by ~0.02%, 30-50% by
~0.12%, 20-30% by ~0.5%, degrading rapidly below ~10%. Buried-bead exclusion
typically retains ~40%, so that paragraph's regime is now measured. Notes
the reported error bounded the true deviation in all 344 validation tests.

Ends with the honest practical caveat: the gain grows with model size,
is negligible for small models, and on top of the default buried-bead
exclusion is typically 2-3x rather than the larger factors available from a
completely unreduced model -- the lasting benefit being the error estimate,
not the speed.

All markup uses named entities, so the page remains pure ASCII under its
declared ISO-8859-1 charset.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: c9ac1177ffb801eaf2ec54bf734b72f5329a554c
      https://github.com/ehb54/ultrascan3/commit/c9ac1177ffb801eaf2ec54bf734b72f5329a554c
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/doc: document the GRPY Numerical Precision control

The Double/Float control shipped with the in-process GRPY work but was never
documented -- a grep across the whole manual returned nothing for it, despite
the box sitting directly above the shell-reduction one just added. Same file,
same window, so it is described here in window order: buried-bead exclusion,
precision, shell reduction.

Explains what the setting actually governs (the precision in which the large
internal matrix is stored and factored), why it matters (that matrix holds
eleven quantities per bead and grows as the square of the bead count, so it
is what limits the treatable model size), and that the tensor arithmetic and
all subsequent processing stay in double precision regardless.

States the accuracy cost from validation rather than in the abstract: every
labelled observable is identical to the four significant figures displayed,
with only the near-zero reoriented coupling terms differing. Recommends
Double as the default nonetheless, since the benefit appears only when memory
is the binding constraint.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 35066a6c487776dbf7c210256ac1985ed5403000
      https://github.com/ehb54/ultrascan3/commit/35066a6c487776dbf7c210256ac1985ed5403000
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html
    M us_somo/somo/doc/manual/somo/somo_misc.html

  Log Message:
  -----------
  Merge remote-tracking branch 'origin/somo-dev' into ehb54-issue-984


  Commit: a782414c59388273e6decf00fb44cc9acfc3ee88
      https://github.com/ehb54/ultrascan3/commit/a782414c59388273e6decf00fb44cc9acfc3ee88
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/doc: drop duplicate precision paragraph, fix shell-reduction cross-ref

Merging somo-dev brought in its own "GRPY Numerical Precision" paragraph
(e34ae210, PR #497), written independently of the one added on this branch.
Git merged both cleanly since they landed in adjacent places, leaving the box
documented twice. Upstream's is the project's own wording and landed first,
so this drops ours and keeps it.

Also fixes a cross-reference this exposed. The shell-reduction paragraph was
written directly after the GRPY buried-bead paragraph and referred to "the
question raised in the paragraph above". Our precision paragraph was later
inserted between the two, silently breaking that reference; upstream's now
sits in the same position. Rather than depend on ordering again, the
reference now names the buried-bead option explicitly.

Box order in the page matches the window: buried beads, precision, shell
reduction. Page remains pure ASCII under its declared ISO-8859-1 charset.


  Commit: 3082031334ff5eac0bcbb03be8bac89be4457614
      https://github.com/ehb54/ultrascan3/commit/3082031334ff5eac0bcbb03be8bac89be4457614
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/grpy: cap the shell-reduction ladder by available memory

Merging somo-dev brought in the issue-987 pre-flight memory guard, which
refuses a GRPY run whose mobility matrix would exceed RAM. It sizes that
matrix from the FULL bead count, in calc_grpy_hydro(), before shell reduction
is applied in grpy_process_next() -- so it would turn away precisely the large
models shell reduction exists to make feasible. The ladder normally stops well
short of the full model, and memory goes as the square of the bead count, so a
run that stops at ~25% of the beads needs ~6% of the refused matrix.

Rather than weaken the guard, the budget is now passed into the ladder:

- ShellOptions::max_beads caps the largest rung. Checked after building the
  subset (cheap) but before the solve (expensive), so a rejected rung costs
  nothing; the ladder ascends, so the first rung over budget ends it.
- A capped ladder reports mem_capped and does NOT claim convergence. The
  result stands on its error bar, which is stated as usual and will generally
  exceed the requested target. It is never passed off as converged.
- The guard defers to shell reduction only when the budget admits at least the
  smallest rung; below that there is nothing to compute and it still refuses.
  When shell reduction is off, its message now offers the option, noting that
  unlike ZENO it still yields rotational diffusion.

So an oversized model now gives the best result that fits, with a quantified
error, instead of nothing.

Two supporting cleanups, both forced by the above: truthy() is promoted from a
lambda local to grpy_process_next() to a file-static, so the guard and the
solver setup resolve the same scripting overrides (the guard previously read
hydro.grpy_single directly and would have ignored a grpy_single override); and
the matrix-size estimate is factored into grpy_matrix_bytes() with
grpy_max_beads_for_ram() as its inverse, so the guard and the cap cannot drift
apart about what fits.

Tests cover a slack cap (must not perturb the unreduced, exact path), a
binding cap (capped, non-converged, bar finite, budget respected, explained in
the report), and a cap below the smallest rung. The last of these found a
latent crash: with no rung run, err_est was empty while require was not, and
the report loop indexed past the end. Guarded, and that case now states
plainly that nothing was computed.

Manual documents the lifted refusal in the shell-reduction section.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 137e3a50fa1da2dc17cd94386b3636fa5f130b7c
      https://github.com/ehb54/ultrascan3/commit/137e3a50fa1da2dc17cd94386b3636fa5f130b7c
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/src/us_hydrodyn_grpy.cpp

  Log Message:
  -----------
  somo/grpy: report ladder progress, and fix stray double percent in messages

Two reported problems with what the shell-reduction ladder shows while running.

PROGRESS. Every rung is a separate solve sweeping 0..100%, and each was
forwarded to the caller raw, so the bar restarted once per rung and the stage
text was only ever "Model 1 : inverting matrices" -- several solves looked
like one stalled repeating one. The ladder now maps each rung onto its share
of the whole run, weighted by predicted cost (~N^3), and prefixes the stage
with "rung i/n, N beads". The bar advances monotonically instead of
restarting. The denominator assumes the ladder runs to its last planned rung,
so converging early makes the bar jump to done, which is correct.

This is done by wrapping the callback inside the module, so the SOMO callback
signature is untouched and the non-shell path still passes progress through
verbatim (asserted by a test).

Also added ShellOptions::on_rung, called as each rung lands, wired to log the
bead count and the error estimate against the target. The ladder was otherwise
silent for its whole duration; now its convergence is visible as it happens.

PERCENT. Reported from a real run: "estimated error 0.489%%". QString::arg(),
unlike printf, has no "%%" escape -- it substitutes %1..%99 and passes every
other "%" through untouched -- so the literal "%%" written in five messages
reached the user verbatim. The tolerance line had it too ("tolerance 0.5%%").
Rather than rely on "%1%%2" parsing correctly (it does, but it is ambiguous to
read), the percent sign is now attached by grpy_pct(), which formats the whole
token. Verified against real Qt: the old form reproduces the reported string
exactly, the new one renders "0.489%".

The module's own report text was checked and is unaffected -- it formats via
snprintf, where "%%" is the correct escape.

Tests assert progress never goes backwards across a multi-rung ladder, stays
in 0..100, names its rung and bead count, and is left unmodified when shell
reduction is off.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 33a5e7950ed7181651b20283cf7affb0e0133b8c
      https://github.com/ehb54/ultrascan3/commit/33a5e7950ed7181651b20283cf7affb0e0133b8c
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/README.md
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/grpy: document the memory cap and progress reporting

Docs lagged the last two commits.

Module README gains two sections. The memory cap covers why a caller's
pre-flight refusal has to size the matrix from the full bead count and so
turns away the very models the ladder can handle, what max_beads does about
it, and the two contract points a caller must honour: a capped run reports
mem_capped and is never marked converged, and levels == 0 means there is no
result and the default-constructed Results must not be read.

The progress section records that ShellSolver wraps the ProgressFn it is
given rather than forwarding it per rung -- the reason the caller's bar no
longer restarts several times per model -- that the callback signature is
unchanged and passes through verbatim when disabled, and what on_rung
reports.

The manual gains one sentence in the shell-reduction description: each
calculation in the series is reported as it completes with its bead count and
error estimate, and the progress bar refers to the series as a whole. That is
what a user actually sees, and previously the page implied a single opaque
calculation.

INTEGRATION.md was checked and needs nothing -- it covers the drop-in core
solver and does not mention shell reduction at all.

Page remains pure ASCII under its declared ISO-8859-1 charset.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 501abad6b701746a02242da35fe66035d8ab2097
      https://github.com/ehb54/ultrascan3/commit/501abad6b701746a02242da35fe66035d8ab2097
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/README.md

  Log Message:
  -----------
  somo/grpy: document how to read ShellReport

The README covered the fields that decide what happened (converged,
unreduced, mem_capped, viscosity_unreliable) but not the ones a caller
actually reads out. Adds a field table.

The points that are not guessable from the names: err_est, extrapolated and
k_obs are parallel to require, not to anything else; k_obs == 0 means
extrapolation was declined, so err_est is the raw inter-rung gap and
extrapolated holds the final rung's value rather than an extrapolated one;
and run() returns the final rung's Results verbatim, so the extrapolated
values live only in the report -- deliberately, since overwriting the scalars
would contradict the report text embedded in them.

Also states that only `unreduced` licenses treating a result as exact.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 4e14d5f3e42909cec2118c512c263d4f04f916ec
      https://github.com/ehb54/ultrascan3/commit/4e14d5f3e42909cec2118c512c263d4f04f916ec
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_core.hpp
    M us_somo/develop/grpy/linalg.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp

  Log Message:
  -----------
  somo/grpy: keep the UI alive during assembly, factor and solve

The GUI froze for seconds at a time during "INVERTING MATRICES", and was dead
outright either side of it. Two causes.

The calling thread is a compute worker: QtConcurrent::blockingMap both blocks
the caller and uses it to run tasks, so the event loop cannot turn for the
whole duration of any for_range. Progress -- and therefore the processEvents()
that keeps the GUI breathing -- only ran between for_range calls.

And progress was emitted from exactly one place in the numeric core: once per
tile column of the Cholesky. Assembly (one for_range over ~4M pair blocks on a
2832-bead model) and solve reported nothing whatsoever. Within the factor, the
trailing update costs O((nt-k)^2), so the earliest columns are by far the
longest -- measured ~5 s each at dim 31152, decaying to milliseconds.

Assembly and the factor's trailing update are now chunked, with the chunk
self-tuning toward ~80 ms per slice (double under 40 ms, halve over 160 ms) so
the granularity holds across machines and model sizes rather than being a
constant tuned here. The factor retunes within a column, not just between
columns, because the first columns are exactly where the freeze was worst.
Solve reports per tile. The bar now spans 0-30 assemble, 30-90 factor,
90-100 solve.

The factor bar is also cost-weighted by trailing-update work completed instead
of being linear in the column index; since cost per column is quadratic, the
old bar crawled at the start and raced at the end.

With no callback -- the CLI and every test -- the work runs as a single chunk,
exactly as before.

Tests assert the chunked path is numerically INERT: same Dt, same eta, and a
byte-identical report versus the unchunked path. That check is the real guard,
since the goldens only ever exercise the no-callback path. It earned its place
immediately: the first version advanced the loop by `chunk`, which is retuned
inside the loop, so a doubling silently skipped beads and corrupted the matrix.
Both loops now advance by what was actually processed. Also asserts progress is
monotone, stays in 0..100, and that no phase is silent.

Tick count is deliberately not asserted tightly -- the chunker targets a wall
time, so a small model that finishes fast correctly uses few large chunks.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 7578f39c336cf8e683897592b08a4377d280cfed
      https://github.com/ehb54/ultrascan3/commit/7578f39c336cf8e683897592b08a4377d280cfed
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/grpy: GRPY_SHELL_MAX_BEADS override, and report the cap every run

The shell-reduction bead cap was derived solely from physical RAM, with no way
to move it. That made the memory-capped path effectively untestable: the ladder
can only reach the cap by FAILING on the rung below it, so on a model large
enough to have a cap at all, exercising it means paying for that rung first --
5h44m on a real 11328-bead model, which then converged one rung short of the
cap anyway and never exercised it.

GRPY_SHELL_MAX_BEADS in the environment, or grpy_shell_max_beads as a script
parameter, now overrides it. With a small value the same model reaches the cap
in seconds. Env/script only with no GUI control, matching grpy_ooc_dir: a
diagnostic and shared-machine knob rather than a user setting. It is also the
only way to hold GRPY under a chosen footprint on a machine shared with other
work, which the 70%-of-RAM rule cannot express.

Honoured in both directions. A value above what memory supports is flagged
rather than silently clamped -- overriding is deliberate, and clamping would
make the reported cap a lie.

Reported every run, which is the point: an override changes which results are
obtainable, and a stale environment variable would otherwise silently bound
every calculation with nothing on screen to say so. With no override and shell
reduction on, the memory-derived cap and the RAM it came from are printed
instead, so the limit is never a mystery. Both go to the progress window, once
per run.

The guard and the ladder resolve the cap through one shared function. If they
resolved it separately the guard could admit a run the ladder then refuses to
compute, or refuse one it could have done.

Manual documents the variable in the shell-reduction section; the parameter is
also added to the gui_script reference ticket (ultrascan-tickets#990).

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: c4e049f8e84edc4de5df9b7af5baf5d6e61338d2
      https://github.com/ehb54/ultrascan3/commit/c4e049f8e84edc4de5df9b7af5baf5d6e61338d2
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/doc: bump somo_hydro last-modified to the actual last edit date

Content was current; only the stamp lagged.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: f3e555a32854d3ee39df4d5df0901640384580c2
      https://github.com/ehb54/ultrascan3/commit/f3e555a32854d3ee39df4d5df0901640384580c2
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_grpy.cpp

  Log Message:
  -----------
  somo/grpy: name the worst observable, and stop announcing an inert cap

Both found by a real capped run (3GUT vdW, GRPY_SHELL_MAX_BEADS=2000).

The result line quoted err_max alone: "estimated error 3.85%". That is the
MAX over the requested observables, and intrinsic viscosity runs ~3.3x the
error of D_t at equal reduction, so on an unconverged run the single number
quoted is essentially always the viscosity's. Reading it as the accuracy of
the whole calculation makes D_t look several times worse than it is -- in that
run, 1416 of 11328 beads kept implies a D_t bar near 1.15%, not 3.85%.
ShellReport::worst was already computed for exactly this and had never been
shown; it is now named, with a following line pointing at the per-observable
estimates in the results file. Suppressed on an exact (unreduced) result,
where every bar is zero and "worst" means nothing.

The cap override also announced itself when shell reduction was OFF, where it
bounds nothing: the same run printed "bead cap OVERRIDDEN to 2000" and then
refused the model on memory, implying the 2000 had caused the refusal when it
was irrelevant. It now says so when inert, rather than going silent -- a stale
environment variable should still be visible.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: faf147fa301e7485b3f1f68bb4c54fceee7f4c70
      https://github.com/ehb54/ultrascan3/commit/faf147fa301e7485b3f1f68bb4c54fceee7f4c70
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_load.cpp

  Log Message:
  -----------
  somo: silence the per-atom debug dump at the end of calc_mw()

It printed "end of calc_mw()" and then one CSV row per atom on every PDB load
-- thousands of lines for a large structure -- which slows a debug cycle when
stdout is a terminal or an editor shell.

Commented rather than deleted: it is diagnostic, not dead, and is the thing to
re-enable when ionization or net-charge numbers look wrong. Left with a note
of the caveat that made it misleading anyway -- its pH is hardwired to 7 rather
than read from the form, so it does not describe the pH actually in use; the
already-commented call immediately above is the variant that does.

Matches the several other commented-out info_* diagnostics around it.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 0c57fbfc96e735c40ff26c82c9a1b31bfeeb0916
      https://github.com/ehb54/ultrascan3/commit/0c57fbfc96e735c40ff26c82c9a1b31bfeeb0916
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_core.hpp
    M us_somo/develop/grpy/linalg.hpp

  Log Message:
  -----------
  somo/grpy: rate-limit progress calls; they cost more than the work they report

A run whose ladder stopped at the same rungs as an earlier one took 8m20s
against 4m09s. Requiring intrinsic viscosity adds no computation -- it only
changes when the ladder stops -- so the doubling was the progress reporting
added in 4e14d5f3.

Progress has two independent problems and one knob cannot solve both:

  FREEZE LENGTH -- the calling thread is a compute worker, so the event loop
  cannot turn until the current slice ends. Fixed by sizing the slice.

  CALL RATE -- the callback itself costs something, paid per call however
  short the slices are. In SOMO it is qApp->processEvents() against an editor
  buffer that grows all session, which is why this hurt a long run and not a
  short one. Nothing controlled this at all: solve() ticked once per tile,
  up to 2*nt calls -- hundreds on a large model -- against an operation that
  is cheap next to the factor.

tune_chunk now times only the work, never the callback. Timing them together
made an expensive callback SHRINK the chunk, which raises the call rate and
makes the overhead worse -- the opposite correction. That defect was real but
was not the main cost: fixing it alone moved 80 calls to 73.

ProgressGate is the actual fix. It rate-limits emissions against what the
callback measurably costs: emit after ~10x the last callback's duration, and
never more often than every ~80 ms. A cheap callback is essentially
unthrottled; an expensive one throttles itself to ~10% overhead instead of
dominating. Applied to assembly, factor and solve.

Measured, 512-bead model with a 20 ms callback: 80 calls and 2.79x the whole
solve before, 11 calls and 1.03x after. A trivial callback is 0.98x, i.e. the
chunking machinery itself costs nothing.

The tradeoff is deliberate: frequent repaints and low overhead are not both
achievable when a repaint is expensive. This favours throughput, so the bar
updates less often exactly where updating is costly.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 8599c88d5ecbfe77c3688ccf953369d0afd7017f
      https://github.com/ehb54/ultrascan3/commit/8599c88d5ecbfe77c3688ccf953369d0afd7017f
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/doc: say that the quoted shell-reduction error is the largest over quantities

The page said the achieved error is written to the results file, but not that
the single figure shown in the progress window is the MAXIMUM over the
requested quantities. Since intrinsic viscosity converges far more slowly than
the rest, that figure is usually its, and reading it as the accuracy of the
translational diffusion coefficient understates the latter severalfold -- a
misreading a real run produced.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 8dcc49382cade039accd9f266f8b37ac37448176
      https://github.com/ehb54/ultrascan3/commit/8dcc49382cade039accd9f266f8b37ac37448176
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_grpy.cpp

  Log Message:
  -----------
  somo/grpy: exclude the overwrite prompt from the GRPY timer

"Time to process" spans the whole run, and the results-writing path prompts
for a filename whenever an output file exists and overwrite_hydro is false.
That prompt is modal, so the operator's response time was counted as compute.
Measured directly: the same binary, model and settings gave 5m 00s answering
slowly and 3m 49s answering quickly -- 71 s of pure click latency, varying per
run, which makes interactive GRPY timings incomparable with each other.

It also cost real analysis time here: a 21% gap between two runs was
attributed to machine contention on the strength of a microbenchmark that
bounded the alternative explanation at ~3%. The bound was right; the
conclusion was wrong. The missing time was the operator.

US_Timer already supports this exactly: stop_timer banks the interval so far
WITHOUT counting a completion, and start_timer restarts from zero, so
stop/start is pause/resume and the paused span is never accumulated. No change
to US_Timer. An RAII guard applies it at the four prompt sites that fall inside
the timed region; the fifth call runs before the timer starts and is untouched.

The prompt itself is deliberately unchanged -- this only stops it being counted
as compute.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 104ed92bc56aef41aaa36f39ccb10212f3422f74
      https://github.com/ehb54/ultrascan3/commit/104ed92bc56aef41aaa36f39ccb10212f3422f74
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_exposure.hpp
    M us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/include/us_hydrodyn.h
    M us_somo/develop/include/us_hydrodyn_hydro.h
    A us_somo/develop/revision.sh
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/develop/src/us_hydrodyn_hydro.cpp
    M us_somo/develop/src/us_hydrodyn_settings.cpp
    M us_somo/develop/src/us_hydrodyn_write.cpp
    A us_somo/develop/us_saxs_cmds_t/moc_predefs.h
    A us_somo/develop/us_saxs_cmds_t/moc_us_cmdline_app.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/grpy: optionally write and display a bead model of each shell rung

A "Save shell bead models" checkbox in the GRPY Shell Reduction box. When set,
the reduced model used at each ladder rung is written and opened in the viewer,
so the shell can be seen thickening as the ladder converges and the retained
beads inspected directly. For validating the reduction by eye; off by default.

Files go to <somo>/tmp as <model>-shell-rung-<n>, overwritten without prompting
-- they are regenerated every run, and prompting per rung would add a modal
dialog per rung to a diagnostic.

The mapping from the shell report back to beads was the part worth care.
ShellReport now records the indices its ranking selected (ShellOptions::
record_subsets, off by default, so nothing is paid when unused), and those
index the bead list handed to GRPY -- i.e. the .grpy file, which holds the
beads that are active and, unless buried beads are included, not buried, in
`use_model` order. That order is NOT bead_model order: bead_output.sequence == 1
reorders into exposed-sidechain / exposed-main-chain / buried. Taking
bead_model order would therefore have written models of the wrong beads,
silently and plausibly.

To make that impossible rather than merely correct today, the ordering is
extracted from write_bead_model() into US_Hydrodyn::bead_model_output_order()
and both callers use it. The writer also refuses and says so if an index falls
outside the rebuilt list, rather than emitting a wrong picture.

Selection now returns indices (reduce_top_frac_idx), which also removes the old
coordinate-matching rebuild -- O(keep*N), some 32M comparisons on an
11328-bead model -- with reduce_top_frac kept as a wrapper so existing callers
and tests are unaffected.

Tests: recorded subsets are sized to their rung, in range, unique, nested
(each rung contains the previous), complete on the full rung, absent unless
requested, and identical to the ranking's own selection.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 7d245291648635d6ac78b2781985c39d165d406a
      https://github.com/ehb54/ultrascan3/commit/7d245291648635d6ac78b2781985c39d165d406a
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    R us_somo/develop/revision.sh
    R us_somo/develop/us_saxs_cmds_t/moc_predefs.h
    R us_somo/develop/us_saxs_cmds_t/moc_us_cmdline_app.cpp

  Log Message:
  -----------
  somo: untrack revision.sh and moc artifacts committed by mistake

The previous commit staged with `git add -A us_somo`, which swept in three
files that were untracked for good reason:

- us_somo/develop/revision.sh -- somo-dev deliberately removed this from the
  tree; each clone supplies its own untracked copy, and qmake runs it as a
  FORCE target on every build. Tracking it both reverses that decision and
  makes every build dirty the working tree.
- us_saxs_cmds_t/moc_predefs.h, moc_us_cmdline_app.cpp -- moc output, i.e.
  build artifacts.

Tracking them also broke `git pull` for anyone holding their own copies:
"untracked working tree files would be overwritten by merge".

Removed from the index only; the local files are untouched. Anyone whose pull
was blocked can now pull cleanly -- the net change across the two commits does
not touch these paths, so their local copies are left alone.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: f9bdfd141549a30df1cab9c799cc167486a11659
      https://github.com/ehb54/ultrascan3/commit/f9bdfd141549a30df1cab9c799cc167486a11659
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/somo/doc/manual/somo/somo_hydro.html
    A us_somo/somo/doc/manual/somo/somo_hydro_shell_reduction.html

  Log Message:
  -----------
  somo/doc: theory page for GRPY shell reduction, with the mathematics and references

The user-facing documentation named Richardson extrapolation in a single
sentence and stopped there. The mathematics existed only in the module README
and the header comments, neither of which a SOMO user reads.

New page somo_hydro_shell_reduction.html, following the existing theory-page
pattern (cormap.html, IntegralBaselineTheory.html), linked from the sentence
that previously stood alone. It covers why bead count dominates the cost, how
the subsets are chosen and why by fraction rather than by exposure threshold,
the extrapolation itself, the safeguards, per-quantity convergence, the extent
of the testing, and what the option is and is not for.

Every formula and constant on the page was checked against the implementation
rather than written from memory: the order equation, the remaining-error and
extrapolated-value expressions, the clamp range, the 1.5 safety factor, the
half-the-raw-gap floor, the default ladder, and the 8/7 geometric cost.

Two things it makes explicit that the documentation did not say anywhere:

- The extrapolation needs THREE subsets. A run that stops after two -- because
  it converged at once, or because memory allowed no more -- reports the raw
  inter-rung difference instead. Still conservative, but cruder, and decided by
  how the ladder happens to stop rather than by anything the user set.
- "Shell reduction" is NOT the classical shell model. That method replaces the
  particle with surface beads and extrapolates to zero bead radius; this one
  retains existing beads and extrapolates to the complete model. Same physical
  motivation, both extrapolations to a limit, different procedures -- and an
  easy confusion for exactly this audience.

References: Richardson 1911 and Richardson & Gaunt 1927 for the method; Roache
1994/1998 for the three-level observed-order practice this follows (the Grid
Convergence Index, with bead count in place of grid spacing); Shrake & Rupley
1973 for the exposure calculation; Garcia de la Torre & Bloomfield 1981 and
Carrasco & Garcia de la Torre 1999 for the shell-model context distinguished
above; Zuk et al 2018 and Brookes & Rocco 2018 for GRPY and SOMO.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 908b2ffbbc38781d8e1125ca14ee1184630ed3e2
      https://github.com/ehb54/ultrascan3/commit/908b2ffbbc38781d8e1125ca14ee1184630ed3e2
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/doc: link the shell-reduction theory page from the section head and the accuracy text

The theory page had a single inbound link, buried in the second of eight
paragraphs. A reader arriving at the box heading, or skimming to the accuracy
figures, would not have seen it.

Now linked from three places, each where a reader would want it: the opening
paragraph, which poses the question the page answers; the Richardson sentence,
which names the method; and the accuracy paragraph, from which the natural next
question is how far the estimate has been tested.

Dates: both stamps on both pages already read the current date, so nothing to
bump. somo_misc.html carries an earlier date but is upstream's edit, correctly
stamped for itself.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 7af0d35d528bfa46d51f00796442cdaf4053e1e1
      https://github.com/ehb54/ultrascan3/commit/7af0d35d528bfa46d51f00796442cdaf4053e1e1
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/include/us_hydrodyn.h
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/develop/src/us_hydrodyn_hydro.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/grpy: show each shell model as it is made, and let Stop end the ladder

Two changes that only make sense together.

The shell models were written in a batch after the whole ladder finished, which
is the least useful moment: by then there is nothing left to decide. They are
now written and displayed as each rung completes, so a shell that is obviously
wrong can be seen while the calculation is still running.

That is worth nothing unless the run can then be stopped, and it could not.
stopFlag was consulted only inside the progress callback, where it skipped a UI
update and nothing else -- pressing Stop did not end a running GRPY calculation
at all. ShellOptions::should_stop is now checked between rungs and wired to
stopFlag, so Stop ends the ladder at the end of the rung in progress. Since each
rung costs roughly eight times the one before, stopping before the next begins
saves nearly all of what remained.

A stopped run keeps what it computed, reports its error bar, and is marked NOT
converged -- the same treatment as a memory-capped one, and never passed off as
if it had converged. Stopping before any rung ran leaves levels == 0 and no
result, which the caller must detect, again as with the memory cap.

Between rungs only: a rung already running goes to completion, since the solve
has no interior abort. Adding one would mean threading cancellation through the
factorization, which is a larger change for much less benefit.

The per-rung write reads srep.kept.back() from the live report, which is sound
because the module records each rung's selection before calling on_rung.

Also widens the options window to 740 and lays the shell-reduction box out in
two rows. That box carries six controls, about a hundred characters of label
text -- half again the widest of the other boxes -- and was clipped on the
right. Two rows drops what it demands to roughly a single row's width, so it
survives larger fonts rather than merely clearing today's threshold; the width
increase is headroom, not the fix.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: f6e539f5bbfeba71515771ef73c4f709bc08b8ef
      https://github.com/ehb54/ultrascan3/commit/f6e539f5bbfeba71515771ef73c4f709bc08b8ef
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_grpy.cpp

  Log Message:
  -----------
  somo/grpy: fix build -- srep was captured by a lambda declared before it

The on_rung lambda added in the previous commit captures srep by reference to
write each rung's shell model as it lands, but srep was declared 26 lines
further down. Moved ahead of the lambda.

This should not have been pushed. It was not caught because the build that
"passed" never compiled this file: both build stages write the same Makefile,
so after an earlier `qmake us_somo.pro` every plain `make` rebuilt only the
apps and linked them against a stale library. The object file was three hours
older than the source.

Verified this time by compiling the translation unit directly, and the full
two-stage sequence (qmake libus_somo.pro && make && qmake us_somo.pro && make)
is what the build notes now require, with an object-newer-than-source check
before any claim that a build passed.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: cb2e4b397ff038bff7ddc99751f1312364cf672d
      https://github.com/ehb54/ultrascan3/commit/cb2e4b397ff038bff7ddc99751f1312364cf672d
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html

  Log Message:
  -----------
  somo/grpy: name shell models for the settings that produced them

The shell bead models were named only for the model, so runs differing in
target accuracy, in whether intrinsic viscosity had to converge, or in
precision produced identically-named files that overwrote each other. Each of
those settings changes which beads are retained, so those are exactly the
files one would want to compare -- and could not.

Encoded in SOMO's existing style (short mnemonic + value, '.' -> '_', present
only when the option is set, as with PR1_4, TH10, pH7, A20, hy, G4):

  ..._R1PR1-SR0_5eta-shell-rung-3      0.5% target, viscosity required
  ..._R1PR1-SR1noeta-shell-rung-3      1% target, viscosity not required
  ..._R1PR1-sp-SR0_5eta-shell-rung-3   as the first, in single precision

Only non-defaults appear, so `sp` is absent from a double-precision run just
as `hy` is absent when hydration is off.

NOT applied to the .grpy_res or .grpy.csv result files, which have the same
collision -- a shell-reduced result and an unreduced one share a name. That is
a wider change (four sites in grpy_finished, a member to carry the suffix,
and names that batch runs may depend on) and is left as a decision rather than
taken unasked.

Built with the full two-stage sequence and the object verified newer than the
source, per the previous commit.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 21d60309ba107bfc69de60554c60a35ba4734d54
      https://github.com/ehb54/ultrascan3/commit/21d60309ba107bfc69de60554c60a35ba4734d54
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/include/us_hydrodyn.h
    M us_somo/develop/include/us_hydrodyn_save.h
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/develop/src/us_hydrodyn_save.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html
    M us_somo/somo/doc/manual/somo/somo_save.html

  Log Message:
  -----------
  somo/grpy: settings suffix on result files, GRPY options in the saved CSV

Extends the shell-model naming of the previous commit to the .grpy_res and
.grpy.csv result files, which had the same collision: a reduced and an
unreduced calculation of one model, or two at different target accuracies,
produced different numbers under the same name. Only non-defaults appear, so
a run with shell reduction off in double precision writes exactly the names it
always has.

Also records the settings IN the saved file, which distinct names alone do not
give you -- a new "GRPY options:" screen in the parameter selector with six
entries: single precision, shell reduction, target accuracy, viscosity
required, the estimated error achieved, and the quantity that error belongs
to. Tabs are built from the section list, so the screen appears without a count
to update anywhere.

The estimated error is the one that matters. The CSV is what gets compared
across runs, and until now it recorded the numbers with no uncertainty attached
to them.

These record the EFFECTIVE settings, not the dialog's: this_data.hydro now
comes from a copy carrying any scripting or environment overrides, so a headless
run reports what it actually used. Saving the dialog values would have been
wrong precisely in the batch case where nobody would notice.

FIX, found while checking this: used_beads was the count from setup, taken
before any reduction, so a run using 2832 of 11328 beads reported 11328 in the
CSV, the results table and the .grpy_res report line alike. Narrowed to what
the ladder actually used, never widened, so shell-off runs are unaffected.

Batch mode needs nothing: it enters through the same calc_grpy_hydro(), so the
guard, the reduction, the cancellation and these names all apply, and the
suffix is cleared at run start so a batch cannot inherit stale settings.

Docs: somo_save.html goes from eight screens to nine, describes the six
parameters, notes that the achieved error may exceed the target when stopped
early or memory-limited, and records the used_beads correction.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: c34218209176ed1ab8c5b034b498a5319abf6405
      https://github.com/ehb54/ultrascan3/commit/c34218209176ed1ab8c5b034b498a5319abf6405
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-07 (Fri, 07 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_save.cpp
    M us_somo/somo/doc/manual/somo/somo_save.html

  Log Message:
  -----------
  somo: shorten the save-parameters tab titles and widen the window

Nine tabs of titles no longer fitted; adding the GRPY options screen is what
tipped it over. Measured: the titles wanted 1818 px of tab bar in a 640 px
window.

Multi-line titles were tried first and do not work. Qt keeps a tab's height
fixed regardless of newlines -- verified at one, two and three lines, all 36 px
-- so the extra lines are simply clipped. Newlines do narrow the bar (1818 ->
1255 px) because the width is measured from the longest line, but 1255 still
does not fit, and making the height follow would need a custom QTabBar
overriding tabSizeHint().

Shortening instead: "Additional" and the trailing colons carry no information
in a window already titled Select Parameters to be Saved. That gives 846 px,
which fits an 880 px window (640 -> 880) with room to spare. Robust across
styles and font sizes in a way that a width bump alone would not have been.

  Main hydro | SMI | ZENO | GRPY | GRPY options | vdW | Solvent | ASA |
  Fractal dim.

Manual updated so the documented names match the screen, including two things
the rename exposed: a paragraph still calling one screen by its old name, and
a doubled period where a name ending in "." met the end of a sentence.

Measurements were taken under Qt's offscreen platform, which may not use the
same style as a live macOS build, so the pixel figures are indicative; there is
~34 px of headroom at 880.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: e890967d575f7a8f9f5d487ac5ad6352207a0e4d
      https://github.com/ehb54/ultrascan3/commit/e890967d575f7a8f9f5d487ac5ad6352207a0e4d
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-08 (Sat, 08 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_hydro.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html
    M us_somo/somo/doc/manual/somo/somo_hydro_shell_reduction.html

  Log Message:
  -----------
  somo/grpy: name the shell viscosity checkbox for what it gates

" Require intrinsic viscosity " read as an on/off switch for the quantity
itself. Because a disabled checkbox keeps its setting, unchecking it with shell
reduction on and then switching the reduction off left an unchecked, greyed-out
box that looked as though viscosity had been turned off for ordinary GRPY runs
too -- and could not be turned back on without re-enabling the reduction.

It never was off, and no behaviour changes here. With shell reduction off,
ShellSolver::run() returns on the unreduced path having set
viscosity_unreliable false; that flag is additionally cleared at the start of
every run, there is exactly one solver construction site so nothing bypasses
it, and it is the only thing that withholds [eta] or the viscosity-derived
Einstein radius from the reported results. The setting reaches nothing else:
the result-file name suffix and the non-default-settings report are both gated
on grpy_shell.

So this is a naming fix. "to converge" says that the box governs when the series
of calculations may stop, not whether the quantity is computed -- intrinsic
viscosity comes from the same matrix as every other quantity, at no additional
cost, and is therefore always computed. A tooltip states the same in the window
itself, the first in this one, following the newer MALS/DAD screens.

The manual had it right but under the old name. Both pages are renamed to match,
and somo_hydro.html now says explicitly that the checkbox bears only on the
stopping decision, that the quantity is always computed, and that the retained
setting has no effect at all while the option is off.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 2604d632f4f228744bec8377554f0b68dffd8328
      https://github.com/ehb54/ultrascan3/commit/2604d632f4f228744bec8377554f0b68dffd8328
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-08 (Sat, 08 Aug 2026)

  Changed paths:
    M us_somo/develop/src/us_hydrodyn_hydro.cpp

  Log Message:
  -----------
  somo/grpy: break the shell viscosity tooltip onto sense-lines

The tags make Qt treat the tip as rich text, so the lines fall where the sense
does rather than at whatever width a single long line happens to take. The point
that answers the question which prompted the rename -- that intrinsic viscosity
is always computed, and always reported when shell reduction is off -- now
stands on its own line instead of trailing a paragraph.

Fixes ehb54/ultrascan-tickets#984

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>


  Commit: 666cc4e110976161fe60504a88064bad4e751e3e
      https://github.com/ehb54/ultrascan3/commit/666cc4e110976161fe60504a88064bad4e751e3e
  Author: emre brookes <ehb54 at users.noreply.github.com>
  Date:   2026-08-08 (Sat, 08 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/README.md
    M us_somo/develop/grpy/grpy_core.hpp
    A us_somo/develop/grpy/grpy_exposure.hpp
    A us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/linalg.hpp
    M us_somo/develop/grpy/tests/run.sh
    A us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/include/us_hydrodyn.h
    M us_somo/develop/include/us_hydrodyn_hydro.h
    M us_somo/develop/include/us_hydrodyn_save.h
    M us_somo/develop/libus_somo.pro
    M us_somo/develop/src/us_hydrodyn_grpy.cpp
    M us_somo/develop/src/us_hydrodyn_hydro.cpp
    M us_somo/develop/src/us_hydrodyn_load.cpp
    M us_somo/develop/src/us_hydrodyn_save.cpp
    M us_somo/develop/src/us_hydrodyn_settings.cpp
    M us_somo/develop/src/us_hydrodyn_write.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html
    A us_somo/somo/doc/manual/somo/somo_hydro_shell_reduction.html
    M us_somo/somo/doc/manual/somo/somo_save.html

  Log Message:
  -----------
  Merge pull request #496 from ehb54/ehb54-issue-984

somo: GRPY shell reduction — self-validating bead-count reduction with reported error bars


Compare: https://github.com/ehb54/ultrascan3/compare/ddeddebe73c2...666cc4e11097

To unsubscribe from these emails, change your notification settings at https://github.com/ehb54/ultrascan3/settings/notifications


More information about the us-commits mailing list