Skip to content

CaviTracer: seed channel searches per chamber, report chamber links, and name outputs after what they hold - #2270

Merged
karolamik13 merged 10 commits into
prody:mainfrom
briza81:CaviTracer-multi-seed-channels
Aug 26, 2026
Merged

CaviTracer: seed channel searches per chamber, report chamber links, and name outputs after what they hold#2270
karolamik13 merged 10 commits into
prody:mainfrom
briza81:CaviTracer-multi-seed-channels

Conversation

@briza81

@briza81 briza81 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary
Channel detection previously started every search at a single point — the deepest tetrahedron of a cavity. That is the right site for a compact pocket, but not for a large or branched one, and especially not when a narrow probe fuses several real pockets into one cavity. This PR replaces the single seed with one seed per chamber, adds a way to report how those chambers connect, and makes the defaults, output names and logs consistent with the multi-seed picture.

Changes implemented here:
1) Channels are seeded per chamber, not at the single deepest point. With one seed, every channel of a cavity was at least as long as the cavity is deep, and the mouths of the other lobes went unreported — a route from the far seed is absorbed at a nearer mouth before it arrives. The cleared void is now carved a second time at seed_radius, and each surviving connected piece is a chamber that seeds its own search. Chambers qualify by volume and depth (seed_volume, min_depth) rather than by tetrahedron count, so a chamber is measured on the same scale as a cavity, and max_seeds bounds the run on structures whose chambers are least trustworthy. Cavities are ordered largest first, so cavity 0 is the biggest void wherever it sits.

2) Chamber links are reported. A deep chamber often has no opening of its own and reaches solvent only through a shallower one; its route out then duplicates the shallower chamber's shorter channel and was dropped, leaving the deep site unreported. That access is now reported separately as a link — the route from the deep chamber, cut where it joins the shallower one — so the whole way out reads as link(deep → shallow) + channel(shallow → surface), and the link's bottleneck is the neck that actually governs passage. Links are returned in details['links'], written beside the channels, and measured by new getLinkParameters / getLinkResidueNames with their multi-frame wrappers.

3) Defaults now describe what a probe can actually pass. bottleneck defaults to inner_radius instead of 0: a channel narrower than the probe that traced it is not something that probe can pass, and reporting it by default invited the opposite reading. min_volume and the tetrahedron-count filters stay purely about what is reported, deliberately decoupled from where a search may start, so asking for only the large channels never removes the seeds that find them.

4) Output files are named after what they hold, and by their start point (start site). Per-object files carry the site they were traced from (out_sp0_chl3.pqr), so everything belonging to one void globs together; a run given only a directory drops the run stem entirely (channels.pqr, links.pqr, cavities.pqr, pores.pqr). Pores no longer collide with channels once the stem is gone, cavity files state their volume, depth and tetrahedron count in a REMARK, and selectChannelBySelection reads the new names back through one shared parser for any object kind.

5)Multi-frame parameter wrappers write one file per frame. They previously wrote every frame under a single name, so frames overwrote one another and only one frame's parameters could be read back. Each frame now writes _frame<i>_ / _model<i>, matching what the residue wrappers already do, so parameters and lining for the same frame stay findable under one prefix.

Behaviour changes to be aware of

  • Multi-seeded cavities report more channels than before, and their mouths are no longer all reached from one deep point.
  • The new bottleneck default filters channels that earlier runs listed; bottleneck=0 restores the old behaviour.
  • Per-object and multi-frame output file names have changed (see above).

briza81 and others added 10 commits August 12, 2026 13:55
…er_radius, and min_volume defaults to 10 A^3. A channel narrower than the probe that traced it is not something that probe can actually pass, so reporting it by default invited the reading that it was open; the old default of 0.0 filtered nothing and left every such entry in the list. min_volume=10, roughly the van der Waals volume of a water molecule, drops the degenerate stubs the same way.
…to avoid dedup's cost explosion when a cavity is searched from more than one seed: on 1tqn with 26 seeds the pathfinding took 238s against 6s single-seed. The opening centres and radii are grown on append rather than rebuilt per candidate, so a small kept set pays nothing for the change. Measured 238s -> 31s on that load, with bit-for-bit identical output on all three regression cases (linb_h, linb_xray, porin).
…epest tetrahedron - findDeepestTetrahedra gives a cavity one seed, the far end of its geodesic depth field. That is proper site for a compact pocket but problematic for a large or branched ones, especially when a small probe connects the real pockets into as single cavity, so the deepest point can land in one of them only. Every channel is then at least as long as the cavity is deep, and the mouths of the other lobes go unreported, because a route from that far seed is absorbed at a nearer mouth before it arrives.

findChambers carves the cleared void a second time with seed_radius (default
max(1.4, inner_radius)) and labels the connected pieces that survive; each is a
chamber and seeds one search. then  setStartingTetrahedraFromChambers picks each chamber's widest tetrahedron among those no shallower than min_depth;

The pass is skipped when start_point is given or  when seed_radius does not exceed
inner_radius, where the carve can prune nothing and would silently move the seed from the deepest to the widest tetrahedron rather than being a no-op.
Per-object files now carry the start point they were traced from, as out_sp0_chl3.pqr, so everything belonging to one void globs together, and a run told only a directory drops the run stem altogether: channels.pqr, cavities.pqr and pores.pqr beside sp0_chl3.pqr, since a placeholder stem repeated on every file names nothing. Pores are written as pores rather than under the channel writer's defaults, which had them colliding with the channels once the stem was gone, and a cavity file states its volume, depth and tetrahedron count in a REMARK, its atoms being fixed-size markers rather than a measurement. selectChannelBySelection reads the new names back through one shared parser, so it finds the residue and parameter rows of any object kind and reports what it found, with file_prefix for the runs whose file names no longer carry one.
The get*ParametersMultipleFrames wrappers wrote every frame under a single name, so the frames either overwrote one another or piled up under colliding keys, and only one frame's parameters could be read back. Each frame now writes its own file, named _frame<i> or _model<i> after the new trajectory argument, matching what the residue wrappers already do so that the parameters and the lining of one frame stay findable under the same prefix.
min_chamber_tetrahedra becomes seed_volume (default 30 A^3), measuring a chamber on the same scale a cavity is measured on so that the two can share a threshold, and max_seeds now keeps the largest chambers rather than the widest. The chamber carve stops at min_depth, so a chamber is a buried site rather than a lobe holding its own mouth, and a cavity where no chamber qualifies is seeded at its widest deep tetrahedron instead of its deepest one, which no longer caps every channel of that cavity at a narrow seed. Cavities are ordered largest first, so cavity 0 is the biggest void wherever it is named. min_volume defaults to None and min_tetrahedra/max_tetrahedra move to the advanced options: what gets reported is deliberately separate from where a search may start.
A deep chamber often has no opening of its own and reaches the solvent through a shallower one, and its route out was dropped as a duplicate of that chamber's shorter channel, leaving the deep site unreported. Such access is now reported as a link: the route from the deep chamber, cut where it joins the shallower one, returned in details['links'], written beside the channels, and measured by getLinkParameters and getLinkResidueNames with their multi-frame wrappers. Every channel and link carries the start point it was traced from, in the file name, in the REMARK and as an attribute, so the objects of one void can be analysed together. The log gains a table of the search sites with their volume, depth and object counts, including the sites that report nothing because every way out of them is narrower than bottleneck.
The bottleneck docstring is reworded, saying the same thing more plainly. Ångström is spelled A with a ring in the messages that used a bare A, and the surface cavity line now reports how many cavities were found alongside how many survived min_depth and what is done with them.
@briza81

briza81 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

@karolamik13 pls have a look. This resolves the 1tqn trouble observed in https://github.com/prody/ProDy-website/blob/master/tutorials/cavitracer_tutorial/singlePDB.rst. We now have roughly the same # of channels but also a few linkers (new channel types, aside from pores and channels) connecting the cavities but not going to the surface via a duplicate path.

Importantly, the bottlenecks are now physically sensible, and paths starts from appropriate locations:
@> Channel ID: Volume [ų] Length [Å] Bottleneck [Å]
@> channel 0: 146.5 5.51 1.77
@> channel 1: 69.03 5.89 1.43
@> channel 2: 64.05 6.59 1.24
@> channel 3: 423.9 16.13 1.92
@> channel 4: 51.56 5.71 1.25
@> channel 5: 57.41 5.74 0.97
@> channel 6: 664.86 23.93 2.15
@> channel 7: 739.84 25.18 2.15
@> channel 8: 86.51 8.15 1.11
@> channel 9: 385.95 16.16 1.29
@> channel 10: 392.91 16.81 1.33
@> channel 11: 40.68 6.49 1.01
@> channel 12: 54.21 7.11 1.11
@> channel 13: 82.22 8.29 1.03
@> channel 14: 81.91 8.71 1.13
@> channel 15: 34.03 5.36 0.9
@> channel 16: 604.32 24.1 1.12
@> channel 17: 49.35 7.36 0.99
@> channel 18: 59.47 8.46 0.99
@> channel 19: 90.68 11.23 1.18
@> channel 20: 603.63 24.3 1.0
@> channel 21: 46.0 7.58 0.94
@> channel 22: 38.39 7.9 0.99
@> channel 23: 89.84 10.84 0.91
@> channel 24: 426.27 20.87 0.91
@> channel 25: 122.11 12.11 0.95
@> channel 26: 72.5 10.84 0.96
@> channel 27: 33.82 7.91 0.93
@> channel 28: 75.64 10.51 0.91
@> channel 29: 98.23 12.75 0.97
@> channel 30: 56.07 8.89 0.94
@> channel 31: 85.59 11.73 0.97
@> channel 32: 301.19 16.72 0.99
@> channel 33: 74.27 11.62 0.97
@> channel 34: 48.36 9.65 0.91
@> channel 35: 63.68 11.17 0.99
@> channel 36: 51.14 10.13 0.9
@> channel 37: 64.48 12.44 1.02
@> channel 38: 96.08 15.8 0.97
@> channel 39: 115.94 17.88 1.11
@> channel 40: 313.89 20.23 0.9
@> channel 41: 602.7 34.6 1.02
@> channel 42: 388.08 23.9 0.91
@> channel 43: 483.05 28.97 0.95
@> channel 44: 596.71 31.5 0.98
@> channel 45: 85.62 16.34 0.95
@> channel 46: 86.68 17.73 0.94
@> channel 47: 124.35 20.39 0.93
@> channel 48: 113.03 20.28 0.93
@> channel 49: 100.52 19.31 0.94
@> channel 50: 629.5 38.68 0.91
@> channel 51: 683.4 42.38 0.91
@> channel 52: 685.23 44.49 0.9
@> channel 53: 514.54 36.79 0.95
@> channel 54: 673.91 47.46 0.9

@karolamik13 karolamik13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I tested the code on different levels.

@karolamik13
karolamik13 merged commit 32bd69e into prody:main Aug 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants