Skip to content

Sweepers require level during instantiation #577

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

Merged
merged 3 commits into from
Aug 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions pySDC/core/level.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ def __init__(self, problem_class, problem_params, sweeper_class, sweeper_params,
level_index (int): custom name for this level
"""

# instantiate sweeper, problem and hooks
self.__sweep = sweeper_class(sweeper_params)
self.__prob = problem_class(**problem_params)

# set level parameters and status
self.params = _Pars(level_params)
self.status = _Status()

# instantiate sweeper, problem and hooks
self.__sweep = sweeper_class(sweeper_params, self)
self.__prob = problem_class(**problem_params)

# set name
self.level_index = level_index

Expand All @@ -87,9 +87,6 @@ def __init__(self, problem_class, problem_params, sweeper_class, sweeper_params,

self.tau = [None] * self.sweep.coll.num_nodes

# pass this level to the sweeper for easy access
self.sweep.level = self

self.__tag = None

# freeze class, no further attributes allowed from this point
Expand Down
9 changes: 3 additions & 6 deletions pySDC/core/sweeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,15 @@ class Sweeper(object):
coll (pySDC.Collocation.CollBase): collocation object
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the base sweeper

Args:
params (dict): parameter object

level (pySDC.Level.level): the level that uses this sweeper
"""

# set up logger
self.logger = logging.getLogger('sweeper')

essential_keys = ['num_nodes']
Expand Down Expand Up @@ -81,9 +80,7 @@ def __init__(self, params):
)
self.params.do_coll_update = True

# This will be set as soon as the sweeper is instantiated at the level
self.__level = None

self.__level = level
self.parallelizable = False

def setupGenerator(self, qd_type):
Expand Down
4 changes: 3 additions & 1 deletion pySDC/helpers/setup_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def generate_description(problem_class, **kwargs):

problem_keys = problem_class.__init__.__code__.co_varnames
level_keys = level_params({}).__dict__.keys()
sweeper_keys = description['sweeper_class']({'num_nodes': 1, 'quad_type': 'RADAU-RIGHT'}).params.__dict__.keys()
sweeper_keys = description['sweeper_class'](
{'num_nodes': 1, 'quad_type': 'RADAU-RIGHT'}, None
).params.__dict__.keys()
step_keys = step_params({}).__dict__.keys()

# TODO: add convergence controllers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ def switch_sweeper(self, S):
nodes_old = L.sweep.coll.nodes.copy()

# change sweeper
L.sweep.__init__(update_params_sweeper)
L.sweep.level = L
L.sweep.__init__(update_params_sweeper, L)

# reset level to tell it the new structure of the solution
L.params.__dict__.update(new_params_level)
Expand Down
29 changes: 26 additions & 3 deletions pySDC/implementations/sweeper_classes/Multistep.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pySDC.core.sweeper import Sweeper, _Pars
from pySDC.core.level import Level


class Cache(object):
Expand Down Expand Up @@ -55,7 +56,7 @@ class MultiStep(Sweeper):
alpha = None
beta = None

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the base sweeper.

Expand All @@ -71,6 +72,7 @@ def __init__(self, params):

Args:
params (dict): parameter object
level (pySDC.Level.level): the level that uses this sweeper
"""
import logging
from pySDC.core.collocation import CollBase
Expand All @@ -88,15 +90,36 @@ def __init__(self, params):
# we need a dummy collocation object to instantiate the levels.
self.coll = CollBase(num_nodes=1, quad_type='RADAU-RIGHT')

# This will be set as soon as the sweeper is instantiated at the level
self.__level = None
self.__level = level

self.parallelizable = False

# proprietary variables for the multistep methods
self.steps = len(self.alpha)
self.cache = Cache(self.steps)

@property
def level(self):
"""
Returns the current level

Returns:
pySDC.Level.level: Current level
"""
return self.__level

@level.setter
def level(self, lvl):
"""
Sets a reference to the current level (done in the initialization of the level)

Args:
lvl (pySDC.Level.level): Current level
"""
assert isinstance(lvl, Level), f"You tried to set the sweeper's level with an instance of {type(lvl)}!"

self.__level = lvl

def predict(self):
"""
Add the initial conditions to the cache if needed.
Expand Down
11 changes: 7 additions & 4 deletions pySDC/implementations/sweeper_classes/Runge_Kutta.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,13 @@ class RungeKutta(Sweeper):
The entries of the Butcher tableau are stored as class attributes.
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""
# set up logger
self.logger = logging.getLogger('sweeper')
Expand All @@ -156,8 +157,9 @@ def __init__(self, params):

self.params = _Pars(params)

# This will be set as soon as the sweeper is instantiated at the level
# set level using the setter in order to adapt residual tolerance if needed
self.__level = None
self.level = level

self.parallelizable = False
self.QI = self.coll.Qmat
Expand Down Expand Up @@ -343,14 +345,15 @@ class RungeKuttaIMEX(RungeKutta):
weights_explicit = None
ButcherTableauClass_explicit = ButcherTableau

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""
super().__init__(params)
super().__init__(params, level)
type(self).weights_explicit = self.weights if self.weights_explicit is None else self.weights_explicit
self.coll_explicit = self.get_Butcher_tableau_explicit()
self.QE = self.coll_explicit.Qmat
Expand Down
5 changes: 3 additions & 2 deletions pySDC/implementations/sweeper_classes/Runge_Kutta_Nystrom.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,15 @@ class RungeKuttaNystrom(RungeKutta):
weights_bar = None
matrix_bar = None

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""
super().__init__(params)
super().__init__(params, level)
self.coll_bar = self.get_Butcher_tableau_bar()
self.Qx = self.coll_bar.Qmat

Expand Down
7 changes: 3 additions & 4 deletions pySDC/implementations/sweeper_classes/boris_2nd_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,21 @@ class boris_2nd_order(Sweeper):
Sx: node-to-node Euler half-step for position update
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""

# call parent's initialization routine

if "QI" not in params:
params["QI"] = "IE"
if "QE" not in params:
params["QE"] = "EE"

super(boris_2nd_order, self).__init__(params)
super(boris_2nd_order, self).__init__(params, level)

# S- and SQ-matrices (derived from Q) and Sx- and ST-matrices for the integrator
[
Expand Down
6 changes: 3 additions & 3 deletions pySDC/implementations/sweeper_classes/explicit.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ class explicit(Sweeper):
QE: explicit Euler integration matrix
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""

if 'QE' not in params:
params['QE'] = 'EE'

# call parent's initialization routine
super(explicit, self).__init__(params)
super(explicit, self).__init__(params, level)

# integration matrix
self.QE = self.get_Qdelta_explicit(qd_type=self.params.QE)
Expand Down
6 changes: 3 additions & 3 deletions pySDC/implementations/sweeper_classes/generic_implicit.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ class generic_implicit(Sweeper):
QI: lower triangular matrix
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""

if 'QI' not in params:
params['QI'] = 'IE'

# call parent's initialization routine
super().__init__(params)
super().__init__(params, level)

# get QI matrix
self.QI = self.get_Qdelta_implicit(qd_type=self.params.QI)
Expand Down
11 changes: 9 additions & 2 deletions pySDC/implementations/sweeper_classes/generic_implicit_MPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ class adds a communicator and nothing else. The `generic_implicit` implicit clas
`generic_implicit`.
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""
self.logger = logging.getLogger('sweeper')

if 'comm' not in params.keys():
params['comm'] = MPI.COMM_WORLD
self.logger.debug('Using MPI.COMM_WORLD for the communicator because none was supplied in the params.')
super().__init__(params)
super().__init__(params, level)

if self.params.comm.size != self.coll.num_nodes:
raise NotImplementedError(
Expand Down
6 changes: 3 additions & 3 deletions pySDC/implementations/sweeper_classes/imex_1st_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ class imex_1st_order(Sweeper):
QE: explicit Euler integration matrix
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""

if 'QI' not in params:
params['QI'] = 'IE'
if 'QE' not in params:
params['QE'] = 'EE'

# call parent's initialization routine
super().__init__(params)
super().__init__(params, level)

# IMEX integration matrices
self.QI = self.get_Qdelta_implicit(qd_type=self.params.QI)
Expand Down
6 changes: 3 additions & 3 deletions pySDC/implementations/sweeper_classes/imex_1st_order_MPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@


class imex_1st_order_MPI(SweeperMPI, imex_1st_order):
def __init__(self, params):
super().__init__(params)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
assert (
self.params.QE == 'PIC'
), f"Only Picard is implemented for explicit precondioner so far in {type(self).__name__}! You chose \"{self.params.QE}\""
), f"Only Picard is implemented for explicit preconditioner so far in {type(self).__name__}! You chose \"{self.params.QE}\""

def integrate(self, last_only=False):
"""
Expand Down
5 changes: 3 additions & 2 deletions pySDC/implementations/sweeper_classes/multi_implicit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ class multi_implicit(Sweeper):
Q2: implicit integration matrix for the second component
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""

# Default choice: implicit Euler
Expand All @@ -27,7 +28,7 @@ def __init__(self, params):
params['Q2'] = 'IE'

# call parent's initialization routine
super(multi_implicit, self).__init__(params)
super(multi_implicit, self).__init__(params, level)

# Integration matrices
self.Q1 = self.get_Qdelta_implicit(qd_type=self.params.Q1)
Expand Down
6 changes: 3 additions & 3 deletions pySDC/implementations/sweeper_classes/verlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ class verlet(Sweeper):
qQ: update rule for final value (if needed)
"""

def __init__(self, params):
def __init__(self, params, level):
"""
Initialization routine for the custom sweeper

Args:
params: parameters for the sweeper
level (pySDC.Level.level): the level that uses this sweeper
"""

if 'QI' not in params:
params['QI'] = 'IE'
if 'QE' not in params:
params['QE'] = 'EE'

# call parent's initialization routine
super(verlet, self).__init__(params)
super(verlet, self).__init__(params, level)

# Trapezoidal rule, Qx and Double-Q as in the Boris-paper
[self.QT, self.Qx, self.QQ] = self.__get_Qd()
Expand Down
Loading