Skip to content

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?


JDBetteridge commented on 2025-08-19T12:37:51Z
----------------------------------------------------------------

More a question for the reviewer, I guess the answer is: Yes, remove it

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?


JDBetteridge commented on 2025-08-19T12:39:44Z
----------------------------------------------------------------

yes

EdCaunt commented on 2025-08-20T10:48:20Z
----------------------------------------------------------------

This comment is still missing a noun

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?


JDBetteridge commented on 2025-08-19T12:42:21Z
----------------------------------------------------------------

I'll double check, I'm fairly sure something was unstable

JDBetteridge commented on 2025-08-19T12:57:03Z
----------------------------------------------------------------

Apparently nothing, I'll update to use a different dt

EdCaunt commented on 2025-08-20T10:49:30Z
----------------------------------------------------------------

Note: this is still in the notebook

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


JDBetteridge commented on 2025-08-19T12:43:29Z
----------------------------------------------------------------

Technically 10^4 isn't wrong...

EdCaunt commented on 2025-08-20T10:52:35Z
----------------------------------------------------------------

It is misleading though and not how most people would interpret that statement

# 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)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, something is fundamentally different about doing it the way you suggest @mloubout.

Previously:
1d_example

With the suggested change:
1d_example

Seems to be an off by k error

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah actually ran into that in something else. _rebuild keeps halo from the original func so the order is right but the halo is too short so it leads to issues unless the new halo is specified as well.

'''
# 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't work, trying TempFunction although I have no idea if it's the correct type to try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TempFunction also doesn't work, I'm honestly not sure what using a different object gains you here. These temporaries are never used, so should never be allocated any memory. Unless I'm misunderstanding something?

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The plotting isn't really the bulk of this, its mainly setting different axis properties for different iteration counts. I can try and refactor it further

# 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)



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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I might try making a more generic function in examples, but this really needs to go in a separate PR as it's unrelated to this work.

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:

)))
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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

np.concat and np.concatenate are the same function! 🙄

@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.
✅ Project coverage is 82.94%. Comparing base (b90bb2b) to head (a43feb7).
⚠️ Report is 29 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2658   +/-   ##
=======================================
  Coverage   82.94%   82.94%           
=======================================
  Files         248      248           
  Lines       50022    50022           
  Branches     4402     4402           
=======================================
  Hits        41492    41492           
  Misses       7776     7776           
  Partials      754      754           
Flag Coverage Δ
pytest-gpu-aomp-amdgpuX 68.81% <ø> (+0.01%) ⬆️
pytest-gpu-nvc-nvidiaX 69.34% <ø> (ø)

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?

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cheers.

Any reason that this file isn't linked in either the readme or the wiki?

Can I suggest moving to ruff again, or if we don't like ruff, just using isort? Lots of the existing "Devito style" nitpicks could quite easily be caught by the linter.

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

Comment on lines 4 to 50
def superstep_generator_iterative(field, stencil, k, tn=0):
''' Generate superstep iteratively:
Aʲ⁺¹ = A·Aʲ
'''
# 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)
u_prev = TimeFunction(name=f'{name}_ss_p', grid=grid, time_order=2, space_order=2*k)

superstep_solution_transfer(field, u, u_prev, tn)

# 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)
current = ss_stencil

# Placeholder fields for forming the superstep
a_tmp = Function(name="a_tmp", grid=grid, space_order=2*k)
b_tmp = Function(name="b_tmp", grid=grid, space_order=2*k)

if k >= 2:
for _ in range(k - 2):
current = current.subs(
{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)
else:
current = u

stencil_next = current.subs(
{u: a_tmp, u_prev: b_tmp}, postprocess=False).subs(
{a_tmp: ss_stencil, b_tmp: u}, postprocess=False
)
stencil_next = stencil_next.expand().expand(add=True, nest=True)
return u, u_prev, Eq(u.forward, stencil_next), Eq(u_prev.forward, current)


Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
def superstep_generator_iterative(field, stencil, k, tn=0):
''' Generate superstep iteratively:
⁺¹ = A·Aʲ
'''
# 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)
u_prev = TimeFunction(name=f'{name}_ss_p', grid=grid, time_order=2, space_order=2*k)
superstep_solution_transfer(field, u, u_prev, tn)
# 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)
current = ss_stencil
# Placeholder fields for forming the superstep
a_tmp = Function(name="a_tmp", grid=grid, space_order=2*k)
b_tmp = Function(name="b_tmp", grid=grid, space_order=2*k)
if k >= 2:
for _ in range(k - 2):
current = current.subs(
{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)
else:
current = u
stencil_next = current.subs(
{u: a_tmp, u_prev: b_tmp}, postprocess=False).subs(
{a_tmp: ss_stencil, b_tmp: u}, postprocess=False
)
stencil_next = stencil_next.expand().expand(add=True, nest=True)
return u, u_prev, Eq(u.forward, stencil_next), Eq(u_prev.forward, current)

Drop this before merge. I don't think there's much utility to having both methods present

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think anyone should write code like this and no longer think this is a good example. I may just have to remove it if I can't refactor it into something presentable

Copy link
Contributor Author

More a question for the reviewer, I guess the answer is: Yes, remove it


View entire conversation on ReviewNB

Copy link
Contributor Author

yes


View entire conversation on ReviewNB

Copy link
Contributor Author

I'll double check, I'm fairly sure something was unstable


View entire conversation on ReviewNB

@mloubout mloubout merged commit 0d90d06 into main Sep 11, 2025
36 checks passed
@mloubout mloubout deleted the JDBetteridge/superstep branch September 11, 2025 12:13
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