Commit Graph

4 Commits

Author SHA1 Message Date
rdk
6fad858bc6 Audit follow-ups: bug fix, doc refresh, exception taxonomy, test hardening
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.
2026-05-19 15:36:12 +02:00
rdk
cb6f7f75eb Doc / comment refresh after the multi-column descriptor migration
- 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.
2026-05-19 14:41:03 +02:00
rdk
73e7c9df9a Per-pocket descriptors: multi-column interface + PrincipalMomentsDescriptor
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.
2026-05-19 14:34:33 +02:00
rdk
60220d7a57 Add pocket-grid + descriptors export with PyMOL / ChimeraX viz
Per-protein 3D grid of points around predicted pockets with per-pocket
assignment, plus per-pocket geometric descriptors (volume, sphericity,
radius_of_gyration, num_residues, num_surface_atoms, num_grid_points).

User-facing knobs (all under -export_pocket_*, -pocket_grid_*, -vis_pocket_*):

  -export_pocket_grid          CSV/Arrow/Parquet grid file
  -export_pocket_descriptors   CSV/Arrow/Parquet descriptors file
  -vis_pocket_grid             PyMOL/ChimeraX overlay scripts
  -pocket_grid_format          csv | csv.gz | csv.zst | arrow{,.gz,.zst} | parquet
  -pocket_grid_spacing         lattice edge (Å)
  -pocket_grid_max_dist        outer bound vs nearest pocket SAS point
  -pocket_grid_atom_buffer     inner bound vs vdw(nearest atom)
  -pocket_grid_assign_cutoff   per-pocket membership cutoff
  -pocket_grid_assigner        kdtree | voxel_hash
  -pocket_grid_fill            morph_closing | none
  -pocket_descriptors          subset of registered descriptors
  -vis_pocket_grid_volume_radius / _gaussian_iso  viz tuning

Renderers (PocketGridPymolRenderer, PocketGridChimeraXRenderer) overlay
on top of the standard pocket viz with per-pocket togglable layers:
discrete spheres, vdW-radius surface union, gaussian-iso (PyMOL only),
convex-hull wireframe (PyMOL only, requires scipy). Both honor
-vis_renderers membership.

Startup validation for all new params (Main.validatePocketGridParams,
Main.validateVisParams) — typos in renderer/format/fill/assigner names
fail fast instead of silently emitting nothing.

Performance: LongIntHashMap-backed lattice index, BitSet pocket
assignments, pluggable range-query (kdtree vs voxel-hash), morph-closing
frontier expansion. Most hot paths converted from Groovy to Java.

Docs: documentation/export-pocket-grid.md, export-pocket-descriptors.md.

Squashed from 70 commits (9b7d7a64..fec803ff). Pre-squash granular
history preserved on branch develop-backup-2026-05-19.
2026-05-19 03:03:33 +02:00