[us-commits] [ehb54/ultrascan3] 957cb9: somo/grpy: add self-contained in-process GRPY modu...
emre brookes
noreply at github.com
Tue Aug 18 07:57:32 MDT 2026
Branch: refs/heads/ehb54-issue-1016-somo-merge-2
Home: https://github.com/ehb54/ultrascan3
Commit: 957cb96251a46900e8dab0c6d58516bbc9b86b39
https://github.com/ehb54/ultrascan3/commit/957cb96251a46900e8dab0c6d58516bbc9b86b39
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-18 (Tue, 18 Aug 2026)
Changed paths:
A us_somo/develop/grpy/README.md
A us_somo/develop/grpy/tests/data/1znf.bead_model
A us_somo/develop/grpy/tests/data/1znf_golden.txt
A us_somo/develop/grpy/tests/data/dumbbell_golden.txt
A us_somo/develop/grpy/tests/run.sh
M us_somo/develop/libus_somo.pro
Log Message:
-----------
somo/grpy: add self-contained in-process GRPY module (Eigen)
Adds us_somo/develop/grpy/: a C++ port of GRPY (generalized Rotne-Prager-Yamakawa
hydrodynamics) callable in-process, to replace the external Fortran binary run via
QProcess with stdout scraping.
The module is self-contained and isolated from the SOMO god classes:
- header-only, own namespace (grpy), dependency-injected threading (la::Parallel;
QtParallel over QThreadPool for SOMO, std::thread for the CLI/tests);
- memory-lean tiled Cholesky (solve M X = T instead of forming the full inverse;
upper-triangle, factored in place) with single-precision and out-of-core (mmap)
options for large systems, and fine-grained in-inversion progress via callback;
- grpy::Solver::run(beads, params, progressCb) returns structured scalars AND the
full report text, so the on-disk results file is preserved as before.
- tests/ validate against the captured GRPY golden (report + scalars) and prove the
Qt in-process path; run standalone via tests/run.sh.
Wires the qmake build (QT += concurrent, INCLUDEPATH += include grpy, HEADERS).
No US_Hydrodyn changes yet; the QProcess call-site swap is a follow-up commit.
Refs ehb54/ultrascan-tickets#972
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 50f27cadff1633fd80b25b4760f680883159395d
https://github.com/ehb54/ultrascan3/commit/50f27cadff1633fd80b25b4760f680883159395d
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-18 (Tue, 18 Aug 2026)
Changed paths:
A us_somo/develop/grpy/tests/data/dumbbell.grpy
M us_somo/develop/src/us_hydrodyn_grpy.cpp
Log Message:
-----------
somo/grpy: run GRPY in-process, drop external binary + Docker path
Wire the self-contained grpy module into US_Hydrodyn::calc_grpy_hydro /
grpy_process_next: read the .grpy file SOMO already writes via
grpy::read_native_file(), run grpy::Solver over a QtParallel backend on
SOMO's thread pool, and fill grpy_stdout from Results::report so the
existing grpy_finished() parsing and .grpy_res preservation are unchanged.
Removes the QProcess launch, the US_Container_Grpy selection, and the
Docker/parallel-image plumbing -- in-process is now the sole path. Progress
is driven by the Solver callback (processEvents keeps the GUI painting).
Adds grpy::read_native_file() + a native-reader test case (dumbbell.grpy)
that reproduces the dumbbell golden.
Refs ehb54/ultrascan-tickets#972
Co-Authored-By: Claude Fable 5 <noreply at anthropic.com>
Commit: d25790b2348fd52a6058e56480e68992e0ad2831
https://github.com/ehb54/ultrascan3/commit/d25790b2348fd52a6058e56480e68992e0ad2831
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-18 (Tue, 18 Aug 2026)
Changed paths:
R us_somo/develop/include/us_container_grpy.h
M us_somo/develop/include/us_hydrodyn.h
M us_somo/develop/include/us_hydrodyn_misc.h
M us_somo/develop/libus_somo.pro
R us_somo/develop/src/us_container_grpy.cpp
M us_somo/develop/src/us_hydrodyn.cpp
M us_somo/develop/src/us_hydrodyn_grpy.cpp
M us_somo/develop/src/us_hydrodyn_misc.cpp
M us_somo/develop/src/us_hydrodyn_settings.cpp
Log Message:
-----------
somo/grpy: remove dead external/Docker GRPY plumbing
Now that GRPY runs in-process (the external binary + Docker path is gone),
delete the symbols that only served the old QProcess path:
- US_Container_Grpy class (src/us_container_grpy.cpp, include/us_container_grpy.h)
and its libus_somo.pro SOURCES/HEADERS entries; the us_container_grpy member,
its includes/init, and grpy_parallel_pulled.
- cb_parallel_grpy checkbox + set_parallel_grpy() slot and the misc.parallel_grpy
field, incl. its settings save/load/default (old saved values are simply ignored).
- the grpy QProcess* member, grpy_prog, and the grpy_readFromStdout/Stderr/started
slots. stop_calc()'s process-terminate block is replaced by a note: cancellation
now flows through stopFlag, honored by the Solver progress callback and
grpy_finished().
Objects rebuild cleanly (us_hydrodyn, us_hydrodyn_grpy, us_hydrodyn_misc,
us_hydrodyn_settings).
Refs ehb54/ultrascan-tickets#972
Co-Authored-By: Claude Fable 5 <noreply at anthropic.com>
Commit: 74c0abb3406272fcc7d3b1c5d35dbe90889439f0
https://github.com/ehb54/ultrascan3/commit/74c0abb3406272fcc7d3b1c5d35dbe90889439f0
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 in-process finish hand-off + resolve input path
Two fixes found while validating the in-process path end-to-end via the
batch gui_script on a real bead model:
1. grpy_finished() was never invoked. It was posted with
QMetaObject::invokeMethod(..., Qt::QueuedConnection, Q_ARG(QProcess::
ExitStatus, ...)), but QProcess::ExitStatus is not a registered
queued-connection metatype, so the invoke silently failed
("Unable to handle unregistered datatype 'QProcess::ExitStatus'").
grpy_running then never cleared and the batch's
while(grpy_running) processEvents() loop hung forever. Replaced both
call sites with QTimer::singleShot(0, this, lambda) which defers to the
event loop (still no deep recursion through the model batch) but calls
grpy_finished() directly, needing no metatype registration.
2. read_native_file() read grpy_last_processed (a bare filename) relative
to the process CWD. The old QProcess set its working directory to
get_somo_dir(); resolve the path against get_somo_dir() to match, and
guard a missing file with a clear error instead of a silent empty solve.
Validated: gui_script batch run on a 165-bead model produces a .grpy_res
whose every physical observable (rotational diffusion, sedimentation,
intrinsic viscosities, all 8 relaxation times, diagonal diffusion tensor)
matches the Fortran GRPY_osx10.11 binary on the same .grpy input exactly;
only the documented eigenvector sign-gauge and ~1e-9 coupling noise differ.
Refs ehb54/ultrascan-tickets#972
Co-Authored-By: Claude Fable 5 <noreply at anthropic.com>
Commit: 8620f4721c98ca61f60f9e02fe1abd8f97c5a90f
https://github.com/ehb54/ultrascan3/commit/8620f4721c98ca61f60f9e02fe1abd8f97c5a90f
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: expose single-precision + out-of-core options
Wire grpy::Options.single and .ooc_dir at the in-process call site. These
matter only for very large bead models (memory-bound; run via batch/cluster),
so they are surfaced through the lightweight, no-UI mechanisms rather than
new god-class dialog controls:
- gui_script `global grpy_single 1` / `global grpy_ooc_dir <dir>`
- GRPY_SINGLE / GRPY_OOC_DIR environment-variable fallback (matches the
standalone CLI's env knobs)
Default off = in-core double precision, byte-identical to prior behavior. A
"GRPY options: ..." editor message is emitted when either is active.
Validated: gui_script run with `global grpy_single 1` on the 165-bead model
completes and reproduces every labeled observable of the double-precision /
Fortran-golden result (single matches to 4 sig figs by design). The module's
single-precision and out-of-core paths themselves are covered by the grpy
unit tests (test_linalg float, test_ooc).
Refs ehb54/ultrascan-tickets#972
Co-Authored-By: Claude Fable 5 <noreply at anthropic.com>
Commit: afdee374e5d99499a8e0f35f4af1a0466d650e84
https://github.com/ehb54/ultrascan3/commit/afdee374e5d99499a8e0f35f4af1a0466d650e84
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-18 (Tue, 18 Aug 2026)
Changed paths:
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: add GRPY precision (double/float) control to Hydro Options
Per review, surface the single-precision option as a proper control in the
SOMO Hydrodynamic Calculation Options window rather than script/env only.
Adds a "GRPY Numerical Precision:" group with "Double (default)" / "Float
(for large systems)" radios to US_Hydrodyn_Hydro, right below the existing
"Inclusion of Buried Beads (for GRPY)" group and mirroring its idiom. Backed
by hydro.grpy_single (default false = double), persisted in the SOMO settings
(save/load/default) and reported in display_default_differences() like the
sibling GRPY setting.
The in-process call site now reads hydro.grpy_single as the primary source;
the gui_script `global grpy_single` param and GRPY_SINGLE env var still
override for headless/batch automation. Out-of-core stays script/env only
(a cluster-scale knob, no GUI control).
Validated: full libus_somo rebuild clean (0 errors); default-double gui_script
run reproduces the Fortran-golden scalars (no regression).
Refs ehb54/ultrascan-tickets#972
Co-Authored-By: Claude Fable 5 <noreply at anthropic.com>
Commit: dfa7079363d71ce319492932925fa40b58f9afc6
https://github.com/ehb54/ultrascan3/commit/dfa7079363d71ce319492932925fa40b58f9afc6
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-18 (Tue, 18 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: 9728a90a216f39e8b37d00848bf8f58ce2a90646
https://github.com/ehb54/ultrascan3/commit/9728a90a216f39e8b37d00848bf8f58ce2a90646
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/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: ce01d78729819c430ef871988325cd5147a7d2fd
https://github.com/ehb54/ultrascan3/commit/ce01d78729819c430ef871988325cd5147a7d2fd
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: 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: fe3b56c73eb62bf1f055eff78d1c1bbcdae3a0e0
https://github.com/ehb54/ultrascan3/commit/fe3b56c73eb62bf1f055eff78d1c1bbcdae3a0e0
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: 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: 08ceeff3fcac01232b1dbb852b2262f5f348ac95
https://github.com/ehb54/ultrascan3/commit/08ceeff3fcac01232b1dbb852b2262f5f348ac95
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: pre-flight memory guard for oversized models
The in-process GRPY solver holds the tiled upper triangle of the 11N x 11N
mobility matrix in RAM (peak ~ (11N)^2/2 * scalar bytes), so a model larger
than physical RAM cannot fit in-core -- e.g. 20k beads needs ~180 GB double /
~90 GB single. Because the solve runs synchronously on the GUI thread, such a
model previously sent the machine into heavy swapping with a frozen interface.
Add a pre-flight guard in calc_grpy_hydro() before the batch starts: estimate
peak memory for the largest selected model from its used-bead count and the
chosen precision, read physical RAM (macOS hw.memsize / Linux sysconf; unknown
-> no guard), and if the estimate exceeds ~70% of RAM:
- interactive: a Cancel / Run-anyway dialog naming the estimate, the machine
RAM, and the remedies (single precision, or a smaller model);
- script mode (gui_script): never block on a dialog -- report the error to the
editor and stderr and fail with a non-zero exit.
Verified: a headless run of a 20,000-bead model on a 32 GB machine reports
"~198.3 GB needed, 32.0 GB available" and exits non-zero pre-flight, instead of
thrashing.
Fixes ehb54/ultrascan-tickets#987
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 4dc23b667d2edc83e5a7ff3dc5ce1e0f0b21afe0
https://github.com/ehb54/ultrascan3/commit/4dc23b667d2edc83e5a7ff3dc5ce1e0f0b21afe0
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: memory guard - add Windows physical-RAM detection
Windows is a shipped SOMO target (GRPY_win64) but was falling through to the
"unknown -> no guard" path. Add GlobalMemoryStatusEx (ullTotalPhys) so the
pre-flight memory guard applies there too. Mac/Linux paths unchanged; the
Windows branch is #if'd out off-Windows and needs verifying on the win build.
Refs ehb54/ultrascan-tickets#987
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 4630de678797468292907e9972c76944982befba
https://github.com/ehb54/ultrascan3/commit/4630de678797468292907e9972c76944982befba
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: memory guard - point oversized models to ZENO
Single precision only ~halves the footprint, so a genuinely huge structure
(e.g. 20k beads ~90 GB single) still will not fit on a modest machine. Add
ZENO to the guard's remedy message as the size-independent alternative, noting
its limitation (it does not compute rotational diffusion).
Refs ehb54/ultrascan-tickets#987
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: d59737c6fd0287ee8d194724f64388b91cebd6e1
https://github.com/ehb54/ultrascan3/commit/d59737c6fd0287ee8d194724f64388b91cebd6e1
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: clear grpy_used_beads at run start (fix stale count)
grpy_used_beads was populated with push_back each run but, unlike its sibling
lists (grpy_to_process, grpy_model_numbers), was never cleared at the top of
calc_grpy_hydro -- it only drained via pop_front as models processed. So any
run that aborts before processing (now including the pre-flight memory guard,
but also the stop-flag and filename-length aborts) left a stale count behind,
and the next run's max()/processing saw it. Observed as: a vdW model triggers
the memory guard, then a subsequent smaller SoMo model in the same session is
wrongly reported with the vdW bead count. Clear grpy_used_beads at run start,
consistent with the other lists.
Refs ehb54/ultrascan-tickets#987
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 0d5f7bb309d44c7b785d8a3c41493d3f04d06725
https://github.com/ehb54/ultrascan3/commit/0d5f7bb309d44c7b785d8a3c41493d3f04d06725
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
M us_somo/somo/doc/manual/somo/somo_misc.html
Log Message:
-----------
somo/doc: update manual for in-process GRPY, precision, memory guard
The GRPY overhaul (#972) and the memory guard (#987) left the SOMO manual
stale/incomplete:
- somo_misc.html documented the removed "Enable Parallel GRPY" checkbox and the
Docker-based parallel path. Rewritten: GRPY now runs in-process, multi-threaded
by default (no checkbox); notes the memory-lean/single-precision solver and the
pre-flight memory guard, and points oversized models to single precision, a
smaller model, or ZENO (size-independent, no rotational diffusion). Also softened
the "GRPY can crash" note to the guarded behavior.
- somo_hydro.html: document the new "GRPY Numerical Precision" (Double/Float)
control; drop the stale "parallel GRPY ... July 2024 release" clause from the
buried-beads paragraph.
Release date in somo_misc.html marked with an HTML comment to confirm at release.
Refs ehb54/ultrascan-tickets#987
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 08c896853cda4ec84d74fb18e7205c9c573eb12a
https://github.com/ehb54/ultrascan3/commit/08c896853cda4ec84d74fb18e7205c9c573eb12a
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
M us_somo/somo/doc/manual/somo/somo_misc.html
Log Message:
-----------
somo/doc: bump manual last-updated/last-modified dates
Update both date fields (top "Last updated" and bottom "Last modified on")
on somo_misc.html and somo_hydro.html to reflect the GRPY doc edits.
Refs ehb54/ultrascan-tickets#987
Commit: a2f85d5c14bb339b7c0afb67f05e055879dd5258
https://github.com/ehb54/ultrascan3/commit/a2f85d5c14bb339b7c0afb67f05e055879dd5258
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: 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: 44b631e157d3ca8f17964a89a701f2f56ad93df2
https://github.com/ehb54/ultrascan3/commit/44b631e157d3ca8f17964a89a701f2f56ad93df2
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: 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: 7fe7c35e50f8c11541d0a52834793159ad012b36
https://github.com/ehb54/ultrascan3/commit/7fe7c35e50f8c11541d0a52834793159ad012b36
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
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: 036b9bcfd343c7e8ccd76f99e2ee5c30ba1bb8e6
https://github.com/ehb54/ultrascan3/commit/036b9bcfd343c7e8ccd76f99e2ee5c30ba1bb8e6
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/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: 6e1320de39609487c6985fe2feed32032da1401b
https://github.com/ehb54/ultrascan3/commit/6e1320de39609487c6985fe2feed32032da1401b
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-18 (Tue, 18 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: d350b7a2e0ff48c622830828f3336f3a70bc515b
https://github.com/ehb54/ultrascan3/commit/d350b7a2e0ff48c622830828f3336f3a70bc515b
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-18 (Tue, 18 Aug 2026)
Changed paths:
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: 34155ff3c29814619e2be299670ecadf3033ff3e
https://github.com/ehb54/ultrascan3/commit/34155ff3c29814619e2be299670ecadf3033ff3e
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: 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: ea71b59b9fdd3d25d522a5f4e7cb90c2e1b10a86
https://github.com/ehb54/ultrascan3/commit/ea71b59b9fdd3d25d522a5f4e7cb90c2e1b10a86
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: 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: b3067a1690a014e926d0cc7f9d2fbd4fe857fa05
https://github.com/ehb54/ultrascan3/commit/b3067a1690a014e926d0cc7f9d2fbd4fe857fa05
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: 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: 438a3c0f57e89a1e0ae420ff4fc0ca930e2864eb
https://github.com/ehb54/ultrascan3/commit/438a3c0f57e89a1e0ae420ff4fc0ca930e2864eb
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: 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: 14d0e0a86d907e7ccf08d0a0f4714d7c4d330be2
https://github.com/ehb54/ultrascan3/commit/14d0e0a86d907e7ccf08d0a0f4714d7c4d330be2
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: 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: 4f6d29a69b56b5036f8cba12e9ec9cb167254336
https://github.com/ehb54/ultrascan3/commit/4f6d29a69b56b5036f8cba12e9ec9cb167254336
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
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: b45710ce5d0ffaf659f9c766532f1eb93a90c763
https://github.com/ehb54/ultrascan3/commit/b45710ce5d0ffaf659f9c766532f1eb93a90c763
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: 6118076937703bc913e82e0a823266fe644f4b7b
https://github.com/ehb54/ultrascan3/commit/6118076937703bc913e82e0a823266fe644f4b7b
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: a65890b2b5a8812bbab1019ca2399e0e1382d547
https://github.com/ehb54/ultrascan3/commit/a65890b2b5a8812bbab1019ca2399e0e1382d547
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: 92fa4d0c355f07301eae13a9fcfe0a33d8a75005
https://github.com/ehb54/ultrascan3/commit/92fa4d0c355f07301eae13a9fcfe0a33d8a75005
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: 3fc28d9dcaed4525dfb2c6526c315e9eebd6151f
https://github.com/ehb54/ultrascan3/commit/3fc28d9dcaed4525dfb2c6526c315e9eebd6151f
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: ebbc4ff182eeddf61eb54fefd0dc9a0da8ed1b4f
https://github.com/ehb54/ultrascan3/commit/ebbc4ff182eeddf61eb54fefd0dc9a0da8ed1b4f
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: 6a4e3d460434f863e079453b92f35c4fa69fed86
https://github.com/ehb54/ultrascan3/commit/6a4e3d460434f863e079453b92f35c4fa69fed86
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: 9388e93e9847cded52d8c69b59399f8bc8014079
https://github.com/ehb54/ultrascan3/commit/9388e93e9847cded52d8c69b59399f8bc8014079
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: 51d7a428af53cee352ed486998ca99e9efc76a40
https://github.com/ehb54/ultrascan3/commit/51d7a428af53cee352ed486998ca99e9efc76a40
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: ef9f493b7e752d014b175e8e31039d8b4503c05e
https://github.com/ehb54/ultrascan3/commit/ef9f493b7e752d014b175e8e31039d8b4503c05e
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: 093206fe6f9cbabc7c19313aacc71b32ec2c73ab
https://github.com/ehb54/ultrascan3/commit/093206fe6f9cbabc7c19313aacc71b32ec2c73ab
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: ba631e65aa2368196b656add81322b35005237cd
https://github.com/ehb54/ultrascan3/commit/ba631e65aa2368196b656add81322b35005237cd
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: 9d71db8c33c31fb8b7c374bbde1fc6a3f9365940
https://github.com/ehb54/ultrascan3/commit/9d71db8c33c31fb8b7c374bbde1fc6a3f9365940
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: 9cb2a455ff8c652287e6089f083f6b8b1392ec6b
https://github.com/ehb54/ultrascan3/commit/9cb2a455ff8c652287e6089f083f6b8b1392ec6b
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: 44ed3c8d86badaf532fd20e4ce5c9d0873980606
https://github.com/ehb54/ultrascan3/commit/44ed3c8d86badaf532fd20e4ce5c9d0873980606
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: c2bf7449debc461105e0606267c8aba0e01b86c0
https://github.com/ehb54/ultrascan3/commit/c2bf7449debc461105e0606267c8aba0e01b86c0
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: 7edafb678d0eeb06cc94b91ea7ce8c7b1afa8934
https://github.com/ehb54/ultrascan3/commit/7edafb678d0eeb06cc94b91ea7ce8c7b1afa8934
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: 9e58f619edec6d0fb53f323d6edaf14bd4085a74
https://github.com/ehb54/ultrascan3/commit/9e58f619edec6d0fb53f323d6edaf14bd4085a74
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: 8e843b1f4ef53f502cf4682f4257eeaf7a7b0f2e
https://github.com/ehb54/ultrascan3/commit/8e843b1f4ef53f502cf4682f4257eeaf7a7b0f2e
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: 03459aa2e5f390fda085ef0eefbe7df75af266df
https://github.com/ehb54/ultrascan3/commit/03459aa2e5f390fda085ef0eefbe7df75af266df
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: 5b81d22e1ce5a7749485ee726e3f067d5941137b
https://github.com/ehb54/ultrascan3/commit/5b81d22e1ce5a7749485ee726e3f067d5941137b
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: f1ffea7400da565b9059555cb861d101d36cd124
https://github.com/ehb54/ultrascan3/commit/f1ffea7400da565b9059555cb861d101d36cd124
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: 42df1f72a7df367cc7938f809a13231cc5514737
https://github.com/ehb54/ultrascan3/commit/42df1f72a7df367cc7938f809a13231cc5514737
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: 3e90a88c129636779b7ced773c1fad8bd713abd6
https://github.com/ehb54/ultrascan3/commit/3e90a88c129636779b7ced773c1fad8bd713abd6
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: 67297e0699bfd3db27d8a47dc02c981c64d5a089
https://github.com/ehb54/ultrascan3/commit/67297e0699bfd3db27d8a47dc02c981c64d5a089
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: 8e3a38bb2fe0a337040e6cb40a4aac108c5212ee
https://github.com/ehb54/ultrascan3/commit/8e3a38bb2fe0a337040e6cb40a4aac108c5212ee
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: b134781706c00df60f598ec0e02c4b4911ff1a86
https://github.com/ehb54/ultrascan3/commit/b134781706c00df60f598ec0e02c4b4911ff1a86
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.
Compare: https://github.com/ehb54/ultrascan3/compare/957cb96251a4%5E...b134781706c0
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