stacked_tensorial_nn / stnn /tests /test_differential_ops.py
caleb2's picture
initial commit
d68c650
import unittest
import numpy as np
from stnn.pde.common import d_dx_upwind, d2_dx2_fourth_order, d_dx_upwind_nonuniform
def assert_derivative(operator, function, expected_derivative, boundary = None, rtol = None, atol = None):
"""
Assert the derivative of a function using a given finite-difference operator
Args:
operator (np.ndarray or sparse matrix): Differential operator matrix.
function (np.ndarray): Values of the function to differentiate
expected_derivative (np.ndarray):: Expected result of the derivative.
boundary (int): Specifies if boundary elements should be excluded, and how many.
rtol: Relative tolerance.
atol: Absolute tolerance.
"""
observed_derivative = operator @ function
if boundary is not None:
observed_derivative = observed_derivative[boundary:-boundary]
expected_derivative = expected_derivative[boundary:-boundary]
np.testing.assert_allclose(observed_derivative, expected_derivative, rtol = rtol, atol = atol)
class TestDifferentialOperators(unittest.TestCase):
def setUp(self):
# ----- Set up grids
# grid for radial coordinate (non-periodic, non-uniform spacing)
self.nx = 100000
zs = np.linspace(-np.pi / 2, np.pi / 2, self.nx + 2)
R1, R2 = 0.5, 1.4
self.z_ = (R2 - R1) * (np.sin(zs) / 2 + 0.5) + R1
self.z = self.z_[1:-1]
# grid for angular coordinates (periodic, uniform spacing)
self.y = np.linspace(0, 2 * np.pi, self.nx, endpoint = False)
dy = (2 * np.pi) / self.nx
# ----- Set tolerances
# Tolerances for "exact" tests, i.e., where the finite differences do not have truncation error
self.atol = 1e-6
self.rtol = 1e-6
# Tolerances for some inexact tests
self.atol_inexact = 1e-3
self.rtol_z = 3 * np.max(np.diff(self.z_)) # relative tolerance of 3*dx for first-order one-sided differences
self.rtol_y = 3 * dy # relative tolerance of 3*dx for first-order one-sided differences
self.rtol_y2 = 3 * dy**4 # relative tolerance of 3*dy**4 for fourth-order central differences
# ----- Test inputs
# "Exact" test inputs
self.f1 = -2.3 * np.ones(self.nx)
self.f2 = 0.8 * self.z
self.f3 = 0.8 * self.y
self.f4 = -0.1 * self.y * self.y
# Inexact test inputs
# noinspection PyRedundantParentheses
self.g1 = (self.z)**2 - (self.z)**3
self.g2 = (np.cos(self.y))**2 - (np.sin(self.y))**3
self.g3 = (np.sin(2 * self.y))**2 - (np.cos(self.y))**3
def test_d_dx_upwind(self):
dx_m, dx_p = d_dx_upwind(self.y, self.nx)
assert_derivative(dx_m, self.f1, np.zeros(self.nx), rtol = self.rtol, atol = self.atol)
assert_derivative(dx_p, self.f1, np.zeros(self.nx), rtol = self.rtol, atol = self.atol)
assert_derivative(dx_m, self.f3, 0.8 * np.ones(self.nx), boundary = 1, rtol = self.rtol, atol = self.atol)
assert_derivative(dx_p, self.f3, 0.8 * np.ones(self.nx), boundary = 1, rtol = self.rtol, atol = self.atol)
expected_dg2 = -2 * np.cos(self.y) * np.sin(self.y) - 3 * np.sin(self.y)**2 * np.cos(self.y)
assert_derivative(dx_m, self.g2, expected_dg2, rtol = self.rtol_y, atol = self.atol_inexact)
assert_derivative(dx_p, self.g2, expected_dg2, rtol = self.rtol_y, atol = self.atol_inexact)
expected_dg3 = 4 * np.sin(2 * self.y) * np.cos(2 * self.y) + 3 * np.cos(self.y)**2 * np.sin(self.y)
assert_derivative(dx_m, self.g3, expected_dg3, rtol = self.rtol_y, atol = self.atol_inexact)
assert_derivative(dx_p, self.g3, expected_dg3, rtol = self.rtol_y, atol = self.atol_inexact)
def test_d2_dx2_fourth_order(self):
d2x = d2_dx2_fourth_order(self.y, self.nx)
assert_derivative(d2x, self.f1, np.zeros(self.nx), rtol = self.rtol, atol = self.atol)
assert_derivative(d2x, self.f3, np.zeros(self.nx), boundary = 4, rtol = self.rtol, atol = self.atol)
assert_derivative(d2x, self.f4, -0.2 * np.ones(self.nx), boundary = 4, rtol = self.rtol, atol = self.atol)
expected_dg2 = 2 * np.sin(self.y)**2 - 2 * np.cos(self.y)**2 - \
6 * np.sin(self.y) * np.cos(self.y)**2 + 3 * np.sin(self.y)**3
assert_derivative(d2x, self.g2, expected_dg2, rtol = self.rtol_y2, atol = self.atol_inexact)
expected_dg3 = 8 * np.cos(2 * self.y)**2 - 8 * np.sin(2 * self.y)**2 - \
6 * np.cos(self.y) * np.sin(self.y)**2 + 3 * np.cos(self.y)**3
assert_derivative(d2x, self.g3, expected_dg3, rtol = self.rtol_y2, atol = self.atol_inexact)
def test_d_dx_upwind_nonuniform(self):
dx_minus, dx_plus = d_dx_upwind_nonuniform(self.z_, self.nx)
assert_derivative(dx_minus, self.f1, np.zeros(self.nx), boundary = 1, rtol = self.rtol, atol = self.atol)
assert_derivative(dx_plus, self.f1, np.zeros(self.nx), boundary = 1, rtol = self.rtol, atol = self.atol)
assert_derivative(dx_minus, self.f2, 0.8 * np.ones(self.nx), boundary = 1, rtol = self.rtol, atol = self.atol)
assert_derivative(dx_plus, self.f2, 0.8 * np.ones(self.nx), boundary = 1, rtol = self.rtol, atol = self.atol)
expected_dg1 = (2 * self.z - 3 * self.z**2)
assert_derivative(dx_minus, self.g1, expected_dg1, boundary = 1, rtol = self.rtol_z, atol = self.atol_inexact)
assert_derivative(dx_plus, self.g1, expected_dg1, boundary = 1, rtol = self.rtol_z, atol = self.atol_inexact)
if __name__ == '__main__':
unittest.main()