LCOV - code coverage report
Current view: top level - star/private - pgstar_equation_residuals.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 112 0
Test Date: 2026-11-11 22:04:34 Functions: 0.0 % 7 0

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2026  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 pgstar_equation_residuals
      21              :    ! Composition equations are not included.
      22              :    use star_private_def
      23              :    use const_def, only: dp
      24              :    use pgstar_support
      25              :    use star_pgstar
      26              :    use pgstar_colors
      27              : 
      28              :    implicit none
      29              : 
      30              :    private
      31              :    public :: Max_eq_resid_plot, do_Max_eq_resid_plot, Max_eq_resid_record
      32              : 
      33              :    ! same line colors as pgstar_abundance
      34              :    integer, parameter :: line_colors(14) = [ &
      35              :       clr_Gold, clr_LightSkyBlue, clr_Crimson, clr_LightSkyGreen, clr_MediumSlateBlue, &
      36              :       clr_Coral, clr_Goldenrod, clr_DarkGray, clr_Lilac, &
      37              :       clr_Tan, clr_IndianRed, clr_Teal, clr_Silver, clr_BrightBlue ]
      38              : 
      39              :    real(dp), parameter :: resid_floor = 1d-20, resid_ceil = 1d30
      40              :    integer, parameter :: default_capacity = 128
      41              : 
      42              :    ! per-star history; indexed by s% id so multiple stars (binaries) don't mix
      43              :    type resid_hist_t
      44              :       integer :: nvar = 0
      45              :       integer :: count = 0
      46              :       integer, allocatable :: model(:)
      47              :       real, allocatable :: vals(:, :)  ! (nvar, capacity)
      48              :       character(len=name_len), allocatable :: names(:)
      49              :    end type resid_hist_t
      50              : 
      51              :    type(resid_hist_t), target, save :: hist(max_star_handles)
      52              : 
      53              : contains
      54              : 
      55            0 :    subroutine Max_eq_resid_plot(id, device_id, ierr)
      56              :       integer, intent(in)  :: id, device_id
      57              :       integer, intent(out) :: ierr
      58              :       type (star_info), pointer :: s
      59              : 
      60              :       ierr = 0
      61            0 :       call get_star_ptr(id, s, ierr)
      62            0 :       if (ierr /= 0) return
      63              : 
      64            0 :       call pgslct(device_id)
      65            0 :       call pgbbuf()
      66            0 :       call pgeras()
      67              : 
      68              :       call do_Max_eq_resid_plot(s, id, &
      69              :          s% pg% Max_eq_resid_xleft, s% pg% Max_eq_resid_xright, &
      70              :          s% pg% Max_eq_resid_ybot, s% pg% Max_eq_resid_ytop, .false., &
      71              :          s% pg% Max_eq_resid_title, s% pg% Max_eq_resid_txt_scale, &
      72            0 :          s% pg% Max_eq_resid_max_width, ierr)
      73            0 :          if (ierr /= 0) return
      74              : 
      75            0 :       call pgebuf()
      76              : 
      77              :    end subroutine Max_eq_resid_plot
      78              : 
      79              : 
      80            0 :    subroutine Max_eq_resid_record(s, max_width, ierr)
      81              :       type (star_info), pointer :: s
      82              :       integer, intent(in) :: max_width  ! <= 0: unlimited
      83              :       integer, intent(out) :: ierr
      84              : 
      85              :       type(resid_hist_t), pointer :: h
      86              :       integer :: nvar, nz, i_eq, k, c
      87              : 
      88            0 :       ierr = 0
      89            0 :       if (s% id < 1 .or. s% id > max_star_handles) then
      90            0 :          ierr = -1
      91            0 :          return
      92              :       end if
      93            0 :       h => hist(s% id)
      94              : 
      95            0 :       nvar = s% nvar_hydro
      96            0 :       nz = s% nz
      97            0 :       if (nvar <= 0 .or. nz <= 0 .or. .not. associated(s% equ)) return
      98            0 :       if (size(s% equ, 1) < nvar .or. size(s% equ, 2) < nz) return
      99              : 
     100              :       ! (re)initialize on first call or if the set of equations changed
     101            0 :       if (.not. allocated(h% model) .or. h% nvar /= nvar) then
     102            0 :          if (max_width > 0) then
     103            0 :             c = max_width
     104              :          else
     105            0 :             c = default_capacity
     106              :          end if
     107            0 :          call reset_hist(h, nvar, c, ierr)
     108            0 :          if (ierr /= 0) return
     109            0 :          do i_eq = 1, nvar
     110            0 :             h% names(i_eq) = s% nameofequ(i_eq)
     111              :          end do
     112              :       end if
     113              : 
     114              :       ! drop entries that are not older than the current model
     115            0 :       do while (h% count > 0)
     116            0 :          if (h% model(h% count) < s% model_number) exit
     117            0 :          h% count = h% count - 1
     118              :       end do
     119              : 
     120            0 :       if (max_width > 0 .and. h% count >= max_width) then
     121              :          ! roll: keep the newest max_width-1 entries
     122            0 :          k = h% count - max_width + 1
     123            0 :          h% model(1:h% count - k) = h% model(k + 1:h% count)
     124            0 :          h% vals(:, 1:h% count - k) = h% vals(:, k + 1:h% count)
     125            0 :          h% count = h% count - k
     126              :       end if
     127              : 
     128            0 :       if (h% count >= size(h% model)) then
     129            0 :          call grow_hist(h, 2 * size(h% model), ierr)
     130            0 :          if (ierr /= 0) return
     131              :       end if
     132              : 
     133            0 :       h% count = h% count + 1
     134            0 :       h% model(h% count) = s% model_number
     135            0 :       do i_eq = 1, nvar
     136              :          h% vals(i_eq, h% count) = real(min(resid_ceil, &
     137            0 :             max(resid_floor, maxval(abs(s% equ(i_eq, 1:nz))))))
     138              :       end do
     139              : 
     140              :    end subroutine Max_eq_resid_record
     141              : 
     142              : 
     143            0 :    subroutine do_Max_eq_resid_plot(s, id, &
     144              :          win_xleft, win_xright, win_ybot, win_ytop, subplot, title, txt_scale, &
     145              :          max_width, ierr)
     146              : 
     147              :       type (star_info), pointer :: s
     148              :       integer, intent(in) :: id, max_width
     149              :       logical, intent(in) :: subplot
     150              :       real, intent(in) :: win_xleft, win_xright, win_ybot, win_ytop, txt_scale
     151              :       character (len=*), intent(in) :: title
     152              :       integer, intent(out) :: ierr
     153              : 
     154              :       type(resid_hist_t), pointer :: h
     155            0 :       real, allocatable :: xvec(:), yvec(:)
     156              :       real :: xmin, xmax, ymin, ymax, dy_leg
     157              :       integer :: i_eq, n
     158              : 
     159              :       ierr = 0
     160            0 :       call Max_eq_resid_record(s, max_width, ierr)
     161            0 :       if (ierr /= 0) return
     162              : 
     163            0 :       h => hist(s% id)
     164            0 :       n = h% count
     165            0 :       if (n < 1) return
     166              : 
     167            0 :       allocate(xvec(n), yvec(n))
     168            0 :       xvec = real(h% model(1:n))
     169              : 
     170            0 :       xmin = xvec(1)
     171            0 :       xmax = xvec(n)
     172            0 :       if (xmin >= xmax) xmax = xmin + 1.0
     173              : 
     174            0 :       ymin = log10(minval(h% vals(:, 1:n)))
     175            0 :       ymax = log10(maxval(h% vals(:, 1:n)))
     176            0 :       if (ymax - ymin < 1.0) then
     177            0 :          ymin = ymin - 0.5
     178            0 :          ymax = ymax + 0.5
     179              :       end if
     180            0 :       ymin = ymin - 0.05 * (ymax - ymin)
     181            0 :       ymax = ymax + 0.05 * (ymax - ymin)
     182              : 
     183            0 :       call pgsave
     184            0 :       call pgsci(clr_Background)                       ! background
     185            0 :       call pgsvp(win_xleft, win_xright, win_ybot, win_ytop)
     186            0 :       call pgrect(0.0, 1.0, 0.0, 1.0)     ! pseudo erase
     187            0 :       call pgswin(xmin, xmax, ymin, ymax)
     188            0 :       call pgsch(txt_scale)
     189            0 :       call pgscf(1)
     190            0 :       call pgsci(clr_Foreground)
     191            0 :       call pgbox('BCNST', 0.0, 0, 'BCNST', 0.0, 0)
     192            0 :       call pgmtxt('B', 2.5, 0.5, 0.5, 'Model Number')
     193            0 :       call pgmtxt('L', 3.0, 0.5, 0.5, 'log\d10\u max\dk\u |residual|')
     194            0 :       call pgmtxt('T', 1.0, 0.5, 0.5, title)
     195              : 
     196            0 :       do i_eq = 1, h% nvar
     197            0 :          yvec = log10(h% vals(i_eq, 1:n))
     198            0 :          call pgsci(line_colors(mod(i_eq - 1, size(line_colors)) + 1))
     199            0 :          if (n == 1) then
     200            0 :             call pgpt(1, xvec, yvec, 17)
     201              :          else
     202            0 :             call pgline(n, xvec, yvec)
     203              :          end if
     204              :       end do
     205              : 
     206              :       ! legend
     207            0 :       call pgsch(0.75 * txt_scale)
     208            0 :       dy_leg = min(0.08, 1.0 / real(h% nvar))
     209            0 :       do i_eq = 1, h% nvar
     210            0 :          call pgsci(line_colors(mod(i_eq - 1, size(line_colors)) + 1))
     211              :          call pgmtxt('RV', 0.5, 1.0 - (real(i_eq) - 0.5) * dy_leg, 0.0, &
     212            0 :             trim(h% names(i_eq)))
     213              :       end do
     214              : 
     215            0 :       call pgunsa
     216            0 :       deallocate(xvec, yvec)
     217              : 
     218            0 :    end subroutine do_Max_eq_resid_plot
     219              : 
     220              : 
     221            0 :    subroutine reset_hist(h, nvar, capacity, ierr)
     222              :       type(resid_hist_t), intent(inout) :: h
     223              :       integer, intent(in) :: nvar, capacity
     224              :       integer, intent(out) :: ierr
     225              : 
     226            0 :       if (allocated(h% model)) deallocate(h% model)
     227            0 :       if (allocated(h% vals)) deallocate(h% vals)
     228            0 :       if (allocated(h% names)) deallocate(h% names)
     229            0 :       allocate(h% model(capacity), h% vals(nvar, capacity), h% names(nvar), stat=ierr)
     230            0 :       if (ierr /= 0) then
     231            0 :          write(*, *) 'Max_eq_resid: allocate failed'
     232            0 :          return
     233              :       end if
     234            0 :       h% nvar = nvar
     235            0 :       h% count = 0
     236              :    end subroutine reset_hist
     237              : 
     238              : 
     239            0 :    subroutine grow_hist(h, new_capacity, ierr)
     240              :       type(resid_hist_t), intent(inout) :: h
     241              :       integer, intent(in) :: new_capacity
     242              :       integer, intent(out) :: ierr
     243            0 :       integer, allocatable :: tmp_model(:)
     244            0 :       real, allocatable :: tmp_vals(:, :)
     245              : 
     246            0 :       call move_alloc(h% model, tmp_model)
     247            0 :       call move_alloc(h% vals, tmp_vals)
     248            0 :       allocate(h% model(new_capacity), h% vals(h% nvar, new_capacity), stat=ierr)
     249            0 :       if (ierr /= 0) then
     250            0 :          write(*, *) 'Max_eq_resid: allocate failed'
     251            0 :          return
     252              :       end if
     253            0 :       h% model(1:h% count) = tmp_model(1:h% count)
     254            0 :       h% vals(:, 1:h% count) = tmp_vals(:, 1:h% count)
     255              : 
     256              :       ! explicitly free temporaries
     257            0 :       if (allocated(tmp_model)) deallocate(tmp_model)
     258            0 :       if (allocated(tmp_vals)) deallocate(tmp_vals)
     259              : 
     260            0 :    end subroutine grow_hist
     261              : 
     262            0 : end module pgstar_equation_residuals
     263              : 
        

Generated by: LCOV version 2.0-1