Skip to content
Snippets Groups Projects
Commit dc8453b3 authored by Martin Bauer's avatar Martin Bauer
Browse files

Merge branch 'derivation_gradient' into 'master'

implemented derivation of gradient weights via rotation

See merge request pycodegen/pystencils!15
parents ddb86435 248a5e0d
No related branches found
No related tags found
1 merge request!15implemented derivation of gradient weights via rotation
Pipeline #17005 passed with warnings
from collections import defaultdict
import sympy as sp
import numpy as np
from pystencils.field import Field
from pystencils.sympyextensions import multidimensional_sum, prod
from pystencils.utils import LinearEquationSystem, fully_contains
import warnings
class FiniteDifferenceStencilDerivation:
"""Derives finite difference stencils.
......@@ -169,6 +172,9 @@ class FiniteDifferenceStencilDerivation:
return sum(f.get_shifted(*offset) * weight for offset, weight in zip(self.stencil, self.weights))
def as_matrix(self):
warnings.warn("as_matrix is deprecated and may be removed in the near future."
"Please use as_array instead which will return an MutableDenseNDimArray."
"as_array therefore can also work in 3 dimensions", category=DeprecationWarning)
dim = len(self.stencil[0])
assert dim == 2
max_offset = max(max(abs(e) for e in direction) for direction in self.stencil)
......@@ -177,6 +183,49 @@ class FiniteDifferenceStencilDerivation:
result[max_offset - direction[1], max_offset + direction[0]] = weight
return result
def as_array(self):
dim = len(self.stencil[0])
assert (dim == 2 or dim == 3), "Only 2D or 3D matrix representations are available"
max_offset = max(max(abs(e) for e in direction) for direction in self.stencil)
shape_list = []
for i in range(dim):
shape_list.append(2 * max_offset + 1)
number_of_elements = np.prod(shape_list)
shape = tuple(shape_list)
result = sp.MutableDenseNDimArray([0] * number_of_elements, shape)
if dim == 2:
for direction, weight in zip(self.stencil, self.weights):
result[max_offset - direction[1], max_offset + direction[0]] = weight
if dim == 3:
for direction, weight in zip(self.stencil, self.weights):
result[max_offset - direction[1], max_offset + direction[0], max_offset + direction[2]] = weight
return result
def rotate_weights_and_apply(self, field_access: Field.Access, axis):
"""derive gradient weights of other direction with already calculated weights of one direction
via rotation and apply them to a field."""
dim = len(self.stencil[0])
assert (dim == 2 or dim == 3), "This function is only for 2D or 3D stencils available"
rotated_weights = np.rot90(np.array(self.as_array()).reshape(self.as_array().shape), 1, axis)
result = []
max_offset = max(max(abs(e) for e in direction) for direction in self.stencil)
if dim == 2:
for direction in self.stencil:
result.append(rotated_weights[max_offset - direction[1],
max_offset + direction[0]])
if dim == 3:
for direction in self.stencil:
result.append(rotated_weights[max_offset - direction[1],
max_offset + direction[0],
max_offset + direction[2]])
f = field_access
return sum(f.get_shifted(*offset) * weight for offset, weight in zip(self.stencil, result))
def __repr__(self):
return "Finite difference stencil of accuracy {}, isotropic error: {}".format(self.accuracy,
self.is_isotropic)
%% Cell type:code id: tags:
``` python
from pystencils.session import *
from pystencils.fd.derivation import *
```
%% Cell type:markdown id: tags:
# 2D standard stencils
%% Cell type:code id: tags:
``` python
stencil = [(-1, 0), (1, 0), (0, -1), (0, 1), (0, 0)]
standard_2d_00 = FiniteDifferenceStencilDerivation((0,0), stencil)
f = ps.fields("f: [2D]")
standard_2d_00_res = standard_2d_00.get_stencil()
res = standard_2d_00_res.apply(f.center)
expected = f[-1, 0] - 2 * f[0, 0] + f[1, 0]
assert res == expected
```
%% Cell type:code id: tags:
``` python
assert standard_2d_00_res.accuracy == 2
assert not standard_2d_00_res.is_isotropic
standard_2d_00_res
```
%% Output
Finite difference stencil of accuracy 2, isotropic error: False
%% Cell type:code id: tags:
``` python
standard_2d_00.get_stencil().as_matrix()
```
%% Output
$$\left[\begin{matrix}0 & 0 & 0\\1 & -2 & 1\\0 & 0 & 0\end{matrix}\right]$$
⎡0 0 0⎤
⎢ ⎥
⎢1 -2 1⎥
⎢ ⎥
⎣0 0 0⎦
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-ea41cd8e50a0> in <module>
----> 1 standard_2d_00.get_stencil().as_matrix()
~/pystencils/pystencils/pystencils/fd/derivation.py in as_matrix(self)
185 for direction, weight in zip(self.stencil, self.weights):
186 result[max_offset - direction[1], max_offset + direction[0]] = weight
--> 187 result.tomatrix().tomatrix()
188 print("test")
189 if dim == 3:
~/anaconda3/envs/pystencils/lib/python3.7/site-packages/sympy/matrices/matrices.py in __getattr__(self, attr)
2139 else:
2140 raise AttributeError(
-> 2141 "%s has no attribute %s." % (self.__class__.__name__, attr))
2142
2143 def __len__(self):
AttributeError: MutableDenseMatrix has no attribute tomatrix.
%% Cell type:markdown id: tags:
# 2D isotropic stencils
## second x-derivative
%% Cell type:code id: tags:
``` python
stencil = [(i, j) for i in (-1, 0, 1) for j in (-1, 0, 1)]
isotropic_2d_00 = FiniteDifferenceStencilDerivation((0,0), stencil)
isotropic_2d_00_res = isotropic_2d_00.get_stencil(isotropic=True)
assert isotropic_2d_00_res.is_isotropic
assert isotropic_2d_00_res.accuracy == 2
isotropic_2d_00_res
```
%% Output
Finite difference stencil of accuracy 2, isotropic error: True
%% Cell type:code id: tags:
``` python
isotropic_2d_00_res.as_matrix()
```
%% Output
$$\left[\begin{matrix}\frac{1}{12} & - \frac{1}{6} & \frac{1}{12}\\\frac{5}{6} & - \frac{5}{3} & \frac{5}{6}\\\frac{1}{12} & - \frac{1}{6} & \frac{1}{12}\end{matrix}\right]$$
⎡1/12 -1/6 1/12⎤
⎢ ⎥
⎢5/6 -5/3 5/6 ⎥
⎢ ⎥
⎣1/12 -1/6 1/12⎦
%% Cell type:code id: tags:
``` python
plt.figure(figsize=(2,2))
isotropic_2d_00_res.visualize()
```
%% Output
%% Cell type:code id: tags:
``` python
expected_result = sp.Matrix([[1, -2, 1], [10, -20, 10], [1, -2, 1]]) / 12
assert expected_result == isotropic_2d_00_res.as_matrix()
```
%% Cell type:code id: tags:
``` python
type(isotropic_2d_00_res.as_matrix())
```
%% Cell type:code id: tags:
``` python
type(expected_result)
```
%% Cell type:markdown id: tags:
## Isotropic laplacian
%% Cell type:code id: tags:
``` python
isotropic_2d_11 = FiniteDifferenceStencilDerivation((1,1), stencil)
isotropic_2d_11_res = isotropic_2d_11.get_stencil(isotropic=True)
iso_laplacian = isotropic_2d_00_res.as_matrix() + isotropic_2d_11_res.as_matrix()
iso_laplacian
```
%% Output
$$\left[\begin{matrix}\frac{1}{6} & \frac{2}{3} & \frac{1}{6}\\\frac{2}{3} & - \frac{10}{3} & \frac{2}{3}\\\frac{1}{6} & \frac{2}{3} & \frac{1}{6}\end{matrix}\right]$$
⎡1/6 2/3 1/6⎤
⎢ ⎥
⎢2/3 -10/3 2/3⎥
⎢ ⎥
⎣1/6 2/3 1/6⎦
%% Cell type:code id: tags:
``` python
expected_result = sp.Matrix([[1, 4, 1], [4, -20, 4], [1, 4, 1]]) / 6
assert iso_laplacian == expected_result
```
......
%% Cell type:code id: tags:
``` python
from pystencils.session import *
from pystencils.fd.derivation import *
```
%% Cell type:markdown id: tags:
# 2D isotropic stencils
%% Cell type:code id: tags:
``` python
stencil_2D = ((0, 0),
(0, 1), (0, -1), (-1, 0), (1, 0),
(-1, 1), (1, 1), (-1, -1), (1, -1))
f = ps.fields("f: [2D]")
```
%% Cell type:code id: tags:
``` python
Isotropic_Gradient = list()
deriv_x = FiniteDifferenceStencilDerivation((0, ), stencil_2D)
deriv_x.assume_symmetric(0, anti_symmetric=True)
deriv_x.assume_symmetric(1, anti_symmetric=False)
deriv_x.set_weight((0, 0), sp.Integer(0))
deriv_x.set_weight((1, 0), sp.Rational(1, 3))
deriv_x.set_weight((1, 1), sp.Rational(1, 12))
res_x = deriv_x.get_stencil(isotropic=True)
```
%% Cell type:code id: tags:
``` python
Isotropic_Gradient = list()
deriv_y = FiniteDifferenceStencilDerivation((1, ), stencil_2D)
deriv_y.assume_symmetric(0, anti_symmetric=False)
deriv_y.assume_symmetric(1, anti_symmetric=True)
deriv_y.set_weight((0, 0), sp.Integer(0))
deriv_y.set_weight((0, 1), sp.Rational(1, 3))
deriv_y.set_weight((1, 1), sp.Rational(1, 12))
res_y = deriv_y.get_stencil(isotropic=True)
```
%% Cell type:code id: tags:
``` python
assert res_x.apply(f.center) - res_y.rotate_weights_and_apply(f.center, (1, 0)) == 0
assert res_y.apply(f.center) - res_x.rotate_weights_and_apply(f.center, (0, 1)) == 0
```
%% Cell type:markdown id: tags:
# 3D isotropic stencils
%% Cell type:code id: tags:
``` python
stencil_3D = ((0, 0, 0),
(0, 1, 0), (0, -1, 0), (-1, 0, 0), (1, 0, 0), (0, 0, 1), (0, 0, -1),
(-1, 1, 0), (1, 1, 0), (-1, -1, 0), (1, -1, 0),
(0, 1, 1), (0, -1, 1), (-1, 0, 1), (1, 0, 1),
(0, 1, -1), (0, -1, -1), (-1, 0, -1), (1, 0, -1),
(1, 1, 1), (-1, 1, 1), (1, -1, 1), (-1, -1, 1), (1, 1, -1), (-1, 1, -1), (1, -1, -1),
(-1, -1, -1))
f = ps.fields("f: [3D]")
```
%% Cell type:code id: tags:
``` python
deriv_x = FiniteDifferenceStencilDerivation((0, ), stencil_3D)
deriv_x.assume_symmetric(0, anti_symmetric=True)
deriv_x.assume_symmetric(1, anti_symmetric=False)
deriv_x.assume_symmetric(2, anti_symmetric=False)
deriv_x.set_weight((0, 0, 0), sp.Integer(0))
deriv_x.set_weight((1, 1, 1), sp.Rational(1, 3360))
res_x = deriv_x.get_stencil(isotropic=True)
```
%% Cell type:code id: tags:
``` python
deriv_y = FiniteDifferenceStencilDerivation((1, ), stencil_3D)
deriv_y.assume_symmetric(0, anti_symmetric=False)
deriv_y.assume_symmetric(1, anti_symmetric=True)
deriv_y.assume_symmetric(2, anti_symmetric=False)
deriv_y.set_weight((0, 0, 0), sp.Integer(0))
deriv_y.set_weight((1, 1, 1), sp.Rational(1, 3360))
res_y = deriv_y.get_stencil(isotropic=True)
```
%% Cell type:code id: tags:
``` python
deriv_z = FiniteDifferenceStencilDerivation((2, ), stencil_3D)
deriv_z.assume_symmetric(0, anti_symmetric=False)
deriv_z.assume_symmetric(1, anti_symmetric=False)
deriv_z.assume_symmetric(2, anti_symmetric=True)
deriv_z.set_weight((0, 0, 0), sp.Integer(0))
deriv_z.set_weight((1, 1, 1), sp.Rational(1, 3360))
res_z = deriv_z.get_stencil(isotropic=True)
```
%% Cell type:code id: tags:
``` python
assert res_x.apply(f.center) - res_y.rotate_weights_and_apply(f.center, (1, 0)) == 0
assert res_x.apply(f.center) - res_z.rotate_weights_and_apply(f.center, (2, 1)) == 0
assert res_y.apply(f.center) - res_x.rotate_weights_and_apply(f.center, (0, 1)) == 0
assert res_y.apply(f.center) - res_z.rotate_weights_and_apply(f.center, (0, 2)) == 0
assert res_z.apply(f.center) - res_x.rotate_weights_and_apply(f.center, (1, 2)) == 0
assert res_z.apply(f.center) - res_y.rotate_weights_and_apply(f.center, (2, 0)) == 0
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment