Skip to content

Add nested multi-resolution volume render backend - #337

Open
ojessop01 wants to merge 3 commits into
SWIFTSIM:masterfrom
ojessop01:master
Open

Add nested multi-resolution volume render backend#337
ojessop01 wants to merge 3 commits into
SWIFTSIM:masterfrom
ojessop01:master

Conversation

@ojessop01

Copy link
Copy Markdown

Implement efficient Sparse Multi-Scale Grid algorithm (Benitez-Llambay 2025)
for 3D volume rendering of SPH data. Particles are hierarchically assigned
to grid levels based on kernel smoothing length, then scattered onto
coarse-to-fine grids and upsampled back to target resolution. This bounds
per-particle work regardless of smoothing length, providing significant
speedups for simulations with wide kernel distributions.

Features:

  • New nested grid backend accessible via backend="nested" parameter
  • Adaptive hierarchy with configurable target voxels per kernel (ntarget)
    and number of levels (nlevels)
  • Kernel arithmetic matches scatter.py implementation (Wendland-C2)
  • Parallel support via Numba prange
  • Trilinear upsampling for hierarchy collapse
  • Efficient bounding-box tracking to skip empty regions

API additions:

  • render_voxel_grid() and render_gas() now accept backend, ntarget, nlevels
  • backends dict updated to include "nested" alongside "scatter"
  • Added validation and warnings for backend-specific parameters

Tests:

  • Added TestNestedVolumeRender class with serial/parallel matching tests
  • Tests cover periodic boundary conditions and edge cases
  • Comparison against single-resolution backend validates correctness

Documentation:

  • Updated volume_render.rst with nested backend description and examples
  • Added parameter documentation for ntarget and nlevels

Code quality:

  • Passes ruff linter
  • Passes numpydoc validation
  • All existing tests continue to pass

ojessop01 and others added 2 commits June 24, 2026 18:38
Implement efficient Sparse Multi-Scale Grid algorithm (Benitez-Llambay 2025)
for 3D volume rendering of SPH data. Particles are hierarchically assigned
to grid levels based on kernel smoothing length, then scattered onto
coarse-to-fine grids and upsampled back to target resolution. This bounds
per-particle work regardless of smoothing length, providing significant
speedups for simulations with wide kernel distributions.

Features:
- New nested grid backend accessible via backend="nested" parameter
- Adaptive hierarchy with configurable target voxels per kernel (ntarget)
  and number of levels (nlevels)
- Kernel arithmetic matches scatter.py implementation (Wendland-C2)
- Parallel support via Numba prange
- Trilinear upsampling for hierarchy collapse
- Efficient bounding-box tracking to skip empty regions

API additions:
- render_voxel_grid() and render_gas() now accept backend, ntarget, nlevels
- backends dict updated to include "nested" alongside "scatter"
- Added validation and warnings for backend-specific parameters

Tests:
- Added TestNestedVolumeRender class with serial/parallel matching tests
- Tests cover periodic boundary conditions and edge cases
- Comparison against single-resolution backend validates correctness

Documentation:
- Updated volume_render.rst with nested backend description and examples
- Added parameter documentation for ntarget and nlevels

Code quality:
- Passes ruff linter
- Passes numpydoc validation
- All existing tests continue to pass

Refs: Benitez-Llambay A., 2025, MNRAS (in prep)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 24, 2026 17:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@kyleaoman

Copy link
Copy Markdown
Member

Thanks Owen, looks promising. Presumably this can be trivially translated to the 2D ("project") case? And maybe slice, too? Or do you think it's not worth it because that scales as N^2 instead of N^3?

@ojessop01

Copy link
Copy Markdown
Author

Yes this could be trivially extended to 2d projection and slice and would scale much better than the current implementations. I just haven't had a chance to implement this and ensure all the formatting etc is correct and the documentation written.

@kyleaoman
kyleaoman marked this pull request as draft June 24, 2026 21:21
@kyleaoman

Copy link
Copy Markdown
Member

Great, and not a problem - marking this as draft for now if you plan to add that (if not feel free to mark ready for review).

@kyleaoman kyleaoman added enhancement New feature or request performance An issue that is impacting performance visualisation Visualisation-related issues labels Jun 24, 2026
@ojessop01

Copy link
Copy Markdown
Author

i plan to add eventually but not sure when I can get around to it. I will submit this ready for review for now.

@ojessop01
ojessop01 marked this pull request as ready for review June 24, 2026 22:07
@kyleaoman
kyleaoman self-requested a review September 5, 2026 13:56

@kyleaoman kyleaoman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi Owen,

Sorry to leave this hanging for a while, for some reason I thought that you were still planning edits. Anyway some suggestions for you here, all fairly straightforward. Will want to make sure Josh is happy to merge this before we do, too.

resolution=256,
project="masses",
parallel=True,
backend="scatter",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
backend="scatter",
backend="scatter", # the default


data = load("cosmo_volume_example.hdf5")

# Standard backend — identical results, familiar behaviour.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
# Standard backend — identical results, familiar behaviour.
# Standard backend

:math:`\mathcal{O}(n_\mathrm{target}^3)` regardless of smoothing length by
scattering large particles onto a coarser grid and trilinearly upsampling
the result back to the finest grid afterwards. This follows the Sparse
Multi-Scale Grid approach described in Benitez-Llambay (2025).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Multi-Scale Grid approach described in Benitez-Llambay (2025).
Multi-Scale Grid approach described in `Benítez-Llambay (2025)`_.
_Benítez-Llambay (2025): https://iopscience.iop.org/article/10.3847/2515-5172/addab2

Comment on lines +498 to +499
received mass is upsampled at each level, so the collapse cost scales with
the number of particles rather than the grid volume.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
received mass is upsampled at each level, so the collapse cost scales with
the number of particles rather than the grid volume.
received mass is upsampled at each level, so the collapse cost scales
approximately with the number of particles rather than the grid volume.

Comment on lines +665 to +669
out = backends["nested"](
x=x, y=y, z=z, h=h, m=m, res=res,
ntarget=6,
nlevels=4,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
out = backends["nested"](
x=x, y=y, z=z, h=h, m=m, res=res,
ntarget=6,
nlevels=4,
)
out = backends["nested"](
x=x,
y=y,
z=z,
h=h,
m=m,
res=res,
ntarget=6,
nlevels=4,
)

Controls the accuracy/speed trade-off. Default is 6.

nlevels : int
Maximum number of coarsening levels in the hierarchy. Default is 4.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Maximum number of coarsening levels in the hierarchy. Default is 4.
Maximum number of coarsening levels in the hierarchy. Default is ``4``.


Returns
-------
np.ndarray[np.float32, np.float32, np.float32]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is content of brackets correct? Thought it was type then shape?


ntarget : int
Target number of voxels spanned by each kernel at its assigned level.
Controls the accuracy/speed trade-off. Default is 6.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Controls the accuracy/speed trade-off. Default is 6.
Controls the accuracy/speed trade-off. Default is ``6``.

Controls the accuracy/speed trade-off. Default is 6.

nlevels : int
Maximum number of coarsening levels in the hierarchy. Default is 4.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Maximum number of coarsening levels in the hierarchy. Default is 4.
Maximum number of coarsening levels in the hierarchy. Default is ``4``.


Returns
-------
np.ndarray[np.float32, np.float32, np.float32]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Again hint on the dtype and shape is incorrectly formatted?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request performance An issue that is impacting performance visualisation Visualisation-related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants