diff --git a/pySDC/core/Level.py b/pySDC/core/Level.py index cb0629fe31..fe12c693f2 100644 --- a/pySDC/core/Level.py +++ b/pySDC/core/Level.py @@ -14,7 +14,7 @@ def __init__(self, params): # freeze class, no further attributes allowed from this point self._freeze() - self.dt_initial = self.dt * 1.0 + self.dt_initial = self.dt * 1.0 if self.dt is not None else None # short helper class to bundle all status variables diff --git a/pySDC/core/Problem.py b/pySDC/core/Problem.py index 5b45fd4518..40b0738f9a 100644 --- a/pySDC/core/Problem.py +++ b/pySDC/core/Problem.py @@ -73,6 +73,10 @@ def f_init(self): """Generate a data variable for RHS""" return self.dtype_f(self.init) + @classmethod + def get_default_sweeper_class(cls): + raise NotImplementedError(f'No default sweeper class implemented for {cls} problem!') + def eval_f(self, u, t): """ Abstract interface to RHS computation of the ODE diff --git a/pySDC/helpers/setup_helper.py b/pySDC/helpers/setup_helper.py new file mode 100644 index 0000000000..e98254a6e7 --- /dev/null +++ b/pySDC/helpers/setup_helper.py @@ -0,0 +1,45 @@ +def generate_description(problem_class, **kwargs): + """ + Generate a description object that you can use to run pySDC based on a problem class and various input. + This function does not set any additional defaults, but distributes the values to where they belong. + + Args: + problem_class (pySDC.Problem): A problem class + + Returns: + dict: A description object for running pySDC + """ + from pySDC.core.Level import _Pars as level_params + from pySDC.core.Step import _Pars as step_params + + description = { + 'level_params': {}, + 'problem_params': {}, + 'sweeper_params': {}, + 'problem_class': problem_class, + 'step_params': {}, + 'sweeper_class': kwargs.get('sweeper_class', problem_class.get_default_sweeper_class()), + 'convergence_controllers': {}, + } + + 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() + step_keys = step_params({}).__dict__.keys() + + # TODO: add convergence controllers + for key, val in kwargs.items(): + if key in problem_keys: + description['problem_params'][key] = val + elif key in level_keys: + description['level_params'][key] = val + elif key in sweeper_keys: + description['sweeper_params'][key] = val + elif key in step_keys: + description['step_params'][key] = val + elif key == 'sweeper_class': + pass + else: + raise ValueError(f'Don\'t know what parameter \"{key}\" is for!') + + return description diff --git a/pySDC/implementations/problem_classes/generic_ND_FD.py b/pySDC/implementations/problem_classes/generic_ND_FD.py index 3cb7e6d7cd..b1d0109f74 100644 --- a/pySDC/implementations/problem_classes/generic_ND_FD.py +++ b/pySDC/implementations/problem_classes/generic_ND_FD.py @@ -167,6 +167,12 @@ def grids(self): if self.ndim == 3: return x[None, :, None], x[:, None, None], x[None, None, :] + @classmethod + def get_default_sweeper_class(cls): + from pySDC.implementations.sweeper_classes.generic_implicit import generic_implicit + + return generic_implicit + def eval_f(self, u, t): """ Routine to evaluate the right-hand side of the problem. diff --git a/pySDC/tests/test_helpers/test_setup_helper.py b/pySDC/tests/test_helpers/test_setup_helper.py new file mode 100644 index 0000000000..9060dfe05b --- /dev/null +++ b/pySDC/tests/test_helpers/test_setup_helper.py @@ -0,0 +1,59 @@ +import pytest + + +@pytest.mark.base +def test_setup_helper(): + from pySDC.helpers.setup_helper import generate_description + from pySDC.implementations.problem_classes.AdvectionEquation_ND_FD import advectionNd + from pySDC.implementations.sweeper_classes.generic_implicit import generic_implicit + + # build classic description + # initialize level parameters + level_params = {} + level_params['dt'] = 0.05 + + # initialize sweeper parameters + sweeper_params = {} + sweeper_params['quad_type'] = 'RADAU-RIGHT' + sweeper_params['num_nodes'] = 3 + sweeper_params['QI'] = 'IE' + + problem_params = {'freq': 2, 'nvars': 2**9, 'c': 1.0, 'stencil_type': 'center', 'order': 4, 'bc': 'periodic'} + + # initialize step parameters + step_params = {} + step_params['maxiter'] = 5 + + description = {} + description['problem_class'] = advectionNd + description['problem_params'] = problem_params + description['sweeper_class'] = generic_implicit + description['sweeper_params'] = sweeper_params + description['level_params'] = level_params + description['step_params'] = step_params + description['convergence_controllers'] = {} + + easy_description = generate_description( + problem_class=advectionNd, **problem_params, **level_params, **sweeper_params, **step_params + ) + + assert ( + easy_description == description + ), 'The generate description function did not reproduce the desired description' + + easy_description = generate_description( + problem_class=advectionNd, + sweeper_class=generic_implicit, + **problem_params, + **level_params, + **sweeper_params, + **step_params + ) + + assert ( + easy_description == description + ), 'The generate description function did not reproduce the desired description when supplying a sweeper class' + + +if __name__ == '__main__': + test_setup_helper()