LCOV - code coverage report
Current view: top level - interp_1d/private - interp_1d_pm_sg.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 53.6 % 207 111
Test Date: 2025-05-08 18:23:42 Functions: 60.0 % 5 3

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2010  The MESA Team
       4              : !
       5              : !   This program is free software: you can redistribute it and/or modify
       6              : !   it under the terms of the GNU Lesser General Public License
       7              : !   as published by the Free Software Foundation,
       8              : !   either version 3 of the License, or (at your option) any later version.
       9              : !
      10              : !   This program is distributed in the hope that it will be useful,
      11              : !   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              : !   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13              : !   See the GNU Lesser General Public License for more details.
      14              : !
      15              : !   You should have received a copy of the GNU Lesser General Public License
      16              : !   along with this program. If not, see <https://www.gnu.org/licenses/>.
      17              : !
      18              : ! ***********************************************************************
      19              : 
      20              :       module interp_1d_pm_sg  ! piecewise monotonic algorithms
      21              : 
      22              :       implicit none
      23              : 
      24              :       contains
      25              : 
      26              :       ! the following produce piecewise monotonic interpolants
      27              :       ! rather than monotonicity preserving
      28              :       ! this stricter limit never introduces interpolated values exceeding the given values,
      29              :       ! even in places where the given values are not monotonic.
      30              :       ! the downside is reduced accuracy on smooth data compared to the mp routines.
      31              : 
      32              : 
      33           46 :       subroutine mk_pmcub_sg(x, nx, f1, slope_only, nwork, work1, str, ierr)
      34              :          ! make piecewise monotonic cubic interpolant
      35              :          use interp_1d_def
      36              :          integer, intent(in) :: nx       ! length of x vector (nx >= 2)
      37              :          real, intent(in) :: x(:)  ! (nx)    ! junction points, strictly monotonic
      38              :          real, intent(inout), pointer :: f1(:)  ! =(4, nx)  ! data & interpolation coefficients
      39              :          logical, intent(in) :: slope_only
      40              :          integer, intent(in) :: nwork  ! nwork must be >= pm_work_size (see interp_1d_def)
      41              :          real, intent(inout), pointer :: work1(:)  ! =(nx, nwork)
      42              :          character (len=*) :: str
      43              :          integer, intent(out) :: ierr
      44              : 
      45           46 :          real, dimension(:), pointer :: h, s, p
      46              :          integer :: i
      47              :          logical, parameter :: dbg = .true.
      48           46 :          real, pointer :: f(:,:)  ! (4, nx)  ! data & interpolation coefficients
      49           46 :          f(1:4,1:nx) => f1(1:4*nx)
      50              : 
      51              :          include 'formats'
      52              : 
      53           46 :          ierr = 0
      54              : 
      55           46 :          if (nx < 2) then
      56            7 :             return
      57              :          end if
      58              : 
      59           46 :          if (nx == 2) then
      60            0 :             call mk_pmlinear_sg(x, f1, slope_only, nwork, work1, str, ierr)
      61            0 :             return
      62              :          end if
      63              : 
      64           46 :          if (nx == 3) then
      65            1 :             call mk_pmquad_sg(x, f1, slope_only, nwork, work1, str, ierr)
      66            1 :             return
      67              :          end if
      68              : 
      69           45 :          if (nx == 4) then
      70            6 :             call mk_pmcub4_sg(x, f1, slope_only, nwork, work1, str, ierr)
      71            6 :             return
      72              :          end if
      73              : 
      74           39 :          if (nwork < pm_work_size) then
      75            0 :             ierr = -1
      76            0 :             return
      77              :          end if
      78              : 
      79           39 :          h(1:nx) => work1(1:nx)
      80           39 :          s(1:nx) => work1(1+nx:2*nx)
      81           39 :          p(1:nx) => work1(1+2*nx:3*nx)
      82              : 
      83              :          if (dbg) then
      84         4494 :             h(:) = 0; s(:) = 0; p(:) = 0
      85              :          end if
      86              : 
      87         1485 :          do i=1,nx-1
      88         1485 :             h(i) = x(i+1) - x(i)  ! width of interval
      89              :          end do
      90         1485 :          do i = 1, nx-1
      91         1485 :             if (h(i) == 0) then
      92              :                write(*, '(a,1x,2i5,1x,a)')  &
      93            0 :                   'same interpolation x values at', i, i+1, 'for ' // trim(str)
      94            0 :                ierr = -1
      95            0 :                return
      96              :             end if
      97              :          end do
      98              : 
      99         1485 :          do i=1,nx-1
     100         1485 :             s(i) = (f(1,i+1) - f(1,i)) / h(i)  ! slope across interval
     101              :          end do
     102         1446 :          do i=2,nx-1
     103         1446 :             p(i) = (s(i-1)*h(i) + s(i)*h(i-1))/(h(i-1)+h(i))
     104              :             ! slope at i of parabola through i-1, i, and i+1
     105              :          end do
     106         1446 :          do i=2,nx-1
     107              :             f(2,i) = (sign(1.0, s(i-1))+sign(1.0, s(i)))* &
     108         1446 :                         min(abs(s(i-1)), abs(s(i)), 0.5*abs(p(i)))
     109              :             ! "safe" slope at i to ensure monotonic -- see Steffen's paper for explanation.
     110              :          end do
     111              : 
     112           39 :          p(1) = s(1)*(1 + h(1) / (h(1) + h(2))) - s(2) * h(1) / (h(1) + h(2))
     113              :             ! slope at 1 of parabola through 1st 3 points
     114           39 :          if (p(1)*s(1) <= 0) then
     115            0 :             f(2, 1) = 0
     116           39 :          else if (abs(p(1)) > 2*abs(s(1))) then
     117            0 :             f(2, 1) = 2*s(1)
     118              :          else
     119           39 :             f(2, 1) = p(1)
     120              :          end if
     121              : 
     122              :          p(nx) = s(nx-1)*(1 + h(nx-1) / (h(nx-1) + h(nx-2)))  &
     123           39 :                      - s(nx-2)*h(nx-1) / (h(nx-1) + h(nx-2))
     124              :             ! slope at nx of parabola through last 3 points
     125           39 :          if (p(nx)*s(nx-1) <= 0) then
     126            0 :             f(2,nx) = 0
     127           39 :          else if (abs(p(nx)) > 2*abs(s(nx-1))) then
     128            0 :             f(2,nx) = 2*s(nx-1)
     129              :          else
     130           39 :             f(2,nx) = p(nx)
     131              :          end if
     132              : 
     133           39 :          if (slope_only) return
     134              : 
     135         1485 :          do i=1,nx-1
     136         1446 :             f(3,i) = (3*s(i) - 2*f(2,i) - f(2,i+1)) / h(i)
     137         1485 :             f(4,i) = (f(2,i) + f(2,i+1) - 2*s(i)) / (h(i)*h(i))
     138              :          end do
     139           39 :          f(3,nx) = 0
     140           39 :          f(4,nx) = 0
     141              : 
     142           46 :       end subroutine mk_pmcub_sg
     143              : 
     144              : 
     145              :       ! optimize special case for nx = 4
     146            6 :       subroutine mk_pmcub4_sg(x, f1, slope_only, nwork, work1, str, ierr)
     147              :          use interp_1d_def
     148              :          integer, parameter :: nx = 4   ! length of x vector (nx >= 2)
     149              :          real, intent(in)    :: x(:)  ! (nx)    ! junction points, strictly monotonic
     150              :          real, intent(inout), pointer :: f1(:)  ! =(4, nx)  ! data & interpolation coefficients
     151              :          logical, intent(in) :: slope_only
     152              :          integer, intent(in) :: nwork  ! nwork must be >= pm_work_size (see interp_1d_def)
     153              :          real, intent(inout), pointer :: work1(:)  ! =(nx, nwork)
     154              :          character (len=*) :: str
     155              :          integer, intent(out) :: ierr
     156              : 
     157            6 :          real, dimension(:), pointer :: h, s, p
     158              :          integer :: i
     159            6 :          real, pointer :: f(:,:)  ! (4, nx)  ! data & interpolation coefficients
     160            6 :          f(1:4,1:nx) => f1(1:4*nx)
     161              : 
     162            6 :          ierr = 0
     163              : 
     164            6 :          if (nwork < pm_work_size) then
     165            0 :             ierr = -1
     166            0 :             return
     167              :          end if
     168              : 
     169            6 :          h(1:nx) => work1(1:nx)
     170            6 :          s(1:nx) => work1(1+nx:2*nx)
     171            6 :          p(1:nx) => work1(1+2*nx:3*nx)
     172              : 
     173           24 :          do i=1,nx-1
     174           24 :             h(i) = x(i+1) - x(i)  ! width of interval
     175              :          end do
     176           24 :          do i = 1, nx-1
     177           24 :             if (h(i) == 0) then
     178              :                write(*, '(a,1x,2i5,1x,a)')  &
     179            0 :                   'same interpolation x values at', i, i+1, 'for ' // trim(str)
     180            0 :                ierr = -1
     181            0 :                return
     182              :             end if
     183              :          end do
     184              : 
     185           24 :          do i=1,nx-1
     186           24 :             s(i) = (f(1,i+1) - f(1,i)) / h(i)  ! slope across interval
     187              :          end do
     188           18 :          do i=2,nx-1
     189           18 :             p(i) = (s(i-1)*h(i) + s(i)*h(i-1))/(h(i-1)+h(i))
     190              :             ! slope at i of parabola through i-1, i, and i+1
     191              :          end do
     192           18 :          do i=2,nx-1
     193              :             f(2,i) = (sign(1.0, s(i-1))+sign(1.0, s(i)))* &
     194           18 :                         min(abs(s(i-1)), abs(s(i)), 0.5*abs(p(i)))
     195              :             ! "safe" slope at i to ensure monotonic -- see Steffen's paper for explanation.
     196              :          end do
     197              : 
     198            6 :          p(1) = s(1)*(1 + h(1) / (h(1) + h(2))) - s(2) * h(1) / (h(1) + h(2))
     199              :             ! slope at 1 of parabola through 1st 3 points
     200            6 :          if (p(1)*s(1) <= 0) then
     201            0 :             f(2, 1) = 0
     202            6 :          else if (abs(p(1)) > 2*abs(s(1))) then
     203            0 :             f(2, 1) = 2*s(1)
     204              :          else
     205            6 :             f(2, 1) = p(1)
     206              :          end if
     207              : 
     208              :          p(nx) = s(nx-1)*(1 + h(nx-1) / (h(nx-1) + h(nx-2)))  &
     209            6 :                      - s(nx-2)*h(nx-1) / (h(nx-1) + h(nx-2))
     210              :             ! slope at nx of parabola through last 3 points
     211            6 :          if (p(nx)*s(nx-1) <= 0) then
     212            0 :             f(2, nx) = 0
     213            6 :          else if (abs(p(nx)) > 2*abs(s(nx-1))) then
     214            0 :             f(2, nx) = 2*s(nx-1)
     215              :          else
     216            6 :             f(2, nx) = p(nx)
     217              :          end if
     218              : 
     219            6 :          if (slope_only) return
     220              : 
     221           24 :          do i=1,nx-1
     222           18 :             f(3,i) = (3*s(i) - 2*f(2,i) - f(2,i+1)) / h(i)
     223           24 :             f(4,i) = (f(2,i) + f(2,i+1) - 2*s(i)) / (h(i)*h(i))
     224              :          end do
     225            6 :          f(3,nx) = 0
     226            6 :          f(4,nx) = 0
     227              : 
     228            6 :       end subroutine mk_pmcub4_sg
     229              : 
     230              : 
     231              :       ! optimize special case for nx = 3
     232            1 :       subroutine mk_pmquad_sg(x, f1, slope_only, nwork, work1, str, ierr)
     233              :          ! make piecewise monotonic quadratic interpolant
     234              :          use interp_1d_def
     235              :          integer, parameter :: nx = 3
     236              :          real, intent(in)    :: x(:)  ! (nx)    ! junction points, strictly monotonic
     237              :          real, intent(inout), pointer :: f1(:)  ! =(4, nx)  ! data & interpolation coefficients
     238              :          logical, intent(in) :: slope_only
     239              :          integer, intent(in) :: nwork  ! nwork must be >= pm_work_size (see interp_1d_def)
     240              :          real, intent(inout), pointer :: work1(:)  ! =(nx, nwork)
     241              :          character (len=*) :: str
     242              :          integer, intent(out) :: ierr
     243              : 
     244            1 :          real, dimension(:), pointer :: h, s, p
     245              :          integer :: i
     246            1 :          real, pointer :: f(:,:)  ! (4, nx)  ! data & interpolation coefficients
     247            1 :          f(1:4,1:nx) => f1(1:4*nx)
     248              : 
     249            1 :          if (nwork < pm_work_size) then
     250            0 :             ierr = -1
     251            0 :             return
     252              :          end if
     253            1 :          ierr = 0
     254              : 
     255            1 :          h(1:nx) => work1(1:nx)
     256            1 :          s(1:nx) => work1(1+nx:2*nx)
     257            1 :          p(1:nx) => work1(1+2*nx:3*nx)
     258              : 
     259            3 :          do i=1,nx-1
     260            2 :             h(i) = x(i+1) - x(i)  ! width of interval
     261            3 :             if (h(i) == 0) then
     262              :                write(*, '(a,1x,2i5,1x,a)')  &
     263            0 :                   'same interpolation x values at', i, i+1, 'for ' // trim(str)
     264            0 :                ierr = -1
     265            0 :                return
     266              :             end if
     267              :          end do
     268              : 
     269            3 :          do i=1,nx-1
     270            3 :             s(i) = (f(1,i+1) - f(1,i)) / h(i)  ! slope across interval
     271              :          end do
     272            2 :          do i=2,nx-1
     273            2 :             p(i) = (s(i-1)*h(i) + s(i)*h(i-1))/(h(i-1)+h(i))
     274              :             ! slope at i of parabola through i-1, i, and i+1
     275              :          end do
     276            2 :          do i=2,nx-1
     277              :             f(2,i) = (sign(1.0, s(i-1))+sign(1.0, s(i)))* &
     278            2 :                         min(abs(s(i-1)), abs(s(i)), 0.5*abs(p(i)))
     279              :             ! "safe" slope at i to ensure monotonic -- see Steffen's paper for explanation.
     280              :          end do
     281              : 
     282            1 :          p(1) = s(1)*(1 + h(1) / (h(1) + h(2))) - s(2) * h(1) / (h(1) + h(2))
     283              :             ! slope at 1 of parabola through 1st 3 points
     284            1 :          if (sign(1.0,p(1)) /= sign(1.0,s(1))) then
     285            0 :             f(2, 1) = 0
     286            1 :          else if (abs(p(1)) > 2*abs(s(1))) then
     287            0 :             f(2, 1) = 2*s(1)
     288              :          else
     289            1 :             f(2, 1) = p(1)
     290              :          end if
     291              : 
     292              :          p(nx) = s(nx-1)*(1 + h(nx-1) / (h(nx-1) + h(nx-2)))  &
     293            1 :                      - s(nx-2)*h(nx-1) / (h(nx-1) + h(nx-2))
     294              :             ! slope at nx of parabola through last 3 points
     295            1 :          if (sign(1.0,p(nx)) /= sign(1.0,s(nx-1))) then
     296            0 :             f(2, nx) = 0
     297            1 :          else if (abs(p(nx)) > 2*abs(s(nx-1))) then
     298            0 :             f(2, nx) = 2*s(nx-1)
     299              :          else
     300            1 :             f(2, nx) = p(nx)
     301              :          end if
     302              : 
     303            1 :          if (slope_only) return
     304              : 
     305            3 :          do i=1,nx-1
     306            2 :             f(3,i) = (3*s(i) - 2*f(2,i) - f(2,i+1)) / h(i)
     307            3 :             f(4,i) = (f(2,i) + f(2,i+1) - 2*s(i)) / (h(i)*h(i))
     308              :          end do
     309            1 :          f(3,nx) = 0
     310            1 :          f(4,nx) = 0
     311              : 
     312            1 :       end subroutine mk_pmquad_sg
     313              : 
     314              : 
     315              :       ! optimize special case for nx = 2
     316            0 :       subroutine mk_pmlinear_sg(x, f1, slope_only, nwork, work1, str, ierr)
     317              :          use interp_1d_def
     318              :          integer, parameter :: nx = 2
     319              :          real, intent(in)    :: x(:)  ! (nx)    ! junction points, strictly monotonic
     320              :          real, intent(inout), pointer :: f1(:)  ! =(4, nx)  ! data & interpolation coefficients
     321              :          logical, intent(in) :: slope_only
     322              :          integer, intent(in) :: nwork  ! nwork must be >= pm_work_size (see interp_1d_def)
     323              :          real, intent(inout), pointer :: work1(:)  ! =(nx, nwork)
     324              :          character (len=*) :: str
     325              :          integer, intent(out) :: ierr
     326              : 
     327            0 :          real :: h, s
     328            0 :          real, pointer :: f(:,:)  ! (4, nx)  ! data & interpolation coefficients
     329            0 :          f(1:4,1:nx) => f1(1:4*nx)
     330              : 
     331            0 :          ierr = 0
     332              : 
     333            0 :          if (nwork < pm_work_size) then
     334            0 :             ierr = -1
     335            0 :             return
     336              :          end if
     337              : 
     338            0 :          h = x(2) - x(1)  ! width of interval
     339            0 :          if (h == 0) then
     340              :             write(*, '(a,1x,2i5,1x,a)')  &
     341            0 :                   'same interpolation x values at', 1, 2, 'for ' // trim(str)
     342            0 :             ierr = -1
     343            0 :             return
     344              :          end if
     345              : 
     346            0 :          s = (f(1, 2) - f(1, 1)) / h  ! slope across interval
     347            0 :          f(2, 1) = s
     348            0 :          f(2, 2) = 0
     349              : 
     350            0 :          if (slope_only) return
     351              : 
     352            0 :          f(3, 1:2) = 0
     353            0 :          f(4, 1:2) = 0
     354              : 
     355            0 :       end subroutine mk_pmlinear_sg
     356              : 
     357              : 
     358            0 :       subroutine mk_pmcub_uniform_sg(dx, n, f1, slope_only, nwork, work1, str, ierr)
     359              :          ! make piecewise monotonic cubic interpolant on unit spaced mesh
     360              :          use interp_1d_def
     361              :          integer, intent(in) :: n     ! length of vector
     362              :          real, intent(in) :: dx
     363              :          real, intent(inout), pointer :: f1(:)  ! =(4, n)  ! data & interpolation coefficients
     364              :          logical, intent(in) :: slope_only
     365              :          integer, intent(in) :: nwork  ! nwork must be >= pm_work_size (see interp_1d_def)
     366              :          real, intent(inout), pointer :: work1(:)  ! =(nx, nwork)
     367              :          character (len=*) :: str
     368              :          integer, intent(out) :: ierr
     369              : 
     370            0 :          real, dimension(:), pointer :: s, p
     371            0 :          real :: x(2)
     372              :          integer :: i
     373            0 :          real, pointer :: f(:,:)  ! (4, n)  ! data & interpolation coefficients
     374            0 :          f(1:4,1:n) => f1(1:4*n)
     375              : 
     376            0 :          ierr = 0
     377              : 
     378            0 :          if (n < 2) then
     379            0 :             return
     380              :          end if
     381              : 
     382            0 :          if (n == 2) then
     383            0 :             x(1) = 0
     384            0 :             x(2) = dx
     385            0 :             call mk_pmlinear_sg(x, f1, slope_only, nwork, work1, str, ierr)
     386            0 :             return
     387              :          end if
     388              : 
     389            0 :          if (dx == 0) then
     390            0 :             ierr = -1
     391            0 :             return
     392              :          end if
     393              : 
     394            0 :          if (nwork < pm_work_size) then
     395            0 :             ierr = -1
     396            0 :             return
     397              :          end if
     398              :          ierr = 0
     399              : 
     400            0 :          s(1:n) => work1(1:n)
     401            0 :          p(1:n) => work1(1+n:2*n)
     402              : 
     403              :          ierr = 0
     404              : 
     405            0 :          do i=1,n-1
     406            0 :             s(i) = f(1,i+1) - f(1,i)  ! slope across interval
     407              :          end do
     408            0 :          do i=2,n-1
     409            0 :             p(i) = 0.5*(s(i-1) + s(i))
     410              :             ! slope at i of parabola through i-1, i, and i+1
     411              :          end do
     412            0 :          do i=2,n-1
     413              :             f(2,i) = (sign(1.0, s(i-1))+sign(1.0, s(i)))* &
     414            0 :                min(abs(s(i-1)), abs(s(i)), 0.5*abs(p(i)))
     415              :             ! "safe" slope at i to ensure monotonic -- see Steffen's paper for explanation.
     416              :          end do
     417              : 
     418            0 :          p(1) = 1.5 * s(1) - 0.5 * s(2)
     419              :             ! slope at 1 of parabola through 1st 3 points
     420            0 :          if (p(1)*s(1) <= 0) then
     421            0 :             f(2, 1) = 0
     422            0 :          else if (abs(p(1)) > 2*abs(s(1))) then
     423            0 :             f(2, 1) = 2*s(1)
     424              :          else
     425            0 :             f(2, 1) = p(1)
     426              :          end if
     427              : 
     428            0 :          p(n) = 1.5 * s(n-1) - 0.5 * s(n-2)
     429              :             ! slope at n of parabola through last 3 points
     430            0 :          if (p(n)*s(n-1) <= 0) then
     431            0 :             f(2, n) = 0
     432            0 :          else if (abs(p(n)) > 2*abs(s(n-1))) then
     433            0 :             f(2, n) = 2*s(n-1)
     434              :          else
     435            0 :             f(2, n) = p(n)
     436              :          end if
     437              : 
     438            0 :          f(2, 1:n) = f(2, 1:n) / dx
     439              : 
     440            0 :          if (slope_only) return
     441              : 
     442              :          ! 2nd and 3rd derivatives
     443            0 :          do i=1,n-1
     444            0 :             f(3,i) = (3*s(i) - 2*f(2,i) - f(2,i+1)) / dx
     445            0 :             f(4,i) = (f(2,i) + f(2,i+1) - 2*s(i)) / (dx*dx)
     446              :          end do
     447            0 :          f(3,n) = (3*f(1, n-1) - 3*f(1, n) + (f(2, n-1) + 2*f(2, n)) * dx) / (dx*dx)
     448            0 :          f(4,n) = (-2*f(1, n-1) + 2*f(1, n) - (f(2, n-1) + f(2, n))*dx) / (dx*dx*dx)
     449              : 
     450            0 :       end subroutine mk_pmcub_uniform_sg
     451              : 
     452              : 
     453              :       end module interp_1d_pm_sg
        

Generated by: LCOV version 2.0-1