File size: 5,149 Bytes
d68c650
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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()