LCOV - code coverage report
Current view: top level - star/private - eps_grav.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 1.5 % 135 2
Test Date: 2025-05-08 18:23:42 Functions: 16.7 % 6 1

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2010-2021  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 eps_grav
      21              : 
      22              :       use star_private_def
      23              :       use const_def, only: dp, ln10
      24              :       use chem_def, only: chem_isos
      25              :       use utils_lib, only: mesa_error, is_bad
      26              :       use auto_diff
      27              : 
      28              :       implicit none
      29              : 
      30              :       private
      31              :       public :: eval_eps_grav_and_partials, zero_eps_grav_and_partials
      32              : 
      33              :       contains
      34              : 
      35            0 :       subroutine eval_eps_grav_and_partials(s, k, ierr)
      36              :          type (star_info), pointer :: s
      37              :          integer, intent(in) :: k
      38              :          integer, intent(out) :: ierr
      39              :          type(auto_diff_real_star_order1) :: eps_grav
      40              :          include 'formats'
      41            0 :          ierr = 0
      42              : 
      43            0 :          if (s% dt <= 0) then
      44            0 :             ierr = -1
      45            0 :             return
      46              :          end if
      47              : 
      48            0 :          call zero_eps_grav_and_partials(s, k)
      49              : 
      50              :          ! zero composition derivatives
      51              :          ! if include_composition_in_eps_grav is true
      52              :          ! then these are set in the call to eval_eps_grav_composition
      53            0 :          s% d_eps_grav_dx(:,k) = 0
      54              : 
      55            0 :          call eval1_eps_grav_and_partials(s, k, eps_grav, ierr)
      56            0 :          if (ierr /= 0) return
      57              : 
      58            0 :          s% eps_grav_ad(k) = eps_grav
      59              : 
      60            0 :          if (s% use_other_eps_grav) then
      61              :             ! note: call this after 1st doing the standard calculation
      62            0 :             call s% other_eps_grav(s% id, k, s% dt, ierr)
      63            0 :             if (ierr /= 0) return
      64              :          end if
      65              : 
      66              :          ! apply user-specified scaling factor after hook
      67            0 :          s% eps_grav_ad(k) = s% eps_grav_factor * s% eps_grav_ad(k)
      68              : 
      69              :       end subroutine eval_eps_grav_and_partials
      70              : 
      71              : 
      72            0 :       subroutine eval1_eps_grav_and_partials(s, k, eps_grav, ierr)
      73              :          type (star_info), pointer :: s
      74              :          integer, intent(in) :: k
      75              :          type(auto_diff_real_star_order1), intent(out) :: eps_grav
      76              :          integer, intent(out) :: ierr
      77              : 
      78              :          type(auto_diff_real_star_order1) :: eps_grav_lnS, eps_grav_std
      79            0 :          real(dp) :: alfa, Gamma
      80              :          logical :: using_PC
      81              : 
      82              :          include 'formats'
      83              :          ierr = 0
      84              : 
      85            0 :          using_PC = (s% eos_frac_PC(k) > 0)
      86              : 
      87            0 :          if (using_PC .and. s% gam_start(k) >= s% Gamma_lnS_eps_grav_full_on) then
      88            0 :             call do_lnS_eps_grav(s, k, eps_grav, ierr)
      89            0 :          else if (using_PC .and. s% gam_start(k) > s% Gamma_lnS_eps_grav_full_off) then
      90            0 :             Gamma = s% gam_start(k)
      91              :             alfa = (Gamma - s% Gamma_lnS_eps_grav_full_off) / &
      92            0 :                (s% Gamma_lnS_eps_grav_full_on - s% Gamma_lnS_eps_grav_full_off)
      93            0 :             call do_lnS_eps_grav(s, k, eps_grav_lnS, ierr)
      94            0 :             if (ierr /= 0) return
      95            0 :             call do_std_eps_grav(s, k, eps_grav_std, ierr)
      96            0 :             if (ierr /= 0) return
      97              :             ! the derivative of the blending function is missing
      98              :             ! but historically we've been able to get away with that
      99              :             ! because the two forms should match in the blend region
     100            0 :             eps_grav = alfa * eps_grav_lnS + (1d0 - alfa) * eps_grav_std
     101              :          else
     102            0 :             call do_std_eps_grav(s, k, eps_grav, ierr)
     103              :          end if
     104              : 
     105              : 
     106            0 :          if (ierr /= 0 .or. is_bad(eps_grav% val)) then
     107            0 :             ierr = -1
     108            0 :             s% retry_message = 'failed in eval_eps_grav_and_partials'
     109            0 :             if (s% report_ierr) then
     110              :                write(*,2) &
     111            0 :                   'failed in eval_eps_grav_and_partials', k, eps_grav% val
     112              :             end if
     113            0 :             if (s% stop_for_bad_nums) then
     114            0 :                call mesa_error(__FILE__,__LINE__,'eval1_eps_grav_and_partials')
     115              :             end if
     116            0 :             return
     117              :          end if
     118              : 
     119              :       end subroutine eval1_eps_grav_and_partials
     120              : 
     121              : 
     122              :       ! this uses the given args to calculate -T*ds/dt
     123            0 :       subroutine do_std_eps_grav(s, k, eps_grav, ierr)
     124              :          use auto_diff_support
     125              :          use eos_def, only: i_latent_ddlnT, i_latent_ddlnRho
     126              :          type (star_info), pointer :: s
     127              :          integer, intent(in) :: k
     128              :          type(auto_diff_real_star_order1), intent(out) :: eps_grav
     129              :          integer, intent(out) :: ierr
     130              : 
     131              :          type(auto_diff_real_star_order1) :: T, cp, grada, chiT, chiRho, dlnd_dt, dlnT_dt, &
     132              :             latent_ddlnT, latent_ddlnRho, eps_grav_start, eps_grav_composition_term
     133              : 
     134              :          logical :: test_partials
     135              : 
     136              :          real(dp) :: theta
     137              : 
     138              :          include 'formats'
     139            0 :          ierr = 0
     140              : 
     141              :          !test_partials = (k == s% solver_test_partials_k)
     142            0 :          test_partials = .false.
     143              : 
     144              :          ! select time-centering
     145            0 :          if (s% use_time_centered_eps_grav .and. .not. s% doing_relax) then
     146            0 :             theta = 0.5_dp
     147              :          else
     148            0 :             theta = 1.0_dp
     149              :          end if
     150              : 
     151              :          ! total time derivatives of (Rho, T) basis
     152            0 :          dlnd_dt = wrap_dxh_lnd(s,k)/s% dt
     153            0 :          dlnT_dt = wrap_dxh_lnT(s,k)/s% dt
     154              : 
     155              :          ! thermo quantities
     156            0 :          T = wrap_T_00(s, k)
     157            0 :          Cp = wrap_Cp_00(s, k)
     158            0 :          grada = wrap_grad_ad_00(s, k)
     159            0 :          chiT = wrap_chiT_00(s, k)
     160            0 :          chiRho = wrap_chiRho_00(s, k)
     161              : 
     162              :          ! MESA I, Equation (12)
     163            0 :          eps_grav = -T*Cp * ((1d0 - grada*chiT)*dlnT_dt - grada*chiRho*dlnd_dt)
     164              : 
     165              :          ! phase transition latent heat
     166              :          ! Jermyn et al. (2021), Equation (47)
     167            0 :          latent_ddlnRho = wrap_latent_ddlnRho_00(s,k)
     168            0 :          latent_ddlnT = wrap_latent_ddlnT_00(s,k)
     169            0 :          eps_grav = eps_grav - (dlnd_dt * latent_ddlnRho + dlnT_dt * latent_ddlnT)
     170              : 
     171              : 
     172              :          ! for time centered version
     173            0 :          if (s% use_time_centered_eps_grav .and. .not. s% doing_relax) then
     174              : 
     175              :             ! start values are constants during Newton iters
     176              :             eps_grav_start = -s% T_start(k)*s% cp_start(k) &
     177            0 :                             * ((1d0 - s% grada_start(k)*s% chiT_start(k))*dlnT_dt - s% grada_start(k)*s% chiRho_start(k)*dlnd_dt)
     178              : 
     179              :             ! phase transition latent heat
     180            0 :             eps_grav_start = eps_grav_start - (dlnd_dt * s% latent_ddlnRho_start(k) + dlnT_dt * s% latent_ddlnT_start(k))
     181              : 
     182            0 :             eps_grav = theta * eps_grav + (1d0-theta) * eps_grav_start
     183              : 
     184              :          end if
     185              : 
     186              : 
     187            0 :          if (s% include_composition_in_eps_grav) then
     188            0 :             call eval_eps_grav_composition(s, k, eps_grav_composition_term, ierr)
     189            0 :             if (ierr /= 0) return
     190            0 :             eps_grav = eps_grav + eps_grav_composition_term
     191              :          end if
     192              : 
     193            0 :          if (is_bad(eps_grav% val)) then
     194            0 :             ierr = -1
     195            0 :             s% retry_message = 'do_lnd_eps_grav -- bad value for eps_grav'
     196            0 :             if (s% report_ierr) &
     197            0 :                write(*,2) 'do_lnd_eps_grav -- bad value for eps_grav', k, eps_grav% val
     198            0 :             if (s% stop_for_bad_nums) call mesa_error(__FILE__,__LINE__,'do_lnd_eps_grav')
     199            0 :             return
     200              :          end if
     201              : 
     202              :          if (test_partials) then
     203              :             s% solver_test_partials_val = 0
     204              :             s% solver_test_partials_var = 0
     205              :             s% solver_test_partials_dval_dx = 0
     206              :             write(*,*) 'do_std_eps_grav chiT', s% solver_test_partials_var
     207              :          end if
     208              : 
     209            0 :       end subroutine do_std_eps_grav
     210              : 
     211              : 
     212            0 :       subroutine do_lnS_eps_grav(s, k, eps_grav, ierr)
     213            0 :          use auto_diff_support
     214              :          type (star_info), pointer :: s
     215              :          integer, intent(in) :: k
     216              :          type(auto_diff_real_star_order1), intent(out) :: eps_grav
     217              :          integer, intent(out) :: ierr
     218              : 
     219            0 :          real(dp) :: entropy_start
     220              :          type(auto_diff_real_star_order1) :: entropy, T
     221              : 
     222              :          include 'formats'
     223            0 :          ierr = 0
     224              : 
     225            0 :          T = wrap_T_00(s,k)
     226            0 :          entropy = wrap_s_00(s,k)
     227            0 :          entropy_start = exp(s% lnS_start(k))
     228              : 
     229            0 :          eps_grav = -T*(entropy - entropy_start)/s% dt
     230              : 
     231              :          ! NOTE: the correct version of the composition term to go with TdS is
     232              :          !     -sum_i (\partial e/\partial Y_i)_{s,\rho} dY_i
     233              :          ! see for example, MESA IV, equation (59)
     234              :          !
     235              :          ! When on PC near crystallization (the only place the lnS form is used)
     236              :          ! there are typically no composition changes, so we drop this term
     237              :          !
     238              :          ! The term we normally use comes from expanding in the (rho,T,X) basis
     239              :          !     -sum_ (\partial e/\partial X_i)_{T,\rho} dX_i
     240              :          ! and in practice can be a reasonable approximation to the above.
     241              :          !
     242              :          ! If an such approximation is desired, one could use the following code:
     243              : 
     244              :          ! if (s% include_composition_in_eps_grav) then
     245              :          !    call eval_eps_grav_composition(s, k, eps_grav_composition_term, ierr)
     246              :          !    if (ierr /= 0) return
     247              :          !    eps_grav = eps_grav + eps_grav_composition_term
     248              :          ! end if
     249              : 
     250            0 :          if (is_bad(eps_grav% val)) then
     251            0 :             ierr = -1
     252            0 :             s% retry_message = 'do_lnS_eps_grav -- bad value for eps_grav'
     253            0 :             if (s% report_ierr) &
     254            0 :                write(*,2) 'do_lnS_eps_grav -- bad value for eps_grav', k, eps_grav% val
     255            0 :             if (s% stop_for_bad_nums) call mesa_error(__FILE__,__LINE__,'do_lnS_eps_grav')
     256              :             return
     257              :          end if
     258              : 
     259            0 :       end subroutine do_lnS_eps_grav
     260              : 
     261              : 
     262            0 :       subroutine eval_eps_grav_composition(s, k, eps_grav_composition_term, ierr)
     263            0 :          use auto_diff_support, only: wrap
     264              :          use eos_support, only: get_eos
     265              :          use eos_def, only: num_eos_basic_results, num_eos_d_dxa_results, i_lnE, i_lnPgas
     266              : 
     267              :          type (star_info), pointer :: s
     268              :          integer, intent(in) :: k
     269              :          type(auto_diff_real_star_order1), intent(out) :: eps_grav_composition_term
     270              :          integer, intent(out) :: ierr
     271              :          real(dp) :: &
     272            0 :             e, e_start, de, d_de_dlnd, d_de_dlnT, &
     273            0 :             e_with_xa_start, d_e_with_xa_start_dlnd, d_e_with_xa_start_dlnT, &
     274            0 :             e_with_DT_start, Pgas_with_DT_start
     275              :          real(dp), dimension(num_eos_basic_results) :: &
     276            0 :             res, dres_dlnd, dres_dlnT
     277            0 :          real(dp) :: dres_dxa(num_eos_d_dxa_results,s% species)
     278              :          integer :: j
     279              :          logical :: test_partials
     280              : 
     281            0 :          real(dp) :: theta
     282              : 
     283              :          include 'formats'
     284            0 :          ierr = 0
     285              : 
     286            0 :          eps_grav_composition_term = 0
     287              : 
     288            0 :          if (s% use_time_centered_eps_grav .and. .not. s% doing_relax) then
     289              :             theta = 0.5_dp
     290              :          else
     291            0 :             theta = 1.0_dp
     292              :          end if
     293              : 
     294              :          ! some EOSes have composition partials and some do not
     295              :          ! those currently without dx partials are PC & Skye
     296              :          ! however, composition partials of lnE & lnP were revised by the call to
     297              :          ! fix_d_eos_dxa_partials at the beginning of eval_equ_for_solver
     298              : 
     299              :          ! directly fill array with composition derivatives
     300              :          ! we only have lnE and lnP derivs, so use
     301              :          ! eps_grav = -de/dt + (P/rho) * dlnd/dt
     302            0 :          do j=1, s% species
     303              :             s% d_eps_grav_dx(j,k) = -s% energy(k) * s% dlnE_dxa_for_partials(j,k)/s% dt + &
     304            0 :                (s% Peos(k) / s% Rho(k)) * s% dlnPeos_dxa_for_partials(j,k) * s% dxh_lnd(k)/s% dt
     305              :          end do
     306              : 
     307            0 :          e = s% energy(k)
     308              :          call get_eos( &
     309              :             s, k, s% xa_start(:,k), &
     310              :             s% rho(k), s% lnd(k)/ln10, s% T(k), s% lnT(k)/ln10, &
     311              :             res, dres_dlnd, dres_dlnT, &
     312            0 :             dres_dxa, ierr)
     313            0 :          if (ierr /= 0) then
     314            0 :             if (s% report_ierr) write(*,2) 'failed in get_eos with xa_start', k
     315            0 :             return
     316              :          end if
     317              : 
     318            0 :          e_with_xa_start = exp(res(i_lnE))
     319            0 :          d_e_with_xa_start_dlnd = dres_dlnd(i_lnE)*e_with_xa_start
     320            0 :          d_e_with_xa_start_dlnT = dres_dlnT(i_lnE)*e_with_xa_start
     321              : 
     322            0 :          de = (e - e_with_xa_start)
     323            0 :          d_de_dlnd = (s% dE_dRho_for_partials(k)*s% Rho(k) - d_e_with_xa_start_dlnd)
     324            0 :          d_de_dlnT = (s% Cv_for_partials(k)*s% T(k) - d_e_with_xa_start_dlnT)
     325              : 
     326            0 :          if (s% use_time_centered_eps_grav .and. .not. s% doing_relax) then
     327              : 
     328            0 :             e_start = s% energy_start(k)
     329              : 
     330              :             call get_eos( &
     331              :                s, k, s% xa(:,k), &
     332              :                s% rho_start(k), s% lnd_start(k)/ln10, s% T_start(k), s% lnT_start(k)/ln10, &
     333              :                res, dres_dlnd, dres_dlnT, &
     334            0 :                dres_dxa, ierr)
     335            0 :             if (ierr /= 0) then
     336            0 :                if (s% report_ierr) write(*,2) 'failed in get_eos with xa_start', k
     337            0 :                return
     338              :             end if
     339              : 
     340            0 :             e_with_DT_start = exp(res(i_lnE))
     341            0 :             de = theta * de + (1d0 - theta) * (e_with_DT_start - e_start)
     342            0 :             d_de_dlnd = theta * d_de_dlnd
     343            0 :             d_de_dlnT = theta * d_de_dlnT
     344              : 
     345              :             Pgas_with_DT_start = exp(res(i_lnPgas))
     346              : 
     347              :             ! combine with end-of-step composition derivatives
     348              : 
     349              :             ! until EOSes always provide composition derivatives, ignore this
     350              :             ! the end-of-step term should generally be similar enough to be OK
     351              : 
     352              :             ! do j=1, s% species
     353              :             !    s% d_eps_grav_dx(j,k) = theta * s% d_eps_grav_dx(j,k) + (1d0 - theta) * &
     354              :             !       (-e_with_DT_start*d_dxa(i_lnE,j)/s% dt + &
     355              :             !       Pgas_with_DT_start*d_dxa(i_lnPgas,j)/s% rho_start(k)*s% dxh_lnd(k)/s% dt)
     356              :             ! end do
     357              : 
     358              :          end if
     359              : 
     360              :          call wrap(eps_grav_composition_term, -de/s% dt, &
     361              :             0d0, -d_de_dlnd/s% dt, 0d0, &
     362              :             0d0, -d_de_dlnT/s% dt, 0d0, &
     363              :             0d0, 0d0, 0d0, &
     364              :             0d0, 0d0, 0d0, &
     365              :             0d0, 0d0, 0d0, &
     366              :             0d0, 0d0, 0d0, &
     367              :             0d0, 0d0, 0d0, &
     368              :             0d0, 0d0, 0d0, &
     369              :             0d0, 0d0, 0d0, &
     370              :             0d0, 0d0, 0d0, &
     371            0 :             0d0, 0d0, 0d0)
     372              : 
     373              :          ! add easy access to this quantity in star
     374            0 :          s% eps_grav_composition_term(k) = eps_grav_composition_term% val
     375              : 
     376              :          !test_partials = (k == s% solver_test_partials_k)
     377            0 :          test_partials = .false.
     378              : 
     379              :          if (test_partials) then
     380              :             s% solver_test_partials_val = de
     381              :             s% solver_test_partials_var = s% i_lnT
     382              :             s% solver_test_partials_dval_dx = d_de_dlnT
     383              :             write(*,*) 'get_dedt', s% solver_test_partials_var
     384              :          end if
     385              : 
     386            0 :          if (is_bad(eps_grav_composition_term% val)) then
     387            0 :             if (s% report_ierr) then
     388            0 :                write(*, *) s% retry_message
     389            0 :                write(*,2) 'eps_grav_composition_term', k, eps_grav_composition_term% val
     390              :             end if
     391            0 :             if (s% stop_for_bad_nums) then
     392            0 :                write(*,2) 'include_composition_in_eps_grav -- bad value for eps_grav_composition_term', &
     393            0 :                           k, eps_grav_composition_term% val
     394            0 :                call mesa_error(__FILE__,__LINE__,'eval_eps_grav_composition')
     395              :             end if
     396            0 :             return
     397              :          end if
     398              : 
     399            0 :       end subroutine eval_eps_grav_composition
     400              : 
     401              : 
     402        52348 :       subroutine zero_eps_grav_and_partials(s, k)
     403              :          type (star_info), pointer :: s
     404              :          integer, intent(in) :: k
     405        52348 :          s% eps_grav_ad(k) = 0
     406            0 :       end subroutine zero_eps_grav_and_partials
     407              : 
     408              :       end module eps_grav
        

Generated by: LCOV version 2.0-1