LCOV - code coverage report
Current view: top level - interp_1d/private - interp_1d_pm_autodiff.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 211 0
Test Date: 2025-05-08 18:23:42 Functions: 0.0 % 5 0

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

Generated by: LCOV version 2.0-1