!> !> @file smooth.f90 !> !> @brief !> !> @copyright !> Copyright (©) 2021 EPFL (Ecole Polytechnique Fédérale de Lausanne) !> SPC (Swiss Plasma Center) !> !> spclibs is free software: you can redistribute it and/or modify it under !> the terms of the GNU Lesser General Public License as published by the Free !> Software Foundation, either version 3 of the License, or (at your option) !> any later version. !> !> spclibs is distributed in the hope that it will be useful, but WITHOUT ANY !> WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS !> FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. !> !> You should have received a copy of the GNU Lesser General Public License !> along with this program. If not, see . !> !> @authors !> (in alphabetical order) !> @author Trach-Minh Tran !> subroutine smooth ( x, y, dy, npoint, s, v, a, sfp ) !************************************************************************* ! !! SMOOTH constructs the cubic smoothing spline to given data. ! ! Discussion: ! ! The data is of the form ! ! (x(i),y(i)), i=1,...,npoint, ! ! The cubic smoothing spline has as small a second derivative as ! possible while ! ! s(f)=sum( ((y(i)-f(x(i)))/dy(i))**2 , i=1,...,npoint ) <= s . ! ! Reference: ! ! Carl DeBoor, ! A Practical Guide to Splines, ! Springer Verlag. ! ! Parameters: ! ! input ! ! Input, real ( kind = 8 ) X(NPOINT), the abscissas, assumed to be strictly ! increasing . ! ! Input, real ( kind = 8 ) Y(NPOINT), the corresponding ordinates. ! ! dy(1),...,dy(npoint) estimate of uncertainty in data, assumed ! to be positive . ! ! npoint.....number of data points, assumed greater than 1 ! ! s.....upper bound on the discrete weighted mean square distance of ! the approximation f from the data . ! ! work arrays: ! ! v of size (npoint,7) ! a of size (npoint,4) ! ! output ! ! a(.,1).....contains the sequence of smoothed ordinates . ! a(i,j)=d**(j-1)f(x(i)), j=2,3,4, i=1,...,npoint-1 , i.e., the ! first three derivatives of the smoothing spline f at the ! left end of each of the data intervals . ! Warning . . . a would have to be transposed before it ! could be used in ppvalu . ! ! Method: ! ! the matrices q-transp*d and q-transp*d**2*q are constructed in ! SETUPQ from x and dy , as is the vector qty=q-transp*y . ! then, for given p , the vector U is determined in CHOL1D as ! the solution of the linear system ! (6(1-p)q-transp*d**2*q+p*r)u =qty . ! from u , the smoothing spline f (for this choice of smoothing par- ! ameter p ) is obtained in the sense that ! f(x(.)) = y-6(1-p)d**2*q*u and ! (d**2)f(x(.)) = 6*p*u . ! ! the smoothing parameter p is found (if possible) so that ! sf(p) = s , ! with sf(p)=s(f) , where f is the smoothing spline as it depends ! on p . if s=0, then p = 1 . if sf(0) <= s , then p = 0 . ! otherwise, the secant method is used to locate an appropriate p in ! the open interval (0,1) . specifically, ! p(0)=0, p(1) = (s-sf(0))/dsf ! with dsf=-24*u-transp*r*u a good approximation to d(sf(0)) = dsf ! +60*(d*q*u)-transp*(d*q*u) , and u as obtained for p=0 . ! after that, for n=1,2,... until sf(p(n)) <= 1.01*s, do.... ! determine p(n+1) as the point at which the secant to sf at the ! points p(n) and p(n-1) takes on the value s . ! if p(n+1) >= 1 , choose instead p(n+1) as the point at which ! the parabola sf(p(n))*((1-.)/(1-p(n)))**2 takes on the value s. ! ! Note that, in exact arithmetic, always p(n+1) < p(n) , hence ! sf(p(n+1)) < sf(p(n)) . therefore, also stop the iteration, ! with final p=1 , in case sf(p(n+1)) >= sf(p(n)) . ! implicit none integer npoint real ( kind = 8 ) a(npoint,4) real ( kind = 8 ) change real ( kind = 8 ) dy(npoint) integer i real ( kind = 8 ) p real ( kind = 8 ) prevp real ( kind = 8 ) prevsf real ( kind = 8 ) s real ( kind = 8 ) sfp real ( kind = 8 ) utru real ( kind = 8 ) v(npoint,7) real ( kind = 8 ) x(npoint) real ( kind = 8 ) y(npoint) call setupq ( x, dy, y, npoint, v, a(1,4) ) if ( 0.0D+00 < s ) then go to 20 end if 10 continue p = 1.0D+00 call chol1d ( p, v, a(1,4), npoint, a(1,3), a(1,1) ) sfp = 0.0D+00 go to 70 20 continue p = 0.0D+00 call chol1d ( p, v, a(1,4), npoint, a(1,3), a(1,1) ) sfp = 0.0D+00 do i = 1, npoint sfp = sfp + ( a(i,1) * dy(i) )**2 end do sfp = sfp * 36.0D+00 if ( sfp <= s ) then go to 70 end if prevp = 0.0D+00 prevsf = sfp utru = 0.0D+00 do i = 2, npoint utru = utru + v(i-1,4) * ( a(i-1,3) * ( a(i-1,3) + a(i,3) ) + a(i,3)**2 ) end do p = ( sfp - s ) / ( 24.0D+00 * utru ) ! ! Secant iteration for the determination of p starts here. ! 50 continue call chol1d ( p, v, a(1,4), npoint, a(1,3), a(1,1) ) sfp = 0.0D+00 do i = 1, npoint sfp = sfp + ( a(i,1) * dy(i) )**2 end do sfp = sfp * 36.0D+00 * ( 1.0D+00 - p )**2 if ( sfp <= 1.01D+00 * s ) then go to 70 end if if ( prevsf <= sfp ) then go to 10 end if change = ( p - prevp ) / ( sfp - prevsf ) * ( sfp - s ) prevp = p p = p - change prevsf = sfp if ( 1.0D+00 <= p ) then p = 1.0D+00 - sqrt ( s / prevsf ) * ( 1.0D+00 - prevp ) end if go to 50 ! ! The correct value of p has been found. ! Compute polynomial coefficients from q*u (in a(.,1)). ! 70 continue do i = 1, npoint a(i,1) = y(i) - 6.0D+00 * ( 1.0D+00 - p ) * dy(i)**2 * a(i,1) end do do i = 1, npoint a(i,3) = 6.0D+00 * a(i,3) * p end do do i = 1, npoint-1 a(i,4) = ( a(i+1,3) - a(i,3) ) / v(i,4) a(i,2) = ( a(i+1,1) - a(i,1) ) / v(i,4) & - ( a(i,3) + a(i,4) / 3.0D+00 * v(i,4) ) / 2.0D+00 * v(i,4) end do return end