Skip to content

Commit b694572

Browse files
committed
TL: added second order derivation matrix for LagrangeApproximation
1 parent 84c731c commit b694572

File tree

2 files changed

+71
-12
lines changed

2 files changed

+71
-12
lines changed

qmat/lagrange.py

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ class LagrangeApproximation(object):
116116
n : int (property)
117117
The number of points
118118
119+
References
120+
----------
119121
.. [1] Berrut, J. P., & Trefethen, L. N. (2004).
120-
"Barycentric Lagrange interpolation." SIAM review, 46(3), 501-517.
122+
"Barycentric Lagrange interpolation." SIAM review, 46(3), 501-517.
123+
URL: https://doi.org/10.1137/S0036144502417715
121124
"""
122125

123126
def __init__(self, points, weightComputation='AUTO', scaleWeights=False, scaleRef='MAX', fValues=None):
@@ -325,10 +328,11 @@ def getIntegrationMatrix(self, intervals, numQuad='FEJER'):
325328

326329
return PInter
327330

328-
def getDerivationMatrix(self):
331+
def getDerivationMatrix(self, order=1):
329332
r"""
330-
Generate the first order derivation matrix :math:`D^{(1)}` based on the
331-
Lagrange interpolant, such that
333+
Generate derivation matrix of first or second order (or both) based on
334+
the Lagrange interpolant.
335+
The first order differentiation matrix :math:`D^{(1)}` approximates
332336
333337
.. math::
334338
D^{(1)} u \simeq \frac{du}{dx}
@@ -343,11 +347,48 @@ def getDerivationMatrix(self):
343347
.. math::
344348
D^{(1)}_{jj} = -\sum_{i \neq j} D^{(1)}_{ij}`
345349
350+
The second order differentiation matrix :math:`D^{(2)}` approximates
351+
352+
.. math::
353+
D^{(2)} u \simeq \frac{d^2u}{dx^2}
354+
355+
on the interpolation points. The formula is :
356+
357+
.. math::
358+
D^{(1)}_{ij} = -2\frac{w_j/w_i}{x_i-x_j}\left[
359+
\frac{1}{x_i-x_j} + \sum_{k \neq i}\frac{w_k/w_i}{x_i-x_k}
360+
\right]
361+
362+
for :math:`i \neq j` and
363+
364+
.. math::
365+
D^{(2)}_{jj} = -\sum_{i \neq j} D^{(2)}_{ij}`
366+
367+
⚠️ If you want a derivation matrix with many points (~1000 or more),
368+
favor the use of weightComputation="STABLE" when initializing
369+
the LagrangeApproximation object. If not, some (very small) weights
370+
could be approximated by zeros, which would make the computation
371+
of the derivation matrices fail ...
372+
373+
Note
374+
----
375+
There is a typo in the formula for :math:`D^{(2)}` given in the paper
376+
of Berrut and Trefethen. The formula above is the correct one.
377+
378+
Parameters
379+
----------
380+
order : int or str, optional
381+
The order of the derivation matrix, use "ALL" to retrieve both.
382+
The default is 1.
383+
346384
Returns
347385
-------
348-
D1 : np.2darray
349-
Derivation matrix.
386+
D : np.2darray or tuple of np.2darray
387+
Derivation matrix. If order="ALL", return a tuple containing all
388+
derivations matrix in increasing derivation order.
350389
"""
390+
if order not in [1, 2, "ALL"]:
391+
raise NotImplementedError(f"order={order}")
351392
w = self.weights
352393
x = self.points
353394

@@ -358,6 +399,17 @@ def getDerivationMatrix(self):
358399
base = w[None, :]/w[:, None]
359400
base *= iDiff
360401

361-
D1 = base
362-
np.fill_diagonal(D1, -D1.sum(axis=-1))
363-
return D1
402+
if order in [1, "ALL"]:
403+
D1 = base.copy()
404+
np.fill_diagonal(D1, -D1.sum(axis=-1))
405+
if order in [2, "ALL"]:
406+
D2 = -2*base
407+
D2 *= iDiff + base.sum(axis=-1)[:, None]
408+
np.fill_diagonal(D2, -D2.sum(axis=-1))
409+
410+
if order == 1:
411+
return D1
412+
elif order == 2:
413+
return D2
414+
else:
415+
return D1, D2

tests/test_2_lagrange.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,17 @@ def testDerivation(nNodes, weightComputation):
101101
nodes = np.sort(np.random.rand(nNodes))
102102
approx = LagrangeApproximation(nodes, weightComputation=weightComputation)
103103

104-
D = approx.getDerivationMatrix()
104+
D1, D2 = approx.getDerivationMatrix(order="ALL")
105+
106+
assert np.allclose(D1, approx.getDerivationMatrix())
107+
assert np.allclose(D2, approx.getDerivationMatrix(order=2))
105108

106109
polyCoeffs = np.random.rand(nNodes)
107110
polyNodes = np.polyval(polyCoeffs, nodes)
108-
polyDeriv = np.polyval(np.polyder(polyCoeffs), nodes)
111+
polyDeriv1 = np.polyval(np.polyder(polyCoeffs), nodes)
112+
polyDeriv2 = np.polyval(np.polyder(polyCoeffs, m=2), nodes)
113+
114+
assert np.allclose(polyDeriv1, D1 @ polyNodes)
109115

110-
assert np.allclose(polyDeriv, D @ polyNodes)
116+
assert np.allclose(polyDeriv2, D2 @ polyNodes)
117+
assert np.allclose(polyDeriv2, D1 @ D1 @ polyNodes)

0 commit comments

Comments
 (0)