Non-uniform grids
By employing non-uniform grids, more effort can be concentrated to those parts where the function is expected to vary more, hopefully leading to a better approximation. StaggeredFiniteDifferences
supports both uniform and non-uniform node distributions; in the former case, the node locations are stored as an AbstractRange
, in the latter, as any other kind of AbstractVector
.
Integrals
Since the quadrature weights in the non-uniform case are location-dependent, they are baked into to the expansion coefficients (as in the FE-DVR case), such that
where $f_i$ is the $i$th expansion coefficient of $f(x)$. In the uniform case, the expansion coefficients instead equal the function value at the nodes, which means
To be sure that you always get the right integration weight, use the metric to perform integrals, e.g.
is computed using
integral = sum(S*f)
where the metric matrix can be found using
S = B'B
Similarly, integrals of the kind
are easily computed as
integral = dot(f, S, g)
Example
We start by constructing two StaggeredFiniteDifferences
grids, one uniform and one log–linear, of approximately the same extent (at least including $r=30$):
julia> N = 50
50
julia> rmax = 30.0
30.0
julia> B1 = StaggeredFiniteDifferences(rmax, N)
Staggered finite differences basis {Float64} on 0.0..30.606060606060606 with 50 points spaced by ρ = 0.6060606060606061
julia> ρmin = 0.001 # Min step-size
0.001
julia> ρmax = 1.0 # Max step-size
1.0
julia> α = 0.1 # Step-size change rate
0.1
julia> B2 = StaggeredFiniteDifferences(ρmin, ρmax, α, rmax)
Staggered finite differences basis {Float64} on 0.0..31.475628188861677 with 103 points
As mentioned above, the metrics are different in the uniform and non-uniform cases:
julia> S1 = B1'B1
UniformScaling{Float64}
0.6060606060606061*I
julia> S2 = B2'B2
UniformScaling{Bool}
true*I
Since the step-size is changing for non-uniform grids, step
is ill-defined, but returns unity to simplify the implementation of the differential operators (the coefficients are divided by step(B)^k
, where k
is the order of the differential; since that is already accounted for in the case of non-uniform grids by scaling the coefficients, 1^k
does not change the result):
julia> step(B1)
0.6060606060606061
julia> step(B2)
1.0
The node locations and weights are shown in the figure below:
The basis functions become asymmetric in the non-uniform case:
julia> # Compute basis functions on a dense grid
ξ = 10.0 .^ range(-3, stop=log10(25), length=1000);
julia> χ1 = B1[ξ, :];
julia> χ2 = B2[ξ, :];
We then expand the function
on both grids and compare the error:
julia> f = x -> sin(2π*x/rmax)*exp(-4x/rmax)
#47 (generic function with 1 method)
julia> xx1 = axes(B1,1)
Inclusion(0.0..30.606060606060606)
julia> c1 = B1 \ f.(xx1);
julia> xx2 = axes(B2,1)
Inclusion(0.0..31.475628188861677)
julia> c2 = B2 \ f.(xx2);
julia> f1 = χ1*c1;
julia> f2 = χ2*c2;
julia> fe = f.(ξ);