B-spline interpolation structure
Once the underlying data is in the correct format, we can start defining B-spline structures which we will use later to define the interpolation functions.
One dimensional structures
For the one dimensional case, the structures LinearBSpline
and CubicBSpline
are available. They contain all relevant values to define linear and cubic B-spline interpolation functions corresponding to linear and cubic B-spline interpolation. These are:
x
: A vector of values in x-direction.Delta
: The length of a single patch in the given data set. A patch is the area between two consecutivex
values.Delta
corresponds to the distance between two consecutive values in x-direction. As we are only considering Cartesian grids,Delta
is equal for all patches.Q
: A vector that contains the control points.IP
: The coefficients matrix.
To populate the structure, the outer constructor functions LinearBSpline(data_path)
and CubicBSpline(data_path)
are implemented. These constructors use the files in data_path
to obtain the values which will be stored in the corresponding structure, as well as LinearBSpline(x,y)
and CubicBSpline(x,y)
which use given vectors x
and y
.
To get a better idea of the constructor functions, we consider the example rhine_data_cubic-nak.jl from the examples folder of this repo. This particular example reads one dimensional bottom topography data from a .txt
file and constructs a cubic B-spline interpolation with not-a-knot end condition and smoothing of the data.
# Include packages
using TrixiBottomTopography
# Define data path
root_dir = pkgdir(TrixiBottomTopography)
data = joinpath(root_dir, "examples", "data", "rhine_data_1d_20_x.txt")
# Define B-spline structure
spline_struct = CubicBSpline(data; end_condition = "not-a-knot", smoothing_factor = 999)
CubicBSpline{Vector{Float64}, Float64, Vector{Float64}, Matrix{Int64}}([357000.0, 357020.0, 357040.0, 357060.0, 357080.0, 357100.0, 357120.0, 357140.0, 357160.0, 357180.0 … 357800.0, 357820.0, 357840.0, 357860.0, 357880.0, 357900.0, 357920.0, 357940.0, 357960.0, 357980.0], 20.0, [46.91426684256018, 47.397904854124576, 46.99689666444544, 46.50889411070535, 46.73154903008692, 46.862924837363984, 46.74903591362994, 46.40702226591983, 46.31904390610859, 46.654018220049146 … 39.07284424693978, 39.1231081223708, 39.13738000648763, 39.136760208084766, 39.10325245367299, 39.08822484618267, 39.09410545889054, 39.1030246596446, 39.08484073202447, 39.009411959609665], [-1 3 -3 1; 3 -6 3 0; -3 0 3 0; 1 4 1 0])
For the cubic case, we can also set the optional parameters end_condition
, which defines (as the name suggests) the end condition of the spline. Available end conditions are the not-a-knot
and the free
end condition. By default, end_condition
is set to free
. If you are not familiar with the differences and influence of these end conditions, see Chapter 1 of
- Quentin Agrapart & Alain Batailly (2020), Cubic and bicubic spline interpolation in Python. hal-03017566v2.
Besides the end condition, we can also specify a smoothing_factor
for the cubic B-spline. This smoothing parameter defines a trade-off for the resulting cubic B-spline interpolation between how well the B-spline models (or fits) the original data and minimizing the curvature by defining new y
values. This procedure is called spline smoothing. There is no general approach to determine which smoothing_factor
is best suited for a given problem. It must be determined by the user via trial and error. However, as a general rule, the larger the smoothing factor, the less curvature will be present in the resulting B-spline. To understand the underlying maths of the smoothing spline procedure, please see:
- Germán Rodríguez (2001), Smoothing and non-parametric regression
Two dimensional structures
For the two dimensional case, the structures BilinearBSpline
and BicubicBSpline
are implemented, which contain all relevant values to define bilinear and bicubic B-spline interpolation functions. These are:
x
: Vector of values in x-direction.y
: Vector of values in y-direction.Delta
: Length of one side of a single patch in the given data set. A patch is the area between two consecutivex
andy
values.Delta
corresponds to the distance between two consecutive values in x-direction. The implementation only considers Cartesian grids, soDelta
is equal for all patches in x and y-direction.Q
: Matrix which contains the control points.IP
: Coefficients matrix.
To populate the structure, the outer constructor functions BilinearBSpline(data_path)
and BicubicBSpline(data_path)
are implemented. They use the files in data_path
to obtain the values which will be stored in the corresponding structure. Further, BilinearBSpline(x,y,z)
and BicubicBSpline(x,y,z)
which use given vectors x
and y
and matrix z
. The x
, y
and z
data are organized in the following form:
\[\begin{aligned} \begin{matrix} & & x_1 & x_2 & ... & x_n\\ & & & & &\\ y_1 & & z_{11} & z_{12} & ... & z_{1n}\\ y_1 & & z_{21} & z_{22} & ... & z_{2n}\\ \vdots & & \vdots & \vdots & \ddots & \vdots\\ y_m & & z_{m1} & z_{m2} & ... & z_{mn} \end{matrix} \end{aligned}\]
To better understand the constructor functions, we consider the example rhine_data_bicubic-nak.jl of this repo. This example reads in two dimensional bottom topography data from a .txt
file and creates a bicubic B-spline interpolation with not-a-knot end condition and smoothing of the data.
# Include packages
using TrixiBottomTopography
# Define data path
root_dir = pkgdir(TrixiBottomTopography)
data = joinpath(root_dir, "examples", "data", "rhine_data_2d_20.txt")
# Define B-spline structure
spline_struct = BicubicBSpline(data; end_condition = "not-a-knot", smoothing_factor = 9999)
BicubicBSpline{Vector{Float64}, Vector{Float64}, Float64, Matrix{Float64}, Matrix{Int64}}([357000.0, 357020.0, 357040.0, 357060.0, 357080.0, 357100.0, 357120.0, 357140.0, 357160.0, 357180.0 … 357800.0, 357820.0, 357840.0, 357860.0, 357880.0, 357900.0, 357920.0, 357940.0, 357960.0, 357980.0], [5.646019e6, 5.646039e6, 5.646059e6, 5.646079e6, 5.646099e6, 5.646119e6, 5.646139e6, 5.646159e6, 5.646179e6, 5.646199e6 … 5.646819e6, 5.646839e6, 5.646859e6, 5.646879e6, 5.646899e6, 5.646919e6, 5.646939e6, 5.646959e6, 5.646979e6, 5.646999e6], 20.0, [46.600451320002854 47.282399695552705 … 47.09971997322787 47.10735082392629; 45.82016087061047 46.502109246160636 … 46.963741530201055 46.97137238089921; … ; 45.99186284178017 45.686484464847375 … 39.098457400873656 39.23988800954714; 46.243985904572256 45.938607527639306 … 39.06537978820518 39.206810396878865], [-1 3 -3 1; 3 -6 3 0; -3 0 3 0; 1 4 1 0])
For the bicubic spline, we can set the optional parameters end_condition
, which defines (as in the one dimensional case) the end condition of the spline. Again, the available end conditions are the not-a-knot
and the free
end condition. By default, end_condition
is set to free
. If you are not familiar with the differences and influence of these end conditions, see Chapter 2 of
- Quentin Agrapart & Alain Batailly (2020), Cubic and bicubic spline interpolation in Python. hal-03017566v2
Besides the end condition, we can also specify a smoothing_factor
for the bicubic spline. Just as in the one dimensional constructor, this smoothing provides a trade-off of the bicubic B-spline interpolation between how well it models (or fits) the original topography data and minimizing the curvature of the spline by defining new z
values. This procedure is called thin plate spline. There is no general approach to which smoothing_factor
is best suited for the problem, and it must be determined via trial and error by the user. To understand the underlying maths of the thin plate spline, please see:
- Gianluca Donato and Serge Belongie (2001), Approximate Thin Plate Spline Mappings DOI: 10.1007/3-540-47977-5_2