[us-commits] [ehb54/ultrascan3] 74123f: Perceive hybridization/vdW radius/electron count f...
emre brookes
noreply at github.com
Mon Aug 10 10:26:43 MDT 2026
Branch: refs/heads/ehb54-issue-980
Home: https://github.com/ehb54/ultrascan3
Commit: 74123ff052110d14c1b81d7e4b8e1b30cdf87a20
https://github.com/ehb54/ultrascan3/commit/74123ff052110d14c1b81d7e4b8e1b30cdf87a20
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/develop/include/us_hydrodyn_perceive.h
A us_somo/develop/include/us_hydrodyn_perceive_elements.h
A us_somo/develop/include/us_hydrodyn_perceive_hybrid.h
A us_somo/develop/include/us_hydrodyn_perceive_saxs.h
A us_somo/develop/include/us_hydrodyn_perceive_somo.h
M us_somo/develop/libus_somo.pro
A us_somo/develop/perceiver/.gitignore
A us_somo/develop/perceiver/DECISIONS.md
A us_somo/develop/perceiver/README.md
A us_somo/develop/perceiver/data/ref/f0_WaasKirf.dat
A us_somo/develop/perceiver/data/ref/it1992.cpp
A us_somo/develop/perceiver/data/ref/somo.saxs_atoms.generated
A us_somo/develop/perceiver/pdb_lite.h
A us_somo/develop/perceiver/residue_oracle.h
A us_somo/develop/perceiver/tests/coverage.cpp
A us_somo/develop/perceiver/tests/emit_residue.cpp
A us_somo/develop/perceiver/tests/regression.cpp
A us_somo/develop/perceiver/tests/tests_unit.cpp
A us_somo/develop/perceiver/tinytest.h
A us_somo/develop/perceiver/tools/gen_saxs_entries.py
A us_somo/develop/src/us_hydrodyn_perceive.cpp
A us_somo/develop/src/us_hydrodyn_perceive_somo.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
Perceive hybridization/vdW radius/electron count for non-coded residues
Phases A and B of ehb54/ultrascan-tickets#978.
SOMO needs an atom's hybridization, vdW radius and electron count to build a
bead model, and today they come only from the hand-curated somo.residue. For a
residue that table does not code, SOMO synthesises a "<RESNAME>_NC<n>"
placeholder and models it as a generic ABB average bead. This adds a chemical
perceiver that derives the real per-atom chemistry from element + coordinates,
so such a residue can be given a proper entry instead of an averaged one.
somo.residue remains the master: perception runs only for residues SOMO itself
recorded in unknown_residues.
Phase A - core compiled into libus_somo:
include/us_hydrodyn_perceive{,_elements,_hybrid,_saxs}.h
src/us_hydrodyn_perceive.cpp
Pipeline: spatial-grid bond perception (honouring CONECT where present, and
H-/ion-agnostic so implicit-H inference is identical with or without explicit
hydrogens) -> ring detection + planarity aromaticity -> Kekule bond orders ->
per-element classification -> emit a somo.residue block plus any somo.hybrid
rows for novel types.
The core is deliberately Qt-free, so the standalone harness in
us_somo/develop/perceiver builds the very same sources without Qt: 23 unit
tests plus a regression that takes residues somo.residue DOES code, pretends
they are unknown, and checks they are reproduced - 41,219 atoms over 8 demo
structures, 99.83% geometric perception, 0.17% genuine error. The residual is
protonation/tautomer state, which heavy-atom geometry cannot resolve in
principle. The harness reads us_somo/etc directly so it cannot drift from the
real tables.
us_hydrodyn_perceive_somo.{h,cpp} is the only Qt-aware layer: it converts
PDB_model/PDB_chain/PDB_atom, reads CONECT and HETNAM from the source file,
and returns tentative entries. It converts the whole model rather than one
residue because bond perception needs surrounding context (peptide links,
disulfides, metal coordination) to get coordination numbers, and hence
implicit-H counts, right. Instances are de-duplicated back to the base residue
name so one confirmation covers all instances of the same chemistry.
Phase B - headless "perceive <pdbfile>" gui_script command, so the whole path
is exercisable against real structures with no GUI:
us3_somo _pad -I -g <script>
Entries carry a REVIEW block naming every atom whose perception was uncertain
(ambiguous protonation, assumed oxidation state, estimated vbar), which is what
the confirmation UI in ehb54/ultrascan-tickets#979 will present to the user.
No existing behaviour changes: nothing calls the perceiver outside the new
command.
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 894686fd90e20d5b1ba8cb1bc37a048d6a32e299
https://github.com/ehb54/ultrascan3/commit/894686fd90e20d5b1ba8cb1bc37a048d6a32e299
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_somo.h
M us_somo/develop/perceiver/README.md
A us_somo/develop/perceiver/examples/perceive.somo
M us_somo/develop/src/us_hydrodyn_perceive_somo.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
Add "perceive compare" hand-testing sub-command
Part of ehb54/ultrascan-tickets#978.
"perceive compare <pdb>" runs perception over the residues somo.residue DOES
code and diffs the result against the curated types, reporting exact-match and
physics-match rates, a curated->perceived difference histogram, and the first
differing atoms. It is the interactive equivalent of the standalone regression
harness, but run inside SOMO against the user's own installed ~/ultrascan/etc
tables - so it also reveals when an installed table is out of date.
Cross-validation on 1HEL: the in-SOMO path and the standalone harness agree
exactly (1000 atoms scored, 984 exact 98.40%, 995 physics 99.50%), despite
using different PDB parsers and different copies of the tables. The five
physics differences are all known, explainable categories: the chain
N-terminus (a real -NH3+, where the perceiver is chemically right and the
residue template cannot know), the His tautomer and its charge, and the
Asp carboxylate protonation convention.
Also adds examples/perceive.somo documenting the invocation, including the
gotchas that cost time otherwise: the throwaway "_pad" argument is required on
macOS and must be omitted on Linux, "-I" avoids blocking config dialogs, and
the first run after rebuilding libus_somo only re-installs configs and exits,
so it must be run twice.
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 3969e7dbd75c4a6428f04e9b5e819dbe7ab7dea2
https://github.com/ehb54/ultrascan3/commit/3969e7dbd75c4a6428f04e9b5e819dbe7ab7dea2
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/README.md
Log Message:
-----------
Correct perceiver README: tables are read from us_somo/etc, not copied
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 64bc533aaafe3c536d6ea31313aa4a9638f172ad
https://github.com/ehb54/ultrascan3/commit/64bc533aaafe3c536d6ea31313aa4a9638f172ad
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/develop/perceiver/tools/psv_model.py
Log Message:
-----------
Add psv group-contribution probe (analysis for ticket #980)
Tests Mattia's suggestion that a rough psv can be suggested for a non-coded
residue from similarity to the hybridizations already in somo.residue, by
fitting a Cohn-Edsall / Durchschlag-Zipper style group-contribution model over
the ~100 genuine polyatomic residues somo.residue codes and scoring it
leave-one-out.
Result (MAE in vbar, cm^3/g; typical vbar ~0.73):
global mean 0.110
sum-of-vdW-spheres (emitted now) 0.193 <- worst option available
residue-type group average 0.070
hybrid group-contribution 0.054 (organic only: 0.036, metals: 0.222)
So the estimate the perceiver currently emits is worse than guessing the mean,
the group-contribution model is good for organic residues, and it should not
be trusted for metal-containing ones. Findings recorded on
ehb54/ultrascan-tickets#980.
NOT pushed: this belongs with the #980 work rather than the #978 PR.
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 8491edd5fef0b4cf3f5c6ed8b9fb9267073055fe
https://github.com/ehb54/ultrascan3/commit/8491edd5fef0b4cf3f5c6ed8b9fb9267073055fe
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/src/us_hydrodyn_perceive.cpp
Log Message:
-----------
Remove the psv/molvol estimate from generated entries
The tentative entry emitted a vbar (and molvol) from a sum of vdW spheres.
Fitting a group-contribution model over the ~100 polyatomic residues
somo.residue codes, scored leave-one-out (perceiver/tools/psv_model.py),
shows that estimate is the WORST option available: mean absolute error 0.193
in vbar, against 0.110 for simply assuming the global mean. Summing spheres
ignores bond overlap and runs systematically high.
vbar enters hydrodynamics through the buoyancy term (1 - vbar*rho), so
emitting a number worse than a guess is actively misleading. Both fields are
now left at 0 = unset, and the entry says why and points at
ehb54/ultrascan-tickets#980, where the tiered replacement is tracked
(group-contribution for organic residues, MAE 0.036; residue-type group
average for out-of-domain cases such as metal-containing residues, where the
fit degrades to 0.222 - worse than that average; always user-overridable).
0 rather than omission because the header must keep its 7 fields to parse,
0 reads unambiguously as unset (the convention somo.saxs_atoms already uses
for F and SE), and it fails loudly: a 0 vbar is obviously wrong if used,
whereas a plausible-looking 0.80 is not.
The entry now reports the residue mass instead, since what decides whether an
imprecise psv matters is its mass fraction of the model - molecular vbar is
mass-weighted, so a 616 Da heme in a 50 kDa protein shifts it ~0.5% even with
a 0.34 vbar error.
Also tightens the REVIEW block to genuinely uncertain atoms: a confident
classification carrying an informational note was being listed as uncertain,
disagreeing with the flagged count callers report.
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: cc7774a574a51cd1104b396d05ff4f8b4004483b
https://github.com/ehb54/ultrascan3/commit/cc7774a574a51cd1104b396d05ff4f8b4004483b
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive.h
M us_somo/develop/src/us_hydrodyn_perceive.cpp
Log Message:
-----------
Fix bead colour field in generated residue entries
emit_residue() wrote the residue's atom count into the bead line's COLOUR
field. The bead line is hydration, colour, placing_method, chain, volume
(US_Hydrodyn::read_residue_file, us_hydrodyn_load.cpp:509), so a generated
entry's colour was whatever the atom count happened to be:
- 6 atoms -> colour 6, RESERVED brown, which marks a bead as buried and
EXCLUDES it from the hydrodynamic computation
- 7 atoms -> colour 7, reserved for fused beads
- 8 atoms -> colour 8, reserved for beads found exposed on re-check
- >15 -> out of range entirely
Six- to eight-atom ligands are common (glycerol has 6, phosphate 5), so this
silently dropped beads from models built with a generated entry.
Emit DEFAULT_BEAD_COLOR instead, defined once in us_hydrodyn_perceive.h with
the documented colour list from the SOMO manual (somo_residue.html, Panel 3)
and helpers bead_color_is_reserved() / bead_color_is_selectable() for the
residue-definition UI. Default is 10 (light green), which the manual records
as the Automatic Bead Builder's colour for non-coded residues; it is a single
named constant so the policy can be changed in one place.
Also spell out the remaining bead-line fields rather than emitting bare zeros.
Refs ehb54/ultrascan-tickets#978
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 901c01d2b81385f3c9f57e349b6a2acf4dc996c3
https://github.com/ehb54/ultrascan3/commit/901c01d2b81385f3c9f57e349b6a2acf4dc996c3
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/develop/include/us_hydrodyn_grid_volume.h
M us_somo/develop/perceiver/DECISIONS.md
A us_somo/develop/perceiver/Makefile
M us_somo/develop/perceiver/README.md
A us_somo/develop/perceiver/tests/grid_volume.cpp
A us_somo/develop/perceiver/tools/calibrate_3v_context.py
A us_somo/develop/perceiver/tools/hydration_table.py
A us_somo/develop/perceiver/tools/psv_durchschlag.py
A us_somo/develop/perceiver/tools/recalc_ligand_volumes.py
A us_somo/develop/src/us_hydrodyn_grid_volume.cpp
Log Message:
-----------
Native grid volume + analysis tools for vbar/molvol/hydration
Groundwork for filling the three fields a generated residue entry currently
leaves at zero: psv (vbar), anhydrous molar volume, and hydration.
Native grid volume (us_hydrodyn_grid_volume.h/.cpp, Qt-free)
accessible A = { v : |v - c_i| > r_i + probe for every atom i }
excluded E = { v : v not in A, and dist(v, A) > probe }
volume = |E| * grid^3
probe 0 gives the bare van der Waals union, probe 1.4 the solvent-excluded
volume. Interior cavities are deliberately not counted, which is what lets
V(complex) - V(complex minus ligand) measure a bound ligand in context. Only
the accessible/blocked boundary is dilated -- the nearest accessible voxel to
any blocked voxel is always a boundary one -- so the cost is a surface rather
than a volume, and a 0.25 A grid runs in about a second for a 1300-atom
protein.
This removes what would otherwise be a new external dependency on 3V
(vossvolvox). 3V is retained only as the validation oracle: tests/grid_volume.cpp
reproduces Volume.exe to within 0.8% on an isolated residue, a whole protein,
and a bound-ligand difference, and adds analytic checks (single sphere,
coincident spheres, and a sealed hollow shell whose cavity must not be counted).
10 checks, 0 failures.
Analysis tools (prototypes, not built into libus_somo)
psv_durchschlag.py Durchschlag & Zipper atomic volume increments, no
fitting. Self-tests against the papers' own worked
values before reporting anything.
hydration_table.py derives a hybrid-type -> waters lookup from
somo.residue; 36 of 48 types unanimous, 7 weak and
flagged for review rather than silently defaulted.
calibrate_3v_context.py calibrates the in-context difference method against
residues whose volume we already know.
recalc_ligand_volumes.py recomputes prosthetic-group volumes, guarded on
elemental formula and on reproducing the stored value.
DECISIONS.md records the findings behind these, including why the anhydrous
volume field wants a 1.4 A probe rather than 0, and the measured-versus-
calculated trap in the Durchschlag 1986 tables.
The perceiver's hand-written test Makefile is force-added: the repository root
ignores "Makefile" for the qmake-generated ones, which left the documented
"make unit" impossible from a fresh clone.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 6fc6d85ed01956401dc09c0a967453ea48a37ccb
https://github.com/ehb54/ultrascan3/commit/6fc6d85ed01956401dc09c0a967453ea48a37ccb
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/.gitignore
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/perceiver/tools/calibrate_3v_context.py
M us_somo/develop/perceiver/tools/psv_durchschlag.py
M us_somo/develop/perceiver/tools/recalc_ligand_volumes.py
Log Message:
-----------
Remove local filesystem paths from perceiver files
The repository is public, so absolute paths from a personal workspace should
not appear in it. Five places carried them, all written by me:
.gitignore a home-directory path naming where the demo PDBs
were copied from
DECISIONS.md the local clone location
psv_durchschlag.py where the source PDFs happened to sit
calibrate_3v_context.py a hardcoded default path to Volume.exe
recalc_ligand_volumes.py the same
The two tools now resolve Volume.exe from $VOLUME_EXE, else PATH, which is
also what makes them runnable by anyone rather than only where that binary
was built. Behaviour is otherwise unchanged; self-tests still pass.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 0d1e1d24187f9a7e110f0e465f8acef746ffd5f9
https://github.com/ehb54/ultrascan3/commit/0d1e1d24187f9a7e110f0e465f8acef746ffd5f9
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/perceiver/Makefile
A us_somo/develop/perceiver/tests/sssr.cpp
A us_somo/develop/perceiver/tests/sssr_real.cpp
M us_somo/develop/src/us_hydrodyn_perceive.cpp
Log Message:
-----------
Add SSSR ring perception and expose the perceived bond graph
Prerequisite for the Durchschlag & Zipper volume increments, which charge a
ring-formation decrement once per ring (3-ring 2.1 through >=9-ring 14.1).
The perceiver exposed only an "aromatic" flag and never returned Bonds at all,
so ring sizes and the topology needed to classify a nitrogen or oxygen
environment were unavailable to any caller.
Bonds gains "rings", the smallest set of smallest rings. find_sssr() is
declared in the header so it can be unit-tested on hand-built adjacency with
no geometry involved. A new perceive(atoms, bonds_out, explicit_bonds)
overload hands the graph back; the existing signature delegates to it, so
current callers are unaffected.
SSSR is kept separate from find_rings(), which enumerates every simple 5- or
6-cycle and feeds aromaticity. That is the right input for "is this atom in a
flat conjugated ring" but the wrong one for anything charged per ring: a fused
bicyclic has three simple cycles and circuit rank two. Keeping them apart also
leaves the validated perception untouched, confirmed by the regression figure
holding at 99.833%.
Method is the standard one for molecule-sized graphs: smallest cycle through
each bond, then greedy smallest-first acceptance while a candidate covers a
bond no accepted ring covers, until the circuit rank is reached. Rings beyond
max_ring (12) are not sought, since the consumer charges a single large-ring
term for everything from nine up.
Tests
sssr.cpp 35 checks on hand-built graphs: acyclic shapes, every ring size
3 to 9, ring with substituent, fused bicyclics (naphthalene,
indole, purine) giving two rings rather than three cycles,
spiro, bridged, three-fused, disconnected components,
macrocycle beyond max_ring, idempotency, and that every
reported ring is a genuine cycle. Also checks the arithmetic
the counts drive: one 6-ring decrement reproduces the published
phenylalanine volume, one 5-ring the proline one.
sssr_real.cpp 18 checks on real coordinates through the whole perception
path, over all eight demo structures. Phe {6}, Tyr {6},
Trp {5,6}, His {5}, Pro {5}, DA/DG {5,5,6}, DC/DT {5,6}, and
Ala/Gly/Leu/Ser/Arg ring-free, with every instance of a type
giving an identical signature.
Some demo structures carry physically impossible geometry, which produces
spurious small rings: 1AO6 models LYS536/NZ 0.73 A from LEU583/CG, shorter
than any covalent bond, and 6LYZ has two oxygens 1.22 A apart. This yields 13
cross-residue and 12 three-membered rings across the demo set, none of them
inside a residue, so no residue's own ring count is affected. The tests assert
this as such: a cross-residue ring must be closed by an inter-residue bond that
is neither a backbone link nor a disulfide, and no three-membered ring may lie
within a single residue.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: edb0798874626def9c02b95641b4332e1548bac9
https://github.com/ehb54/ultrascan3/commit/edb0798874626def9c02b95641b4332e1548bac9
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/develop/include/us_hydrodyn_psv.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/perceiver/Makefile
A us_somo/develop/perceiver/tests/psv.cpp
A us_somo/develop/src/us_hydrodyn_psv.cpp
Log Message:
-----------
Add the Durchschlag & Zipper psv engine
Computes partial specific volume from published atomic volume increments,
consuming the perceived atoms and the bond/ring graph added in the previous
commit. Returns molar volume, vbar and a labelled decomposition -- atomic sum,
covolume, ring decrement, electrostriction, ring and charge counts -- so a
value can be audited rather than taken on trust. Atoms whose environment
cannot be classified are reported for review rather than guessed.
The covolume defaults off, since a residue is a monomeric unit and SOMO
already adds one structure-level covolume in calc_vbar_updated; a flag turns
it on for a standalone molecule. Only rings lying entirely inside the residue
are charged, which is both correct chemistry and immunity to the spurious
cross-residue rings that clashing coordinates produce.
Two rules a naive implementation gets wrong, both verified against the
published tables:
The hydroxyl increment is topological, not a running count. A second
NEIGHBOURING hydroxyl takes 0.4 but an isolated one is a fresh 2.3, so
hydroxyls are clustered by whether their carrier atoms are bonded.
1,2-ethanediol comes out at 53.5 and 1,8-octanediol at 152.0, exactly the
published calculated volumes; a running count reproduces neither.
Guanidinium charges only the terminal nitrogens. Table 1's footnote limits
the 8.0 increment "in Arg only to the two terminal N", so arginine's NE
takes the amine value.
Tests reproduce urea 44.2, glycerol 70.0 and all seven published group
increments exactly, and give mean absolute error of 1.64% against the stored
somo.residue vbar over the fifteen charge-neutral amino acids, none worse
than 6%.
Arginine and histidine sit outside that: the perceiver assigns them no formal
charge while somo.residue stores them protonated, so they lose an
electrostriction term. Arginine is essentially always protonated at
physiological pH, so that is a perception gap for the deferred pH layer rather
than ambiguous chemistry.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: b5710a31a1277ea592fa2c59e5538bb4596967c8
https://github.com/ehb54/ultrascan3/commit/b5710a31a1277ea592fa2c59e5538bb4596967c8
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/develop/include/us_hydrodyn_hydration.h
M us_somo/develop/include/us_hydrodyn_perceive.h
A us_somo/develop/include/us_hydrodyn_residue_builder.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/perceiver/Makefile
A us_somo/develop/perceiver/tests/builder.cpp
M us_somo/develop/perceiver/tests/emit_residue.cpp
A us_somo/develop/perceiver/tests/hydration.cpp
A us_somo/develop/src/us_hydrodyn_hydration.cpp
M us_somo/develop/src/us_hydrodyn_perceive.cpp
A us_somo/develop/src/us_hydrodyn_residue_builder.cpp
Log Message:
-----------
Fill in psv, anhydrous volume and hydration for generated entries
A generated somo.residue entry left vbar, molvol and hydration at zero. This
computes all three and emits a complete entry, with anything still unset
stated as "NOT SET" in the header so a partly filled entry always looks
partly filled.
Hydration lookup (us_hydrodyn_hydration.h/.cpp)
Derived at run time from whatever somo.residue is loaded rather than
embedded, so it cannot drift from the user's own tables. 1581 observations
give 48 hybrid types, 41 of them confident and 7 flagged; flagged types are
surfaced for review rather than silently defaulted, and ties break toward
the lower water count, since under-hydrating a proposal the user will edit
is safer than over-hydrating one they may accept unchanged.
The per-atom values in somo.residue are a hand distribution of a
per-residue Kuntz total rather than a per-atom rule, so this is a starting
point for editing and says so. The residue total is the quantity with
literature backing; tests confirm the totals reproduce Kuntz for 14 of 14
applicable residues, with Asp and Glu deliberately low because SOMO stores
them protonated.
One test records a gap Mattia suspected: every nucleotide entry carries
zero hydration. It is asserted as the current state, so it turns red the day
someone fills those in, which is when models built on the old values need
revisiting.
Orchestration (us_hydrodyn_residue_builder.h/.cpp)
perceive, then psv, volume and hydration, then emit. emit_residue takes an
optional Properties argument rather than including the new headers, which
include it in turn.
A generated tryptophan entry now gives vbar 0.753 against a stored 0.738,
molvol 224.29 against 228.2, and hydration 2.0 against 2.0. Over twelve
residues treated as unknown, computed molvol is within 2.32% of the stored
values on average and never worse than 4.6%.
The volume convention factor is specific to the radius set. The 1.204/1.131
measured previously came from 3V's default radii; this pipeline uses SOMO's
own, which are smaller, so the right factors are 1.248 and 1.179. The factor
tracks polarity as much as size, hydrophobics sitting near 1.26 to 1.29 and
polar residues near 1.16 to 1.21, so the band split is placed below the
hydrophobic cluster rather than at the midpoint, where it separated leucine
from isoleucine and cost both 8%.
Also corrects a comment that described the residue header's ASA field as a
molecular weight column.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 670110805dc743ff825a2cc7e71c70592d373829
https://github.com/ehb54/ultrascan3/commit/670110805dc743ff825a2cc7e71c70592d373829
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_somo.h
M us_somo/develop/libus_somo.pro
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/src/us_hydrodyn_perceive_somo.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
Wire the computed properties into SOMO and add a headless perceive
libus_somo.pro did not list the four sources added since the perceiver work
resumed, so the grid volume, psv engine, hydration lookup and residue builder
were never compiled into the library at all. Registered, and the symbols are
confirmed present in the built dylib.
The Qt adapter now takes the bond graph from the new perceive overload and
goes through somo_residue_builder, so a proposal made inside SOMO carries
vbar, molvol and hydration rather than zeros. Tentative gained those three
fields for callers that want the numbers instead of the text block. The
hydration lookup is built from SOMO's own loaded residue_list, so a proposal
is always consistent with the user's tables rather than a snapshot compiled
into the binary.
"perceive auto <pdb>" is the headless sub-command: every default accepted,
nothing prompted, nothing written back to somo.residue. It also prints how
many atoms were flagged, since in a pipeline nobody is watching the REVIEW
block go past.
Defaults are pinnable through gparams in the same way as covolume:
perceive_psv, perceive_volume, perceive_hydration, perceive_volume_probe,
perceive_volume_grid and perceive_bead_color. A reserved bead colour is a
hard error rather than a silent substitution, because two of the reserved
values exclude a bead from the hydrodynamic computation and accepting one
would quietly produce a model missing beads.
Verified end to end through us3_somo -g on 2CMD, whose citrate is genuinely
not coded: vbar 0.542, molvol 186.56, and seven review flags. On the residue
basis SOMO stores, that vbar corresponds to 0.607 for the free molecule
against a literature value near 0.59 to 0.62. The proposed hydration of zero
for a triacid is plainly too low, and all four hydroxyl oxygens come back
flagged at 49% agreement, which is the intended behaviour: propose, then say
what cannot be supported.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 00118929e83385759ef54c44a775754120cc5bbc
https://github.com/ehb54/ultrascan3/commit/00118929e83385759ef54c44a775754120cc5bbc
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_somo.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/src/us_hydrodyn_perceive_somo.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
Add perceive validate: predict the coded residues as if unknown
Rebuilds every residue somo.residue does code, pretending it is unknown, and
reports computed vbar, molvol and hydration against the stored values with a
summary. The same idea as perceive compare, which does this for atom typing,
but for the computed properties, and it exercises the whole pipeline
including perception. That makes it the only end-to-end measure of accuracy.
Chain termini and residues with unmodelled atoms are counted and skipped
rather than quietly averaged in.
Over seven structures and 1157 rebuilt residue instances, ordinary amino
acids give mean absolute errors of about 2.7% on vbar and 2.1% on molvol,
matching what the standalone harness claimed.
The ligand rows are the interesting ones. Heme's computed volume is 32.6%
above the stored value and NAD's is 28.1% above. Earlier work in this branch
reached the same conclusion by a completely different route, using 3V both on
the isolated molecule and by difference in context, and arrived at comparable
numbers. Two independent methods agree the stored ligand volumes are around
30% low. NAD's computed vbar of 0.619 is also closer to the measured 0.620
than the stored 0.599 is.
Heme's vbar comes out 24% low, which is the expected failure: it is a
metalloporphyrin, Durchschlag and Zipper publish no increment for iron, and
the engine reports the atom for review rather than inventing a value.
Hydration is much the weakest of the three, around half the residues within
half a water and systematically low, the misses being the polar and charged
side chains. A per-type majority cannot express residue-specific hydration.
This also confirms the pH coupling from the runtime side: residue_list
carries pH-adjusted hydration, so aspartate reads 6.0 and glutamate 7.0,
exactly the charged-carboxylate values, where the raw file fields give 1.0.
The lookup counts one vote per residue name, since residue_list holds several
entries for residues with ionization variants and counting each weights them
more heavily purely for being pH-aware, which was enough to flip a borderline
type and cost serine and threonine a water each.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 39fd984788128ab669b66be14b38729293be1a92
https://github.com/ehb54/ultrascan3/commit/39fd984788128ab669b66be14b38729293be1a92
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn.h
A us_somo/develop/include/us_hydrodyn_perceive_dialog.h
M us_somo/develop/libus_somo.pro
M us_somo/develop/perceiver/DECISIONS.md
A us_somo/develop/perceiver/run_dialog_test.sh
A us_somo/develop/perceiver/tests/dialog_qt.cpp
M us_somo/develop/src/us_hydrodyn.cpp
A us_somo/develop/src/us_hydrodyn_perceive_dialog.cpp
Log Message:
-----------
Add a GUI review dialog for non-coded residues
Everything built so far was reachable only from gui_script. A user working in
SOMO saw none of it, and a non-coded residue still fell silently to the
Automatic Bead Builder's averaged generic bead. This makes it visible and
correctable.
US_Hydrodyn_Perceive_Dialog presents one modal review per non-coded residue,
in the order Mattia laid out: view the residue in RasMol, confirm the
perceived atom table, edit the proposed per-atom hydration, check the
anhydrous volume, see the bead count and position, choose a bead colour, and
check the psv, with the somo.residue entry rebuilt live below so what is on
screen is exactly what Accept returns.
Only hydration is editable in the atom table. Name, hybrid, mass and radius
are perceived, and editing them would desynchronise the entry from the
geometry it came from. Hydration is editable because it is the weakest of the
three computed numbers; the residue total is shown beneath it and updates
live, with a note that the total is the quantity with literature backing while
the per-atom split is convention.
The colour combo is built from the manual's table and shows index, name and
meaning. The four reserved colours are never offered, since two of them mark a
bead for exclusion from the hydrodynamic computation and the other two are
assigned automatically during model generation.
Appending to somo.residue is opt-in and off by default, so accepting an entry
is per-session unless the user asks otherwise. After a save the user is told
to reload the structure, since hot-reloading the residue table would
invalidate indices the loaded model already holds.
The trigger is a Lookup Tables menu item rather than a hook inside model
building: check_for_missing_atoms runs per model and inside batch paths, so
opening a modal dialog from there would be invasive and wrong for scripted
runs. Skip leaves the residue to the Automatic Bead Builder exactly as before.
Testing a widget needs a widget test: run_dialog_test.sh builds the dialog
against the real library and exercises it under an offscreen Qt platform, so
it needs no display. Eighteen checks cover the proposal round-tripping through
parse and rebuild, a hydration edit propagating to both the atom line and the
bead total, a nonsense edit being rejected and the previous value restored,
read-only columns actually being read-only, the colour combo offering twelve
of the sixteen colours and defaulting to the configured one, and nothing being
marked accepted or saved until the user acts.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: fbf7da800e6866e0b3c203cfbe900c5af41f4050
https://github.com/ehb54/ultrascan3/commit/fbf7da800e6866e0b3c203cfbe900c5af41f4050
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_hydration.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/perceiver/tests/builder.cpp
M us_somo/develop/src/us_hydrodyn_hydration.cpp
M us_somo/develop/src/us_hydrodyn_perceive_somo.cpp
M us_somo/develop/src/us_hydrodyn_residue_builder.cpp
Log Message:
-----------
Propose hydration from pH 7 chemistry rules rather than type averages
Mattia supplied the chemistry: at pH 7 an acidic side chain is a carboxylate,
with one oxygen double-bonded and neutral and the other deprotonated and
charged; an aliphatic amine is protonated; and the water a carboxylate carries
depends on the length of the aliphatic chain linking it to the backbone, which
is what separates aspartate from glutamate. No general pH machinery, just the
pH 7 assumption.
The residue table confirms this exactly. The ionised hydration is field 15 of
an atom line, not 13 as the layout first suggests, and reading it gives
aspartate 5 waters on its carboxylate against glutamate's 6, lysine 3 on its
ammonium, arginine 1 on each terminal guanidinium nitrogen. So aspartate's
runtime total of 6 and glutamate's 7 are carboxylate plus backbone amide, and
the difference between them really is the extra methylene.
The rules replace the previous per-hybrid-type majority vote, which could not
express residue-specific hydration and systematically under-predicted the
polar and charged side chains. Anything the rules do not recognise still falls
back to the observed averages and is flagged, so a novel group never receives
a confident-looking number by accident.
Hydration within half a water improves from roughly 69 of 145 residue types to
136 of 145 across seven structures, 48% to 94%, with lysozyme and both
ribonucleases now exact on every type. All fifteen applicable coded residues
reproduce exactly in the unit tests, including the aspartate/glutamate
methylene difference. Partial specific volume and anhydrous volume are
unchanged, and perception still scores 99.833%.
Validation also has to skip chain termini at both ends, not just the carboxy
end. An amino-terminal residue has a free protonated backbone nitrogen rather
than an amide, so an amino-terminal lysine genuinely carries two ammonium
groups and six waters where the tabulated internal residue has four. The rules
were right and the comparison was wrong.
Two rule edges the coded residues exposed: a disulfide sulfur has two heavy
neighbours just as a thioether does, so both partners must be carbon before it
counts as hydrated; and proline's tertiary ring amide nitrogen carries no
hydrogen, so amide nitrogens are identified by their carbonyl neighbour rather
than by hydrogen count.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 3e1508f51183466c7aa40e80697f4fd817310078
https://github.com/ehb54/ultrascan3/commit/3e1508f51183466c7aa40e80697f4fd817310078
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_psv.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/perceiver/tests/psv.cpp
M us_somo/develop/src/us_hydrodyn_psv.cpp
Log Message:
-----------
Measure pH 7 ionisation for psv, and leave it off
The pH 7 assumption transformed hydration, so the same reasoning was applied
to partial specific volume: charge the carboxyl as a carboxylate, protonate
the guanidinium, and take an electrostriction term for each. Implemented as an
option and measured against the stored values.
The measurement says not to use it. Aspartate moves from 1.3% low to 11.0%
low, glutamate from 1.2% to 9.0%, while arginine improves from 15.9% high to
9.7%. Mean absolute error goes from 6.23% to 9.05%: a clear net loss.
The reason the symmetry with hydration breaks is visible in the residue table.
It lists the same vbar for the protonated and deprotonated state of every
ionisable residue, so ionisation there changes mass, radius, protons and
hydration but never volume. Those values descend from densitometry on real
amino acids at neutral pH and already contain whatever ionisation was present,
whereas the Durchschlag and Zipper electrostriction is an explicit correction
to a neutral-basis increment sum. Applying both counts it twice.
Hydration was different precisely because the table does carry two hydration
numbers per ionisable atom, with the pH 7 value in its own field. There the
assumption had a ground truth to match, and matching it took hydration from
48% to 94% of residue types within half a water.
The option stays, off by default, with the measured numbers in the header
comment and a test that asserts the default is whichever setting actually
measures better. If the reference values are ever placed on a single stated
ionisation basis, flipping it will be a one-line change and the test will
report whether it helped.
This does not explain arginine, which is still 15.9% high untouched and 9.7%
high with electrostriction. It is the known outlier of the scheme: the six
published residue-volume sets in Perkins 1986 disagree with each other about
arginine by 17%.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 8a415144544fffc27d929ee9cdb7cc46e9620c0a
https://github.com/ehb54/ultrascan3/commit/8a415144544fffc27d929ee9cdb7cc46e9620c0a
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/DECISIONS.md
Log Message:
-----------
Correct the record on the vbar ionisation design
An earlier note inferred that the identical vbar for the protonated and
deprotonated states meant the stored values already absorbed whatever
ionisation the underlying measurements carried. Mattia has corrected this: the
amino acid vbar values were recalculated by him at pH 7, and setting the
ionised value equal to the base is a deliberate statement that at pH 7 the
volume does not change.
There is also no stored "pH 7 value" anywhere. SOMO derives the species
fractions at run time from the stored pKa by Henderson-Hasselbalch, in
basic_fractions(), and mixes the base and ionised values by those fractions.
For aspartate at pH 7 that is 99.95% deprotonated, which is why the runtime
hydration is effectively the fully ionised value. The field described earlier
as "the pH 7 one" is the fully ionised one; pH 7 merely sits almost entirely
at that end.
This also reframes arginine. Its 0.698 is Mattia's recalculated pH 7 value,
which he states is better than the published sets, so the calculation being
16% high means it is reproducing a published-style number while the reference
is deliberately better than those, rather than the reference being uncertain.
The decision to leave the electrostriction option off is unchanged, and so is
the measurement behind it. Only the explanation changes: the correction
overshoots relative to a recalculated pH 7 reference.
What this confirms is that the implemented design is the intended one. Work at
pH 7 for a non-coded residue, since its pKa cannot be known, and assume the
hydrations determined for the amino acids, every carboxyl deprotonated and
every amine protonated. That is what the hydration rules already do.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: f355b4f83f28e5c3f287ee030841d44f257043b1
https://github.com/ehb54/ultrascan3/commit/f355b4f83f28e5c3f287ee030841d44f257043b1
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_psv.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/perceiver/tests/psv.cpp
M us_somo/develop/src/us_hydrodyn_psv.cpp
Log Message:
-----------
Implement the published metal and ion volumes
The engine reported "no volume increment for this element" for every metal.
That was wrong: Durchschlag and Zipper tabulate them, and the values had
already been extracted into the Python prototype and then never carried across
to the C++ implementation.
Table 2 supplies atomic volumes for a metal bonded into a complex, such as a
haem iron or B12's cobalt. Table 3 supplies aqueous volumes for free monatomic
ions, including iron at -32.3 and -55.1 for the divalent and trivalent forms.
Those ion values already contain the ionisation contribution, so
electrostriction is not charged a second time on top of them, and a test
asserts an isolated magnesium comes out at exactly its tabulated value with no
electrostriction term.
The trivalent iron value of -55.1 is -0.99 mL/g, the same sign and order as the
figure quoted from another compilation. Negative because electrostriction
draws water inward around the ion more than the bare ion displaces.
The effect on haem is real but small: its partial specific volume moves from
24.3% low to 23.0% low. Iron is about a percent of a 616 dalton molecule, so it
was never going to close that gap. The bulk of the error is most likely the
porphyrin itself, four fused pyrrole rings and a heavily conjugated macrocycle,
which is not what Traube-style additivity was built for.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: 18f170ffb8e37948c975e05f7f8b56c4698bb646
https://github.com/ehb54/ultrascan3/commit/18f170ffb8e37948c975e05f7f8b56c4698bb646
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_dialog.h
M us_somo/develop/perceiver/DECISIONS.md
M us_somo/develop/src/us_hydrodyn.cpp
M us_somo/develop/src/us_hydrodyn_perceive_dialog.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
Fix cross-structure state leak and add Skip all remaining
Two defects found while preparing structures for GUI testing, both visible
only when perceive is run over more than the demo set.
Running perceive twice in one session reported the previous structure's
non-coded residues against the new one. Ubiquitin alone has none, but after
malate dehydrogenase it reported two, which were citrate's. SOMO fills
unknown_residues during model building and never clears it, so the set
accumulates across loads. The emitted entries were still right, since a stale
name absent from the new model produces nothing, but the reported counts
described a structure that was no longer open. Both the script command and the
GUI slot now intersect that set with the residue names actually present in the
loaded model.
The second is a usability failure with real consequences. 5PTI is a neutron
structure carrying heavy water and explicit deuteriums, so its residues have
more atoms than the table's entries and SOMO matches none of them: fifty-five
non-coded instances across eighteen types, which meant eighteen modal dialogs
in succession with no way out but to dismiss each one. The dialog now offers
Skip all remaining, which abandons the review and leaves everything to the
Automatic Bead Builder, with a tooltip naming the likely cause so a user
seeing an implausible count suspects the file rather than the tool. The caller
honours it and reports how many residues went unreviewed.
Worth noting that the perceiver itself handles these structures correctly, as
it excludes hydrogens from bond perception by design. It is SOMO's residue
matching that fails on the atom count. Making that match hydrogen-agnostic
would be the real fix.
Refs ehb54/ultrascan-tickets#980
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
Commit: b14eb3c1403d622b64d9c2bcaf59cb85955629ed
https://github.com/ehb54/ultrascan3/commit/b14eb3c1403d622b64d9c2bcaf59cb85955629ed
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/.gitignore
M us_somo/develop/perceiver/README.md
A us_somo/develop/perceiver/data/non-coded/1MBO.pdb
A us_somo/develop/perceiver/data/non-coded/2CMD.pdb
A us_somo/develop/perceiver/data/non-coded/3PTB.pdb
A us_somo/develop/perceiver/data/non-coded/5PTI.pdb
A us_somo/develop/perceiver/data/non-coded/README.md
M us_somo/develop/perceiver/examples/perceive.somo
Log Message:
-----------
perceiver: add tracked test structures containing non-coded residues
The demo structures under perceiver/data/ are all fully coded, so nothing in
the tree exercised the path the perceiver exists for. Add four structures that
do, tracked in git since they are copies of nothing else here (the parent
data/*.pdb ignore rule is deliberately non-recursive).
1MBO SO4 only -- heme and O2 are coded, so it also shows selectivity
2CMD citrate, 13 atoms, 3 flagged for review -- the clean first test
3PTB benzamidine -- the only case reaching SSSR/ring-decrement and
electrostriction; its Ca2+ is coded and correctly left alone
5PTI neutron/deuterated -- 50 instances across 18 types, the stress case
for "Skip all remaining" (residue matching fails on atom count, not
chemistry: the perceiver excludes hydrogens by design)
data/non-coded/README.md records the expected scan output for each so a
regression is visible without rerunning everything.
Also correct the example gui_script: paths must be absolute, since SOMO
changes directory during startup and a shell-relative path fails to resolve.
Commit: fb9664846f9c72140d61c2fcc88b3aa887c033a8
https://github.com/ehb54/ultrascan3/commit/fb9664846f9c72140d61c2fcc88b3aa887c033a8
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/tests/dialog_qt.cpp
M us_somo/develop/src/us_hydrodyn.cpp
M us_somo/develop/src/us_hydrodyn_perceive_dialog.cpp
Log Message:
-----------
perceiver: fix the review dialog never appearing from the GUI menu
Lookup Tables -> Perceive Non-Coded Residues... appeared to do nothing:
the dialog was constructed as a CHILD WIDGET of the main window, not as a
window. A QFrame given a parent and no window flag is an ordinary child, so
setWindowTitle and setWindowModality were silently ignored and the
setGeometry( x, y, 0, 0 ) idiom the other 116 SOMO dialogs use -- which a
window manager expands to the layout minimum -- left it 0 x 0, positioned by
a screen-coordinate global inside (and clipped by) the parent. The caller
then waited on isVisible(), which for a child never goes false, so SOMO sat
in the wait loop with nothing on screen.
Measured, with the dialog built the way the slot builds it:
before isWindow 0, 0 x 0
after isWindow 1, 1154 x 718
Every other SOMO dialog avoids this by passing no parent at all. Keeping the
parent and adding Qt::Window is better on macOS: the dialog then also stays
stacked above its owner. Adds fixWinButtons/raise/activateWindow to match
what the other dialogs do on show.
Also make the slot incapable of failing silently again: every exit path now
writes to the text window, including both "nothing to do" cases, and the
message boxes get a parent so they cannot open behind the main window.
dialog_qt.cpp gained the check that catches this -- it constructed the dialog
with no parent, i.e. exactly the case that works. Building it with a parent
and asserting isWindow() plus a non-degenerate size after show() reproduces
the failure offscreen (3 failures on the old code, 21/21 on the new).
Commit: dfa109153157c58937f05c55b23b8eec154dc55b
https://github.com/ehb54/ultrascan3/commit/dfa109153157c58937f05c55b23b8eec154dc55b
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn.h
M us_somo/develop/include/us_hydrodyn_perceive.h
M us_somo/develop/perceiver/README.md
M us_somo/develop/perceiver/examples/perceive.somo
M us_somo/develop/perceiver/tests/builder.cpp
M us_somo/develop/src/us_hydrodyn.cpp
M us_somo/develop/src/us_hydrodyn_perceive.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
perceiver: make an accepted entry actually take effect
Accepting an entry in the review dialog only printed it. The bead builder
still saw a non-coded residue and fell back to a generic averaged bead --
the outcome this feature exists to prevent. Three separate defects, each of
which alone was enough to break it:
1. ASA was emitted as 0. Every SOMO residue loader -- read_residue_file
(us_hydrodyn_load.cpp:534), US_AddResidue (:1288), US_Saxs_Util (:693) --
skips a record whose ASA is zero, and skips it SILENTLY. The entry parsed,
never entered residue_list or multi_residue_map, and the residue went on
being reported non-coded with nothing to indicate why. All 127 coded
residues have ASA > 0. Emit 18 A^2/atom, the coded-residue mean, labelled
a placeholder in the REVIEW block: nothing computes with the tabulated
residue ASA (it appears only in info dumps), but it may not be zero.
Properties::asa lets a caller supply a measured value later.
2. The block was not a valid record. The table is strictly positional -- one
free-text comment line, then header, atoms, beads -- and has no comment
syntax. The emitted block's ~20 '#' lines were consumed two at a time as
comment/header pairs, silently creating zero-atom residues named "#".
Ticking "add to somo.residue" would have written that into the user's own
table. perceived_table_record() collapses the block to one comment line.
3. Nothing applied the entry. apply_perceived_entries() puts accepted entries
into a session overlay (<table>.perceived), re-runs read_residue_file on
it and re-reads the structure, so the atoms rebind. The user's table is
written only when they tick the box. Novel hybrid rows, previously dropped
on the floor, are injected into hybrid_to_protons/electrons first, since
read_residue_file rejects atoms whose hybrid it cannot resolve.
Adds "perceive apply <pdb>" -- the headless equivalent of accepting every
entry -- reporting the non-coded count after the fact. That assertion is what
found (1) and (2); the GUI path had no way to tell "file written" from
"entry in effect". builder.cpp now checks the emitted ASA is positive.
Verified: 1MBO SO4 and 2CMD CIT both apply, 0 types still non-coded, and a
fresh perceive of 1MBO reports every residue already coded. Suites green
(54/35/18/60/5/37/55/21), regression 99.833%.
Commit: 2f95e97d15f10253a1b9946b9a863eb233b6e6f2
https://github.com/ehb54/ultrascan3/commit/2f95e97d15f10253a1b9946b9a863eb233b6e6f2
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_dialog.h
M us_somo/develop/perceiver/tests/dialog_qt.cpp
M us_somo/develop/src/us_hydrodyn_perceive_dialog.cpp
Log Message:
-----------
perceiver: stop the review dialog overriding the emitted ASA
The dialog does not write the perceiver's block -- refresh_entry() rebuilds
the whole entry from its widgets, and its header format string hardcoded the
ASA slot to 0:
s += QString( "%1\t0\t%2\t0\t%3\t1\t%4\n" )
so fixing the emitter changed nothing for anyone using the GUI. A run shows
both the new REVIEW line about the ASA placeholder AND a header reading
"SO4 0 61.75 0 5 1 0.390" -- one binary, two emitters, and the loaders drop a
zero-ASA record silently, so the residue stayed non-coded after acceptance.
"perceive apply" could not catch this: it uses the Tentative block directly
and never constructs a dialog, so it exercised the half that was already
right. The dialog now parses type and ASA out of the proposed header and
writes them back untouched, editing only what it owns -- molvol, vbar,
hydration and colour. Anything it hardcodes silently overrides perception,
so it should hold nothing it does not edit.
The dialog test could not catch it either: its fixture block carried ASA 0,
so any assertion would have passed vacuously. The fixture now carries 54.00
and the test parses the rebuilt header and asserts the ASA field is positive
and still 54.0, alongside molvol -- the two fields the loaders gate on.
25 checks, 0 failures; Qt-free suites unchanged (54/35/18/60/37/55).
Commit: 57804dfa39b133200c82b059fbd356cb6ee7663f
https://github.com/ehb54/ultrascan3/commit/57804dfa39b133200c82b059fbd356cb6ee7663f
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn.h
M us_somo/develop/src/us_hydrodyn_beads.cpp
M us_somo/develop/src/us_hydrodyn_core.cpp
M us_somo/develop/src/us_hydrodyn_fractal_dimension.cpp
M us_somo/develop/src/us_hydrodyn_load.cpp
M us_somo/develop/src/us_hydrodyn_saxs_1d.cpp
M us_somo/develop/src/us_hydrodyn_saxs_2d.cpp
M us_somo/develop/src/us_hydrodyn_saxs_iqq.cpp
Log Message:
-----------
somo: derive an atom-table entry instead of dropping the atom
somo.atom is keyed by ATOM NAME, so an atom whose name that table has never
seen resolves to nothing and is dropped from both the ASA and the excluded
volume -- reported once, then ignored. That is routine for a non-coded
ligand: 1MBO's sulfate sulfur is named "S", and the table carries only the
Fe-S cluster names S1..S4B.
Chain A Molecule 2 Atom S Residue SO4 154 Hybrid S name missing from
Atom file. Atom skipped for SAS.
ensure_atom_entry() derives one instead. It prefers another atom with the
SAME HYBRID -- which fixes element, mass and radius exactly, leaving only the
excluded volume a convention -- and falls back to the same ELEMENT. The
derived entry goes into the map, so it is also the memo: the next lookup is
an ordinary hit.
Excluded volume takes the value that key uses MOST OFTEN in the table rather
than whichever row is found first, because it is not a function of the
hybrid: C4H3 appears with both 31.89 and 16.44 A^3 depending on atom name.
For sulfur the question does not arise -- every S row is 19.86.
It reports what it did, once per atom name. A silent substitution here would
be the same class of defect as the silent skip it replaces:
Note: S (S) is not in the atom table; using the hybrid match:
mw 32.065, radius 0.43, excluded volume 19.86 A^3
Applied at all six lookup sites, not only the two a bead build reaches: bead
building, SAS, fractal dimension, and the SAXS 1D/2D/I(q) paths.
Verified on 1MBO: the fallback fires once for S, nothing is skipped, and the
perceived SO4 entry still applies cleanly (0 types still non-coded).
Commit: ba5015a5511fb573d07cf81b27f13e979bb95cd0
https://github.com/ehb54/ultrascan3/commit/ba5015a5511fb573d07cf81b27f13e979bb95cd0
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/README.md
M us_somo/develop/perceiver/examples/perceive.somo
M us_somo/develop/src/us_hydrodyn_load.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
perceiver: add "perceive build" so bead-build defects are testable headlessly
"perceive apply" cannot cover the model builder. A clean load takes calc_mw(),
not the excluded-volume path, so the somo.atom lookup -- and anything else
that only bites at bead-build time -- was exercised solely by clicking
through the GUI. That is how the atom-table skip reached the user in the
first place.
perceive build [somo|somo_o|vdw|grid|a2b] <pdb>
is apply plus what the user does next: select the model, call the same
function the button does (calc_somo / calc_somo_o / calc_vdw_beads /
calc_grid_pdb), report the bead count, and FAIL the script if the build
fails or yields no beads. vdw is the sharpest of the four for a perceived
residue: it relies on atomic hydration, which the perceiver only proposes.
Two things this immediately caught in the harness itself:
- Counting bead_models[current_model] reported 0 beads for a build that had
plainly succeeded (rc 0, three overlap-reduction stages, a written bead
model). The build walks current_model as its loop variable and leaves it
ONE PAST the last model, so that index is out of range right afterwards.
Count bead_model, the model just built.
- ensure_atom_entry's report went out through editor_msg(), which only ever
writes to the GUI text widget -- so in a gui_script run the substitution
was completely silent, and its absence from a log proved nothing. It now
also goes to stdout. A silent substitution is the same defect class as the
silent skip it replaces.
Verified on 1MBO/2CMD against the reference tables: the fallback fires once
during the first build ("atom table: S (S) is not in the atom table; using
the hybrid match ... 19.86 A^3") and all four methods build --
somo 305, somo_o 305, vdw 1267, grid 331 beads. The 305 matches the count
from the user's GUI session.
Docs record that a script using "perceive build" must set "somo overwrite"
first -- without it the bead-model write calls setSomoGridFile(), which
raises a modal dialog and hangs a headless run (batch suppresses those
prompts, a gui_script does not) -- and that there is no equivalent flag for
the hydrodynamic calculations.
Commit: 26495b76b360b4c7ad8b70a164531dc54439f8ab
https://github.com/ehb54/ultrascan3/commit/26495b76b360b4c7ad8b70a164531dc54439f8ab
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/develop/perceiver/GUI_TESTING.md
M us_somo/develop/src/us_hydrodyn_perceive_dialog.cpp
M us_somo/somo/doc/manual/somo/somo.html
A us_somo/somo/doc/manual/somo/somo_perceive.html
Log Message:
-----------
perceiver: dedicated help page for the non-coded residue dialog
The review dialog's Help button pointed at somo_residue.html, which documents
the residue table editor and says nothing about perception. Point it at a new
somo_perceive.html and link that from the Lookup Tables list in somo.html,
beside Add/Edit Residue.
The page follows the manual's conventions and uses the dialog's actual
labels -- Skip this residue, Also append this entry to somo.residue, Flagged
for review -- rather than paraphrases. It reserves an <img> slot for
somo-perceive.png next to the HTML.
Its substance is what a GUI tester cannot get from the interface itself:
- perceive BEFORE building, or the Automatic Bead Builder's _NC placeholders
are already in the model
- what Accept actually does. An accepted entry goes to somo.residue.perceived
and becomes the active table IMMEDIATELY, but only for this session:
residue_filename is reset to somo.residue at every start and is not stored
in the config, and the next session's first Accept REBUILDS that file from
somo.residue, discarding what an earlier session accepted. Tick the
checkbox, or re-select the file by hand before accepting anything new
- what each computed number is worth, and that hydration is the weakest --
which is why vdW models are where a weak proposal becomes a real problem
- that ASA is a placeholder which nonetheless may not be zero
Also adds perceiver/GUI_TESTING.md, the same ground as a cheat sheet for
someone testing only through the GUI, with the four test structures and what
to be sceptical of in a proposed entry.
Note US_Help::show_help() falls back to underconstruction.html when the file
is missing, so "under construction" means help_dir points at an installed
release rather than at this tree.
Commit: a2e127b4d827e58300de76c52522b858b3657e9a
https://github.com/ehb54/ultrascan3/commit/a2e127b4d827e58300de76c52522b858b3657e9a
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/develop/perceiver/data/non-coded/8RATNC.pdb
A us_somo/develop/perceiver/data/non-coded/8RAT_gap3.pdb
A us_somo/develop/perceiver/data/non-coded/8RAT_run3.pdb
A us_somo/develop/perceiver/tools/make_noncoded_variant.py
Log Message:
-----------
perceiver: chain-embedded test residues with a known answer
Every non-coded structure in the test set so far is a free ligand -- citrate,
benzamidine, sulfate -- so nothing exercises a non-coded residue INSIDE a
chain, and nothing has a ground truth to check the computed numbers against.
us_somo/somo/demo/8RATNC.pdb is 8RAT with LYS 31 renamed to LYD, done by hand
upstream. That trick is the useful one: the atoms are untouched, so whatever
the perceiver derives can be compared directly against the coded entry for
the residue it really is. Add it here and generalise it.
tools/make_noncoded_variant.py IN.pdb OUT.pdb A:31=LYZ ...
renames residues by chain and resSeq, refusing to write if a spec matched
nothing -- a silently unmodified file would quietly pass any test built on
it. Two shapes beyond the single-residue case, both aimed at the chain
handling rather than the chemistry:
8RAT_run3.pdb LYS31/SER32/ARG33 -> LYZ/SEZ/ARZ, an unbroken run
8RAT_gap3.pdb LYS31/ARG33/LEU35 -> LYZ/ARZ/LEZ, alternating with coded
Coded values to check against (somo.residue.new):
LYS vbar 0.818 molvol 167.30 hydration 4.0
SER vbar 0.628 molvol 95.40 hydration 2.0
ARG vbar 0.698 molvol 194.00 hydration 3.0
LEU vbar 0.898 molvol 164.00 hydration 1.0
Expect a systematic offset rather than a match: the perceiver treats a
residue as an isolated unit, while the coded amino-acid entries describe it
in a chain.
Commit: cdd288a85403d8acde8bccbb73c6c2c01266521c
https://github.com/ehb54/ultrascan3/commit/cdd288a85403d8acde8bccbb73c6c2c01266521c
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/GUI_TESTING.md
M us_somo/somo/doc/manual/somo/somo_perceive.html
Log Message:
-----------
perceiver: record the measured accuracy in the help page
The help page said what each computed number is worth in general terms.
Now there are numbers, from residues where the answer is known: rename a
residue the table already codes, leave its atoms untouched, and the coded
entry becomes the answer key.
really psv perceived/coded volume perceived/coded hydration
SER 0.632 / 0.628 +0.6% 97.95 / 95.40 +2.7% 2.0 / 2.0
LEU 0.892 / 0.898 -0.7% 158.61 / 164.00 -3.3% 1.0 / 1.0
LYS 0.871 / 0.818 +6.5% 172.85 / 167.30 +3.3% 4.0 / 4.0
ARG 0.809 / 0.698 +15.9% 196.19 / 194.00 +1.1% 3.0 / 3.0
Hydration was exact on all four -- the quantity with the least theoretical
backing, and the one a tester is most able to sanity-check. Volume within
+-3.3%. psv splits by charge: neutral side chains +-0.7%, charged ones high.
The arginine +15.9% is reproduced independently by the isolated-residue
validation and matches the reading that the tabulated Arg psv was measured
unprotonated while the increments assume protonation -- so the help page
tells the user to treat a perceived psv for a charged group as provisional.
Chain embedding caused no difficulty: one non-coded residue, three in a row
and three alternating with coded ones gave identical values and built
cleanly (244/242/242 beads). The residuals track charge, not chain context
-- the opposite of what was expected, since the perceiver sees the real
bonded neighbours and so perceives backbone N and C as amide and carbonyl.
Same table added to the GUI cheat sheet, with the three new test files.
Commit: d78eaba38000ea86a71fde17cb204f085409417f
https://github.com/ehb54/ultrascan3/commit/d78eaba38000ea86a71fde17cb204f085409417f
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
A us_somo/somo/doc/manual/somo/somo-perceive.png
M us_somo/somo/doc/manual/somo/somo_perceive.html
Log Message:
-----------
perceiver: add the review dialog screenshot to the help page
The page referenced somo-perceive.png without the file existing, so it
rendered a broken image. Adds it, with a caption.
The screenshot is citrate rather than benzamidine deliberately: benzamidine
has 0 atoms flagged and an empty-looking review box, while citrate shows the
Flagged for review box populated with the carboxyl/carboxylate protonation
and pH 7 hydration notes -- the part of the dialog the page spends most of
its words on, and the part a tester most needs to recognise.
Verified: every SRC and href on the page resolves, all three tables close.
Commit: 64b54a4df90fa2200154bbb7fecc3176213d95ef
https://github.com/ehb54/ultrascan3/commit/64b54a4df90fa2200154bbb7fecc3176213d95ef
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn.h
M us_somo/develop/src/us_hydrodyn.cpp
M us_somo/develop/src/us_hydrodyn_perceive_dialog.cpp
M us_somo/develop/src/us_hydrodyn_script.cpp
Log Message:
-----------
wip: mattia review items 1-3
Commit: 80e08e9dc5f268df0224b53014c5fe76bd609b11
https://github.com/ehb54/ultrascan3/commit/80e08e9dc5f268df0224b53014c5fe76bd609b11
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/src/us_hydrodyn.cpp
Log Message:
-----------
wip: fix coded-test, map operator[] inserts on read
Commit: c3dfad80e43d0d95d71045119cfe2b9b50243ed1
https://github.com/ehb54/ultrascan3/commit/c3dfad80e43d0d95d71045119cfe2b9b50243ed1
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn.h
M us_somo/develop/include/us_hydrodyn_perceive_dialog.h
M us_somo/develop/src/us_hydrodyn.cpp
M us_somo/develop/src/us_hydrodyn_perceive_dialog.cpp
Log Message:
-----------
wip: revisit accepted entries + reset-all
Commit: 93a8250cbe4f4c68d26de4826e678b9a54643e3c
https://github.com/ehb54/ultrascan3/commit/93a8250cbe4f4c68d26de4826e678b9a54643e3c
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn.h
Log Message:
-----------
fix: perceived_entries member must not live in a slots block (moc)
Commit: cdd317c12649f7998590441633172f998bd7ef39
https://github.com/ehb54/ultrascan3/commit/cdd317c12649f7998590441633172f998bd7ef39
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/src/us_hydrodyn.cpp
Log Message:
-----------
fix: raw newline inside a string literal from a scripted edit
Commit: f7a45de8f2384fb01f344630a2c4d70e974b3150
https://github.com/ehb54/ultrascan3/commit/f7a45de8f2384fb01f344630a2c4d70e974b3150
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_elements.h
M us_somo/develop/perceiver/tests/tests_unit.cpp
M us_somo/develop/src/us_hydrodyn_perceive_somo.cpp
M us_somo/develop/src/us_hydrodyn_psv.cpp
Log Message:
-----------
perceiver: never compute a psv from unidentified elements
A PDB with an empty element column (77-78) produced psv values of 0.001,
-0.068, -0.087 -- meaningless numbers that still look like numbers.
1AO6-compl_monA.pdb in the demo set is such a file, which is how it surfaced.
Two independent defects, either alone enough:
1. The atom-name fallback used norm_element(), which only strips
non-alphabetics. So the alpha carbon "CA" became CALCIUM, "NE2" became
NEON, and "CB", "CG", "OD1" matched no element at all. Every atomic volume
increment then evaluated to zero and the sum collapsed to roughly the
electrostriction terms. element_from_atom_name() takes the leading letter,
accepting a two-letter element only when the atom name equals the residue
name -- HETATM "CA" in residue "CA" is calcium, "CA" in "ALA" is carbon.
2. somo_psv::compute() reported ok on a residue containing atoms it had no
increment for. It now counts them, records why in the review block, and
returns ok == false, so the caller emits no psv rather than a plausible
figure. An unidentified element is not a small error; it is not a volume.
The grid volume was unaffected and still looked sensible (Ala 77.45 A^3),
which is precisely why this was not obvious from the output.
tests_unit.cpp gains the case that catches it: CA/CB/CG1/OD1/NE2/NZ/SD/OXT
must resolve by leading letter, while CA-in-CA, FE-in-FE, ZN-in-ZN, MG-in-MG
resolve as the two-letter element. 69 checks, 0 failures.
Commit: cf4246cc29f0abb38781388293af0d633ac2df95
https://github.com/ehb54/ultrascan3/commit/cf4246cc29f0abb38781388293af0d633ac2df95
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_elements.h
M us_somo/develop/perceiver/tests/tests_unit.cpp
Log Message:
-----------
perceiver: element inference must key on the residue, not the atom name
The previous rule took the leading letter unless the atom name equalled the
residue name. That is right for proteins and wrong for ligands -- exactly the
population this code exists for. Six of fifteen test cases failed:
FE in HEM -> F (fluorine) CU in CUA -> C
CL in CIT -> C SE in MSE -> S
NA in XYZ -> N BR in LIG -> B
so a heme iron contributed a fluorine volume, silently.
The discriminator is the RESIDUE. In a standard residue the naming convention
is fixed and the element is always the leading letter: CA is the alpha carbon,
NE2 a nitrogen, SD a sulfur. In a ligand it is not, and a two-letter atom name
that matches a known element is that element.
Residual ambiguity, stated in the header: a ligand carbon named bare "CA"
reads as calcium. Ligand carbons are normally C1/CAA/CB1, and the psv now
refuses to compute rather than guessing when an element cannot be resolved,
so the failure is visible either way.
Answers the question directly: with this, a PDB with no element column is
handled correctly for proteins AND for ligands with two-letter elements.
Without it, only proteins were safe.
79 checks, 0 failures.
Commit: cd05f6908b8f9fe815785b83e87ae31d869ac939
https://github.com/ehb54/ultrascan3/commit/cd05f6908b8f9fe815785b83e87ae31d869ac939
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_residue_builder.h
M us_somo/develop/perceiver/tests/builder.cpp
M us_somo/develop/src/us_hydrodyn_residue_builder.cpp
Log Message:
-----------
perceiver: emit psv at the table temperature, not the increments'
The Durchschlag & Zipper increments describe 25 C. somo.residue is a 20 C
table -- SOMO itself converts with tc_vbar() using 4.25e-4 cm^3 g^-1 K^-1.
The perceiver applied no conversion at all, so every generated entry was
5 K x 4.25e-4 = 0.0021 cm^3/g high: about +0.3%, small enough to read as
method noise and not noise at all.
Found by comparing against Mattia Rocco's independent recalculation. Matching
the temperature reference collapses the disagreement:
residue vs his 25 C vs his 20 C
GLY ALA VAL -0.07 .. +0.02% +0.20 .. +0.33%
LEU ILE THR
CYS MET PHE
TRP ASN GLN
SER +0.07% +0.41%
ASP +0.16% +0.52%
14 of 20 residues agree to within +-0.07% once the reference matches.
His conversion itself is exact, which was the other possibility Mattia raised:
the dv/dT implied by his 25 C and 20 C columns is 4.2500e-04 for all 20
residues with ZERO spread, and equals SOMO's tc_vbar coefficient. No rounding
error there -- the discrepancy was entirely on this side.
The temperatures and the coefficient are Options fields rather than literals,
so a table at another reference temperature is a parameter change.
builder.cpp gains the check: ALA psv 0.7413 at 25 C -> 0.7392 at 20 C, which
is Rocco's tabulated 20 C value exactly. 57 checks, 0 failures.
Commit: e95f8708da6bdf9072a82b2a4b5334f77670f1f0
https://github.com/ehb54/ultrascan3/commit/e95f8708da6bdf9072a82b2a4b5334f77670f1f0
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/include/us_hydrodyn_perceive_elements.h
M us_somo/develop/perceiver/tests/tests_unit.cpp
Log Message:
-----------
perceiver: protein atom names win over two-letter elements
Keying element inference on the residue name alone broke modified residues --
the very population this feature exists for. MSE, PTR, CGU and any renamed
residue carry protein atom names (CA, CB, CD, NE) under a NON-standard
residue name, so they took the ligand branch and every backbone CA became
CALCIUM. That changed the hybrid, the radius, the grid volume and the
hydration as well as the psv: alanine came back as vbar 0.171, molvol 83.9,
hydration 4.0 instead of 0.739 / 87.4 / 1.0.
Caught by re-running the 20-residue comparison rather than trusting the unit
tests, which used real residue names and so never entered that branch.
Resolution order is now explicit:
1. lone ion atom name == residue name ("CA" in residue "CA")
2. standard residue leading letter
3. protein atom name leading letter, whatever the residue is called
4. two-letter element FE, CL, SE, BR, CU, ZN, MG
5. leading letter
Step 3 is the one that makes modified and renamed residues work, and it is
why "SE in MSE" still resolves to selenium while "CA in MSE" resolves to
carbon: the collision set is atom NAMES, not residues.
86 checks, 0 failures, with MSE/PTR/CGU/renamed cases in the suite.
Commit: ad6a2302da74ee7c1ecab449fdb7f593cbf19f60
https://github.com/ehb54/ultrascan3/commit/ad6a2302da74ee7c1ecab449fdb7f593cbf19f60
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/Makefile
A us_somo/develop/perceiver/data/psv/1BEB.pdb
A us_somo/develop/perceiver/data/psv/2CGA.pdb
A us_somo/develop/perceiver/data/psv/README.md
A us_somo/develop/perceiver/data/psv_measured.txt
A us_somo/develop/perceiver/tests/protein_psv.cpp
Log Message:
-----------
perceiver: whole-protein psv, incumbent table vs D&Z increments vs D&Z+pH7
Mattia's question was whether to substitute somo.residue's amino-acid psv set with
one derived from the atomic volume increments. The residue-level four-way table
localised the differences but could not say which is right; this compares all three
against measured protein psv, using SOMO's own aggregation (calc_vbar_updated).
Measured values mined from Durchschlag Table 5 + the albumin table, taking only the
CORRECTED columns (phi, phi(0), v(0)) -- the apparent columns vary 5.7% at constant
pH purely from cosolvent, so they cannot be compared against a computed value.
Two guards the first run needed:
- explicit H/D are stripped. D&Z is a united-atom scheme and so is somo.residue
(ALA: N 15.02, CA 13.02, CB 15.04), so explicit hydrogens double count. The psv
guard already refused them; the tool must not hand them over in the first place.
- a structure whose usable residues fall below 95% is EXCLUDED, not reported. Before
this, 2AAS averaged a handful of surviving residues into a confident-looking
+13.7%. With H stripped it agrees with 8RAT to four decimals.
All three columns are gated on table membership so they cover identical residues;
otherwise a ligand the table cannot represent (1BEB's SO4) would count for D&Z only.
Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
Commit: 17dd65468fbc3f2c188106285b6d777acf5c8fb6
https://github.com/ehb54/ultrascan3/commit/17dd65468fbc3f2c188106285b6d777acf5c8fb6
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/pdb_lite.h
M us_somo/develop/perceiver/tests/protein_psv.cpp
Log Message:
-----------
perceiver: strip hydrogens and index MODELs in pdb_lite, as SOMO does
Mattia: 'US-SOMO should strip them away! Why isn't it doing it in its tests?
Also the multiple models in NMR-style files. It should correctly recognize them
and perform the calculations anyway.'
He is right, and SOMO itself was never the problem:
- US_Hydrodyn::read_pdb strips H by name (us_hydrodyn_load.cpp:1489-1492,
including the ^\dH form that catches 1HB / HD21)
- it keeps every MODEL in model_vector, with a workaround for files whose
MODEL tag is missing
perceiver/pdb_lite.h did neither properly: it passed hydrogens straight through
and silently kept only model 1. That is not confined to the psv test -- 2AAS is
the one demo file with hydrogens (7840, 245 in model 1) and it drives regress,
coverage and sssrreal, so every one of those has been perceiving a structure
SOMO would never have handed it.
Fixed at the reader so every test inherits it, rather than patching one test:
- is_hydrogen_atom() reproduces SOMO's name rule, with one deliberate
divergence -- an explicit element column wins, so a mercury named HG survives
where SOMO's name rule would drop it. The perceiver exists for such ligands.
- MODELs are indexed on the atom; model_count()/model_of() split them. protein_psv
now computes every model and reports the ensemble spread instead of assuming
model 1, since which model to use is a later choice (his point exactly).
Effect: 2AAS goes from unusable to agreeing with 8RAT -- the same protein solved
by a different method -- to four decimals, and across its 32 NMR models the psv
spread is 0.0005 cm3/g (0.07%), which is the precision check that ensemble buys.
Regression unchanged and healthy: 97.894% exact, 99.833% geometric perception.
Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
Commit: c18f890853e48ded035a956bbff0cb7f78cf35d1
https://github.com/ehb54/ultrascan3/commit/c18f890853e48ded035a956bbff0cb7f78cf35d1
Author: ehb54 <brookes at uthscsa.edu>
Date: 2026-08-10 (Mon, 10 Aug 2026)
Changed paths:
M us_somo/develop/perceiver/pdb_lite.h
Log Message:
-----------
perceiver: recognize deuterium too, matching somo-dev's new skip option
somo-dev 10d48cec added a 'Skip deuterium atoms' parsing option (default on) with
pdb_parse_is_deuterium() in us_hydrodyn_pdb_parsing.h. pdb_lite's hydrogen test
already stripped D via the element column but not via the atom name, so a neutron
structure without col 77-78 would have leaked deuteriums into the perceiver.
Aligned with their helper, including its rationale: the element column wins over
the name because a name test alone cannot tell a deuterium from the second letter
of a two-letter element -- their example is 'CD' (cadmium), mine was 'HG'
(mercury). Same conclusion reached independently, now spelled the same way.
5PTI (data/non-coded) is the case: 229 deuteriums, all carrying an element column,
so behaviour there is unchanged -- this closes the no-element-column path.
Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
Compare: https://github.com/ehb54/ultrascan3/compare/8acebd135a02...c18f890853e4
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