Skip to content

Commit 8b5e6b5

Browse files
Weights for integrating across entire interval in spectral methods (#591)
* Added method for integrating across whole interval in Chebychev discretization * Added integration weights for Fourier discretization
1 parent 981a498 commit 8b5e6b5

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed

pySDC/helpers/spectral_helper.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ def get_differentiation_matrix(self):
161161
def get_integration_matrix(self):
162162
raise NotImplementedError()
163163

164+
def get_integration_weights(self):
165+
"""Weights for integration across entire domain"""
166+
raise NotImplementedError()
167+
164168
def get_wavenumbers(self):
165169
"""
166170
Get the grid in spectral space
@@ -379,6 +383,16 @@ def get_integration_matrix(self, lbnd=0):
379383
raise NotImplementedError(f'This function allows to integrate only from x=0, you attempted from x={lbnd}.')
380384
return S
381385

386+
def get_integration_weights(self):
387+
"""Weights for integration across entire domain"""
388+
n = self.xp.arange(self.N, dtype=float)
389+
390+
weights = (-1) ** n + 1
391+
weights[2:] /= 1 - (n**2)[2:]
392+
393+
weights /= 2 / self.L
394+
return weights
395+
382396
def get_differentiation_matrix(self, p=1):
383397
'''
384398
Keep in mind that the T2T differentiation matrix is dense.
@@ -808,6 +822,12 @@ def get_integration_matrix(self, p=1):
808822
k[0] = 1j * self.L
809823
return self.linalg.matrix_power(self.sparse_lib.diags(1 / (1j * k)), p)
810824

825+
def get_integration_weights(self):
826+
"""Weights for integration across entire domain"""
827+
weights = self.xp.zeros(self.N)
828+
weights[0] = self.L / self.N
829+
return weights
830+
811831
def get_plan(self, u, forward, *args, **kwargs):
812832
if self.fft_lib.__name__ == 'mpi4py_fft.fftw':
813833
if 'axes' in kwargs.keys():

pySDC/tests/test_helpers/test_spectral_helper_1d_chebychev.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,35 @@ def test_integration_matrix(N):
139139
assert np.allclose(exact.coef[:-1], du)
140140

141141

142+
@pytest.mark.base
143+
@pytest.mark.parametrize('x0', [-1, 0])
144+
@pytest.mark.parametrize('x1', [0.789, 1])
145+
@pytest.mark.parametrize('N', [4, 7])
146+
def test_integral_whole_interval(x0, x1, N):
147+
import numpy as np
148+
from pySDC.helpers.spectral_helper import ChebychevHelper
149+
from qmat.lagrange import LagrangeApproximation
150+
151+
cheby = ChebychevHelper(N, x0=x0, x1=x1)
152+
x = cheby.get_1dgrid()
153+
154+
coeffs = np.random.random(N)
155+
coeffs[-1] = 0
156+
157+
u_hat = coeffs
158+
u = cheby.itransform(u_hat)
159+
160+
weights = cheby.get_integration_weights()
161+
integral = weights @ u_hat
162+
163+
# generate a reference solution with qmat
164+
lag = LagrangeApproximation(points=x)
165+
Q = lag.getIntegrationMatrix(intervals=[(x0, x1)])
166+
integral_ref = (Q @ u)[0]
167+
168+
assert np.isclose(integral, integral_ref)
169+
170+
142171
@pytest.mark.base
143172
@pytest.mark.parametrize('N', [4])
144173
@pytest.mark.parametrize('d', [1, 2, 3])

pySDC/tests/test_helpers/test_spectral_helper_1d_fft.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,36 @@ def test_integration_matrix(N, plot=False):
112112
assert np.allclose(expect, Du)
113113

114114

115+
@pytest.mark.base
116+
@pytest.mark.parametrize('x0', [-1, 0])
117+
@pytest.mark.parametrize('x1', [0.789, 1])
118+
@pytest.mark.parametrize('N', [32, 45])
119+
def test_integral_whole_interval(x0, x1, N):
120+
import numpy as np
121+
from pySDC.helpers.spectral_helper import FFTHelper
122+
from qmat.lagrange import LagrangeApproximation
123+
124+
helper = FFTHelper(N, x0=x0, x1=x1)
125+
x = helper.get_1dgrid()
126+
127+
u = np.zeros_like(x)
128+
129+
num_coef = N // 2 - 1
130+
coeffs = np.random.random((2, N))
131+
u += coeffs[0, 0]
132+
for i in range(1, num_coef + 1):
133+
u += coeffs[0, i] * np.sin(2 * np.pi * i * x / helper.L)
134+
u += coeffs[1, i] * np.cos(2 * np.pi * i * x / helper.L)
135+
136+
u_hat = helper.transform(u)
137+
138+
weights = helper.get_integration_weights()
139+
integral = weights @ u_hat
140+
integral_ref = coeffs[0, 0] * helper.L
141+
142+
assert np.isclose(integral, integral_ref, atol=1e-7), abs(integral_ref - integral)
143+
144+
115145
@pytest.mark.base
116146
@pytest.mark.parametrize('N', [4, 32])
117147
@pytest.mark.parametrize('v', [0, 4.78])
@@ -141,4 +171,5 @@ def test_tau_method(N, v):
141171
# test_tau_method(6, 1)
142172
# test_transform(True)
143173
# test_transform(False)
144-
test_transform_cupy(4)
174+
# test_transform_cupy(4)
175+
test_integral_whole_interval(0, 2, 90)

0 commit comments

Comments
 (0)