Semi-infinite spectral mappings¶
The rational and algebraic mappings both transform Chebyshev points on $[-1, 1)$ to the semi-infinite physical interval $[0, \infty)$. This notebook compares their point distributions and the convergence of a mapped first-derivative operator.
In [1]:
Copied!
import matplotlib.pyplot as plt
import numpy as np
from aerokit.common.mapping import (
SemiInfiniteAlgebraicMapping,
SemiInfiniteRationalMapping,
)
from aerokit.common.numspectral import ChebCollocation
L = 1.0
mappings = {
"Rational": SemiInfiniteRationalMapping(L),
"Algebraic": SemiInfiniteAlgebraicMapping(L),
}
import matplotlib.pyplot as plt
import numpy as np
from aerokit.common.mapping import (
SemiInfiniteAlgebraicMapping,
SemiInfiniteRationalMapping,
)
from aerokit.common.numspectral import ChebCollocation
L = 1.0
mappings = {
"Rational": SemiInfiniteRationalMapping(L),
"Algebraic": SemiInfiniteAlgebraicMapping(L),
}
Mapping and collocation-point comparison¶
Both mappings include the origin at $\xi=-1$ and approach infinity at $\xi=1$. The latter endpoint is omitted from the plot.
In [2]:
Copied!
xi_plot = np.linspace(-1.0, 0.995, 500)
npts = 24
xi_nodes = ChebCollocation(npts).xi
fig, (ax_map, ax_grid) = plt.subplots(1, 2, figsize=(12, 4))
for name, mapping in mappings.items():
r = mapping.xi_to_x(xi_plot)
ax_map.plot(xi_plot, r, label=name)
r_nodes = mapping.xi_to_x(xi_nodes)
finite = np.isfinite(r_nodes)
ax_grid.plot(r_nodes[finite], np.zeros(finite.sum()), "o", label=name)
ax_map.set(xlabel=r"$\xi$", ylabel=r"$r/L$", ylim=(0, 20), title="Coordinate maps")
ax_map.legend()
ax_grid.set(xlabel=r"$r/L$", title=f"Finite collocation points ($N={npts}$)", yticks=[])
ax_grid.legend()
fig.tight_layout()
xi_plot = np.linspace(-1.0, 0.995, 500)
npts = 24
xi_nodes = ChebCollocation(npts).xi
fig, (ax_map, ax_grid) = plt.subplots(1, 2, figsize=(12, 4))
for name, mapping in mappings.items():
r = mapping.xi_to_x(xi_plot)
ax_map.plot(xi_plot, r, label=name)
r_nodes = mapping.xi_to_x(xi_nodes)
finite = np.isfinite(r_nodes)
ax_grid.plot(r_nodes[finite], np.zeros(finite.sum()), "o", label=name)
ax_map.set(xlabel=r"$\xi$", ylabel=r"$r/L$", ylim=(0, 20), title="Coordinate maps")
ax_map.legend()
ax_grid.set(xlabel=r"$r/L$", title=f"Finite collocation points ($N={npts}$)", yticks=[])
ax_grid.legend()
fig.tight_layout()
First-derivative convergence¶
For $f(r)=\exp(-r^2)$, the exact derivative is $f'(r)=-2r\exp(-r^2)$. ChebCollocation creates only reference-coordinate matrices; each mapping applies the chain rule to obtain the physical derivative matrix.
In [3]:
Copied!
def derivative_error(npts, mapping):
reference = ChebCollocation(npts)
reference.compute_matder(1)
derivative = mapping.transform_derivative_matrices(reference._matder)[:, :, 0]
r = mapping.xi_to_x(reference.xi)
values = np.exp(-r**2)
exact = np.zeros_like(r)
finite = np.isfinite(r)
exact[finite] = -2.0 * r[finite] * values[finite]
return np.sqrt(np.mean((derivative @ values - exact) ** 2))
sizes = np.array([12, 16, 20, 28, 36, 48, 64])
fig, ax = plt.subplots(figsize=(6, 4))
for name, mapping in mappings.items():
errors = [derivative_error(npts, mapping) for npts in sizes]
ax.semilogy(sizes, errors, "o-", label=name)
ax.set(xlabel="Number of collocation points", ylabel="RMS error in $f'(r)$", title="Mapped first-derivative convergence")
ax.grid(True, which="both")
ax.legend();
def derivative_error(npts, mapping):
reference = ChebCollocation(npts)
reference.compute_matder(1)
derivative = mapping.transform_derivative_matrices(reference._matder)[:, :, 0]
r = mapping.xi_to_x(reference.xi)
values = np.exp(-r**2)
exact = np.zeros_like(r)
finite = np.isfinite(r)
exact[finite] = -2.0 * r[finite] * values[finite]
return np.sqrt(np.mean((derivative @ values - exact) ** 2))
sizes = np.array([12, 16, 20, 28, 36, 48, 64])
fig, ax = plt.subplots(figsize=(6, 4))
for name, mapping in mappings.items():
errors = [derivative_error(npts, mapping) for npts in sizes]
ax.semilogy(sizes, errors, "o-", label=name)
ax.set(xlabel="Number of collocation points", ylabel="RMS error in $f'(r)$", title="Mapped first-derivative convergence")
ax.grid(True, which="both")
ax.legend();