LCOV - code coverage report
Current view: top level - kap/private - condint.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 73.3 % 195 143
Test Date: 2025-05-08 18:23:42 Functions: 100.0 % 5 5

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2011-2019  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 condint
      21              : 
      22              :       use const_def, only: dp, iln10
      23              :       use math_lib
      24              :       use utils_lib, only: mesa_error
      25              : 
      26              :       implicit none
      27              : 
      28              :       private
      29              :       public :: init_potekhin
      30              :       public :: do_electron_conduction
      31              : 
      32              :       integer, parameter :: num_logTs=29, num_logRhos=71, num_logzs=15
      33              :       !!! NB: These parameters must be consistent with the table "condtabl.d"!
      34              :       logical :: initialized = .false.
      35              : 
      36              :       real(dp) :: logTs(num_logTs), logRhos(num_logRhos), logzs(num_logzs)
      37              :       real(dp), target :: f_ary(4*num_logRhos*num_logTs*num_logzs)  ! for bicubic splines
      38              :       real(dp), pointer :: f(:,:,:,:)
      39              :       integer :: ilinx(num_logzs), iliny(num_logzs)
      40              : 
      41              :       contains
      42              : 
      43           11 :       subroutine init_potekhin(ierr)
      44              :          use kap_def, only: kap_dir
      45              :          use interp_2d_lib_db, only: interp_mkbicub_db
      46              :          integer, intent(out) :: ierr
      47              : 
      48              :          character (len=256) :: filename
      49              :          integer :: read_err, iz, it, ir, shift
      50              :          integer :: ibcxmin                   ! bc flag for x=xmin
      51          330 :          real(dp) :: bcxmin(num_logTs)               ! bc data vs. y at x=xmin
      52              :          integer :: ibcxmax                   ! bc flag for x=xmax
      53          330 :          real(dp) :: bcxmax(num_logTs)               ! bc data vs. y at x=xmax
      54              :          integer :: ibcymin                   ! bc flag for y=ymin
      55          792 :          real(dp) :: bcymin(num_logRhos)               ! bc data vs. x at y=ymin
      56              :          integer :: ibcymax                   ! bc flag for y=ymax
      57          792 :          real(dp) :: bcymax(num_logRhos)               ! bc data vs. x at y=ymax
      58           11 :          real(dp) :: Z
      59           11 :          real(dp), pointer :: f1(:)
      60              : 
      61              :          include 'formats'
      62              : 
      63           11 :          ierr = 0
      64           11 :          if (initialized) return
      65              : 
      66            5 :          shift = 4*num_logRhos*num_logTs
      67            5 :          f(1:4,1:num_logRhos,1:num_logTs,1:num_logzs) => f_ary(1:shift*num_logzs)
      68              : 
      69            5 :          filename = trim(kap_dir) // '/condtabl.data'
      70            5 :          open(1,file=trim(filename),status='OLD',iostat=ierr)
      71            5 :          if (ierr /= 0) then
      72            0 :             write(*,'(A)')
      73            0 :             write(*,'(A)')
      74            0 :             write(*,'(A)')
      75            0 :             write(*,'(A)')
      76            0 :             write(*,'(A)')
      77            0 :             write(*,*) 'NOTICE: missing ' // trim(filename)
      78            0 :             write(*,*) 'Please remove the directory mesa/data/kap_data,'
      79            0 :             write(*,*) 'and rerun the mesa ./install script.'
      80            0 :             write(*,'(A)')
      81            0 :             call mesa_error(__FILE__,__LINE__)
      82              :          end if
      83              :          !print*,'Reading thermal conductivity data...'
      84            5 :          read_err = 0
      85            5 :          read(1,'(A)',iostat=read_err)  ! skip the first line
      86            5 :          if (read_err /= 0) ierr = read_err
      87           80 :          do iz = 1, num_logzs
      88           75 :             read (1,*,iostat=read_err) z, (logTs(it),it=1,num_logTs)
      89           75 :             if (read_err /= 0) ierr = read_err
      90           75 :             if (z == 1d0) then
      91            5 :                logzs(iz) = 0d0
      92              :             else
      93           70 :                logzs(iz) = log10(z)
      94              :             end if
      95         5405 :             do ir = 1, num_logRhos
      96         5325 :                read(1,*,iostat=read_err) logRhos(ir), (f(1,ir,it,iz),it=1,num_logTs)
      97         5400 :                if (read_err /= 0) ierr = read_err
      98              :             end do
      99              :          end do
     100            5 :          close(1)
     101            5 :          if (ierr /= 0) then
     102            0 :             write(*,'(A)')
     103            0 :             write(*,'(A)')
     104            0 :             write(*,'(A)')
     105            0 :             write(*,'(A)')
     106            0 :             write(*,'(A)')
     107            0 :             write(*,*) 'NOTICE: error trying to read ' // trim(filename)
     108            0 :             write(*,*) 'Please remove the directory mesa/data/kap_data,'
     109            0 :             write(*,*) 'and rerun the mesa ./install script.'
     110            0 :             write(*,'(A)')
     111            0 :             call mesa_error(__FILE__,__LINE__)
     112              :          end if
     113              :          ! boundary condition is slope=0 at edges of tables
     114              :          ! this ensures continuous derivatives when we clip
     115            5 :          ibcxmin = 3; bcxmin(1:num_logTs) = 0d0
     116            5 :          ibcxmax = 3; bcxmax(1:num_logTs) = 0d0
     117            5 :          ibcymin = 3; bcymin(1:num_logRhos) = 0d0
     118            5 :          ibcymax = 3; bcymax(1:num_logRhos) = 0d0
     119           80 :          do iz = 1, num_logzs
     120           75 :             f1(1:shift) => f_ary(1+(iz-1)*shift:iz*shift)
     121              :             call interp_mkbicub_db( &
     122              :                logRhos, num_logRhos, logTs, num_logTs, f1, num_logRhos, &
     123              :                ibcxmin, bcxmin, ibcxmax, bcxmax, &
     124              :                ibcymin, bcymin, ibcymax, bcymax, &
     125           75 :                ilinx(iz), iliny(iz), ierr)
     126           80 :             if (ierr /= 0) then
     127            0 :                write(*,'(A)')
     128            0 :                write(*,'(A)')
     129            0 :                write(*,'(A)')
     130            0 :                write(*,'(A)')
     131            0 :                write(*,'(A)')
     132            0 :                write(*,*) 'NOTICE: error in ' // trim(filename)
     133            0 :                write(*,*) 'Please report the problem.'
     134            0 :                write(*,'(A)')
     135            0 :                call mesa_error(__FILE__,__LINE__)
     136              :             end if
     137              :          end do
     138            5 :          initialized = .true.
     139           11 :       end subroutine init_potekhin
     140              : 
     141              : 
     142        86216 :       subroutine do_electron_conduction_potekhin( &
     143              :             zbar, logRho_in, logT_in, kap, dlogkap_dlogRho, dlogkap_dlogT, ierr)
     144              : 
     145              :          use const_def, only: boltz_sigma
     146              :          real(dp), intent(in) :: zbar, logRho_in, logT_in
     147              :          real(dp), intent(out) :: kap, dlogkap_dlogRho, dlogkap_dlogT
     148              :          integer, intent(out) :: ierr
     149              : 
     150              :          integer :: iz, iz1, iz2, shift
     151        86216 :          real(dp) :: zlog, logRho, logT
     152        86216 :          real(dp) :: alfa, beta, &
     153        86216 :             logK1, kap1, dlogK1_dlogRho, dlogK1_dlogT, &
     154        86216 :             logK2, kap2, dlogK2_dlogRho, dlogK2_dlogT, &
     155        86216 :             logK, logkap, dlogK_dlogRho, dlogK_dlogT
     156        86216 :          real(dp), pointer :: f1(:)
     157              : 
     158              :          logical :: clipped_logRho, clipped_logT
     159              : 
     160              :          include 'formats'
     161              : 
     162        86216 :          ierr = 0
     163        86216 :          shift = 4*num_logRhos*num_logTs
     164              : 
     165        86216 :          if (logRho_in < logRhos(1)) then
     166        22832 :             logRho = logRhos(1)
     167        22832 :             clipped_logRho = .true.
     168        63384 :          else if (logRho_in > logRhos(num_logRhos)) then
     169            0 :             logRho = logRhos(num_logRhos)
     170            0 :             clipped_logRho = .true.
     171              :          else
     172        63384 :             logRho = logRho_in
     173        63384 :             clipped_logRho = .false.
     174              :          end if
     175              : 
     176        86216 :          if (logT_in < logTs(1)) then
     177            0 :             logT = logTs(1)
     178            0 :             clipped_logT = .true.
     179        86216 :          else if (logT_in > logTs(num_logTs)) then
     180            0 :             logT = logTs(num_logTs)
     181            0 :             clipped_logT = .true.
     182              :          else
     183        86216 :             logT = logT_in
     184        86216 :             clipped_logT = .false.
     185              :          end if
     186              : 
     187        86216 :          zlog = max(logzs(1),min(logzs(num_logzs),log10(max(1d-30,zbar))))
     188              : 
     189        86216 :          if (zlog <= logzs(1)) then  ! use 1st
     190            0 :             call get1(1, logK, dlogK_dlogRho, dlogK_dlogT, ierr)
     191        86216 :          else if (zlog >= logzs(num_logzs)) then  ! use last
     192            0 :             call get1(num_logzs, logK, dlogK_dlogRho, dlogK_dlogT, ierr)
     193              :          else  ! interpolate
     194        86216 :             iz1 = -1
     195        86218 :             do iz = 2, num_logzs
     196        86218 :                if (zlog >= logzs(iz-1) .and. zlog <= logzs(iz)) then
     197        86216 :                   iz1 = iz-1; iz2 = iz; exit
     198              :                end if
     199              :             end do
     200        86216 :             if (iz1 < 0) then
     201            0 :                write(*,2) 'num_logzs', num_logzs
     202            0 :                do iz = 1, num_logzs
     203            0 :                   write(*,2) 'logzs(iz)', iz, logzs(iz)
     204              :                end do
     205            0 :                write(*,1) 'zlog', zlog
     206            0 :                write(*,*) 'confusion in do_electron_conduction'
     207            0 :                call mesa_error(__FILE__,__LINE__)
     208              :             end if
     209              : 
     210        86216 :             call get1(iz1, logK1, dlogK1_dlogRho, dlogK1_dlogT, ierr)
     211        86216 :             if (ierr /= 0) then
     212            0 :                write(*,*) 'interp failed for iz1 in do_electron_conduction', iz1, logRho, logT
     213            0 :                call mesa_error(__FILE__,__LINE__)
     214              :             end if
     215              : 
     216        86216 :             call get1(iz2, logK2, dlogK2_dlogRho, dlogK2_dlogT, ierr)
     217        86216 :             if (ierr /= 0) then
     218            0 :                write(*,*) 'interp failed for iz2 in do_electron_conduction', iz2, logRho, logT
     219            0 :                call mesa_error(__FILE__,__LINE__)
     220              :             end if
     221              : 
     222              :             ! linear interpolation in zlog
     223        86216 :             alfa = (zlog - logzs(iz1)) / (logzs(iz2) - logzs(iz1))
     224        86216 :             beta = 1d0-alfa
     225        86216 :             logK = alfa*logK2 + beta*logK1
     226        86216 :             if (clipped_logRho) then
     227        22832 :                dlogK_dlogRho = 0
     228              :             else
     229        63384 :                dlogK_dlogRho = alfa*dlogK2_dlogRho + beta*dlogK1_dlogRho
     230              :             end if
     231        86216 :             if (clipped_logT) then
     232            0 :                dlogK_dlogT = 0
     233              :             else
     234        86216 :                dlogK_dlogT = alfa*dlogK2_dlogT + beta*dlogK1_dlogT
     235              :             end if
     236              :          end if
     237              : 
     238              :          ! chi = thermal conductivity, = 10**logK (cgs units)
     239              :          ! conduction opacity kappa = 16*boltz_sigma*T^3 / (3*rho*chi)
     240              :          ! logkap = 3*logT - logRho - logK + log10(16*boltz_sigma/3)
     241              : 
     242        86216 :          logkap = 3d0*logT_in - logRho_in - logK + log10(16d0 * boltz_sigma / 3d0)
     243              : 
     244        86216 :          kap = exp10(logkap)
     245        86216 :          dlogkap_dlogRho = -1d0 - dlogK_dlogRho
     246        86216 :          dlogkap_dlogT = 3d0 - dlogK_dlogT
     247              : 
     248              :          contains
     249              : 
     250              : 
     251       172432 :          subroutine get1(iz, logK, dlogK_dlogRho, dlogK_dlogT, ierr)
     252              :             use kap_eval_support, only: Do_Kap_Interpolations
     253              :             integer, intent(in) :: iz
     254              :             real(dp), intent(out) :: logK, dlogK_dlogRho, dlogK_dlogT
     255              :             integer, intent(out) :: ierr
     256              :             logical, parameter :: dbg = .false.
     257              :             real(dp) :: fval, df_dx, df_dy, &
     258       172432 :                logRho0, logRho1, logT0, logT1
     259              :             integer :: i_logRho, j_logT, k
     260              :             include 'formats'
     261       172432 :             ierr = 0
     262       172432 :             f1(1:shift) => f_ary(1+(iz-1)*shift:iz*shift)
     263              : 
     264       172432 :             if (logRho < logRhos(2)) then
     265        48472 :                i_logRho = 1
     266              :             else
     267       123960 :                i_logRho = num_logRhos-1
     268      2464396 :                do k=2,num_logRhos-1
     269      2464396 :                   if (logRho >= logRhos(k) .and. logRho < logRhos(k+1)) then
     270       123960 :                      i_logRho = k; exit
     271              :                   end if
     272              :                end do
     273              :             end if
     274       172432 :             logRho0 = logRhos(i_logRho)
     275       172432 :             logRho1 = logRhos(i_logRho+1)
     276              : 
     277       172432 :             if (logT < logTs(2)) then
     278           32 :                j_logT = 1
     279              :             else
     280       172400 :                j_logT = num_logTs-1
     281      1995158 :                do k=2,num_logTs-1
     282      1995158 :                   if (logT >= logTs(k) .and. logT < logTs(k+1)) then
     283       172400 :                      j_logT = k; exit
     284              :                   end if
     285              :                end do
     286              :             end if
     287       172432 :             logT0 = logTs(j_logT)
     288       172432 :             logT1 = logTs(j_logT+1)
     289              : 
     290              :             call Do_Kap_Interpolations( &
     291              :                f1, num_logRhos, num_logTs, i_logRho, j_logT, logRho0, &
     292       172432 :                logRho, logRho1, logT0, logT, logT1, logK, dlogK_dlogRho, dlogK_dlogT)
     293       172432 :             if (ierr /= 0) return
     294              : 
     295       172432 :          end subroutine get1
     296              : 
     297              : 
     298              :       end subroutine do_electron_conduction_potekhin
     299              : 
     300              : 
     301        86216 :       subroutine do_electron_conduction_blouin( &
     302              :          zbar, logRho, logT, &
     303              :          kap, dlnkap_dlnRho, dlnkap_dlnT, ierr)
     304              :          use const_def, only: dp
     305              :          use auto_diff
     306              :          real(dp), intent(in) :: zbar  ! average ionic charge (for electron conduction)
     307              :          real(dp), intent(in) :: logRho  ! the density
     308              :          real(dp), intent(in) :: logT  ! the temperature
     309              :          real(dp), intent(out) :: kap  ! electron conduction opacity
     310              :          real(dp), intent(out) :: dlnkap_dlnRho  ! partial derivative at constant T
     311              :          real(dp), intent(out) :: dlnkap_dlnT   ! partial derivative at constant Rho
     312              :          integer, intent(out) :: ierr  ! 0 means AOK.
     313              : 
     314              :          ! this implements the correction formulae from Blouin et al. (2020)
     315              :          ! https://ui.adsabs.harvard.edu/abs/2020ApJ...899...46B/abstract
     316              : 
     317              :          real(dp), parameter :: alpha_H = -0.52d0, alpha_He = -0.46d0
     318              :          real(dp), parameter :: a_H = 2.0d0, a_He = 1.25d0
     319              :          real(dp), parameter :: b_H = 10.0d0, b_He = 2.5d0
     320              :          real(dp), parameter :: logRho0_H = 5.45d0, logRho0_He = 6.50d0
     321              :          real(dp), parameter :: logT0_H = 8.40d0, logT0_He = 8.57d0
     322              :          real(dp), parameter :: sigRho_H = 5.14d0, sigRho_He = 6.20d0
     323              :          real(dp), parameter :: sigT_H = 0.45d0, sigT_He = 0.55d0
     324              : 
     325              :          type(auto_diff_real_2var_order1) :: logRho_auto, logT_auto
     326              :          type(auto_diff_real_2var_order1) :: Rhostar, Tstar
     327              :          type(auto_diff_real_2var_order1) :: g_H, g_He, H_H, H_He
     328              :          type(auto_diff_real_2var_order1) :: log_correction, log_correction_H, log_correction_He
     329              : 
     330        86216 :          real(dp) :: alfa, frac_H, frac_He
     331              : 
     332              :          ! auto_diff
     333              :          ! var1: lnRho
     334              :          ! var2: lnT
     335              : 
     336        86216 :          logRho_auto = logRho
     337        86216 :          logRho_auto% d1val1 = iln10
     338        86216 :          logT_auto = logT
     339        86216 :          logT_auto% d1val2 = iln10
     340              : 
     341              :          ! call previous standard routines (Cassisi/Potekhin)
     342              :          call do_electron_conduction_potekhin( &
     343              :             zbar, logRho, logT, &
     344        86216 :             kap, dlnkap_dlnRho, dlnkap_dlnT, ierr)
     345              : 
     346              :          ! combined correction
     347              :          !
     348              :          ! The thermal conductivity is tabulated at Zbar = {1,2,3,4,6,...}
     349              :          ! and linear interpolation in logK vs logZbar is applied.
     350              :          !
     351              :          ! Therefore, we apply the Blouin+ 2020 corrections in a manner
     352              :          ! equivalent to individually correcting the Zbar = {1,2} tables.
     353              : 
     354        86216 :          if (Zbar <= 1d0) then  ! all H
     355            0 :             frac_H = 1d0
     356            0 :             frac_He = 0d0
     357        86216 :          else if (Zbar <= 2d0) then  ! mix H and He
     358        86214 :             alfa = (log10(Zbar) - log10(1d0)) / (log10(2d0) - log10(1d0))
     359        86214 :             frac_H = 1d0 - alfa
     360        86214 :             frac_He = alfa
     361            2 :          else if (Zbar <= 3d0) then  ! mix He and no correction
     362            2 :             alfa = (log10(Zbar) - log10(2d0)) / (log10(3d0) - log10(2d0))
     363            2 :             frac_H = 0d0
     364            2 :             frac_He = 1d0 - alfa
     365              :          else  ! no correction
     366              :             frac_H = 0d0
     367              :             frac_He = 0d0
     368              :             return
     369              :          end if
     370              : 
     371              : 
     372        86216 :          if (frac_H > 0) then
     373              : 
     374              :             ! correction for H
     375        86214 :             Rhostar = logRho_auto - logRho0_H
     376        86214 :             Tstar = logT_auto - logT0_H
     377              : 
     378              :             g_H = a_H * exp(&
     379              :                -pow2(Tstar*cos(alpha_H) + Rhostar*sin(alpha_H))/pow2(sigT_H)  &
     380        86214 :                -pow2(Tstar*sin(alpha_H) - Rhostar*cos(alpha_H))/pow2(sigRho_H))
     381              : 
     382        86214 :             H_H = 0.5d0 * tanh(b_H*(g_H-0.5d0)) + 0.5d0
     383              : 
     384        86214 :             log_correction_H = -log(1d0 + g_H * H_H)
     385              : 
     386              :          else
     387              : 
     388            2 :             log_correction_H = 0d0
     389              : 
     390              :          end if
     391              : 
     392              : 
     393        86216 :          if (frac_He > 0) then
     394              : 
     395              :             ! correction for He
     396        86216 :             Rhostar = logRho_auto - logRho0_He
     397        86216 :             Tstar = logT_auto - logT0_He
     398              : 
     399              :             g_He = a_He * exp(&
     400              :                -pow2(Tstar*cos(alpha_He) + Rhostar*sin(alpha_He))/pow2(sigT_He)  &
     401        86216 :                -pow2(Tstar*sin(alpha_He) - Rhostar*cos(alpha_He))/pow2(sigRho_He))
     402              : 
     403        86216 :             H_He = 0.5d0 * tanh(b_He*(g_He-0.5d0)) + 0.5d0
     404              : 
     405        86216 :             log_correction_He = -log(1d0 + g_He * H_He)
     406              : 
     407              :          else
     408              : 
     409            0 :             log_correction_He = 0d0
     410              : 
     411              :          end if
     412              : 
     413              : 
     414              :          ! blend H correction, He correction, and other correction (none)
     415        86216 :          log_correction = frac_H * log_correction_H + frac_He * log_correction_He
     416              : 
     417              :          ! apply correction factor
     418        86216 :          kap = exp(log(kap) + log_correction% val)
     419        86216 :          dlnkap_dlnRho = dlnkap_dlnRho + log_correction% d1val1
     420        86216 :          dlnkap_dlnT = dlnkap_dlnT + log_correction% d1val2
     421              : 
     422        86216 :       end subroutine do_electron_conduction_blouin
     423              : 
     424        86216 :       subroutine do_electron_conduction( &
     425              :          rq, zbar, logRho, logT, &
     426              :          kap, dlnkap_dlnRho, dlnkap_dlnT, ierr)
     427        86216 :          use kap_def, only: Kap_General_Info
     428              :          type (Kap_General_Info), pointer, intent(in) :: rq
     429              :          real(dp), intent(in) :: zbar  ! average ionic charge (for electron conduction)
     430              :          real(dp), intent(in) :: logRho  ! the density
     431              :          real(dp), intent(in) :: logT  ! the temperature
     432              :          real(dp), intent(out) :: kap  ! electron conduction opacity
     433              :          real(dp), intent(out) :: dlnkap_dlnRho  ! partial derivative at constant T
     434              :          real(dp), intent(out) :: dlnkap_dlnT   ! partial derivative at constant Rho
     435              :          integer, intent(out) :: ierr  ! 0 means AOK.
     436              : 
     437        86216 :          if (rq% use_blouin_conductive_opacities) then
     438              :             call do_electron_conduction_blouin( &
     439              :                zbar, logRho, logT, &
     440        86216 :                kap, dlnkap_dlnRho, dlnkap_dlnT, ierr)
     441              :          else
     442              :             call do_electron_conduction_potekhin( &
     443              :                zbar, logRho, logT, &
     444            0 :                kap, dlnkap_dlnRho, dlnkap_dlnT, ierr)
     445              :          end if
     446              : 
     447        86216 :       end subroutine do_electron_conduction
     448              : 
     449              :       end module condint
        

Generated by: LCOV version 2.0-1