from pystencils.session import *

Demo: Working with derivatives#

Overview#

This notebook demonstrates how to formulate continuous differential operators in pystencils and automatically derive finite difference stencils from them.

Instead of using the built-in derivatives in sympy, pystencils comes with its own derivative objects. They represent spatial derivatives of pystencils fields.

f = ps.fields("f: [2D]")
first_derivative_x = ps.fd.diff(f, 0)
first_derivative_x
../_images/5dfa1bff5fb9a7ed5e8b1f0ec68acfd5097e34ca5ae59529702d3086f03f0107.png

This object is the derivative of the field \(f\) with respect to the first spatial coordinate \(x\). To get a finite difference approximation a discretization strategy is required:

discretize_2nd_order = ps.fd.Discretization2ndOrder(dx=sp.symbols("h"))
discretize_2nd_order(first_derivative_x)
../_images/99dad558d8b98f5c434be14c88c06f9581e1c1e8ad9d469cd7ae8e3622b4a62a.png

Strictly speaking, derivative objects act on field accesses, not fields, that why there is a \((0,0)\) index at the field:

first_derivative_x
../_images/5dfa1bff5fb9a7ed5e8b1f0ec68acfd5097e34ca5ae59529702d3086f03f0107.png

Sometimes it might be useful to specify derivatives at an offset e.g.

derivative_offset = ps.fd.diff(f[0, 1], 0)
derivative_offset, discretize_2nd_order(derivative_offset)
../_images/44cd8d98429020f010c36a2c5778b668671666cfc97282c59f1701590124b60f.png

Another example with second order derivatives:

laplacian = ps.fd.diff(f, 0, 0) + ps.fd.diff(f, 1, 1)
laplacian
../_images/59d4d073b2d553a0cdb1ab4b196911fcbbc47094e39da2ae16759f10dde7a58f.png
discretize_2nd_order(laplacian)
../_images/1f55aa3f2ebc4d43c2d239d29b1ba2e794ba13f0d881b6671fd03e30aba689e3.png

Working with derivative terms#

No automatic simplifications are done on derivative terms i.e. linearity relations or product rule are not applied automatically.

f, g = ps.fields("f, g :[2D]")
c = sp.symbols("c")
δ = ps.fd.diff

expr = δ( δ(f, 0) +  δ(g, 0) + c + 5 , 0)
expr
../_images/0fff60a592a71b4e6fd5338fae7f4b192262b1a060f1586fdf0fb54563cd8111.png

This nested term can not be discretized automatically.

try:
    discretize_2nd_order(expr)
except ValueError as e:
    print(e)
Only derivatives with field or field accesses as arguments can be discretized

Linearity#

The following function expands all derivatives exploiting linearity:

ps.fd.expand_diff_linear(expr)
../_images/4ff20950f95ac21495749c5a064022b64e1e6d509a78428ee86f07035582c777.png

The symbol \(c\) that was included is interpreted as a function by default. We can control the simplification behaviour by specifying all functions or all constants:

ps.fd.expand_diff_linear(expr, functions=(f[0,0], g[0, 0]))
../_images/5ad9f537a76e6a2c65a1a94d20a12f8188a482e562b278a09a58d9bdac162026.png
ps.fd.expand_diff_linear(expr, constants=[c])
../_images/5ad9f537a76e6a2c65a1a94d20a12f8188a482e562b278a09a58d9bdac162026.png

The expanded term can then be discretized:

discretize_2nd_order(ps.fd.expand_diff_linear(expr, constants=[c]))
../_images/2fcfd87c2d51b8005675d62e43d4f16298f35cec55d47fe6b2f322449349b883.png

Product rule#

The next cells show how to apply product rule and its reverse:

expr = δ(f[0, 0] * g[0, 0], 0 )
expr
../_images/12ab544f8a47f13389946923d911fd20ad9ab52a53f523cb7b78c00a48ae4652.png
expanded_expr = ps.fd.expand_diff_products(expr)
expanded_expr
../_images/c3024dd3b2d0b8834fab6386f1b6a04e280dd27fb9e06c364aa159e765a588e1.png
recombined_expr = ps.fd.combine_diff_products(expanded_expr)
recombined_expr
../_images/12ab544f8a47f13389946923d911fd20ad9ab52a53f523cb7b78c00a48ae4652.png
assert recombined_expr == expr

Evaluate derivatives#

Arguments of derivative need not be to be fields, only when trying to discretize them. The next cells show how to transform them to sympy derivatives and evaluate them.

k = sp.symbols("k")
expr = δ(k**3 + 2 * k, 0 )
expr
../_images/2b04f43c21f5e38b5c349ea53c51b7467b9ecdb4b28b18d31820031d8a258bfa.png
ps.fd.evaluate_diffs(expr, var=k)
../_images/f56236bd0b95f83d68c53aba398bbc98beedfeac5b3c48949de5d2d27d246db5.png