Skip to content

Commit 27d242b

Browse files
authored
Merge pull request #285 from Parallel-in-Time/v6
Proposition for release 5.2
2 parents d09fb83 + 348606c commit 27d242b

File tree

120 files changed

+2832
-2787
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+2832
-2787
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ Additionally, a _few_ rules are set to enforce code readability, consistency and
77
Others are specific conventions chosen for the pySDC library, that may follow Python standards (or not ...), detailed in the [naming conventions](./docs/contrib/03_naming_conventions.md) page.
88

99
Finally, while `pySDC` provides many base functionalities that implement classical flavors of SDC, it also allows problem-specific applications through Object-Oriented Programming (OOP) and the implementation of custom inherited classes.
10-
This follows a specific OOP framework, you can look at the page on [custom implementations](./docs/contrib/04_custom_implementations.md) for more details.
10+
This follows a specific OOP framework, you can look at the page on [custom implementations](./docs/contrib/04_custom_implementations.md) for more details. Additional guideline are also given on how to [document the code](./docs/contrib/05_documenting_code.md) in `pySDC`.
1111

1212
1. [GitHub Forks and Pull Requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models)
1313
2. [Pull Requests Recommendations](./docs/contrib/01_pull_requests.md)
1414
3. [Continuous Integration](./docs/contrib/02_continuous_integration.md)
1515
4. [Naming Conventions](./docs/contrib/03_naming_conventions.md)
1616
5. [Custom Implementations](./docs/contrib/04_custom_implementations.md)
17+
6. [Documenting Code](./docs/contrib/05_documenting_code.md)
1718

1819
:arrow_left: [Back to main page](./README.md)

docs/contrib/02_continuous_integration.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ This stage allows to checks how much of the `pySDC` code is tested by the previo
5656
- `pySDC/projects`
5757
- `pySDC/tutorial`
5858
59-
This analysis is done in parallel to the test each time a pull is done on any branch (main repository or fork).
59+
This analysis is done in parallel to the test each time a pull is done on any branch (main repository or fork).
6060
You can look at the current coverage report for the master branch [here](https://parallel-in-time.org/pySDC/coverage/index.html) or compare the results with previous builds [here](https://app.codecov.io/gh/Parallel-in-Time/pySDC). Codecov will also comment on any pull request, indicating the change of coverage.
6161
6262
During developments, you can also run the coverage tests locally, using :
@@ -82,20 +82,18 @@ This will generate the coverage report in a `htmlcov` folder, and you can open t
8282
### Coverage exceptions
8383

8484
Some types of code lines will be ignored by the coverage analysis (_e.g_ lines starting with `raise`, ...), see the `[tool.coverage.report]` section in `pyproject.toml`.
85-
Part of code (functions, conditionaly, for loops, etc ...) can be ignored by coverage analysis using the `# pragma: no cover`, for instance
85+
Part of code (functions, conditionaly, for loops, etc ...) can be ignored by coverage analysis using the `# pragma: no cover`, for instance
8686

8787
```python
8888
# ...
8989
# code analyzed by coverage
9090
# ...
91-
# pragma: no cover
92-
if condition:
91+
if condition: # pragma: no cover
9392
# code ignored by coverage
9493
# ...
9594
# code analyzed by coverage
9695
# ...
97-
# pragma: no cover
98-
def function():
96+
def function(): # pragma: no cover
9997
# all function code is ignored by coverage
10098
```
10199

@@ -109,7 +107,7 @@ If you think the pragma should be used in other parts of your pull request, plea
109107
## Documentation generation
110108

111109
Documentation is built using [sphinx](https://www.sphinx-doc.org/en/master/).
112-
To check its generation, you can wait for all the CI tasks to download the `docs` artifacts, unzip it and open the `index.html` file there with you favorite browser.
110+
To check its generation, you can wait for all the CI tasks to download the `docs` artifacts, unzip it and open the `index.html` file there with you favorite browser.
113111

114112
However, when you are working on documentation (of the project, of the code, etc ...), you can already build and check the website locally :
115113

@@ -124,7 +122,7 @@ sphinx-build -b html docs/source docs/build/html
124122

125123
Then you can open `docs/build/html/index.html` using you favorite browser and check how your own documentation looks like on the website.
126124

127-
> :bell: **Important** : running all the tests is necessary to generate graphs and images used by the website.
125+
> :bell: **Important** : running all the tests is necessary to generate graphs and images used by the website.
128126
> But you can still generate the website without it: just all images for the tutorials, projects and playgrounds will be missing.
129127
> This approach can be considered for local testing of your contribution when it does not concern parts containing images (_i.e_ project or code documentation).
130128
Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,62 @@
11
# Custom implementation guidelines
22

3-
... in construction ...
3+
`pySDC` solves (non-)linear ODE of the form
4+
5+
$$
6+
\frac{du}{dt} = f(u, t), \quad u(0)=u_0, \quad t \in [0, T].
7+
$$
8+
9+
where $u$ a vector or scalar, representing one or more variables.
10+
11+
The type of variable, the definition of the right-hand side $f(u,t)$ and initial solution value $u_0$ are defined in a given `Problem` class.
12+
13+
... to be continued ...
14+
15+
## Implementing a custom problem class
16+
17+
Any problem class inherit from the same base class, that is (currently) the `ptype` class from the module `pySDC.code.Problem`.
18+
Each custom problem should inherit from this base class, like for the following example template :
19+
20+
- right-hand side $f(u,t)=\lambda u + ct$ with
21+
- $\lambda$ one or more complex values (in vector form)
22+
- $c$ one scalar value
23+
24+
```python
25+
import numpy as np
26+
27+
from pySDC.core.Problem import ptype
28+
from pySDC.core.Errors import ProblemError
29+
from pySDC.implementations.datatype_classes.mesh import mesh
30+
31+
class MyCustomProblem(ptype):
32+
# 1) Provide datatype class as class attribute
33+
dtype_u = mesh # -> used for u values
34+
dtype_f = mesh # -> used for f(u,t) values
35+
36+
# 2) Define constructor
37+
def __init__(self, lam, c):
38+
39+
# Store lambda values into a numpy array (with copy) + check
40+
lam = np.array(lam)
41+
if len(lam.shape) > 1:
42+
raise ProblemError(f"lambda values must be given as 1D vector, got shape {lam.shape}")
43+
44+
# Call base constructor
45+
super().__init__(init=(lam.size, None, lam.dtype))
46+
47+
# Register parameters
48+
self._makeAttributeAndRegister('lam', 'c', localVars=locals(), readOnly=True)
49+
50+
# 3) Define RHS function
51+
def eval_f(self, u, t):
52+
f = self.f_init # Generate new datatype to store result
53+
f[:] = self.lam*u + self.c*t # Compute RHS value
54+
return f
55+
```
56+
57+
:bell: The `_makeAttributeAndRegister` automatically add `lam` and `c`, and register them in a list of parameters that are printed in the outputs of `pySDC`.
58+
If you set `readOnly=True`, then those parameters cannot be changed after initializing the problem (if not specifies, use `readOnly=False`).
459

560
:arrow_left: [Back to Naming Conventions](./03_naming_conventions.md) ---
661
:arrow_up: [Contributing Summary](./../../CONTRIBUTING.md) ---
7-
:arrow_right: [Next to a cute picture of cat](https://www.vecteezy.com/photo/2098203-silver-tabby-cat-sitting-on-green-background)
62+
:arrow_right: [Next to Documenting Code](./05_documenting_code.md)

docs/contrib/05_documenting_code.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Documenting Code
2+
3+
When developing a new class or function, or improving current classes in `pySDC`, adding Python docstring to document the code is important, in particular :
4+
5+
- to help developer understanding how classes and functions work when reading the code
6+
- to help user understanding how classes and functions work when reading the [documentation](https://parallel-in-time.org/pySDC/#api-documentation)
7+
8+
`pySDC` follows the [NumPy Style Python Docstring](https://numpydoc.readthedocs.io/en/latest/format.html), below is simplified example
9+
for a class and a function :
10+
11+
> :bell: Don't document the `init` function, but rather the class itself. Also describe parameters (given to the `__init__`) and attributes (stored into the class) separately.
12+
13+
```python
14+
class LagrangeApproximation(object):
15+
r"""
16+
Class approximating any function on a given set of points using barycentric
17+
Lagrange interpolation.
18+
19+
Let note :math:`(t_j)_{0\leq j<n}` the set of points, then any scalar
20+
function :math:`f` can be approximated by the barycentric formula :
21+
22+
.. math::
23+
p(x) =
24+
\frac{\displaystyle \sum_{j=0}^{n-1}\frac{w_j}{x-x_j}f_j}
25+
{\displaystyle \sum_{j=0}^{n-1}\frac{w_j}{x-x_j}},
26+
27+
where :math:`f_j=f(t_j)` and
28+
29+
.. math::
30+
w_j = \frac{1}{\prod_{k\neq j}(x_j-x_k)}
31+
32+
are the barycentric weights.
33+
The theory and implementation is inspired from `this paper <http://dx.doi.org/10.1137/S0036144502417715>`_.
34+
35+
Parameters
36+
----------
37+
points : list, tuple or np.1darray
38+
The given interpolation points, no specific scaling, but must be
39+
ordered in increasing order.
40+
41+
Attributes
42+
----------
43+
points : np.1darray
44+
The interpolating points
45+
weights : np.1darray
46+
The associated barycentric weights
47+
"""
48+
49+
def __init__(self, points):
50+
pass # Implementation ...
51+
52+
@property
53+
def n(self):
54+
"""Number of points"""
55+
pass # Implementation ...
56+
57+
def getInterpolationMatrix(self, times):
58+
r"""
59+
Compute the interpolation matrix for a given set of discrete "time"
60+
points.
61+
62+
For instance, if we note :math:`\vec{f}` the vector containing the
63+
:math:`f_j=f(t_j)` values, and :math:`(\tau_m)_{0\leq m<M}`
64+
the "time" points where to interpolate.
65+
Then :math:`I[\vec{f}]`, the vector containing the interpolated
66+
:math:`f(\tau_m)` values, can be obtained using :
67+
68+
.. math::
69+
I[\vec{f}] = P_{Inter} \vec{f},
70+
71+
where :math:`P_{Inter}` is the interpolation matrix returned by this
72+
method.
73+
74+
Parameters
75+
----------
76+
times : list-like or np.1darray
77+
The discrete "time" points where to interpolate the function.
78+
79+
Returns
80+
-------
81+
PInter : np.2darray(M, n)
82+
The interpolation matrix, with :math:`M` rows (size of the **times**
83+
parameter) and :math:`n` columns.
84+
"""
85+
pass # Implementation ...
86+
87+
```
88+
89+
A more detailed example is given [here ...](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html)
90+
91+
:arrow_left: [Back to custom implementations](./04_custom_implementations.md) ---
92+
:arrow_up: [Contributing Summary](./../../CONTRIBUTING.md) ---
93+
:arrow_right: [Next to a cute picture of cat](https://www.vecteezy.com/photo/2098203-silver-tabby-cat-sitting-on-green-background)

pySDC/core/Collocation.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import logging
2-
32
import numpy as np
4-
import scipy.interpolate as intpl
53

64
from pySDC.core.Nodes import NodesGenerator
75
from pySDC.core.Errors import CollocationError

pySDC/core/Common.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Description
5+
-----------
6+
7+
Module containing utility classe(s) from which inherit some of the pySDC base
8+
classes.
9+
"""
10+
from pySDC.core.Errors import ReadOnlyError
11+
12+
13+
class _MetaRegisterParams(type):
14+
"""Metaclass for RegisterParams base class"""
15+
16+
def __new__(cls, name, bases, dct):
17+
obj = super().__new__(cls, name, bases, dct)
18+
obj._parNamesReadOnly = set()
19+
obj._parNames = set()
20+
return obj
21+
22+
23+
class RegisterParams(metaclass=_MetaRegisterParams):
24+
"""
25+
Base class to register parameters.
26+
27+
Attributes
28+
----------
29+
params : dict (property)
30+
Dictionary containing names and values of registered parameters.
31+
_parNames : set of str
32+
Names of all the registered parameters.
33+
_parNamesReadOnly : set of str
34+
Names of all the parameters registered as read-only.
35+
"""
36+
37+
def _makeAttributeAndRegister(self, *names, localVars=None, readOnly=False):
38+
"""
39+
Register a list of attribute name as parameters of the class.
40+
41+
Parameters
42+
----------
43+
*names : list of str
44+
The name of the parameters to be registered (should be class attributes).
45+
localVars : dict
46+
Dictionary containing key=names and values=paramValues for each
47+
parNames given in names. Can be provided, for instance, using
48+
`locals()` built-in dictionary. MUST BE provided as soon as
49+
names contains anything.
50+
readOnly : bool, optional
51+
Wether or not store the parameters as read-only attributes
52+
"""
53+
if len(names) > 1 and localVars is None:
54+
raise ValueError("a dictionary must be provided in localVars with parameters values")
55+
# Set parameters as attributes
56+
for name in names:
57+
try:
58+
super().__setattr__(name, localVars[name])
59+
except KeyError: # pragma: no cover
60+
raise ValueError(f'value for {name} not given in localVars')
61+
# Register as class parameter
62+
if readOnly:
63+
self._parNamesReadOnly = self._parNamesReadOnly.union(names)
64+
else:
65+
self._parNames = self._parNames.union(names)
66+
67+
@property
68+
def params(self):
69+
"""Dictionary containing names and values of registered parameters"""
70+
return {name: getattr(self, name) for name in self._parNamesReadOnly.union(self._parNames)}
71+
72+
def __setattr__(self, name, value):
73+
if name in self._parNamesReadOnly:
74+
raise ReadOnlyError(name)
75+
super().__setattr__(name, value)

pySDC/core/Controller.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,11 @@ def dump_setup(self, step, controller_params, description):
185185
else:
186186
out += ' %s = %s\n' % (k, v)
187187
out += '--> Problem: %s\n' % L.prob.__class__
188-
for k, v in vars(L.prob.params).items():
189-
if not k.startswith('_'):
190-
if k in description['problem_params']:
191-
out += '--> %s = %s\n' % (k, v)
192-
else:
193-
out += ' %s = %s\n' % (k, v)
188+
for k, v in L.prob.params.items():
189+
if k in description['problem_params']:
190+
out += '--> %s = %s\n' % (k, v)
191+
else:
192+
out += ' %s = %s\n' % (k, v)
194193
out += '--> Data type u: %s\n' % L.prob.dtype_u
195194
out += '--> Data type f: %s\n' % L.prob.dtype_f
196195
out += '--> Sweeper: %s\n' % L.sweep.__class__

pySDC/core/Errors.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,12 @@ class ProblemError(Exception):
6868
"""
6969

7070
pass
71+
72+
73+
class ReadOnlyError(Exception): # pragma: no cover
74+
"""
75+
Exception thrown when setting a read-only class attribute
76+
"""
77+
78+
def __init__(self, name):
79+
super().__init__(f'cannot set read-only attribute {name}')

pySDC/core/Lagrange.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ class LagrangeApproximation(object):
7474
are the barycentric weights.
7575
The theory and implementation is inspired from `this paper <http://dx.doi.org/10.1137/S0036144502417715>`_.
7676
77+
Parameters
78+
----------
79+
points : list, tuple or np.1darray
80+
The given interpolation points, no specific scaling, but must be
81+
ordered in increasing order.
82+
7783
Attributes
7884
----------
7985
points : np.1darray
@@ -83,14 +89,6 @@ class LagrangeApproximation(object):
8389
"""
8490

8591
def __init__(self, points):
86-
"""
87-
88-
Parameters
89-
----------
90-
points : list, tuple or np.1darray
91-
The given interpolation points, no specific scaling, but must be
92-
ordered in increasing order.
93-
"""
9492
points = np.asarray(points).ravel()
9593

9694
diffs = points[:, None] - points[None, :]

pySDC/core/Level.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def __init__(self, problem_class, problem_params, sweeper_class, sweeper_params,
7070

7171
# instantiate sweeper, problem and hooks
7272
self.__sweep = sweeper_class(sweeper_params)
73-
self.__prob = problem_class(problem_params)
73+
self.__prob = problem_class(**problem_params)
7474

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

0 commit comments

Comments
 (0)