Skip to content

Commit

Permalink
Merge pull request #244 from tlunet/v6
Browse files Browse the repository at this point in the history
Tentative solution to replace FrozenClass
  • Loading branch information
brownbaerchen authored Jan 23, 2023
2 parents d8540a7 + 60c9ec6 commit ed38531
Show file tree
Hide file tree
Showing 6 changed files with 319 additions and 173 deletions.
2 changes: 0 additions & 2 deletions pySDC/core/Collocation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import logging

import numpy as np
import scipy.interpolate as intpl

from pySDC.core.Nodes import NodesGenerator
from pySDC.core.Errors import CollocationError
Expand Down
75 changes: 75 additions & 0 deletions pySDC/core/Common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Description
-----------
Module containing utility classe(s) from which inherit some of the pySDC base
classes.
"""
from pySDC.core.Errors import ReadOnlyError


class _MetaRegisterParams(type):
"""Metaclass for RegisterParams base class"""

def __new__(cls, name, bases, dct):
obj = super().__new__(cls, name, bases, dct)
obj._parNamesReadOnly = set()
obj._parNames = set()
return obj


class RegisterParams(metaclass=_MetaRegisterParams):
"""
Base class to register parameters.
Attributes
----------
params : dict (property)
Dictionnary containing names and values of registered parameters.
_parNames : set of str
Names of all the registered parameters.
_parNamesReadOnly : set of str
Names of all the parameters registered as read-only.
"""

def _makeAttributeAndRegister(self, *names, localVars=None, readOnly=False):
"""
Register a list of attribute name as parameters of the class.
Parameters
----------
*names : list of str
The name of the parameters to be registered (should be class attributes).
localVars : dict
Dictionnary containing key=names and values=paramValues for each
parNames given in names. Can be provided, for instance, using
`locals()` built-in dictionary. MUST BE provided as soon as
names contains anything.
readOnly : bool, optional
Wether or not store the parameters as read-only attributes
"""
if len(names) > 1 and localVars is None:
raise ValueError("a dictionnary must be provided in localVars with parameters values")
# Set parameters as attributes
for name in names:
try:
super().__setattr__(name, localVars[name])
except KeyError:
raise ValueError(f'value for {name} not given in localVars')
# Register as class parameter
if readOnly:
self._parNamesReadOnly = self._parNamesReadOnly.union(names)
else:
self._parNames = self._parNames.union(names)

@property
def params(self):
"""Dictionnary containing names and values of registered parameters"""
return {name: getattr(self, name) for name in self._parNamesReadOnly.union(self._parNames)}

def __setattr__(self, name, value):
if name in self._parNamesReadOnly:
raise ReadOnlyError(name)
super().__setattr__(name, value)
11 changes: 5 additions & 6 deletions pySDC/core/Controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,11 @@ def dump_setup(self, step, controller_params, description):
else:
out += ' %s = %s\n' % (k, v)
out += '--> Problem: %s\n' % L.prob.__class__
for k, v in vars(L.prob.params).items():
if not k.startswith('_'):
if k in description['problem_params']:
out += '--> %s = %s\n' % (k, v)
else:
out += ' %s = %s\n' % (k, v)
for k, v in L.prob.params.items():
if k in description['problem_params']:
out += '--> %s = %s\n' % (k, v)
else:
out += ' %s = %s\n' % (k, v)
out += '--> Data type u: %s\n' % L.prob.dtype_u
out += '--> Data type f: %s\n' % L.prob.dtype_f
out += '--> Sweeper: %s\n' % L.sweep.__class__
Expand Down
9 changes: 9 additions & 0 deletions pySDC/core/Errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,12 @@ class ProblemError(Exception):
"""

pass


class ReadOnlyError(Exception):
"""
Exception thrown when setting a read-only class attribute
"""

def __init__(self, name):
super().__init__(f'cannot set read-only attribute {name}')
77 changes: 47 additions & 30 deletions pySDC/core/Problem.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,37 @@
import logging

from pySDC.helpers.pysdc_helper import FrozenClass

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Description
-----------
# short helper class to add params as attributes
class _Pars(FrozenClass):
def __init__(self, pars):
Module containing the base Problem class for pySDC
"""

for k, v in pars.items():
setattr(self, k, v)
import logging

self._freeze()
from pySDC.core.Common import RegisterParams


class ptype(object):
"""
Prototype class for problems, just defines the attributes essential to get started
Attributes:
logger: custom logger for problem-related logging
params (__Pars): parameter object containing the custom parameters passed by the user
init: number of degrees-of-freedom (whatever this may represent)
dtype_u: variable data type
dtype_f: RHS data type
class ptype(RegisterParams):
"""
Prototype class for problems, just defines the attributes essential to get started.
def __init__(self, init, dtype_u, dtype_f, **kwargs):
"""
Initialization routine.
Add the problem parameters as keyword arguments.
Parameters
----------
init : list of args
Argument(s) used to initialize data types.
dtype_u : type
Variable data type. Should generate a data variable using dtype_u(init).
dtype_f : type
RHS data type. Should generate a data variable using dtype_f(init).
Args:
init: number of degrees-of-freedom (whatever this may represent)
dtype_u: variable data type
dtype_f: RHS data type
"""
self.params = _Pars(kwargs)
Attributes
----------
logger: logging.Logger
custom logger for problem-related logging.
"""

def __init__(self, init, dtype_u, dtype_f):
# set up logger
self.logger = logging.getLogger('problem')

Expand All @@ -45,9 +40,31 @@ def __init__(self, init, dtype_u, dtype_f, **kwargs):
self.dtype_u = dtype_u
self.dtype_f = dtype_f

@property
def u_init(self):
"""Generate a data variable for u"""
return self.dtype_u(self.init)

@property
def f_init(self):
"""Generate a data variable for RHS"""
return self.dtype_f(self.init)

def eval_f(self, u, t):
"""
Abstract interface to RHS computation of the ODE
Parameters
----------
u : dtype_u
Current values.
t : float
Current time.
Returns
-------
f : dtype_f
The RHS values.
"""
raise NotImplementedError('ERROR: problem has to implement eval_f(self, u, t)')

Expand Down
Loading

0 comments on commit ed38531

Please sign in to comment.