LCOV - code coverage report
Current view: top level - star/private - tdc_hydro_support.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 500 0
Test Date: 2026-08-20 21:51:39 Functions: 0.0 % 22 0

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2010-2025  Ebraheem Farag & 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 tdc_hydro_support
      21              : 
      22              :    use star_private_def
      23              :    use const_def, only: dp, ln10, pi, lsun, rsun
      24              :    use utils_lib, only: is_bad
      25              :    use auto_diff
      26              :    use auto_diff_support
      27              :    use star_utils
      28              : 
      29              :    implicit none
      30              : 
      31              :    private
      32              :    public :: remesh_for_TDC_pulsations
      33              : 
      34              : contains
      35              : 
      36            0 :    subroutine remesh_for_TDC_pulsations(s, ierr)
      37              :       ! uses these controls
      38              :       !  TDC_hydro_nz = 190
      39              :       !  TDC_hydro_nz_outer = 40
      40              :       !  TDC_hydro_nz_inner = 20
      41              :       !  TDC_hydro_nz_T_gradient = 20
      42              :       !  TDC_hydro_T_anchor = 11d3
      43              :       !  TDC_hydro_dq_1_factor = 2d0
      44              :       use interp_1d_def, only: pm_work_size
      45              :       use interp_1d_lib, only: interpolate_vector_pm
      46              :       use hydro_vars, only: set_cgrav
      47              :       type(star_info), pointer :: s
      48              :       integer, intent(out) :: ierr
      49              :       integer :: k, j, nz_old, nz
      50              :       real(dp) :: xm_anchor, P_surf, T_surf, old_L1, old_r1, old_J, old_abs_J
      51              :       real(dp), allocatable, dimension(:) :: &
      52            0 :          xm_old, xm, xm_mid_old, xm_mid, v_old, v_new
      53            0 :       real(dp), pointer :: work1(:)  ! =(nz_old+1, pm_work_size)
      54              :       include 'formats'
      55            0 :       ierr = 0
      56            0 :       nz_old = s%nz
      57            0 :       nz = s%TDC_hydro_nz
      58            0 :       call validate_controls2
      59            0 :       if (ierr /= 0) return
      60            0 :       call setvars2(ierr)
      61            0 :       if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'remesh_for_TDC failed in setvars')
      62            0 :       old_J = 0d0
      63            0 :       old_abs_J = 0d0
      64            0 :       if (s%rotation_flag) then
      65            0 :          old_J = dot_product(s%dm_bar(1:nz_old), s%j_rot(1:nz_old))
      66            0 :          old_abs_J = dot_product(s%dm_bar(1:nz_old), abs(s%j_rot(1:nz_old)))
      67              :       end if
      68            0 :       old_L1 = s%L(1)
      69            0 :       old_r1 = s%r(1)
      70            0 :       call set_phot_info(s)  ! sets Teff
      71            0 :       call get_PT_surf2(P_surf, T_surf, ierr)
      72            0 :       if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'remesh_for_TDC failed in get_PT_surf')
      73              :       allocate ( &
      74            0 :          xm_old(nz_old + 1), xm_mid_old(nz_old), v_old(nz_old + 1), &
      75            0 :          xm(nz + 1), xm_mid(nz), v_new(nz + 1), work1((nz_old + 1)*pm_work_size))
      76            0 :       call set_xm_old2
      77            0 :       call find_xm_anchor2
      78            0 :       if (ierr /= 0) then
      79            0 :          deallocate(work1)
      80            0 :          return
      81              :       end if
      82            0 :       call set_xm_new2
      83            0 :       if (ierr /= 0) then
      84            0 :          deallocate(work1)
      85            0 :          return
      86              :       end if
      87            0 :       call interpolate1_face_val2(s%i_lnR, log(max(1d0, s%r_center)))
      88            0 :       call check_new_lnR2
      89            0 :       call interpolate1_face_val2(s%i_lum, s%L_center)
      90            0 :       if (s%i_v /= 0) call interpolate1_face_val2(s%i_v, s%v_center)
      91            0 :       call set_new_lnd2
      92            0 :       call interpolate1_cell_val2(s%i_lnT)
      93            0 :       if (s%i_u /= 0) call interpolate1_cell_val2(s%i_u)
      94            0 :       do j = 1, s%species
      95            0 :          call remap1_xa2(j)
      96              :       end do
      97            0 :       if (s%rotation_flag) call remap_rotation2(old_J, old_abs_J)
      98            0 :       s%nz = nz
      99            0 :       call update_composition_info2
     100            0 :       call set_cgrav(s, ierr)
     101            0 :       if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'remesh_for_TDC failed in set_cgrav')
     102            0 :       call revise_lnT_for_QHSE2(P_surf, ierr)
     103            0 :       if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'remesh_for_TDC failed in revise_lnT_for_QHSE')
     104            0 :       if (s%rotation_flag) call set_rotation_seed2
     105            0 :       deallocate (work1)
     106            0 :       write (*, 1) 'new old L_surf/Lsun', s%xh(s%i_lum, 1)/Lsun, old_L1/Lsun
     107            0 :       write (*, 1) 'new old R_surf/Rsun', exp(s%xh(s%i_lnR, 1))/Rsun, old_r1/Rsun
     108            0 :       write (*, '(A)')
     109              : 
     110              :    contains
     111              : 
     112            0 :       subroutine validate_controls2
     113              :          integer :: nz_base
     114              :          include 'formats'
     115            0 :          nz_base = nz - s%TDC_hydro_nz_T_gradient
     116            0 :          if (s%RSP_flag .or. s%RSP2_flag) then
     117            0 :             write(*,'(A)') 'TDC remesh cannot be applied after enabling RSP or RSP2'
     118            0 :             ierr = -1
     119            0 :          else if (nz > nz_old) then
     120            0 :             write(*,3) 'TDC remesh cannot increase the number of zones', nz, nz_old
     121            0 :             ierr = -1
     122            0 :          else if (s%TDC_hydro_nz_T_gradient < 0) then
     123            0 :             write(*,1) 'TDC_hydro_nz_T_gradient must be nonnegative', &
     124            0 :                real(s%TDC_hydro_nz_T_gradient, dp)
     125            0 :             ierr = -1
     126            0 :          else if (s%TDC_hydro_nz_inner < 0) then
     127            0 :             write(*,1) 'TDC_hydro_nz_inner must be nonnegative', &
     128            0 :                real(s%TDC_hydro_nz_inner, dp)
     129            0 :             ierr = -1
     130            0 :          else if (s%TDC_hydro_nz_outer < 2) then
     131            0 :             write(*,1) 'TDC_hydro_nz_outer must be at least 2', &
     132            0 :                real(s%TDC_hydro_nz_outer, dp)
     133            0 :             ierr = -1
     134            0 :          else if (s%remesh_for_TDC_pulsations_log_core_zoning .and. &
     135              :                nz_base - s%TDC_hydro_nz_outer < 2) then
     136            0 :             write(*,3) 'TDC remesh needs at least two interior zones', &
     137            0 :                nz_base, s%TDC_hydro_nz_outer
     138            0 :             ierr = -1
     139            0 :          else if (.not. s%remesh_for_TDC_pulsations_log_core_zoning .and. &
     140              :                nz_base - s%TDC_hydro_nz_outer - s%TDC_hydro_nz_inner < 2) then
     141            0 :             write(*,3) 'TDC remesh needs at least two middle zones', &
     142            0 :                nz_base, s%TDC_hydro_nz_outer, s%TDC_hydro_nz_inner
     143            0 :             ierr = -1
     144              :          else if (.not. s%remesh_for_TDC_pulsations_log_core_zoning .and. &
     145            0 :                s%TDC_hydro_nz_inner > 0 .and. s%max_center_cell_dq <= 0d0) then
     146            0 :             write(*,1) 'max_center_cell_dq must be positive for inner TDC zoning', &
     147            0 :                s%max_center_cell_dq
     148            0 :             ierr = -1
     149            0 :          else if (s%TDC_hydro_dq_1_factor <= 0d0) then
     150            0 :             write(*,1) 'TDC_hydro_dq_1_factor must be positive', &
     151            0 :                s%TDC_hydro_dq_1_factor
     152            0 :             ierr = -1
     153            0 :          else if (s%TDC_hydro_T_anchor <= 0d0) then
     154            0 :             write(*,1) 'TDC_hydro_T_anchor must be positive', s%TDC_hydro_T_anchor
     155            0 :             ierr = -1
     156              :          end if
     157            0 :       end subroutine validate_controls2
     158              : 
     159            0 :       subroutine setvars2(ierr)
     160              :          use hydro_vars, only: unpack_xh, set_hydro_vars
     161              :          integer, intent(out) :: ierr
     162              :          logical, parameter :: &
     163              :             skip_basic_vars = .false., &
     164              :             skip_micro_vars = .false., &
     165              :             skip_m_grav_and_grav = .false., &
     166              :             skip_net = .true., &
     167              :             skip_neu = .true., &
     168              :             skip_kap = .false., &
     169              :             skip_grads = .true., &
     170              :             skip_rotation = .true., &
     171              :             skip_brunt = .true., &
     172              :             skip_other_cgrav = .true., &
     173              :             skip_mixing_info = .true., &
     174              :             skip_set_cz_bdy_mass = .true., &
     175              :             skip_mlt = .true., &
     176              :             skip_eos = .false.
     177              :          ierr = 0
     178            0 :          call unpack_xh(s, ierr)
     179            0 :          if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'remesh_for_TDC failed in unpack_xh')
     180              :          call set_hydro_vars( &
     181              :             s, 1, nz_old, skip_basic_vars, &
     182              :             skip_micro_vars, skip_m_grav_and_grav, skip_eos, skip_net, skip_neu, &
     183              :             skip_kap, skip_grads, skip_rotation, skip_brunt, skip_other_cgrav, &
     184            0 :             skip_mixing_info, skip_set_cz_bdy_mass, skip_mlt, ierr)
     185            0 :          if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'remesh_for_TDC failed in set_hydro_vars')
     186            0 :       end subroutine setvars2
     187              : 
     188            0 :       subroutine get_PT_surf2(P_surf, T_surf, ierr)
     189              :          use atm_support, only: get_atm_PT
     190              :          real(dp), intent(out) :: P_surf, T_surf
     191              :          integer, intent(out) :: ierr
     192              :          real(dp) :: &
     193              :             Teff, lnT_surf, dlnT_dL, dlnT_dlnR, dlnT_dlnM, dlnT_dlnkap, &
     194              :             lnP_surf, dlnP_dL, dlnP_dlnR, dlnP_dlnM, dlnP_dlnkap
     195              :          logical, parameter :: skip_partials = .true.
     196            0 :          ierr = 0
     197            0 :          call set_phot_info(s)  ! sets s% Teff
     198            0 :          Teff = s%Teff
     199              :          call get_atm_PT( &  ! this uses s% opacity(1)
     200              :             s, s%tau_factor*s%tau_base, s%L(1), s%r(1), s%m(1), s%cgrav(1), skip_partials, &
     201              :             Teff, lnT_surf, dlnT_dL, dlnT_dlnR, dlnT_dlnM, dlnT_dlnkap, &
     202            0 :             lnP_surf, dlnP_dL, dlnP_dlnR, dlnP_dlnM, dlnP_dlnkap, ierr)
     203            0 :          if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'get_P_surf failed in get_atm_PT')
     204            0 :          P_surf = exp(lnP_surf)
     205            0 :          T_surf = exp(lnT_surf)
     206            0 :       end subroutine get_PT_surf2
     207              : 
     208            0 :       subroutine set_xm_old2
     209            0 :          xm_old(1) = 0d0
     210            0 :          do k = 2, nz_old
     211            0 :             xm_old(k) = xm_old(k - 1) + s%dm(k - 1)
     212              :          end do
     213            0 :          xm_old(nz_old + 1) = s%xmstar
     214            0 :          do k = 1, nz_old
     215            0 :             xm_mid_old(k) = xm_old(k) + 0.5d0*s%dm(k)
     216              :          end do
     217            0 :       end subroutine set_xm_old2
     218              : 
     219            0 :       subroutine find_xm_anchor2
     220              :          real(dp) :: lnT_anchor, xmm1, xm00, lnTm1, lnT00
     221              :          include 'formats'
     222            0 :          lnT_anchor = log(s%TDC_hydro_T_anchor)
     223            0 :          if (lnT_anchor <= s%xh(s%i_lnT, 1)) then
     224            0 :             write (*, 1) 'T_anchor < T_surf', s%TDC_hydro_T_anchor, exp(s%xh(s%i_lnT, 1))
     225            0 :             call mesa_error(__FILE__, __LINE__, 'find_xm_anchor')
     226              :          end if
     227            0 :          xm_anchor = xm_old(nz_old)
     228            0 :          do k = 2, nz_old
     229            0 :             if (s%xh(s%i_lnT, k) >= lnT_anchor) then
     230            0 :                xmm1 = xm_mid_old(k - 1)
     231            0 :                xm00 = xm_mid_old(k)
     232            0 :                lnTm1 = s%xh(s%i_lnT, k - 1)
     233            0 :                lnT00 = s%xh(s%i_lnT, k)
     234              :                xm_anchor = xmm1 + &
     235            0 :                            (xm00 - xmm1)*(lnT_anchor - lnTm1)/(lnT00 - lnTm1)
     236            0 :                if (is_bad(xm_anchor) .or. xm_anchor <= 0d0) then
     237            0 :                   write (*, 2) 'bad xm_anchor', k, xm_anchor, xmm1, xm00, lnTm1, lnT00, lnT_anchor, s%lnT(1)
     238            0 :                   call mesa_error(__FILE__, __LINE__, 'find_xm_anchor')
     239              :                end if
     240            0 :                return
     241              :             end if
     242              :          end do
     243            0 :          write(*,1) 'T_anchor exceeds the model temperature', s%TDC_hydro_T_anchor
     244            0 :          ierr = -1
     245              :       end subroutine find_xm_anchor2
     246              : 
     247            0 :       subroutine set_xm_new2  ! sets xm, dm, m, dq, q
     248              :          integer :: nz_outer, nz_inner, nz_T_gradient, nz_base, n_middle, k
     249              :          real(dp) :: dq_1_factor, dxm_outer, lnx, dlnx, base_dm, &
     250              :             rem_mass, center_dm, peak_dm, correction, ratio, H, H_inner
     251              :          real(dp) :: H_low, H_high, H_mid, f_low, f_high, f_mid
     252              :          integer :: iter
     253              :          include 'formats'
     254            0 :          nz_outer = s%TDC_hydro_nz_outer
     255            0 :          nz_inner = s%TDC_hydro_nz_inner
     256            0 :          nz_T_gradient = s%TDC_hydro_nz_T_gradient
     257            0 :          nz_base = nz - nz_T_gradient
     258            0 :          dq_1_factor = s%TDC_hydro_dq_1_factor
     259            0 :          ratio = max(2.5d0, s%mesh_max_allowed_ratio)
     260            0 :          dxm_outer = xm_anchor/(nz_outer - 1d0 + dq_1_factor)
     261            0 :          xm(1) = 0d0
     262            0 :          xm(2) = dxm_outer*dq_1_factor
     263            0 :          s%dm(1) = xm(2)
     264            0 :          do k = 3, nz_outer + 1
     265            0 :             xm(k) = xm(k - 1) + dxm_outer
     266            0 :             s%dm(k - 1) = dxm_outer
     267              :          end do
     268              : 
     269            0 :          if (.not. s%remesh_for_TDC_pulsations_log_core_zoning) then
     270              :             ! do rsp style core zoning with a power law on dq
     271            0 :             n_middle = nz_base - nz_outer - nz_inner
     272            0 :             rem_mass = s%xmstar - xm(nz_outer + 1)
     273            0 :             base_dm = dxm_outer
     274              : 
     275            0 :             if (nz_inner == 0) then
     276              :                ! Original single inward-increasing power law.
     277            0 :                H_low = 1.001d0
     278            0 :                H_high = 1.40d0
     279            0 :                f_low = base_dm*(1d0 - pow(H_low, real(n_middle, dp)))/(1d0 - H_low) - rem_mass
     280            0 :                f_high = base_dm*(1d0 - pow(H_high, real(n_middle, dp)))/(1d0 - H_high) - rem_mass
     281            0 :                if (f_low*f_high > 0d0) then
     282            0 :                   write(*,2) 'failed to bracket TDC core zoning ramp', &
     283            0 :                      n_middle, f_low, f_high
     284            0 :                   ierr = -1
     285            0 :                   return
     286              :                end if
     287            0 :                do iter = 1, 1000
     288            0 :                   H_mid = 0.5d0*(H_low + H_high)
     289            0 :                   f_mid = base_dm*(1d0 - pow(H_mid, real(n_middle, dp)))/(1d0 - H_mid) - rem_mass
     290            0 :                   if (abs(f_mid) < 1d-12*rem_mass) exit
     291            0 :                   if (f_low*f_mid <= 0d0) then
     292            0 :                      H_high = H_mid
     293            0 :                      f_high = f_mid
     294              :                   else
     295            0 :                      H_low = H_mid
     296            0 :                      f_low = f_mid
     297              :                   end if
     298              :                end do
     299            0 :                H = H_mid
     300              : 
     301            0 :                s%dm(nz_outer + 1) = base_dm
     302            0 :                do k = nz_outer + 2, nz_base - 1
     303            0 :                   s%dm(k) = H*s%dm(k - 1)
     304              :                end do
     305            0 :                s%dm(nz_base) = s%xmstar - sum(s%dm(1:nz_base - 1))
     306              : 
     307              :             else
     308              :                ! Add an inward-decreasing ramp that ends at max_center_cell_dq.
     309              :                H_low = 1d0
     310            0 :                H_high = ratio
     311            0 :                center_dm = s%max_center_cell_dq*s%xmstar
     312              :                H_low = max(H_low, &
     313            0 :                   pow(center_dm/base_dm, 1d0/real(n_middle - 1, dp)))
     314            0 :                if (H_low > H_high) then
     315            0 :                   write(*,2) 'TDC inner zoning requires a larger mesh ratio', &
     316            0 :                      H_low, H_high
     317            0 :                   ierr = -1
     318            0 :                   return
     319              :                end if
     320              :                f_low = double_ramp_mass2( &
     321            0 :                   H_low, base_dm, center_dm, n_middle, nz_inner) - rem_mass
     322              :                f_high = double_ramp_mass2( &
     323            0 :                   H_high, base_dm, center_dm, n_middle, nz_inner) - rem_mass
     324            0 :                if (f_low*f_high > 0d0) then
     325            0 :                   write(*,2) 'failed to bracket TDC core zoning ramp', &
     326            0 :                      n_middle, f_low, f_high
     327            0 :                   ierr = -1
     328            0 :                   return
     329              :                end if
     330            0 :                do iter = 1, 1000
     331            0 :                   H_mid = 0.5d0*(H_low + H_high)
     332              :                   f_mid = double_ramp_mass2( &
     333            0 :                      H_mid, base_dm, center_dm, n_middle, nz_inner) - rem_mass
     334            0 :                   if (abs(f_mid) < 1d-12*rem_mass) exit
     335            0 :                   if (f_low*f_mid <= 0d0) then
     336            0 :                      H_high = H_mid
     337            0 :                      f_high = f_mid
     338              :                   else
     339            0 :                      H_low = H_mid
     340            0 :                      f_low = f_mid
     341              :                   end if
     342              :                end do
     343            0 :                H = H_mid
     344              : 
     345            0 :                do k = nz_outer + 1, nz_outer + n_middle
     346            0 :                   s%dm(k) = base_dm*pow(H, real(k - nz_outer - 1, dp))
     347              :                end do
     348              : 
     349            0 :                peak_dm = s%dm(nz_outer + n_middle)
     350            0 :                H_inner = pow(peak_dm/center_dm, 1d0/real(nz_inner, dp))
     351            0 :                do k = nz_outer + n_middle + 1, nz_base
     352            0 :                   s%dm(k) = center_dm*pow(H_inner, real(nz_base - k, dp))
     353              :                end do
     354              : 
     355            0 :                correction = s%xmstar - sum(s%dm(1:nz_base))
     356              :                s%dm(nz_outer + n_middle) = &
     357            0 :                   s%dm(nz_outer + n_middle) + correction
     358              :             end if
     359              : 
     360              :          else ! use log zoning inward from anchor to core.
     361            0 :             lnx = log(xm(nz_outer + 1))
     362            0 :             if (is_bad(lnx)) then
     363            0 :                write (*, 2) 'bad lnx', nz_outer + 1, lnx, xm(nz_outer + 1)
     364            0 :                call mesa_error(__FILE__, __LINE__, 'set_xm_new')
     365              :             end if
     366            0 :             dlnx = (log(s%xmstar) - lnx)/(nz_base - nz_outer)
     367            0 :             do k = nz_outer + 2, nz_base
     368            0 :                lnx = lnx + dlnx
     369            0 :                xm(k) = exp(lnx)
     370            0 :                s%dm(k - 1) = xm(k) - xm(k - 1)
     371              :             end do
     372            0 :             s%dm(nz_base) = s%xmstar - xm(nz_base)
     373              : 
     374              :             ! enforce the last boundary at total mass
     375            0 :             xm(nz_base + 1) = s%xmstar
     376              : 
     377              :             ! recompute cell masses
     378            0 :             do k = nz_outer + 1, nz_base
     379            0 :                s%dm(k) = xm(k + 1) - xm(k)
     380              :             end do
     381              : 
     382              :          end if
     383              : 
     384            0 :          xm(1) = 0d0
     385            0 :          do k = 1, nz_base
     386            0 :             xm(k + 1) = xm(k) + s%dm(k)
     387              :          end do
     388            0 :          xm(nz_base + 1) = s%xmstar
     389              : 
     390            0 :          if (nz_T_gradient > 0) then
     391            0 :             call add_T_gradient_zones2(nz_outer, nz_base, nz_T_gradient)
     392            0 :             if (ierr /= 0) return
     393              :          end if
     394              : 
     395            0 :          do k = 1, nz
     396            0 :             if (s%dm(k) <= 0d0 .or. is_bad(s%dm(k))) then
     397            0 :                write(*,2) 'bad cell mass in TDC remesh', k, s%dm(k)
     398            0 :                ierr = -1
     399            0 :                return
     400              :             end if
     401              :          end do
     402              : 
     403            0 :          if ((.not. s%remesh_for_TDC_pulsations_log_core_zoning .and. nz_inner > 0) .or. &
     404              :                nz_T_gradient > 0) then
     405            0 :             do k = 2, nz
     406            0 :                if (s%dm(k) > ratio*(1d0 + 1d-10)*s%dm(k - 1) .or. &
     407            0 :                      s%dm(k - 1) > ratio*(1d0 + 1d-10)*s%dm(k)) then
     408            0 :                   write(*,2) 'bad adjacent cell mass ratio in TDC mesh', &
     409            0 :                      k, s%dm(k)/s%dm(k - 1), ratio
     410            0 :                   ierr = -1
     411            0 :                   return
     412              :                end if
     413              :             end do
     414              :          end if
     415              : 
     416            0 :          xm(1) = 0d0
     417            0 :          do k = 1, nz
     418            0 :             xm(k + 1) = xm(k) + s%dm(k)
     419              :          end do
     420            0 :          xm(nz + 1) = s%xmstar
     421              : 
     422            0 :          do k = 1, nz - 1
     423            0 :             xm_mid(k) = 0.5d0*(xm(k) + xm(k + 1))
     424              :          end do
     425            0 :          xm_mid(nz) = 0.5d0*(xm(nz) + s%xmstar)
     426            0 :          s%m(1) = s%mstar
     427            0 :          s%q(1) = 1d0
     428            0 :          s%dq(1) = s%dm(1)/s%xmstar
     429            0 :          do k = 2, nz
     430            0 :             s%m(k) = s%m(k - 1) - s%dm(k - 1)
     431            0 :             s%dq(k) = s%dm(k)/s%xmstar
     432            0 :             s%q(k) = s%q(k - 1) - s%dq(k - 1)
     433              :          end do
     434            0 :          call set_dm_bar(s, nz, s%dm, s%dm_bar)
     435              :       end subroutine set_xm_new2
     436              : 
     437            0 :       real(dp) function geometric_sum2(H, n) result(sum_H)
     438              :          real(dp), intent(in) :: H
     439              :          integer, intent(in) :: n
     440              : 
     441            0 :          if (abs(H - 1d0) < 1d-12) then
     442            0 :             sum_H = real(n, dp)
     443              :          else
     444            0 :             sum_H = (pow(H, real(n, dp)) - 1d0)/(H - 1d0)
     445              :          end if
     446            0 :       end function geometric_sum2
     447              : 
     448            0 :       real(dp) function double_ramp_mass2( &
     449              :             H, base_dm, center_dm, n_middle, n_inner) result(total_mass)
     450              :          real(dp), intent(in) :: H, base_dm, center_dm
     451              :          integer, intent(in) :: n_middle, n_inner
     452              :          real(dp) :: H_inner, peak_dm
     453              : 
     454            0 :          peak_dm = base_dm*pow(H, real(n_middle - 1, dp))
     455            0 :          H_inner = pow(peak_dm/center_dm, 1d0/real(n_inner, dp))
     456              :          total_mass = base_dm*geometric_sum2(H, n_middle) + &
     457            0 :             center_dm*geometric_sum2(H_inner, n_inner)
     458            0 :       end function double_ramp_mass2
     459              : 
     460            0 :       subroutine add_T_gradient_zones2(nz_outer, nz_base, nz_T_gradient)
     461              :          integer, intent(in) :: nz_outer, nz_base, nz_T_gradient
     462              :          integer :: i, i_base, i_old, j, k_new, max_points, npts, nz_core_base
     463              :          real(dp) :: dlnT_total, eps_xm, fraction, next_base, next_old, &
     464              :             next_xm, target
     465            0 :          real(dp), allocatable :: base_xm(:), lnT_monitor(:), &
     466            0 :             monitor(:), monitor_xm(:)
     467              :          include 'formats'
     468              : 
     469            0 :          nz_core_base = nz_base - nz_outer
     470            0 :          max_points = nz_old + nz_core_base + 2
     471              :          allocate(base_xm(nz_base + 1), lnT_monitor(max_points), &
     472            0 :             monitor(max_points), monitor_xm(max_points))
     473            0 :          base_xm = xm(1:nz_base + 1)
     474            0 :          eps_xm = 16d0*epsilon(1d0)*max(abs(s%xmstar), 1d0)
     475              : 
     476              :          ! Include both old temperature points and base-grid boundaries in the monitor.
     477            0 :          npts = 1
     478            0 :          monitor_xm(npts) = xm_anchor
     479            0 :          i_base = nz_outer + 2
     480            0 :          i_old = 1
     481            0 :          do while (i_old <= nz_old .and. xm_mid_old(i_old) <= xm_anchor + eps_xm)
     482            0 :             i_old = i_old + 1
     483              :          end do
     484              : 
     485              :          do
     486            0 :             next_base = huge(1d0)
     487            0 :             if (i_base <= nz_base) next_base = base_xm(i_base)
     488            0 :             next_old = huge(1d0)
     489            0 :             if (i_old <= nz_old) next_old = xm_mid_old(i_old)
     490            0 :             next_xm = min(next_base, next_old)
     491            0 :             if (next_xm >= s%xmstar - eps_xm) exit
     492              : 
     493            0 :             if (next_xm > monitor_xm(npts) + eps_xm) then
     494            0 :                npts = npts + 1
     495            0 :                monitor_xm(npts) = next_xm
     496              :             end if
     497            0 :             if (next_base <= next_xm + eps_xm) i_base = i_base + 1
     498            0 :             if (next_old <= next_xm + eps_xm) i_old = i_old + 1
     499              :          end do
     500            0 :          npts = npts + 1
     501            0 :          monitor_xm(npts) = s%xmstar
     502              : 
     503            0 :          do i = 1, npts
     504            0 :             lnT_monitor(i) = lnT_at_xm2(monitor_xm(i))
     505              :          end do
     506            0 :          monitor(1) = 0d0
     507            0 :          do i = 2, npts
     508              :             monitor(i) = monitor(i - 1) + &
     509            0 :                abs(lnT_monitor(i) - lnT_monitor(i - 1))
     510              :          end do
     511            0 :          dlnT_total = monitor(npts)
     512              : 
     513            0 :          if (dlnT_total > 0d0) then
     514            0 :             do i = 1, npts
     515              :                monitor(i) = base_mesh_coordinate2( &
     516              :                   monitor_xm(i), base_xm, nz_outer, nz_base) + &
     517            0 :                   nz_T_gradient*monitor(i)/dlnT_total
     518              :             end do
     519              :          else
     520            0 :             do i = 1, npts
     521              :                monitor(i) = base_mesh_coordinate2( &
     522              :                   monitor_xm(i), base_xm, nz_outer, nz_base)* &
     523            0 :                   real(nz_core_base + nz_T_gradient, dp)/real(nz_core_base, dp)
     524              :             end do
     525              :          end if
     526            0 :          monitor(1) = 0d0
     527            0 :          monitor(npts) = real(nz_core_base + nz_T_gradient, dp)
     528              : 
     529            0 :          xm(1:nz_outer + 1) = base_xm(1:nz_outer + 1)
     530            0 :          j = 2
     531            0 :          do k_new = nz_outer + 2, nz
     532            0 :             target = real(k_new - nz_outer - 1, dp)
     533            0 :             do while (j < npts .and. monitor(j) < target)
     534            0 :                j = j + 1
     535              :             end do
     536            0 :             if (monitor(j) <= monitor(j - 1)) then
     537            0 :                write(*,2) 'bad TDC temperature-gradient monitor', &
     538            0 :                   j, monitor(j - 1), monitor(j)
     539            0 :                ierr = -1
     540            0 :                deallocate(base_xm, lnT_monitor, monitor, monitor_xm)
     541              :                return
     542              :             end if
     543            0 :             fraction = (target - monitor(j - 1))/(monitor(j) - monitor(j - 1))
     544              :             xm(k_new) = monitor_xm(j - 1) + &
     545            0 :                fraction*(monitor_xm(j) - monitor_xm(j - 1))
     546              :          end do
     547            0 :          xm(nz + 1) = s%xmstar
     548            0 :          do i = nz_outer + 1, nz
     549            0 :             s%dm(i) = xm(i + 1) - xm(i)
     550              :          end do
     551              : 
     552            0 :          deallocate(base_xm, lnT_monitor, monitor, monitor_xm)
     553              :       end subroutine add_T_gradient_zones2
     554              : 
     555            0 :       real(dp) function base_mesh_coordinate2( &
     556            0 :             x, base_xm, nz_outer, nz_base) result(coordinate)
     557              :          real(dp), intent(in) :: x, base_xm(:)
     558              :          integer, intent(in) :: nz_outer, nz_base
     559              :          integer :: i
     560              : 
     561            0 :          if (x <= base_xm(nz_outer + 1)) then
     562            0 :             coordinate = 0d0
     563              :             return
     564              :          end if
     565            0 :          do i = nz_outer + 1, nz_base
     566            0 :             if (x <= base_xm(i + 1)) then
     567              :                coordinate = real(i - nz_outer - 1, dp) + &
     568            0 :                   (x - base_xm(i))/(base_xm(i + 1) - base_xm(i))
     569            0 :                return
     570              :             end if
     571              :          end do
     572            0 :          coordinate = real(nz_base - nz_outer, dp)
     573            0 :       end function base_mesh_coordinate2
     574              : 
     575            0 :       real(dp) function lnT_at_xm2(x) result(lnT_at_xm)
     576              :          real(dp), intent(in) :: x
     577              :          integer :: i
     578              :          real(dp) :: fraction
     579              : 
     580            0 :          if (nz_old == 1) then
     581            0 :             lnT_at_xm = s%xh(s%i_lnT, 1)
     582            0 :             return
     583              :          end if
     584            0 :          if (x <= xm_mid_old(1)) then
     585              :             i = 2
     586              :          else
     587            0 :             do i = 2, nz_old
     588            0 :                if (x <= xm_mid_old(i)) exit
     589              :             end do
     590            0 :             if (i > nz_old) i = nz_old
     591              :          end if
     592              :          fraction = (x - xm_mid_old(i - 1))/ &
     593            0 :             (xm_mid_old(i) - xm_mid_old(i - 1))
     594              :          lnT_at_xm = s%xh(s%i_lnT, i - 1) + fraction* &
     595            0 :             (s%xh(s%i_lnT, i) - s%xh(s%i_lnT, i - 1))
     596            0 :       end function lnT_at_xm2
     597              : 
     598            0 :       subroutine interpolate1_face_val2(i, cntr_val)
     599              :          integer, intent(in) :: i
     600              :          real(dp), intent(in) :: cntr_val
     601            0 :          do k = 1, nz_old
     602            0 :             v_old(k) = s%xh(i, k)
     603              :          end do
     604            0 :          v_old(nz_old + 1) = cntr_val
     605              :          call interpolate_vector_pm( &
     606            0 :             nz_old + 1, xm_old, nz + 1, xm, v_old, v_new, work1, 'remesh_for_TDC', ierr)
     607            0 :          if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'TDC remesh face interpolation failed')
     608            0 :          do k = 1, nz
     609            0 :             s%xh(i, k) = v_new(k)
     610              :          end do
     611            0 :       end subroutine interpolate1_face_val2
     612              : 
     613            0 :       subroutine check_new_lnR2
     614              :          include 'formats'
     615            0 :          do k = 1, nz
     616            0 :             s%lnR(k) = s%xh(s%i_lnR, k)
     617            0 :             s%r(k) = exp(s%lnR(k))
     618              :          end do
     619            0 :          do k = 1, nz - 1
     620            0 :             if (s%r(k) <= s%r(k + 1)) then
     621            0 :                write (*, 2) 'bad r', k, s%r(k), s%r(k + 1)
     622            0 :                call mesa_error(__FILE__, __LINE__, 'check_new_lnR remesh TDC')
     623              :             end if
     624              :          end do
     625            0 :          if (s%r(nz) <= s%r_center) then
     626            0 :             write (*, 2) 'bad r center', nz, s%r(nz), s%r_center
     627            0 :             call mesa_error(__FILE__, __LINE__, 'check_new_lnR remesh TDC')
     628              :          end if
     629            0 :       end subroutine check_new_lnR2
     630              : 
     631            0 :       subroutine set_new_lnd2
     632              :          real(dp) :: vol, r300, r3p1
     633              :          include 'formats'
     634            0 :          do k = 1, nz
     635            0 :             r300 = pow3(s%r(k))
     636            0 :             if (k < nz) then
     637            0 :                r3p1 = pow3(s%r(k + 1))
     638              :             else
     639            0 :                r3p1 = pow3(s%r_center)
     640              :             end if
     641            0 :             vol = (4d0*pi/3d0)*(r300 - r3p1)
     642            0 :             s%rho(k) = s%dm(k)/vol
     643            0 :             s%lnd(k) = log(s%rho(k))
     644            0 :             s%xh(s%i_lnd, k) = s%lnd(k)
     645            0 :             if (is_bad(s%lnd(k))) then
     646            0 :                write (*, 2) 'bad lnd vol dm r300 r3p1', k, s%lnd(k), vol, s%dm(k), r300, r3p1
     647            0 :                call mesa_error(__FILE__, __LINE__, 'remesh for TDC')
     648              :             end if
     649              :          end do
     650            0 :       end subroutine set_new_lnd2
     651              : 
     652            0 :       subroutine interpolate1_cell_val2(i)
     653              :          integer, intent(in) :: i
     654            0 :          do k = 1, nz_old
     655            0 :             v_old(k) = s%xh(i, k)
     656              :          end do
     657              :          call interpolate_vector_pm( &
     658            0 :             nz_old, xm_mid_old, nz, xm_mid, v_old, v_new, work1, 'remesh_for_TDC', ierr)
     659            0 :          if (ierr /= 0) call mesa_error(__FILE__, __LINE__, 'TDC remesh cell interpolation failed')
     660            0 :          do k = 1, nz
     661            0 :             s%xh(i, k) = v_new(k)
     662              :          end do
     663            0 :       end subroutine interpolate1_cell_val2
     664              : 
     665            0 :       subroutine remap1_xa2(j)
     666              :          integer, intent(in) :: j
     667              :          integer :: k_old, k_scan, k_new
     668              :          real(dp) :: overlap, species_mass
     669              : 
     670            0 :          v_old(1:nz_old) = s%xa(j, 1:nz_old)
     671            0 :          k_old = 1
     672            0 :          do k_new = 1, nz
     673            0 :             do while (k_old < nz_old .and. xm_old(k_old + 1) <= xm(k_new))
     674            0 :                k_old = k_old + 1
     675              :             end do
     676              :             species_mass = 0d0
     677              :             k_scan = k_old
     678            0 :             do while (k_scan <= nz_old .and. xm_old(k_scan) < xm(k_new + 1))
     679              :                ! A mass-overlap average conserves the total mass of each species.
     680              :                overlap = min(xm(k_new + 1), xm_old(k_scan + 1)) - &
     681            0 :                   max(xm(k_new), xm_old(k_scan))
     682            0 :                if (overlap > 0d0) species_mass = species_mass + overlap*v_old(k_scan)
     683            0 :                k_scan = k_scan + 1
     684              :             end do
     685            0 :             s%xa(j, k_new) = species_mass/s%dm(k_new)
     686              :          end do
     687            0 :       end subroutine remap1_xa2
     688              : 
     689            0 :       subroutine remap_rotation2(old_J, old_abs_J)
     690              :          real(dp), intent(in) :: old_J, old_abs_J
     691              :          real(dp) :: new_J
     692              :          include 'formats'
     693              : 
     694            0 :          v_old(1:nz_old) = s%j_rot(1:nz_old)
     695            0 :          call remap1_face_average2
     696            0 :          s%j_rot(1:nz) = v_new(1:nz)
     697            0 :          if (s%i_j_rot /= 0) s%xh(s%i_j_rot,1:nz) = s%j_rot(1:nz)
     698              : 
     699            0 :          v_old(1:nz_old) = s%omega(1:nz_old)
     700            0 :          call remap1_face_average2
     701            0 :          s%omega(1:nz) = v_new(1:nz)
     702              : 
     703            0 :          new_J = dot_product(s%dm_bar(1:nz), s%j_rot(1:nz))
     704            0 :          if (abs(new_J - old_J) > 1d-12*max(old_abs_J, tiny(1d0))) then
     705            0 :             write(*,1) 'relative angular momentum error in TDC remesh', &
     706            0 :                (new_J - old_J)/max(abs(old_J), old_abs_J, tiny(1d0))
     707            0 :             call mesa_error(__FILE__, __LINE__, 'TDC remesh failed to conserve angular momentum')
     708              :          end if
     709            0 :          s%total_angular_momentum = new_J
     710              :          s%total_abs_angular_momentum = &
     711            0 :             dot_product(s%dm_bar(1:nz), abs(s%j_rot(1:nz)))
     712            0 :       end subroutine remap_rotation2
     713              : 
     714            0 :       subroutine remap1_face_average2
     715              :          integer :: k_old, k_scan, k_new
     716              :          real(dp) :: new_outer, new_inner, old_outer, old_inner, &
     717              :             overlap, integral
     718              : 
     719            0 :          k_old = 1
     720            0 :          do k_new = 1, nz
     721            0 :             if (k_new == 1) then
     722              :                new_outer = 0d0
     723              :             else
     724            0 :                new_outer = xm_mid(k_new - 1)
     725              :             end if
     726            0 :             if (k_new == nz) then
     727            0 :                new_inner = s%xmstar
     728              :             else
     729            0 :                new_inner = xm_mid(k_new)
     730              :             end if
     731              : 
     732            0 :             do while (k_old < nz_old .and. xm_mid_old(k_old) <= new_outer)
     733            0 :                k_old = k_old + 1
     734              :             end do
     735              :             integral = 0d0
     736              :             k_scan = k_old
     737            0 :             do while (k_scan <= nz_old)
     738            0 :                if (k_scan == 1) then
     739              :                   old_outer = 0d0
     740              :                else
     741            0 :                   old_outer = xm_mid_old(k_scan - 1)
     742              :                end if
     743            0 :                if (k_scan == nz_old) then
     744            0 :                   old_inner = s%xmstar
     745              :                else
     746            0 :                   old_inner = xm_mid_old(k_scan)
     747              :                end if
     748            0 :                if (old_outer >= new_inner) exit
     749            0 :                overlap = min(new_inner, old_inner) - max(new_outer, old_outer)
     750            0 :                if (overlap > 0d0) integral = integral + overlap*v_old(k_scan)
     751            0 :                k_scan = k_scan + 1
     752              :             end do
     753            0 :             v_new(k_new) = integral/(new_inner - new_outer)
     754              :          end do
     755            0 :       end subroutine remap1_face_average2
     756              : 
     757            0 :       subroutine update_composition_info2
     758              :          use chem_lib, only: basic_composition_info
     759              :          real(dp) :: sumx
     760              : 
     761            0 :          do k = 1, nz
     762              :             call basic_composition_info( &
     763              :                s%species, s%chem_id, s%xa(1:s%species,k), &
     764              :                s%X(k), s%Y(k), s%Z(k), s%abar(k), s%zbar(k), s%z2bar(k), &
     765            0 :                s%z53bar(k), s%ye(k), s%mass_correction(k), sumx)
     766              :          end do
     767            0 :       end subroutine update_composition_info2
     768              : 
     769            0 :       subroutine set_rotation_seed2
     770              :          use hydro_rotation, only: w_div_w_roche_jrot
     771              : 
     772            0 :          do k = 1, nz
     773              :             s%w_div_w_crit_roche(k) = &
     774              :                w_div_w_roche_jrot(s%r(k), s%m(k), s%j_rot(k), s%cgrav(k), &
     775            0 :                   s%w_div_wcrit_max, s%w_div_wcrit_max2, s%w_div_wc_flag)
     776              :          end do
     777            0 :          if (s%i_w_div_wc /= 0) &
     778            0 :             s%xh(s%i_w_div_wc,1:nz) = s%w_div_w_crit_roche(1:nz)
     779            0 :       end subroutine set_rotation_seed2
     780              : 
     781            0 :       subroutine revise_lnT_for_QHSE2(P_surf, ierr)
     782              :          use eos_def, only: num_eos_basic_results, num_eos_d_dxa_results
     783              :          use chem_def, only: chem_isos
     784              :          use eos_support, only: solve_eos_given_DP
     785              :          use eos_def, only: i_eta, i_lnfree_e
     786              :          use kap_def, only: num_kap_fracs
     787              :          use kap_support, only: get_kap
     788              :          real(dp), intent(in) :: P_surf
     789              :          integer, intent(out) :: ierr
     790              :          real(dp) :: logRho, logP, logT_guess, &
     791              :                      logT_tol, logP_tol, logT, P_m1, P_00, dm_face, &
     792              :                      kap_fracs(num_kap_fracs), kap, dlnkap_dlnRho, dlnkap_dlnT, &
     793              :                      old_kap, new_P_surf, new_T_surf
     794              :          real(dp), dimension(num_eos_basic_results) :: &
     795              :             res, d_dlnd, d_dlnT
     796            0 :          real(dp) :: dres_dxa(num_eos_d_dxa_results, s%species)
     797              :          include 'formats'
     798            0 :          ierr = 0
     799            0 :          P_m1 = P_surf
     800            0 :          do k = 1, nz
     801            0 :             s%lnT(k) = s%xh(s%i_lnT, k)
     802            0 :             s%lnR(k) = s%xh(s%i_lnR, k)
     803            0 :             s%r(k) = exp(s%lnR(k))
     804              :          end do
     805            0 :          do k = 1, nz
     806            0 :             if (k < nz) then
     807            0 :                dm_face = s%dm_bar(k)
     808              :             else
     809            0 :                dm_face = 0.5d0*(s%dm(k - 1) + s%dm(k))
     810              :             end if
     811            0 :             P_00 = P_m1 + s%cgrav(k)*s%m(k)*dm_face/(4d0*pi*pow4(s%r(k)))
     812            0 :             logP = log10(P_00)  ! value for QHSE
     813            0 :             s%lnPeos(k) = logP*ln10
     814            0 :             s%Peos(k) = P_00
     815            0 :             logRho = s%lnd(k)/ln10
     816            0 :             logT_guess = s%lnT(k)/ln10
     817            0 :             logT_tol = 1d-11
     818            0 :             logP_tol = 1d-11
     819              :             call solve_eos_given_DP( &
     820              :                s, k, s%xa(:, k), &
     821              :                logRho, logP, logT_guess, logT_tol, logP_tol, &
     822            0 :                logT, res, d_dlnd, d_dlnT, dres_dxa, ierr)
     823            0 :             if (ierr /= 0) then
     824            0 :                write (*, 2) 'solve_eos_given_DP failed', k
     825            0 :                write (*, '(A)')
     826            0 :                write (*, 1) 'sum(xa)', sum(s%xa(:, k))
     827            0 :                do j = 1, s%species
     828            0 :                   write (*, 4) 'xa(j,k) '//trim(chem_isos%name(s%chem_id(j))), j, j + s%nvar_hydro, k, s%xa(j, k)
     829              :                end do
     830            0 :                write (*, 1) 'logRho', logRho
     831            0 :                write (*, 1) 'logP', logP
     832            0 :                write (*, 1) 'logT_guess', logT_guess
     833            0 :                write (*, 1) 'logT_tol', logT_tol
     834            0 :                write (*, 1) 'logP_tol', logP_tol
     835            0 :                write (*, '(A)')
     836            0 :                call mesa_error(__FILE__, __LINE__, 'revise_lnT_for_QHSE')
     837              :             end if
     838            0 :             s%lnT(k) = logT*ln10
     839            0 :             s%xh(s%i_lnT, k) = s%lnT(k)
     840            0 :             P_m1 = P_00
     841              : 
     842            0 :             if (k == 1) then  ! get opacity and recheck surf BCs
     843              :                call get_kap( &
     844              :                   s, k, s%zbar(k), s%xa(:, k), logRho, logT, &
     845              :                   res(i_lnfree_e), d_dlnd(i_lnfree_e), d_dlnT(i_lnfree_e), &
     846              :                   res(i_eta), d_dlnd(i_eta), d_dlnT(i_eta), &
     847              :                   kap_fracs, kap, dlnkap_dlnRho, dlnkap_dlnT, &
     848            0 :                   ierr)
     849            0 :                if (ierr /= 0) then
     850            0 :                   write (*, 2) 'get_kap failed', k
     851            0 :                   call mesa_error(__FILE__, __LINE__, 'revise_lnT_for_QHSE')
     852              :                end if
     853            0 :                old_kap = s%opacity(1)
     854            0 :                s%opacity(1) = kap  ! for use by atm surf PT
     855            0 :                call get_PT_surf2(new_P_surf, new_T_surf, ierr)
     856            0 :                if (ierr /= 0) then
     857            0 :                   write (*, 2) 'get_PT_surf failed', k
     858            0 :                   call mesa_error(__FILE__, __LINE__, 'revise_lnT_for_QHSE')
     859              :                end if
     860            0 :                write (*, 1) 'new old T_surf', new_T_surf, T_surf
     861            0 :                write (*, 1) 'new old P_surf', new_P_surf, P_surf
     862            0 :                write (*, 1) 'new old kap(1)', kap, old_kap
     863              :             end if
     864              : 
     865              :          end do
     866            0 :       end subroutine revise_lnT_for_QHSE2
     867              : 
     868              :    end subroutine remesh_for_TDC_pulsations
     869              : 
     870              : end module tdc_hydro_support
        

Generated by: LCOV version 2.0-1