src/splines.f90: add mollifier_1d

This commit is contained in:
Michele Guerini Rocco 2025-02-13 09:54:28 +01:00
parent 1c000db14b
commit 98a855bdaa
Signed by: rnhmjoj
GPG Key ID: BFBAF4C975F76450

View File

@ -3,6 +3,8 @@
!
! `spline_1d` and `spline_2d` are wrapper around the DIERCKX cubic splines,
! `linear_1d` is a linear interpolation.
! `mollifier_1d` is a smooth interpolation scheme based on a C² mollifier
module splines
use const_and_precisions, only : wp_
@ -57,8 +59,19 @@ module splines
procedure :: raw_eval => linear_1d_raw_eval
end type
! A 1D smooth interpolation based on a C² mollifier
type mollifier_1d
integer :: ndata ! Number of data points
real(wp_), allocatable :: xdata(:) ! X data
real(wp_), allocatable :: ydata(:) ! Y data
real(wp_) :: w ! kernel width
contains
procedure :: init => mollifier_1d_init
procedure :: eval => mollifier_1d_eval
end type
private
public spline_1d, spline_2d, linear_1d
public spline_1d, spline_2d, linear_1d, mollifier_1d
contains
@ -495,4 +508,137 @@ contains
y = self%ydata(i) + dy/dx * (x - self%xdata(i))
end function linear_1d_raw_eval
subroutine mollifier_1d_init(self, x, y, width)
! Initialises the mollifier
! subroutine arguments
class(mollifier_1d), intent(inout) :: self
real(wp_), dimension(:), intent(in) :: x, y
real(wp_), intent(in) :: width
self%ndata = size(x)
self%xdata = x
self%ydata = y
self%w = width
end subroutine mollifier_1d_init
pure function mollifier_1d_eval(self, x, order) result(y)
! Evaluates the interpolated data, or the derivative
! of given `order`, at x
use utils, only : locate
! subroutine arguments
class(mollifier_1d), intent(in) :: self
real(wp_), intent(in) :: x
integer, optional, intent(in) :: order
real(wp_) :: y
! local variables
real(wp_) :: m, m1, q, a, b
real(wp_) :: J, K
integer :: i, left, right
integer :: order_
order_ = 0
if (present(order)) order_ = order
! We compute the convolution f(x)g((x-x)/w)/w dx
! where f is a linear interpolation of the data (x, y),
! g(x) = 70(1/2 + x/2)³(1/2 - x/2)³ χ_[1,+1](x)
!
! The integral can be rewritten as:
! I(x) = ¹ f(x-tw)g(t) dt
! = Σ_i _aᵢ^bᵢ (mᵢ(x-tw) + qᵢ)g(t) dt
! where the sum is over the samples within [x-w, x+w] with
! aᵢ = max((x-xᵢ)/w, -1)
! bᵢ = min((x-xᵢ])/w, +1)
!
! The convolution reduces to only 2 integrals:
! I(x) = Σ_i { (mᵢx + qᵢ)Jᵢ - mᵢw Kᵢ}
! where:
! Jᵢ = _aᵢ^bᵢ g(t) dt,
! Kᵢ = _aᵢ^bᵢ tg(t) dt.
! Find data within [x-w, x+w]
call locate(self%xdata, x - self%w, left)
call locate(self%xdata, x + self%w, right)
! if x(left) = x-w increase left by one to get
! the line segment actually within the window.
if (x - self%w == self%xdata(left)) left = left + 1
y = 0
do i = left+1, right+1
! Compute f
if (i <= 1) then
! f=y(1) for xx(1)
m = 0
q = self%ydata(1)
a = max((x-self%xdata(1))/self%w, -1.0)
b = 1
else if (i == self%ndata + 1) then
! f=y(n) for xx(n)
if (order_ == 2) exit
m = 0
q = self%ydata(self%ndata)
a = -1
b = min((x-self%xdata(self%ndata))/self%w, +1.0)
else
! linear interpolation: f(x) = mx + q
m = (self%ydata(i) - self%ydata(i-1)) / (self%xdata(i) - self%xdata(i-1))
q = self%ydata(i-1) - m * self%xdata(i-1)
a = max((x-self%xdata(i))/self%w, -1.0)
b = min((x-self%xdata(i-1))/self%w, +1.0)
end if
! Convolve with mollifier
select case (order_)
case (0) ! Compute f*g
J = g_integral(b) - g_integral(a)
K = g_x_integral(b) - g_x_integral(a)
y = y + (m*x + q)*J - (m*self%w)*K
case (1) ! Compute f'*g
J = g_integral(b) - g_integral(a)
y = y + m*J
case (2) ! Compute f"*g
m1 = 0
if (i < size(self%xdata)) &
m1 = (self%ydata(i+1) - self%ydata(i)) / (self%xdata(i+1) - self%xdata(i))
y = y + g((x-self%xdata(i))/self%w)/self%w * (m1 - m)
end select
end do
contains
pure function g(x)
! A C²_c([-1,1]) polynomial mollifier
real(wp_), intent(in) :: x
real(wp_) :: g
if (abs(x) >= 1) then
g = 0
else
g = 35 * (1 + x)**3 * (1 - x)**3 / 32
end if
end function
pure function g_integral(x)
! Returns ^x g(t)dt
real(wp_), intent(in) :: x
real(wp_) :: g_integral
g_integral = x*(x**2*(x**2*(21 - 5*x**2) - 35) + 35)/32
end function
pure function g_x_integral(x)
! Returns ^x tg(t)dt
real(wp_), intent(in) :: x
real(wp_) :: g_x_integral
g_x_integral = -35*x**2*(x**2 - 2)*(x**2*(x**2 - 2) + 2)/256
end function
end function mollifier_1d_eval
end module splines