[us-commits] [ehb54/ultrascan3] 64de26: somo/grpy: optionally write and display a bead mod...

emre brookes noreply at github.com
Tue Aug 18 09:45:25 MDT 2026


  Branch: refs/heads/ehb54-issue-1016-somo-merge-2
  Home:   https://github.com/ehb54/ultrascan3
  Commit: 64de263a8d3652904146a29834262555665eb4de
      https://github.com/ehb54/ultrascan3/commit/64de263a8d3652904146a29834262555665eb4de
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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
    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
    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: 2c3340cfae89a4327875bf557a92be2496e695e4
      https://github.com/ehb54/ultrascan3/commit/2c3340cfae89a4327875bf557a92be2496e695e4
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: 6890857538a071c14ac2259e2729f02f25e1845f
      https://github.com/ehb54/ultrascan3/commit/6890857538a071c14ac2259e2729f02f25e1845f
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: 76bb13833ea7c6aa7a21baf1f95f5fb5c6660544
      https://github.com/ehb54/ultrascan3/commit/76bb13833ea7c6aa7a21baf1f95f5fb5c6660544
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: 551a81a6b14082989eb4d86d0e2b0fb673f45a1c
      https://github.com/ehb54/ultrascan3/commit/551a81a6b14082989eb4d86d0e2b0fb673f45a1c
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: 615f53bf508720ba14420bac19c2e257e2de4485
      https://github.com/ehb54/ultrascan3/commit/615f53bf508720ba14420bac19c2e257e2de4485
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: f42ba901aa57c168171940cf412e6ef3198cd20f
      https://github.com/ehb54/ultrascan3/commit/f42ba901aa57c168171940cf412e6ef3198cd20f
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: c8693bcb2c4f5eb5b18325d8870677817a951277
      https://github.com/ehb54/ultrascan3/commit/c8693bcb2c4f5eb5b18325d8870677817a951277
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: e23ab8ab3cb6bd509ef2f14142aea561d08625ba
      https://github.com/ehb54/ultrascan3/commit/e23ab8ab3cb6bd509ef2f14142aea561d08625ba
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: 392aa0b584154cd8c1a983f156b2b34b6d6cffd2
      https://github.com/ehb54/ultrascan3/commit/392aa0b584154cd8c1a983f156b2b34b6d6cffd2
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: 8f3797b286908f4a1a61fd69fd95ab7178b322ed
      https://github.com/ehb54/ultrascan3/commit/8f3797b286908f4a1a61fd69fd95ab7178b322ed
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 Aug 2026)

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

  Log Message:
  -----------
  somo/grpy: report the computed and extrapolated values side by side

The shell report gave the estimated error but only one value, and an audit of the
development benchmarks turned up why that matters: the research harness that produced
them accepted the Richardson-extrapolated value as the answer and scored the error
estimate against ITS deviation, while the shipped solver returns the finest rung's own
value and delivers the extrapolated one separately. Those are two different claims. On
the same archived runs the estimate covers the extrapolated value's deviation in 92 of
92 cases but the finest rung's in 82 of 92, so which value is reported decides what the
error estimate means. The module's own tests happened to score the finest rung, which is
why the divergence went unnoticed.

Reporting both settles it. The scalars keep the computed value -- a reported result
should be one the program computed, not one inferred from a trend -- and the report now
prints the extrapolated value beside it, with the estimate stated as the distance
between the two and a sentence saying which column the results elsewhere in the file
are. A reader shown one column alone cannot tell what the estimate applies to, which is
the entire content of the estimate.

ShellReport gains `reported`, the finest rung's value per requested observable, parallel
to the existing `extrapolated`. It is what Results carries, and a test asserts that
equality exactly -- if they ever diverge, the report and the returned scalars would be
describing different answers. A further test asserts the two columns are genuinely
distinct, so the block cannot pass by printing one number twice.

Manual updated to describe both columns and to say plainly that they are two answers
rather than a value and a correction to it.

Fixes ehb54/ultrascan-tickets#984

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


  Commit: 89a6773a3ecda473f45eb69c164f3475a9e64b62
      https://github.com/ehb54/ultrascan3/commit/89a6773a3ecda473f45eb69c164f3475a9e64b62
  Author: ehb54 <brookes at uthscsa.edu>
  Date:   2026-08-18 (Tue, 18 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: record what the ladder did, so a validation run can be audited

Three additions, all in service of re-validating shell reduction against a frozen
implementation rather than against benchmarks whose provenance cannot be reconstructed.

ShellReport::values -- every rung's value for every requested observable. The ladder
already computes this; it was a local. Exposed, any variant of the estimator can be
recomputed from the stored sequence instead of re-solving, which is the difference
between seconds and hours per corpus when comparing estimators.

ShellReport::prov -- which mechanism produced each observable's estimate: whether
Richardson succeeded or the raw gap was used and why it declined, whether the observed
order was pinned at either clamp, and whether the 2x tightening floor set the number
rather than the extrapolation. This matters more than it sounds: on the archived
development runs the extrapolation tightened the estimate in only ~11% of cases, so most
of the reported margin came from the gap and the safety factor -- a fact that had to be
inferred from a separate harness's columns because the solver recorded none of it.

grpy_bead_inclusion scripting override -- the run already honours grpy_single,
grpy_shell, grpy_shell_tol, grpy_shell_require_eta, grpy_shell_max_beads and
grpy_ooc_dir, but not this one, so the screened and unscreened cases could not be swept
headlessly. Unlike the others it sets hydro rather than a call-site local, because bead
selection happens in several places across a run including after calc_grpy_hydro
returns; applied for the run and announced, since a silent change to which beads are
used would invalidate every number the run produces.

Tests: values has one row per rung and one column per observable, its last row IS the
reported value, and it changes between rungs (so it cannot pass by copying one rung);
prov is self-consistent (extrapolated xor declined, k_obs set iff extrapolated, no
safeguard flags when extrapolation did not run, never both clamps); and a deliberately
two-rung ladder declines to extrapolate and says why. Full module suite passes, clean
libus_somo and app builds.

Fixes ehb54/ultrascan-tickets#984


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

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

  Log Message:
  -----------
  somo/grpy: an exact result must not carry a stale error estimate

When the rung just solved is the full model the answer is exact and every bar should be
zero, but the loop tested the tolerance first and the full-model case second. If the full
rung also happened to satisfy the tolerance, the run exited as merely "converged", kept
the inter-rung gap as its estimate, and left `unreduced` false. Measured on a 216-bead
model at tol=0.5%: the ladder reached all 216 beads, the value was bit-identical to the
unreduced solve, and the report claimed 0.159% on a true error of zero.

The value was never wrong and the estimate was conservative rather than misleading, but
"converged, 0.16% estimated error" is a false statement about an exact calculation, and
`unreduced` is the flag the report and the results file use to say the answer is exact.

Test the full-model condition first, and widen it to cover n_used >= n_full so it does not
depend on the nominal fraction matching the realised bead count.

The existing exhaustion test missed this because it used tol=1e-12: unsatisfiable by
construction, so the tolerance branch could never win the race. The new test uses a
satisfiable tolerance on a ladder ending at 1.0 and asserts the full rung is flagged
unreduced, that err_max and every per-observable estimate are zero, that the result equals
the plain unreduced solve, and that viscosity is not flagged unreliable.

Found by a smoke run of the re-validation driver, which prints the retained fraction beside
the estimate: a row reading keep_frac=1.0, true error 0.000000, estimate 0.159% cannot all
be true at once. Worth landing before those runs, since full-rung rows would otherwise be
scored as an 0.16% estimate against a true error of zero -- inflating the reported
conservatism and hiding that the answers are exact.

Fixes ehb54/ultrascan-tickets#984


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

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

  Log Message:
  -----------
  somo/grpy: break exposure ties on geometry, not on input order

Exposure is quantised to the K surface sample points, so ties are large -- at ~2500 beads
the mean tie class holds ~90 of them, and the boundary between ladder rungs generally falls
inside one. Whatever breaks those ties therefore decides a large part of which beads are
kept, and it was breaking them by bead index: the order the beads happened to appear in the
input file.

Measured before this change, by permuting the input order of real models with geometry and
radii held fixed: a 3712-bead model gave a different retained subset in 20 of 20
permutations, and two more models in 20 and 17 of 20. Little downstream moved -- the
stopping rung was invariant, reported values shifted by at most 0.0098%, and all 240 runs
met their tolerance -- so this was never a correctness bug. But a selection that changes
when a file is rewritten cannot be reproduced from the model alone, which is worth more
than the noise it caused.

Ties now break by radius, then by distance from the model centroid, then by coordinate, and
only then by index. Farthest-first among equally exposed beads follows the same argument
that motivates ranking by exposure at all: the outermost contribute most to the drag. The
index fallback survives only for beads identical in exposure, radius and position, which are
interchangeable anyway.

A test permutes a model eight times and asserts the retained set, compared by coordinates
rather than indices, is unchanged. It fails on the previous tie-break.

This changes which beads are selected, so every frozen validation figure must be regenerated
against it; the tag will move.

Fixes ehb54/ultrascan-tickets#984

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


  Commit: 6cc1fe8f0753441ee2c3b081f1d19529f41174e7
      https://github.com/ehb54/ultrascan3/commit/6cc1fe8f0753441ee2c3b081f1d19529f41174e7
  Author: Emre Brookes <emre.brookes at umontana.edu>
  Date:   2026-08-18 (Tue, 18 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_shell.hpp

  Log Message:
  -----------
  somo/grpy: raise the shell-reduction error floor to 0.75 of the raw gap

The estimate is max(safety * Richardson remainder, floor * raw inter-rung gap).
Validating the full 23-model grid with all five observables -- the previous
multi-observable grid ran only 12 of the 23 models, so eleven never had intrinsic
viscosity or rotational diffusion checked -- exposed two evaluations that passed
the estimator's stop test yet missed the requested tolerance (2GD1 intrinsic
viscosity at 1%: estimate 0.859%, true error 1.066%), plus eleven more that were
compliant but undercovered.

Tolerance compliance is a subset of estimator coverage: the ladder stops when the
estimate falls below the tolerance, so an estimate that covers the true error
makes stopping imply compliance. Verified on the grid -- no row missed its
tolerance while covered, and all 235 reduced rows converged.

Raising the floor rather than `safety` is deliberate. The two terms are combined
with max(), so while the floor binds the safety factor does nothing: with a
doubling ladder and the order at k_max = 3 the remainder is gap/7, and
safety * gap/7 stays under 0.5 * gap for any safety <= 3.5. Measured on the same
grid, safety = 2.0 fixed neither failure and drove the median case to retain every
bead, because it inflates the estimate for the well-behaved majority where the
floor is slack. The floor acts only on the high-observed-order rows that fail.

At 0.75 the grid is clean: 200/200 reduced evaluations inside their tolerance and
all 200 covered, worst case spending 55.3% of the allowed budget (was 106.6%), for
4.6 percentage points more beads retained on average.

floor_frac is exposed on ShellOptions rather than left hardcoded, alongside k_min,
k_max and safety. Measuring it required patching a private copy of this header,
and a second copy of an estimator is what makes validation results unattributable.

The file's own MEASURED note claimed 92/92 and 252/252; both were wrong (seven
observables counted, three research-only; and the narrow model grid). Corrected to
what this code actually returns -- the finest rung, not the extrapolation.

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


  Commit: eda0c70b198e9b39d6d589158206563e8833c0e1
      https://github.com/ehb54/ultrascan3/commit/eda0c70b198e9b39d6d589158206563e8833c0e1
  Author: Emre Brookes <emre.brookes at umontana.edu>
  Date:   2026-08-18 (Tue, 18 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_exposure.hpp
    M us_somo/develop/grpy/grpy_shell.hpp

  Log Message:
  -----------
  somo/grpy: raise the exposure quadrature to 512 points per bead

The exposure point pattern is generated once in the coordinate frame and applied to
every bead by translation and scaling, so it does not rotate with the structure. A
rigid rotation therefore changes 34-77% of bead exposures by a few of the K sample
points and changes the ranking. A translation-only control is completely inert and
the unreduced result is invariant to zero parts per million, so this is the
quadrature, not arithmetic.

The effect reaches the answer almost entirely through the stopping decision, not
through which beads are kept: where the ladder stops at the same rung in every frame
the reported value moves by at most 0.075%, and where the orientation flips the rung
by one it moves by up to 1.043% -- a ratio of roughly fourteen, replicated on two
independent grids.

Measured over twelve rigid motions of six models, matched but for K: at 64, one case
of eighteen changed its stopping rung and the frame-to-frame spread reached 0.23%; at
512, no case changes rung and the spread falls to 0.016%. Retained beads are unchanged
(mean 31.5% vs 31.3%), and compliance and coverage are perfect at both values, so the
finer quadrature costs only exposure time -- about 3% of the ladder, exposure being
milliseconds against a solve of seconds.

This does not make selection frame-independent; subsets still differ in nearly every
frame. It makes the delivered result about 14x less sensitive to orientation, which is
what matters to a user who reorients their coordinates and reruns.

The low-level exposure() default stays 64 and is now annotated as such: every
production caller passes K explicitly, and the shipped value is ShellOptions::K.

This changes which beads are selected, so all frozen validation figures must be
regenerated and the freeze tag moved.

Fixes ehb54/ultrascan-tickets#984

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


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

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

  Log Message:
  -----------
  somo/grpy: add a failure boundary, and let Stop reach the solve

Two defects that both come from the move to an in-process solver.

A failure boundary. As a subprocess a GRPY failure was isolated -- the process
died, SOMO read a non-zero exit and carried on. In-process there is nothing
between a throw and the application: a failed allocation, an unreadable input
or a bad out-of-core path would terminate SOMO and lose the session. The parse
and the whole compute now run inside a try; the catch reports the error and
restores the interface exactly as the pre-flight memory guard does when it
refuses a model, and in gui_script mode fails properly to stderr with a
non-zero exit rather than reporting success. The body is left at its existing
indentation so the diff shows the boundary and nothing else.

Stop. sopt.should_stop was installed inside 'if ( sopt.enabled )', so it existed
only for a shell-reduced run. With shell reduction off -- the default, and now
the only configuration -- nothing was installed and Stop could not reach the
solve at all: the progress callback's 'if ( stopFlag ) return;' suppresses
repaints but does not end the computation, so a long model ran to completion
looking hung. Install it for every run.

This does not make Stop instant. A solve already running still finishes, since
the factor has no interior abort; with a ladder, Stop takes effect between rungs
and saves nearly all of what remained. Said plainly in the comment rather than
implied.

Reported-by: aaron-auc
(cherry picked from commit 876a76a1fd47c75e70def12fab87b0fff40f1dce)


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

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

  Log Message:
  -----------
  somo: don't let Close act during a running GRPY solve

The GRPY solve runs synchronously on the GUI thread and calls processEvents()
to keep the interface responsive, which also leaves Close live. Taking it would
hide the window, clear the temporary directories and ask the application to quit
while the solve was still running over those files -- SOMO appearing closed
while it went on computing.

closeEvent() now treats Close as a Stop request while grpy_running: it sets
stopFlag, says that the current model must finish first because the solve cannot
be interrupted part-way, and ignores the event. Closing again once it has
stopped behaves normally.

Reported-by: aaron-auc
(cherry picked from commit 5bf6554af39173167c0b78fb1307373d4329f15e)


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

  Changed paths:
    M us_somo/develop/grpy/grpy_exposure.hpp
    M us_somo/develop/grpy/grpy_shell.hpp
    A us_somo/develop/grpy/grpy_types.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/libus_somo.pro
    M us_somo/develop/src/us_hydrodyn_grpy.cpp

  Log Message:
  -----------
  somo/grpy: reach the solver through an injected function

The shell reduction and the exposure ranking are original work; the solver they drive is
a translation of GPLv3 GRPY.f. This separates the two so the latter can move to its own
program (ehb54/grpy-cpp) and be called rather than linked.

grpy_shell.hpp and grpy_exposure.hpp no longer name a solver, include grpy_core.hpp or
use Eigen. They take grpy::SolveFn -- one bead list in, Results out -- which SOMO will
implement by running the external GRPY program, once per rung. At most five rungs per
model, against O((11N)^3) where the last rung alone is ~7/8 of the work, so the extra
invocations cost nothing measurable.

New grpy_types.hpp holds the value types both sides exchange (Bead, PhysParams, Options,
Results, ProgressFn, Obs) plus the SolveFn injection point. It is plain data with nothing
translated from GRPY.f, so it stays with UltraScan; grpy_api.hpp now includes it instead
of defining the same types.

full_rg2() is computed here from the definition -- the volume-weighted second moment of a
union of uniform spheres, with each sphere's own (3/5)a^2 term -- rather than calling into
the core. Rg is pinned to the full model across rungs, so the ladder needs it even though
no rung computes the whole structure.

The call site is transitional: it still solves in process, through a lambda that wraps
grpy::Solver, so this commit changes no results. Replacing that lambda with a run of the
external program is the next step, after which grpy_api.hpp and grpy_core.hpp leave the
tree and nothing shipped links GPLv3 code.

Verified: the whole grpy module test suite passes, including test_shell driving the ladder
through the injection point; a translation unit including only grpy_shell.hpp compiles and
runs the ladder with neither the core nor Eigen present; and us_hydrodyn_grpy.cpp compiles
against the real Qt flags.

Refs ehb54/ultrascan-tickets#1012


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

  Changed paths:
    A us_somo/develop/grpy/grpy_process.hpp
    M us_somo/develop/grpy/tests/run.sh
    A us_somo/develop/grpy/tests/test_process.cpp
    M us_somo/develop/libus_somo.pro
    M us_somo/develop/src/us_hydrodyn_grpy.cpp

  Log Message:
  -----------
  somo/grpy: run GRPY as a program again, not as linked code

Implements the injection point from the previous commit: grpy::ProcessSolver runs the GRPY
program on a bead list and returns its results, and the shell reduction calls it once per
rung. Nothing GPLv3-derived is invoked here through anything but a process boundary.

The contract is the one SOMO used before the in-process port -- run it with '-e <file>',
read the report from stdout, scrape the carriage-return-separated 'NN% TASK:' banner for
progress -- so the program's command line is still the one the Fortran GRPY published. The
controls added since (precision, out-of-core, thread count) are passed as environment
variables rather than as new flags, which is what keeps that true.

A rung that uses every bead runs on the .grpy file SOMO already wrote, so the ordinary
unreduced calculation reads exactly the file the user sees, as it always has. Only a
reduced rung gets a temporary file, and it is removed however the run ends.

The binary lookup is the one the external path used before, minus the Docker/container
branch, which is not coming back: bin/GRPY_{osx10.11,linux64,win64.exe}, which add_to_bin
still ships and linux.pl still installs.

read_grpy_input() reads the .grpy file written by us_hydrodyn_write.cpp. It is written
against our own writer rather than translated from the Fortran reader, so the last thing
the call site needed from grpy_api.hpp is gone.

Out of process restores two things the in-process port had given up: a failure kills a
child rather than the session, and Stop ends a solve that is ALREADY RUNNING -- the
in-process factorization had no interior abort, so Stop only took effect between models.
That arrives as grpy::Stopped and is handled as a stop, not as a failure.

New test_process.cpp drives all of it with a fake GRPY program that replays a real golden
report and the real banner: parsing, progress, a non-zero exit, a missing program, and the
mid-solve kill. 19 checks, all passing, and the whole module suite still passes.

Caught by that test: the sedimentation label contains '(1. - (vbar*rho))', so taking the
first number after the label parsed 1.0 as the sedimentation coefficient. The value is
identified by its exponent, not by its position.

Refs ehb54/ultrascan-tickets#1012


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

  Changed paths:
    M us_somo/develop/grpy/README.md
    M us_somo/develop/grpy/tests/run.sh
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/develop/libus_somo.pro
    M us_somo/develop/src/us_hydrodyn_grpy.cpp

  Log Message:
  -----------
  somo/grpy: remove the GRPY-derived solver from the tree

This is the commit that resolves the licence conflict: the GPLv3-derived translation is no
longer present in an LGPLv3 repository, and no shipped binary can link it.

Deleted: grpy_core.hpp and grpy_report.hpp (translations of GRPY.f), grpy_api.hpp (its
readers and display formulas), and linalg.hpp / parallel_std.hpp / parallel_qt.hpp, which
existed to serve them. All of it now lives in ehb54/grpy-cpp, where it is GPLv3 and is
validated against the original Fortran by its own golden tests.

What remains in grpy/ is original work: the exposure ranking, the self-validating shell
reduction, the value types, and the process boundary. It is Eigen-free and QtConcurrent-
free as a result, so libus_somo.pro drops 'QT += concurrent'.

test_shell now drives the ladder with an ANALYTIC model instead of the real solver, which
is a stronger test rather than a weaker one: each observable approaches the full-model
value at a known rate, so the true error of every rung is known exactly and the reported
bar can be checked for actually bounding it. The model derives mass and Rg from the beads
it is given, exactly as the solver did, so the ladder's pinning of both to full-model
values is still what those tests exercise -- a model that echoed them back could not tell
a pinned value from an unpinned one.

Deleted with their subject: test_api, test_linalg, test_assemble, test_threaded, test_ooc
and qt_proof, which tested the solver, its linear algebra and its threading. Their
successors live in grpy-cpp.

Also updated the module README, removed INTEGRATION.md (it documented an integration that
no longer exists), and corrected the comments and messages that still described GRPY as
running in process.

Fixes ehb54/ultrascan-tickets#1012


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

  Changed paths:
    M us_somo/add_to_bin/GRPY_linux64
    M us_somo/add_to_bin/GRPY_osx10.11
    M us_somo/add_to_bin/GRPY_win64.exe

  Log Message:
  -----------
  somo/grpy: ship the C++ GRPY binaries in add_to_bin

Replaces the Fortran GRPY binaries with builds of ehb54/grpy-cpp, under the same names, so
the lookup, add_to_bin and linux.pl all keep working untouched. Without this the branch
calls the program it always called and none of the port's work reaches a user.

  GRPY_osx10.11    universal x86_64 + arm64, ad-hoc signed. The Fortran binary was x86_64
                   only, so every Apple Silicon Mac was running GRPY under Rosetta.
  GRPY_linux64     fully static, as its predecessor was: no glibc floor. Threading checked
                   under -static (67.1 s on 1 thread vs 2.81 s on 32 at 1200 beads).
  GRPY_win64.exe   cross-compiled with mingw-w64, imports only KERNEL32 and the UCRT stubs.

The macOS and Linux binaries pass the full golden suite against the original Fortran output
at worst relative error 0.00e+00. THE WINDOWS BINARY HAS NOT BEEN RUN -- no Windows machine
or wine was reachable from any build host -- so it needs a smoke test on Windows before
this branch ships. Everything else about it is verified statically only.

What a user gets that the Fortran binaries could not give: threading, single precision,
out-of-core, and native arm64.

Refs ehb54/ultrascan-tickets#1012


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

  Changed paths:
    M us_somo/develop/grpy/grpy_process.hpp
    M us_somo/develop/grpy/grpy_shell.hpp
    M us_somo/develop/grpy/grpy_types.hpp
    M us_somo/develop/grpy/tests/test_shell.cpp
    M us_somo/somo/doc/manual/somo/somo_hydro.html
    M us_somo/somo/doc/manual/somo/somo_misc.html

  Log Message:
  -----------
  somo/grpy: keep the finished rungs when Stop lands mid-calculation, and correct the manual

Updating the manual turned up a regression rather than just stale prose. somo_hydro.html
promises that a stopped shell reduction 'keeps the result and error estimate obtained so
far'. That held while GRPY ran in process, because it could only be stopped between rungs.
A separate program can be killed the instant Stop is pressed -- which is the improvement --
but the exception was propagating straight past the ladder, discarding every rung that had
already completed. The promise in the manual was no longer true of the code.

ShellSolver now treats a stop during a rung exactly as it treats one between rungs: the
ladder ends where it stands, reports itself as not converged, and keeps its result and its
error bar. Only a stop before the first rung has finished leaves nothing to report, and
that one still propagates. grpy::Stopped moved from grpy_process.hpp to grpy_types.hpp so
the ladder can catch it without acquiring a Qt dependency.

Two tests pin it: a solver that stops during the third rung must leave two usable rungs
with a bar, and one that stops immediately must propagate.

Manual:
  * somo_misc.html said GRPY is 'built directly into US-SOMO and runs multi-core
    in-process'. It is again a separate multi-core program, which US-SOMO installs and
    runs; no Docker image and no checkbox, as before. The historical sentence about the
    original external and Docker versions is left as it stands, being history.
  * somo_hydro.html said Stop ends the series 'at the end of the calculation currently in
    progress'. It now ends immediately, per the change above.

Refs ehb54/ultrascan-tickets#1012


  Commit: 6d1e39e44ea3cedaa2ba8ef0afbfdb7927e7b664
      https://github.com/ehb54/ultrascan3/commit/6d1e39e44ea3cedaa2ba8ef0afbfdb7927e7b664
  Author: Emre Brookes <emre.brookes at umontana.edu>
  Date:   2026-08-18 (Tue, 18 Aug 2026)

  Changed paths:
    M us_somo/develop/grpy/grpy_process.hpp
    M us_somo/develop/grpy/tests/test_process.cpp

  Log Message:
  -----------
  somo/grpy: ask the solver for its high-precision report

The GRPY program's default report is the Fortran's own ES11.3 -- four significant
figures. That is right for a drop-in replacement a human reads, and wrong for a
caller that DIFFERENCES successive results, which is exactly what the shell
reduction does: the error estimate is built from the gap between consecutive rungs
and from the ratio of two such gaps, so a relative quantisation of ~1e-4 in the
parsed values lands directly on the reported bar.

Measured over 266 reduced evaluations, by rounding recorded full-precision rung
values through %11.3E and re-running the estimator on both:

  reported estimate   median 2.5% different, up to 17.6%
  observed order k    shifts by up to 0.26
  floor activation    changes which term governs in 14 cases
  stopping decision   flips in 2 cases

Coverage happened to survive on that corpus -- the minimum margin fell only from
1.131x to 1.119x, and no evaluation lost coverage -- so this is not a correctness
fix. But none of that movement should exist. It is an output format leaking into a
numerical result, and into the delivered value too, which was being truncated to
four significant figures.

The program already supports this: GRPY_HP switches the report to %24.15E. Nothing
changes for anyone running the program directly, since the default is untouched and
the drop-in behaviour is preserved.

The parser finds values by their exponent rather than by column, so it is indifferent
to the field width. Tests assert that rather than leaving it to inspection: a
%24.15E report round-trips a double to better than 1e-12, and the legacy ES11.3
report still parses, since an older binary on PATH would still emit it.

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


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

  Changed paths:
    M us_somo/etc/somo.residue.new

  Log Message:
  -----------
  somo.residue - add glucose

(cherry picked from commit 78465e20f3484e65b83e09bfccd8cd624204beaa)


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

  Changed paths:
    M us_somo/develop/perceiver/.gitignore
    A us_somo/develop/perceiver/data/1ADO_noSO4.pdb
    A us_somo/develop/perceiver/data/1AO6-compl_monA.pdb
    A us_somo/develop/perceiver/data/1HEL.pdb
    A us_somo/develop/perceiver/data/2AAS.pdb
    A us_somo/develop/perceiver/data/3CRO.pdb
    A us_somo/develop/perceiver/data/3GUT.pdb
    A us_somo/develop/perceiver/data/6LDH1.pdb
    A us_somo/develop/perceiver/data/6LYZ.pdb
    A us_somo/develop/perceiver/data/8RAT.pdb

  Log Message:
  -----------
  perceiver: commit the regression fixtures instead of ignoring them

data/*.pdb was ignored on the grounds that those structures are copies of
us_somo/somo/demo/*.pdb, so make regress could not score anything from a clean
checkout -- it read no atoms, divided by zero into 0.000%, and (before the guard
added in the previous merge) exited 0.

Track them instead.  The two sets serve different purposes and are free to
diverge: the demo structures exist for the demos and may be edited for demo
reasons, while these are the perceiver's regression fixtures and the calibrated
score must not move underneath them.  They are byte-identical today; that is a
coincidence of history, not a guarantee, and coupling a regression baseline to
someone else's demo data is the kind of thing that fails silently.

Nine files, 7.7 MB: the eight DEMOS consumed by regress/coverage/sssrreal, plus
1ADO_noSO4.pdb for protein_psv.

Scoring 41219 atoms, the harness now reports 99.833% geometric perception with
69 genuine errors remaining.


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

  Changed paths:
    M us_somo/develop/perceiver/data/2AAS.pdb

  Log Message:
  -----------
  perceiver: trim the 2AAS fixture to its first model

2AAS is a 32-model NMR ensemble and was 3.0 MB -- 40% of the fixture set -- while
contributing the fewest scored atoms of any structure in it (950).  pdb_lite's
read_pdb() defaults to keep_first_model = true and stops at the first ENDMDL, so
the regression only ever read model 1; the other 31 were carried for nothing.

Keep the header, MODEL 1 and its ENDMDL, and the trailing CONECT records -- those
are the four disulfides, and the harness uses CONECT connectivity where a file
provides it, which is what tells a cystine from a free cysteine.  Drop MASTER: its
counts describe the 32-model file and would be numerically false here.

3060 KB -> 120 KB.  Verified inert: the full regression output is byte-identical
before and after (sha 41f02d6f7b8a70e7 both ways), 41219 atoms scored, 99.833%
geometric perception, and 2AAS itself still scores 950 atoms at 98.63% exact.

The only multi-model consumer, protein_psv, does not use 2AAS.


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

  Changed paths:
    M us_somo/develop/perceiver/Makefile
    R us_somo/develop/perceiver/data/2AAS.pdb
    A us_somo/develop/perceiver/data/2AAS_mod1.pdb
    M us_somo/develop/perceiver/data/psv_measured.txt
    M us_somo/develop/perceiver/pdb_lite.h

  Log Message:
  -----------
  perceiver: name the trimmed fixture 2AAS_mod1, not 2AAS

A file trimmed to one model is no longer the published entry, so it should not
carry the bare PDB ID -- anyone comparing it against 2AAS from the PDB, or against
us_somo/somo/demo/2AAS.pdb, would find 31 models missing with nothing in the name
to say why.

Rename to 2AAS_mod1.pdb and follow it everywhere it is referenced:

- Makefile: the DEMOS entry the regression consumes.
- pdb_lite.h: the explicit-H note cited '7840 H, 245 in model 1'.  7840 is the
  count for the whole ensemble and is no longer true of this file; it now reads
  245 explicit H, with the ensemble figure kept as context.
- data/psv_measured.txt: rows are keyed by pdb-basename, so the 2AAS key would
  have stopped matching.  Re-keyed to 2AAS_mod1, label '(2AAS, model 1)'.  The
  file is not in PSV_PROTEINS today, so nothing was broken -- but a silent lookup
  miss later would have been hard to spot.

DECISIONS.md keeps saying 2AAS: those entries are a historical design log about
the structure, not references to a path.

Verified: regression totals identical to the untrimmed baseline (41219 atoms,
99.833%), 2AAS_mod1 itself still 950 atoms at 98.63%, unit tests 147/0, and the
no-input guard still exits 2.


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

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

  Log Message:
  -----------
  somo/doc: drop the resolved release-date query from somo_misc

The GRPY paragraph carried an author note asking for the release date to be
confirmed:

  <!-- confirm actual release date; merged to somo-dev 2026-08-06 -->

August 2026 is correct, so the note has served its purpose and should not ship.
The six user-facing 'August 2026' claims across the four changed manual pages
stand unchanged; only the comment is removed.


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

  Changed paths:
    M .gitignore

  Log Message:
  -----------
  gitignore: ignore generated moc output, not just the moc/ directory

Two qmake-generated moc artifacts had been committed under
us_saxs_cmds_t/ and rode in with the GRPY history. They break the Qt6
CI build outright: moc output carries

  #error "This file was generated using the moc from 5.15.14. It
   cannot be used with the include files from this version of Qt."

so any Qt6 compile of a Qt5-generated moc_*.cpp fails, and moc_predefs.h
is a dump of one compiler's predefined macros, wrong on any other
platform. Both are regenerated by qmake on every build.

The existing rule was moc/, which only covers the output directory used
by some .pro files; files generated beside the source were not matched.
Add moc_*.cpp and moc_predefs.h. No tracked file matches either rule.

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


Compare: https://github.com/ehb54/ultrascan3/compare/8b5fd63461da...2eb987336bc7

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