# coding=utf-8 # SPDX-FileCopyrightText: Copyright (c) 2022 The torch-harmonics Authors. All rights reserved. # SPDX-License-Identifier: BSD-3-Clause # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # import numpy as np def _precompute_latitudes(nlat, grid="equiangular"): r""" Convenience routine to precompute latitudes """ # compute coordinates if grid == "legendre-gauss": xlg, wlg = legendre_gauss_weights(nlat) elif grid == "lobatto": xlg, wlg = lobatto_weights(nlat) elif grid == "equiangular": xlg, wlg = clenshaw_curtiss_weights(nlat) else: raise ValueError("Unknown grid") lats = np.flip(np.arccos(xlg)).copy() wlg = np.flip(wlg).copy() return lats, wlg def legendre_gauss_weights(n, a=-1.0, b=1.0): r""" Helper routine which returns the Legendre-Gauss nodes and weights on the interval [a, b] """ xlg, wlg = np.polynomial.legendre.leggauss(n) xlg = (b - a) * 0.5 * xlg + (b + a) * 0.5 wlg = wlg * (b - a) * 0.5 return xlg, wlg def lobatto_weights(n, a=-1.0, b=1.0, tol=1e-16, maxiter=100): r""" Helper routine which returns the Legendre-Gauss-Lobatto nodes and weights on the interval [a, b] """ wlg = np.zeros((n,)) tlg = np.zeros((n,)) tmp = np.zeros((n,)) # Vandermonde Matrix vdm = np.zeros((n, n)) # initialize Chebyshev nodes as first guess for i in range(n): tlg[i] = -np.cos(np.pi*i / (n-1)) tmp = 2.0 for i in range(maxiter): tmp = tlg vdm[:,0] = 1.0 vdm[:,1] = tlg for k in range(2, n): vdm[:, k] = ( (2*k-1) * tlg * vdm[:, k-1] - (k-1) * vdm[:, k-2] ) / k tlg = tmp - ( tlg*vdm[:, n-1] - vdm[:, n-2] ) / ( n * vdm[:, n-1]) if (max(abs(tlg - tmp).flatten()) < tol ): break wlg = 2.0 / ( (n*(n-1))*(vdm[:, n-1]**2)) # rescale tlg = (b - a) * 0.5 * tlg + (b + a) * 0.5 wlg = wlg * (b - a) * 0.5 return tlg, wlg def clenshaw_curtiss_weights(n, a=-1.0, b=1.0): r""" Computation of the Clenshaw-Curtis quadrature nodes and weights. This implementation follows [1] Joerg Waldvogel, Fast Construction of the Fejer and Clenshaw-Curtis Quadrature Rules; BIT Numerical Mathematics, Vol. 43, No. 1, pp. 001–018. """ assert(n > 1) tcc = np.cos(np.linspace(np.pi, 0, n)) if n == 2: wcc = np.array([1., 1.]) else: n1 = n - 1 N = np.arange(1, n1, 2) l = len(N) m = n1 - l v = np.concatenate([2 / N / (N-2), 1 / N[-1:], np.zeros(m)]) v = 0 - v[:-1] - v[-1:0:-1] g0 = -np.ones(n1) g0[l] = g0[l] + n1 g0[m] = g0[m] + n1 g = g0 / (n1**2 - 1 + (n1%2)) wcc = np.fft.ifft(v + g).real wcc = np.concatenate((wcc, wcc[:1])) # rescale tcc = (b - a) * 0.5 * tcc + (b + a) * 0.5 wcc = wcc * (b - a) * 0.5 return tcc, wcc def fejer2_weights(n, a=-1.0, b=1.0): r""" Computation of the Fejer quadrature nodes and weights. This implementation follows [1] Joerg Waldvogel, Fast Construction of the Fejer and Clenshaw-Curtis Quadrature Rules; BIT Numerical Mathematics, Vol. 43, No. 1, pp. 001–018. """ assert(n > 2) tcc = np.cos(np.linspace(np.pi, 0, n)) n1 = n - 1 N = np.arange(1, n1, 2) l = len(N) m = n1 - l v = np.concatenate([2 / N / (N-2), 1 / N[-1:], np.zeros(m)]) v = 0 - v[:-1] - v[-1:0:-1] wcc = np.fft.ifft(v).real wcc = np.concatenate((wcc, wcc[:1])) # rescale tcc = (b - a) * 0.5 * tcc + (b + a) * 0.5 wcc = wcc * (b - a) * 0.5 return tcc, wcc