push!(LOAD_PATH,joinpath(@__DIR__, "../src/"))
using LinearAlgebra
using StableApproxEPW

Target of the approximation problem

We consider the Helmholtz solution in the unit disk, with wavenumber

k = 5;

We need to define the maximum Fourier mode number P in the approximation target:

P = 25;

The target approximation will be of the form

\[u = \mathbf{x} ↦ \sum_{|p| \leq P} u_p b_{p}(\mathbf{x}).\]

Next we construct the vector of coefficients in the basis b_p for p in [-P,P]:

U = zeros(ComplexF64, 2P+1);
U[P+1]     = 0.5;  # This is the constant mode (`p=0`)
U[P+1 + P] = 1im;  # This is mode `p = P`

Here the only non-zero coefficients are $u_0 = \frac{1}{2}$ and $u_P = \imath$ so that

\[u = \frac{1}{2} b_{0} + \imath b_{P}.\]

The target of the approximation problem can then be constructed as:

u = solution_surrogate(U; k=k);

$u$ can be evaluated at any (r,θ) point. Alternatively, the target $u$ could have been a single mode. For instance, to get the circular wave with mode number p=15, simply set $u = bp(15; k=k)$. Of course, any other function (defined in polar coordinates) can be defined by the user as target of the approximation problem.

Reconstruction method

The approximation will be reconstructed by sampling the target on the boundary of the unit disk. To do so, we need to know now the dimension N of the approximation sets that we are going to use:

N = 100;

We can determine the number of sampling nodes necessary for a successful reconstruction, based on N, P (to avoid aliasing) and the oversampling ratio η:

S = number_of_boundary_sampling_nodes(N; η=2, P=P);

The (equispaced) boundary nodes are then constructed as:

X = boundary_sampling_nodes(S);

The right-hand-side of the linear system can then be readily constructed:

b = samples_from_nodes(u, X);

Approximation with propagative plane waves

We construct the approximation set of PPW:

Φppw = approximation_set(N; k=k);

The matrix and its (SVD) factorization

Appw = samples_from_nodes(Φppw, X);
iAppw = RegularizedSVDPseudoInverse(Appw; ϵ=1e-14);

The coefficients of the approximation are computed:

ξppw = solve_via_regularizedSVD(iAppw, b);
ũppw = (r,θ) -> sum([ξi * ϕi(r,θ) for (ξi, ϕi) in zip(ξppw, Φppw)]);

Absolute error function

eppw = (r,θ) -> ũppw(r,θ) - u(r,θ); eppw(1,π/2)
0.3953438357519964 + 0.00013750791549692318im

Let's compute the relative residual:

resppw = norm(Appw * ξppw - b) / norm(b)
0.9698191572780571

We did not obtained any accuracy whatsoever! The reason is that the coefficients are too large:

nrmppw = norm(ξppw) / norm(U)
4.8709350760371086e10

Approximation with evanescent plane waves

We construct the approximation set of EPW:

Φepw = approximation_set(N, P, sobol_sampling; k=k);

It is possible to choose other types of sampling methods, instead of sobol_sampling, for instance uniform_sampling and random_sampling. The matrix and its (SVD) factorization

Aepw = samples_from_nodes(Φepw, X);
iAepw = RegularizedSVDPseudoInverse(Aepw; ϵ=1e-14);

The coefficients of the approximation are computed:

ξepw = solve_via_regularizedSVD(iAepw, b);
ũepw = (r,θ) -> sum([ξi * ϕi(r,θ) for (ξi, ϕi) in zip(ξepw, Φepw)]);

Absolute error function

eepw = (r,θ) -> ũepw(r,θ) - u(r,θ); eepw(1,π/2)
1.985829578554643e-8 - 1.6459998561442484e-8im

Let's compute the relative residual:

resepw = norm(Aepw * ξepw - b) / norm(b)
6.539282045362622e-8

We get almost 8 digits of accuracy! The size of the coefficients remains quite high:

nrmepw = norm(ξepw) / norm(U)
687584.6485423842

Down to machine precision

Let's double the number of EPW

N = 200
200

The above approximation process can be obtained much more rapidly with the following convenience function

_, ξepw, ũepw, resepw, nrmepw, eepw = Dirichlet_sampling(k, U, N; smpl_type=sobol_sampling);

One can check that we obtain now a relative residual very close to machine precision (13 digits of accuracy):

resepw
1.389777192737946e-13

The size of the coefficients is also greatly reduced:

nrmepw
101.60136124586003

This page was generated using Literate.jl.