Skip to content

tomo: respect _rlnTomoVisibleFrames in particle reconstruction#1344

Open
amineuron wants to merge 1 commit into
3dem:ver5.1from
amineuron:fix/reconstruct-particle-visible-frames
Open

tomo: respect _rlnTomoVisibleFrames in particle reconstruction#1344
amineuron wants to merge 1 commit into
3dem:ver5.1from
amineuron:fix/reconstruct-particle-visible-frames

Conversation

@amineuron
Copy link
Copy Markdown

Summary

reconstruct_particle.cpp::processTomograms determines per-tilt visibility for each particle purely from Tomogram::determineVisiblity(traj, s/2.0) (geometric center-in-frame test). This silently ignores the explicit per-particle _rlnTomoVisibleFrames mask that subtomo writes into particles.star when --max_dose (or any frame filter) is set at extraction.

ml_optimiser (Refine3D / Class3D) already respects the mask — see src/exp_model.cpp "Add only the visible images for this particle". So in a --stack2d --max_dose pipeline, Extract and Refine3D use one frame set, but standalone relion_tomo_reconstruct_particle uses a different (geometric-only) frame set.

This patch makes reconstruction match the same authoritative pattern: when EMDL_TOMO_VISIBLE_FRAMES is present on the particle table, intersect geometric visibility with the explicit mask.

Rationale

When --max_dose filtered the 2D stacks down to e.g. 16.5 visible frames per particle on average, reconstruction was using ~39.2 frames per particle (all geometrically visible tilts including dose-excluded ones). The per-particle CTF + dose-weight math at reconstruction time therefore used a different frame set than:

  1. The frames baked into the *_stack2d.mrcs data on disk
  2. The frames Refine3D used for the alignment that we're reconstructing from

The symptom in 2D-stack mode is a "cloud-of-points" looking merged.mrc while data_*.mrc (the un-Wiener-divided numerator) still looks healthy. This is hard to attribute to anything in the user-facing inputs.

Compatibility

  • Backwards-compatible: behaviour unchanged for inputs without _rlnTomoVisibleFrames. The has_explicit_visible_frames check only engages the new path when the column is present.
  • No new dependencies; uses the existing ParticleSet::getVisibleFrames helper that exp_model.cpp already calls.
  • Hard-errors with a clear message if the column exists but has an unexpected length for the tomogram.

Tests

  • git diff --check
  • Builds clean on top of ver5.1 (verified locally as part of the same fix in an unaffiliated fork; same one-file patch).
  • On the project used for diagnosis (4 tomograms, ~6200 2D-stack particles, --max_dose 50): post-fix <isVisible> per particle drops from 39.2 to 16.5, matching the _rlnTomoVisibleFrames mask that Extract wrote.

🤖 Generated with Claude Code

reconstruct_particle determined per-tilt particle visibility from geometry
only (Tomogram::determineVisiblity on the projection trajectory). This
ignored the explicit visibility mask written by subtomo at extract time
(rlnTomoVisibleFrames in particles.star).

When Extract runs with --max_dose, the 2D-stacks contain only the
dose-filtered frames; ml_optimiser (Refine3D / Class3D) aligns only those
frames (exp_model.cpp 'Add only the visible images for this particle'
loop); but reconstruct_particle was silently bringing high-dose frames
back. The per-particle CTF / dose-weight math then used a different frame
set than the data on disk, producing cloud-of-points-looking merged maps
despite data_*.mrc (the un-Wiener-divided numerator) looking healthy.

Fix: when EMDL_TOMO_VISIBLE_FRAMES is present on particleSet.partTable,
intersect the geometric visibility with the explicit mask before
extractAt3D_Fourier. Backwards-compatible: behavior unchanged for inputs
without the column.

Tested on a 4-tomogram 2D-stack project with --max_dose 50:
- pre-fix: 39.2 mean visible frames per particle at reconstruction time
- post-fix: 16.5 mean visible frames per particle, matching what Extract
  baked into the 2D stacks and what Refine3D used for alignment.
@amineuron
Copy link
Copy Markdown
Author

Worth noting: this fix is complementary to (and independent of) @daniel-ji's open #1305, which corrects the geometric-visibility radius from s/2.0 to binning * s / 2.0 in the same call. Both bugs live in the same line and both should be merged:

I have both fixes stacked locally and they apply cleanly without conflict. Happy to rebase on top of #1305 if it's preferred to merge that one first.

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.

1 participant