Bug fix:
- PrincipalMomentsDescriptor.clampNonNegative now also clamps NaN. The
v<0 check was false for NaN, so a NaN eigenvalue (possible if a future
code path bypasses GridGenerator.isFiniteBox) would have propagated
to the CSV output.
Doc refresh:
- breaking-changes.md: 2.6 entry for the multi-column descriptor
migration + the -vis_pocket_grid / pocket_grid_vis_* renames.
- export-pocket-descriptors.md: step 4 rewrites a self-contradicting
rationale — adding to the default list IS a breaking change for
index-based parsers; recommends parse-by-name + breaking-changes.md
note for future additions.
- export-pocket-grid.md: added "Adding a new per-grid-point descriptor"
recipe (parallel to the per-pocket one); unified √3/2 precision to
0.866 across docs and Params.groovy.
- README.md: added an "Opt-in tabular exports" subsection mentioning
-export_pocket_descriptors, -export_pocket_grid, -vis_pocket_grid.
- testsets.sh "Full descriptor menu" now lists all seven shipped
descriptors (was six).
Exception taxonomy:
- PocketDescriptorsRows.groovy and PocketGridBuilder.java now throw
PrankException (was IllegalArgumentException) for user-facing config
errors, matching the rest of the codebase.
Registry hardening:
- Both PocketDescriptorRegistry and PocketGridPointDescriptorRegistry
now assert columnNames.size() == columnTypes.size() in register().
A future descriptor with mismatched lists fails fast at class-load.
Quality fixes:
- PocketGridRows.getColumn uses BASE_COLS-1 instead of literal 3 for
the pocket column. Removed dead 2-arg PocketGridRows constructor
(only 3 test sites used it; now inlined).
- PocketGridPointContext gets a compact-constructor validator that
rejects negative pointIndex/pocketRank, limiting blast radius of an
int-arg swap.
Test hardening:
- VolsiteSmoothGridPointDescriptorTest + VolsiteGridPointDescriptorTest
now pin sigma/radius in @BeforeEach AND restore in @AfterEach, so
the Params singleton is clean for subsequent test classes.
- New tests: HIS ND1 double-flag (single atom setting donor+acceptor),
PrincipalMoments at cardinality=2, PrincipalMoments two coincident
points, GridGenerator NaN-box throw, PocketDescriptorRegistry
register/unregister round-trip, MorphologicalCloser maxIters=1.
- Renamed respectsMaxIters → maxItersZeroIsNoOp (the test only covered
the maxIters=0 case despite the general name); added maxIters=1
companion that verifies one iteration of fill actually runs.
- Extracted RendererTestFixtures.tinyGrid (was byte-identical in both
renderer test files); unified the volsite atomAt signatures so the
parameter order can't get swapped between the two volsite tests.
- Params.groovy: pocket_descriptors javadoc now lists all 7 shipped
descriptors (was: 6); softens the "essentially free" rationale to
acknowledge principal_moments' small eigendecomposition cost.
- PocketDescriptorsTest.groovy: class javadoc "six shipped descriptors"
→ "seven", names principal_moments alongside the rest.
- export-pocket-descriptors.md: "6 base shipped descriptors use this
adapter" → "6 of 7 use the adapter; principal_moments (multi-column)
implements PocketDescriptor directly". Removes a misleading count.
- export-pocket-{grid,descriptors}.md: default-list rationale no longer
claims adding descriptors is "essentially free" — clarifies that
grid-derived scalars are cheap once the grid is built but
principal_moments adds a small per-pocket compute on top, still
negligible vs the grid build.
Caught by deep audit of 60220d7a..73e7c9df focused on doc/comment drift
after the recent multi-column interface migration.
Unifies the per-pocket descriptor framework with the per-grid-point
framework: same shape (name + columnNames + columnTypes + double[]
compute), same multi-column "{name}.{col}" header convention, same
public register / unregister / dup-column-check registry. Shipped as
breaking change behind the same -pocket_descriptors knob.
Interface change:
String name();
List<String> columnNames();
List<ColumnType> columnTypes();
double[] compute(PocketGridContext);
boolean needsGrid(); // unchanged
Scalar descriptors stay one-liners via the new
AbstractScalarPocketDescriptor adapter (name + scalarType +
computeScalar). The 6 existing descriptors migrated; behavior and
output byte-identical to before.
New descriptor: PrincipalMomentsDescriptor (3 × DOUBLE) — the three
eigenvalues of the pocket grid points' gyration tensor, sorted
descending. Implementation uses Apache Commons Math 3
EigenDecomposition. Shape signature complement to sphericity /
radius_of_gyration; sum equals radius_of_gyration² (verified in test).
Added to the default -pocket_descriptors list.
Default list reordered to put num_* (cheap, integer-valued) first,
then geometric scalars, then principal_moments:
num_residues, num_surface_atoms, num_grid_points,
volume, sphericity, radius_of_gyration,
principal_moments
Tests:
- 5 new PrincipalMomentsDescriptor tests (cube isotropy, rod-shape
eigenvalues, sort order, degenerate empty/single, sum=Rg²)
- PocketDescriptorsRowsTest +2 (multi-column prefix rule, mixed
scalar + multi ordering)
- existing 13 callsites updated for the double[] return signature
- columnType() registry test → columnTypes()
User-visible change: the default -pocket_descriptors output now has
three new columns (principal_moments.lambda1/2/3) and the existing
columns appear in a different order. Scripts parsing by column name
are unaffected; scripts parsing by column index need updating.