Skip to content

dsl: Superstep #2658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

dsl: Superstep #2658

wants to merge 11 commits into from

Conversation

JDBetteridge
Copy link
Contributor

Currently a PR into #2559 as the diff is smaller

Adds superstep functionality as well as example scripts and notebook

@JDBetteridge JDBetteridge added documentation API api (symbolics, types, ...) examples examples labels Jul 3, 2025
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

Copy link

review-notebook-app bot commented Jul 4, 2025

View / edit / reply to this conversation on ReviewNB

EdCaunt commented on 2025-07-04T10:14:01Z
----------------------------------------------------------------

"In Nemeth et al k=30 is chosen."


Copy link

review-notebook-app bot commented Jul 4, 2025

View / edit / reply to this conversation on ReviewNB

EdCaunt commented on 2025-07-04T10:14:01Z
----------------------------------------------------------------

Line #3.    src_term = source.inject(field=u.forward, expr=source*velocity*velocity*dt*dt)

expr=source*velocity**2*dt**2


Copy link

review-notebook-app bot commented Jul 4, 2025

View / edit / reply to this conversation on ReviewNB

EdCaunt commented on 2025-07-04T10:14:02Z
----------------------------------------------------------------

Typo: arguemnts

"the size of the superstep: k in the paper, superstep_size in the code" <- add a colon

"Written in maths:" <- odd phrasing, reconsider


Copy link

review-notebook-app bot commented Jul 4, 2025

View / edit / reply to this conversation on ReviewNB

EdCaunt commented on 2025-07-04T10:14:03Z
----------------------------------------------------------------

Line #1.    # Should the timer context be removed???

Leftover?


Copy link

review-notebook-app bot commented Jul 4, 2025

View / edit / reply to this conversation on ReviewNB

EdCaunt commented on 2025-07-04T10:14:03Z
----------------------------------------------------------------

Line #7.    # Fetch and setup the 

Set up the what?


Copy link

review-notebook-app bot commented Jul 4, 2025

View / edit / reply to this conversation on ReviewNB

EdCaunt commented on 2025-07-04T10:14:04Z
----------------------------------------------------------------

"we would have to redefine much of what we have done above."

What would need redefining?


Copy link

review-notebook-app bot commented Jul 4, 2025

View / edit / reply to this conversation on ReviewNB

EdCaunt commented on 2025-07-04T10:14:05Z
----------------------------------------------------------------

x10^-4



def superstep_generator_iterative(field, stencil, k, tn=0):
''' Generate superstep iteratively:
A^j+1 = A·A^j
Copy link
Contributor

Choose a reason for hiding this comment

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

Some brackets here would improve readability and reduce ambiguity

# New fields, for vector formulation both current and previous timestep are needed
name = field.name
grid = field.grid
u = TimeFunction(name=f'{name}_ss', grid=grid, time_order=2, space_order=2*k)
Copy link
Contributor

Choose a reason for hiding this comment

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

I presume the space_order=2*k is to ensure the halo is sufficiently wide for the operators? Would taking the space order from the original fields and using padding here be strictly more correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to use ._rebuild (or .func) to make sure it's consistent with field

u = field._rebuild(name=f'{name}_ss', space_order=2*k)
u_prev = field._rebuild(name=f'{name}_ss', space_order=2*k)

return u, u_prev, Eq(u.forward, stencil_next), Eq(u_prev.forward, current)


def superstep_generator(field, stencil, k, tn=0):
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is there both superstep_generator and superstep_generator_iterative? There is also some code overlap between these two functions which should be consolidated. Perhaps a SuperStepGenerator class with suitable methods?

ss_stencil = stencil.subs({field: u, field.backward: u_prev}, postprocess=False)
ss_stencil = ss_stencil.expand().expand(add=True, nest=True)

# Binary decomposition algorithm
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: this could use more comments

''' Transfer state from a previous TimeFunction to a 2 field superstep
Used after injecting source using standard timestepping.
'''
idx = tn % 3 if old.save is None else -1
Copy link
Contributor

Choose a reason for hiding this comment

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

This should check if there is a buffer, then either check the buffer size or take the last index, rather than hardcoding 3

'''
# Placeholder fields for forming the superstep
grid = u.grid
a_tmp = Function(name="a_tmp", grid=grid, space_order=2*k)
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these Functions just used as symbolic objects? If so, why not just use Symbol?

data = acoustic_model(model, step=step, snapshots=m)
time = np.linspace(model.t1, model.t2, (m - 1)//step + 1)
idx = 0
for ii, ax in enumerate(ax_row):
Copy link
Contributor

Choose a reason for hiding this comment

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

I would set up a separate plotting function for the contents of this loop to improve readability



def superstep_generator_iterative(field, stencil, k, tn=0):
''' Generate superstep iteratively:
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpicking: For consistency with codebase, use """ and start doctrine on next line"

Copy link
Contributor

Choose a reason for hiding this comment

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

seconded

# New fields, for vector formulation both current and previous timestep are needed
name = field.name
grid = field.grid
u = TimeFunction(name=f'{name}_ss', grid=grid, time_order=2, space_order=2*k)
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to use ._rebuild (or .func) to make sure it's consistent with field

u = field._rebuild(name=f'{name}_ss', space_order=2*k)
u_prev = field._rebuild(name=f'{name}_ss', space_order=2*k)


# Substitute new fields into stencil
ss_stencil = stencil.subs({field: u, field.backward: u_prev}, postprocess=False)
ss_stencil = ss_stencil.expand().expand(add=True, nest=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

why double expand?

{u: a_tmp, u_prev: b_tmp}, postprocess=False).subs(
{a_tmp: ss_stencil, b_tmp: u}, postprocess=False
)
current = current.expand().expand(add=True, nest=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

same?


if k >= 2:
for _ in range(k - 2):
current = current.subs(
Copy link
Contributor

Choose a reason for hiding this comment

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

split in two lines for readability?



def marmousi(grid, step=0):
# Grab dataset from pwd or download
Copy link
Contributor

Choose a reason for hiding this comment

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

This should just throw a warning/erro people should git clone the data repo

Copy link
Contributor

Choose a reason for hiding this comment

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

And it's a preset in the Model as well so this should be reusing the examples

# Stencil and operator
idx = tn1 % 3
if step == 1:
# Non-superstep case
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't get that case, it just copies into a new Function to do the same thing?

else:
new_u, new_u_p, *stencil = superstep_generator(u, stencil.rhs, step)

new_u.data[0, :] = u.data[idx - 1]
Copy link
Contributor

Choose a reason for hiding this comment

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

why isn't superstep_generator doing that?

return u_save.data


layered_model = model(
Copy link
Contributor

Choose a reason for hiding this comment

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

USe existing preset

@@ -0,0 +1,114 @@
''' Script that demonstrates the functionality of the superstep in 2D
Copy link
Contributor

Choose a reason for hiding this comment

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

That's a lot of duplicate from the 1D case, can probably be merged into one file with 1d/2d as input

@ggorman ggorman requested a review from Copilot July 24, 2025 08:53
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces superstep functionality to Devito, enabling multi-step time advancement with larger effective time steps while maintaining stability. The implementation includes both iterative and binary decomposition algorithms for generating superstep operators.

  • Adds core superstep generation functionality with two algorithmic approaches
  • Provides comprehensive examples demonstrating 1D and 2D wave propagation scenarios
  • Updates test configuration to support higher-order spatial discretizations

Reviewed Changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
devito/timestepping/superstep.py Core superstep implementation with generator functions and solution transfer utilities
examples/timestepping/superstep_1d.py 1D wave equation example demonstrating superstep functionality
examples/timestepping/superstep_2d.py 2D ripple simulation example with visualization
examples/timestepping/acoustic_superstep_2d.py Advanced 2D acoustic modeling with source injection and velocity models
tests/test_pickle.py Updates space_order from 2 to 4 for derivative testing
Comments suppressed due to low confidence (2)

examples/timestepping/superstep_1d.py:49

  • [nitpick] Variable name 'newu' is inconsistent with the naming convention used elsewhere in the codebase. Consider using 'new_u' to match the pattern used in line 55 and other files.
        newu = u

examples/timestepping/acoustic_superstep_2d.py:28

  • Class name 'model' should follow Python naming conventions and be capitalized as 'Model'.
class model:

b_tmp = Function(name="b_tmp", grid=grid, space_order=2*k)

if k >= 2:
for _ in range(k - 2):
Copy link
Preview

Copilot AI Jul 24, 2025

Choose a reason for hiding this comment

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

[nitpick] The loop variable is unused. Consider using a more descriptive name like 'step' or 'iteration' to improve code readability, or use 'for step in range(k - 2):' if the index might be useful for debugging.

Suggested change
for _ in range(k - 2):
for step in range(k - 2):

Copilot uses AI. Check for mistakes.

Comment on lines +78 to +79
# 3 should be replaced with `old.time_order + 1` although this needs some thought
idx = tn % 3 if old.save is None else -1
Copy link
Preview

Copilot AI Jul 24, 2025

Choose a reason for hiding this comment

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

This TODO comment indicates incomplete implementation. The hardcoded value '3' should be replaced with a proper calculation based on time_order, or this limitation should be documented if intentional.

Suggested change
# 3 should be replaced with `old.time_order + 1` although this needs some thought
idx = tn % 3 if old.save is None else -1
# Dynamically calculate the modulo base using `old.time_order + 1`
idx = tn % (old.time_order + 1) if old.save is None else -1

Copilot uses AI. Check for mistakes.


def marmousi(grid, step=0):
# Grab dataset from pwd or download
url = 'https://github.com/devitocodes/data/raw/refs/heads/master/Simple2D/vp_marmousi_bi' # noqa: E501
Copy link
Preview

Copilot AI Jul 24, 2025

Choose a reason for hiding this comment

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

[nitpick] The long URL should be broken into multiple lines or stored as a constant to improve readability instead of using noqa comment.

Suggested change
url = 'https://github.com/devitocodes/data/raw/refs/heads/master/Simple2D/vp_marmousi_bi' # noqa: E501
url = MARMOUSI_URL

Copilot uses AI. Check for mistakes.

)))
ax.set_xlim(model.origin[0], model.origin[0] + model.extent[0])
yticks = ax.get_yticks()
ax.set_yticks(np.concat(((model.origin[1],), yticks[2::2])))
Copy link
Preview

Copilot AI Jul 24, 2025

Choose a reason for hiding this comment

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

[nitpick] Using np.concat with nested tuples is unnecessarily complex. Consider using np.concatenate([model.origin[1:2], yticks[2::2]]) or np.hstack for better readability.

Suggested change
ax.set_yticks(np.concat(((model.origin[1],), yticks[2::2])))
ax.set_yticks(np.concatenate([model.origin[1:2], yticks[2::2]]))

Copilot uses AI. Check for mistakes.

@JDBetteridge JDBetteridge force-pushed the JDBetteridge/expand_derivative branch 2 times, most recently from 4fb9198 to 1268740 Compare July 31, 2025 16:14
Base automatically changed from JDBetteridge/expand_derivative to main July 31, 2025 18:17
@JDBetteridge JDBetteridge force-pushed the JDBetteridge/superstep branch from 438a601 to b1b0846 Compare August 5, 2025 13:15
Copy link

codecov bot commented Aug 5, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@dd1e6c7). Learn more about missing BASE report.
⚠️ Report is 19 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2658   +/-   ##
=======================================
  Coverage        ?   83.76%           
=======================================
  Files           ?      248           
  Lines           ?    49625           
  Branches        ?     4366           
=======================================
  Hits            ?    41569           
  Misses          ?     7274           
  Partials        ?      782           
Flag Coverage Δ
pytest-gpu-nvc-nvidiaX 72.07% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@FabioLuporini FabioLuporini left a comment

Choose a reason for hiding this comment

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

Somewhere, add a docstring referencing Tamas 's paper? or have I missed it?

Perhaps add it to a short README to examples/timestepping, which would also help navigating the directory?



def superstep_generator_iterative(field, stencil, k, tn=0):
''' Generate superstep iteratively:
Copy link
Contributor

Choose a reason for hiding this comment

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

seconded

solve,
)
from devito.timestepping.superstep import superstep_generator
from scipy.interpolate import interpn
Copy link
Contributor

Choose a reason for hiding this comment

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

imports from third-parties should go in between those from the stdlin and the devito ones

see here https://github.com/devitocodes/devito/blob/main/CONTRIBUTING.md#coding-guidelines


import matplotlib.pyplot as plt
import numpy as np
from devito import (
Copy link
Contributor

Choose a reason for hiding this comment

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

blankline between third-party imports and devito imports

pde = (1/velocity**2)*u.dt2 - u.laplace
stencil = Eq(u.forward, solve(pde, u.forward))

tn1 = int(np.ceil((t1 - t0)/model.critical_dt))
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpicking, for homogeneity: why not just tn? rather, "nt", since it's "number of tiemsteps"?

new.data[0, :] = old.data[idx - 1]
new.data[1, :] = old.data[idx]
new_p.data[0, :] = old.data[idx - 2]
new_p.data[1, :] = old.data[idx - 1]
Copy link
Contributor

Choose a reason for hiding this comment

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

yes, this is very suspicious...
However, you may as well throw a warning for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API api (symbolics, types, ...) documentation examples examples
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants