LCOV - code coverage report
Current view: top level - binary/private - binary_ce.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 208 0
Test Date: 2025-10-14 06:41:40 Functions: 0.0 % 4 0

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2010-2019  Pablo Marchant & 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              : 
      21              :       module binary_ce
      22              : 
      23              :       use const_def, only: dp, avo, secyer, boltzm, standard_cgrav, crad, ev2erg, rsun, msun
      24              :       use math_lib
      25              :       use star_lib
      26              :       use star_def
      27              :       use binary_def
      28              :       use interp_1d_def, only: pm_work_size
      29              :       use interp_1d_lib, only: interp_pm, interp_values, interp_value
      30              : 
      31              :       implicit none
      32              : 
      33              :       contains
      34              : 
      35            0 :       subroutine CE_init(b, restart, ierr)
      36              :          use interp_1d_def, only: pm_work_size
      37              :          use interp_1d_lib, only: interp_pm
      38              :          type (binary_info), pointer :: b
      39              :          logical, intent(in) :: restart
      40              :          integer, intent(out) :: ierr
      41              :          type (star_info), pointer :: s
      42            0 :          real(dp), pointer :: interp_work(:), adjusted_energy(:)
      43              :          integer :: k, op_err
      44            0 :          real(dp) :: rec_energy_HII_to_HI, &
      45            0 :                      rec_energy_HeII_to_HeI, &
      46            0 :                      rec_energy_HeIII_to_HeII, &
      47            0 :                      diss_energy_H2, &
      48            0 :                      frac_HI, frac_HII, &
      49            0 :                      frac_HeI, frac_HeII, frac_HeIII, &
      50            0 :                      avg_charge_He, energy_comp
      51              :          include 'formats'
      52              : 
      53              :          ! TODO: no care is taken in here when model_twins_flag is true
      54              :          ! not a priority, but needs to be sorted out whenever double core
      55              :          ! evolution is implemented
      56              : 
      57            0 :          ierr = 0
      58              : 
      59            0 :          if (b% use_other_CE_init) then
      60            0 :             call b% other_CE_init(b% binary_id, restart, ierr)
      61            0 :             return
      62              :          end if
      63              : 
      64            0 :          write(*,*) "TURNING ON CE"
      65              : 
      66            0 :          b% s_donor% mix_factor = 0d0
      67            0 :          b% s_donor% dxdt_nuc_factor = 0d0
      68            0 :          if (b% point_mass_i == 0) then
      69            0 :             b% s_accretor% mix_factor = 0d0
      70            0 :             b% s_accretor% dxdt_nuc_factor = 0d0
      71              :          end if
      72              : 
      73            0 :          if (b% d_i == 1) then
      74            0 :             b% CE_num1 = b% CE_num1 + 1
      75              :          else
      76            0 :             b% CE_num2 = b% CE_num2 + 1
      77              :          end if
      78              : 
      79            0 :          b% keep_donor_fixed = .true.
      80              : 
      81            0 :          b% CE_init = .true.
      82              : 
      83            0 :          if (.not. restart) then
      84            0 :             b% CE_years_detached = 0d0
      85            0 :             s => b% s_donor
      86              : 
      87            0 :             write(*,*) "Initiating common envelope phase"
      88              : 
      89            0 :             allocate(b% CE_m(s% nz), b% CE_entropy(4*s% nz), stat=ierr)
      90            0 :             if(ierr /= 0) then
      91            0 :                write(*,*) "CE_init: Error during allocation"
      92            0 :                return
      93              :             end if
      94            0 :             allocate(b% CE_U_in(4*s% nz), b% CE_U_out(4*s% nz), b% CE_Omega_in(4*s% nz), b% CE_Omega_out(4*s% nz), stat=ierr)
      95            0 :             if(ierr /= 0) then
      96            0 :                write(*,*) "CE_init: Error during allocation"
      97            0 :                return
      98              :             end if
      99              : 
     100              :             ! get energy from the EOS and adjust the different contributions from recombination/dissociation
     101            0 :             allocate(adjusted_energy(s% nz))
     102            0 :             do k=1, s% nz
     103              :                ! the following lines compute the fractions of HI, HII, HeI, HeII and HeIII
     104              :                ! things like ion_ifneut_H are defined in $MESA_DIR/ionization/public/ionization.def
     105              :                ! this file can be checked for additional ionization output available
     106            0 :                frac_HI = 0d0  !get_ion_info(s,ion_ifneut_H,k)
     107            0 :                frac_HII = 1 - frac_HI
     108              : 
     109              :                ! ionization module provides neutral fraction and average charge of He.
     110              :                ! use these two to compute the mass fractions of HeI and HeII
     111            0 :                frac_HeI = 0d0  !get_ion_info(s,ion_ifneut_He,k)
     112            0 :                avg_charge_He = 2d0  !get_ion_info(s,ion_iZ_He,k)
     113              :                ! the following is the solution to the equations
     114              :                !   avg_charge_He = 2*fracHeIII + 1*fracHeII
     115              :                !               1 = fracHeI + fracHeII + fracHeIII
     116            0 :                frac_HeII = 2 - 2*frac_HeI - avg_charge_He
     117            0 :                frac_HeIII = 1 - frac_HeII - frac_HeI
     118              : 
     119              :                ! recombination energies from https://physics.nist.gov/PhysRefData/ASD/ionEnergy.html
     120            0 :                rec_energy_HII_to_HI = avo*13.59843449d0*frac_HII*ev2erg*s% X(k)
     121            0 :                diss_energy_H2 = avo*4.52d0/2d0*ev2erg*s% X(k)
     122            0 :                rec_energy_HeII_to_HeI = avo*24.58738880d0*(frac_HeII+frac_HeIII)*ev2erg*s% Y(k)/4d0
     123            0 :                rec_energy_HeIII_to_HeII = avo*54.4177650d0*frac_HeIII*ev2erg*s% Y(k)/4d0
     124              : 
     125              :                adjusted_energy(k) = s% energy(k) &
     126              :                                     - (1d0-b% CE_energy_factor_HII_toHI)*rec_energy_HII_to_HI &
     127              :                                     - (1d0-b% CE_energy_factor_HeII_toHeI)*rec_energy_HeII_to_HeI &
     128              :                                     - (1d0-b% CE_energy_factor_HeIII_toHeII)*rec_energy_HeIII_to_HeII &
     129            0 :                                     - (1d0-b% CE_energy_factor_H2)*diss_energy_H2
     130              : 
     131            0 :                if (adjusted_energy(k) < 0d0 .or. adjusted_energy(k) > s% energy(k)) then
     132            0 :                   write(*,*) "Error when computing adjusted energy in CE, ", &
     133            0 :                      "s% energy(k):", s% energy(k), " adjusted_energy, ", adjusted_energy(k)
     134            0 :                   stop
     135              :                end if
     136              : 
     137            0 :                if(.false.) then
     138              :                   ! for debug, check the mismatch between the EOS energy and that of a gas+radiation
     139              :                   energy_comp = 3*avo*boltzm*s% T(k)/(2*s% mu(k)) + crad*pow4(s% T(k))/s% rho(k) &
     140              :                                 + rec_energy_HII_to_HI &
     141              :                                 + rec_energy_HeII_to_HeI &
     142              :                                 + rec_energy_HeIII_to_HeII &
     143              :                                 + diss_energy_H2
     144              : 
     145              :                   write(*,*) "compare energies", k, s%m(k)/Msun, s% energy(k), energy_comp, &
     146              :                      (s% energy(k)-energy_comp)/s% energy(k)
     147              :                end if
     148              : 
     149              :             end do
     150              : 
     151            0 :             do k=1, s% nz
     152            0 :                b% CE_m(:) = s% m(:s% nz)
     153              :             end do
     154              : 
     155              :             ! setup values of starting model for the interpolant
     156            0 :             do k=1, s% nz
     157            0 :                b% CE_entropy(4*k-3) = exp(s% lnS(k))
     158              :             end do
     159              : 
     160              :             ! Compute internal and potential energies from the inside out, and in the opposite direction.
     161            0 :             b% CE_U_out(1) = adjusted_energy(1)*s% dm(1)
     162            0 :             b% CE_Omega_out(1) = - standard_cgrav*s% m(1)*s% dm_bar(1)/s% r(1)
     163            0 :             do k=2, s% nz
     164            0 :                b% CE_U_out(4*k-3) = b% CE_U_out(4*(k-1)-3) + adjusted_energy(k)*s% dm(k)
     165            0 :                b% CE_Omega_out(4*k-3) = b% CE_Omega_out(4*(k-1)-3) - standard_cgrav*s% m(k)*s% dm_bar(k)/s% r(k)
     166              :             end do
     167            0 :             b% CE_U_in(4*s% nz-3) = adjusted_energy(s% nz)*s% dm(s% nz)
     168            0 :             b% CE_Omega_in(4*s% nz-3) = - standard_cgrav*s% m(s% nz)*s% dm_bar(s% nz)/s% r(s% nz)
     169            0 :             do k=s% nz-1, 1, -1
     170            0 :                b% CE_U_in(4*k-3) = b% CE_U_in(4*(k+1)-3) + adjusted_energy(k)*s% dm(k)
     171            0 :                b% CE_Omega_in(4*k-3) = b% CE_Omega_in(4*(k+1)-3) - standard_cgrav*s% m(k)*s% dm_bar(k)/s% r(k)
     172              :             end do
     173              : 
     174            0 :             b% CE_initial_radius = b% r(b% d_i)
     175            0 :             b% CE_initial_separation = b% separation
     176            0 :             b% CE_initial_Mdonor = b% m(b% d_i)
     177            0 :             b% CE_initial_Maccretor = b% m(b% a_i)
     178            0 :             b% CE_initial_age = s% star_age
     179            0 :             b% CE_initial_model_number = s% model_number
     180            0 :             b% CE_b_initial_age = b% binary_age
     181            0 :             b% CE_b_initial_model_number = b% model_number
     182            0 :             b% CE_nz = s% nz
     183              : 
     184            0 :             allocate(interp_work(s% nz*pm_work_size), stat=ierr)
     185            0 :             call interp_pm(b% CE_m, s% nz, b% CE_entropy, pm_work_size, interp_work, 'entropy interpolant', op_err)
     186            0 :             call interp_pm(b% CE_m, s% nz, b% CE_U_in, pm_work_size, interp_work, 'U_in interpolant', op_err)
     187            0 :             call interp_pm(b% CE_m, s% nz, b% CE_U_out, pm_work_size, interp_work, 'U_out interpolant', op_err)
     188            0 :             call interp_pm(b% CE_m, s% nz, b% CE_Omega_in, pm_work_size, interp_work, 'Omega_in interpolant', op_err)
     189            0 :             call interp_pm(b% CE_m, s% nz, b% CE_Omega_out, pm_work_size, interp_work, 'Omega_out interpolant', op_err)
     190            0 :             if(op_err /= 0) then
     191            0 :                ierr = -1
     192            0 :                write(*,*) "CE_init: Error while creating interpolants"
     193            0 :                return
     194              :             end if
     195            0 :             deallocate(adjusted_energy,interp_work)
     196              :          end if
     197              : 
     198            0 :       end subroutine CE_init
     199              : 
     200            0 :       subroutine CE_rlo_mdot(binary_id, rlo_mdot, ierr)
     201              :          integer, intent(in) :: binary_id
     202              :          real(dp), intent(out) :: rlo_mdot
     203              :          integer, intent(out) :: ierr
     204              :          type (binary_info), pointer :: b
     205            0 :          real(dp) :: exp_factor
     206              : 
     207              :          ierr = 0
     208              : 
     209            0 :          call binary_ptr(binary_id, b, ierr)
     210              : 
     211            0 :          if (b% use_other_CE_rlo_mdot) then
     212            0 :             call b% other_CE_rlo_mdot(b% binary_id, rlo_mdot, ierr)
     213            0 :             return
     214              :          end if
     215              : 
     216            0 :          exp_factor = -log(b% CE_mass_loss_rate_low/b% CE_mass_loss_rate_high)
     217              : 
     218            0 :          if (b% r(b% d_i)-b% rl(b% d_i) > 0d0) then
     219            0 :             rlo_mdot = -Msun/secyer*b% CE_mass_loss_rate_high
     220            0 :          else if (b% r(b% d_i)-b% rl(b% d_i) < -b% CE_rel_rlo_for_detachment*b% rl(b% d_i)) then
     221            0 :             rlo_mdot = -Msun/secyer*b% CE_mass_loss_rate_low
     222              :          else
     223              :             rlo_mdot = -Msun/secyer*b% CE_mass_loss_rate_high * &
     224            0 :                exp(exp_factor*(b% r(b% d_i)-b% rl(b% d_i))/(b% rl(b% d_i)*b% CE_rel_rlo_for_detachment))
     225              :          end if
     226              : 
     227            0 :       end subroutine CE_rlo_mdot
     228              : 
     229            0 :       integer function CE_binary_evolve_step(b)
     230              :          use binary_utils, only:set_separation_eccentricity
     231              :          type (binary_info), pointer :: b
     232              :          type (star_info), pointer :: s
     233            0 :          real(dp) :: Ebind, Ecore, Ecore_i, lambda, &
     234            0 :             U_removed ,Omega_removed, U_inold, Omega_inold
     235            0 :          real(dp) :: separation, initial_Eorb
     236              :          integer :: ierr, op_err, k
     237              : 
     238            0 :          if (b% use_other_CE_binary_evolve_step) then
     239            0 :             CE_binary_evolve_step = b% other_CE_binary_evolve_step(b% binary_id)
     240            0 :             return
     241              :          end if
     242              : 
     243              :          ! setup mdot_system_wind to get output right, it is not actually used.
     244              :          ! setup jdots to zero as well
     245            0 :          b% mdot_system_wind(b% d_i) = b% s_donor% mstar_dot - b% mtransfer_rate
     246            0 :          if (b% point_mass_i == 0) then
     247            0 :             b% mdot_system_wind(b% a_i) = b% s_accretor% mstar_dot
     248              :          else
     249            0 :             b% mdot_system_wind(b% a_i) = 0.0d0
     250              :          end if
     251            0 :          b% jdot = 0d0
     252            0 :          b% jdot_mb = 0d0
     253            0 :          b% jdot_gr = 0d0
     254            0 :          b% jdot_ml = 0d0
     255            0 :          b% jdot_missing_wind = 0d0
     256            0 :          b% extra_jdot = 0d0
     257            0 :          b% jdot_ls = 0d0
     258              : 
     259            0 :          s => b% s_donor
     260              : 
     261            0 :          if (b% CE_fixed_lambda < 0d0) then
     262            0 :             Ecore = 0
     263            0 :             do k=1, s% nz
     264              :                Ecore = Ecore + s% energy(k)*s% dm(k) - standard_cgrav*s% m(k)*s% dm_bar(k)/s% r(k)
     265              :             end do
     266              : 
     267            0 :             call interp_value(b% CE_m, b% CE_nz, b% CE_U_out, s% m(1), U_removed, op_err)
     268            0 :             call interp_value(b% CE_m, b% CE_nz, b% CE_Omega_out, s% m(1), Omega_removed, op_err)
     269            0 :             call interp_value(b% CE_m, b% CE_nz, b% CE_U_in, s% m(1), U_inold, op_err)
     270            0 :             call interp_value(b% CE_m, b% CE_nz, b% CE_Omega_in, s% m(1), Omega_inold, op_err)
     271              : 
     272            0 :             Ecore_i = U_inold + Omega_inold
     273            0 :             Ebind = b% CE_alpha_th*U_removed + Omega_removed
     274              :             lambda = -(standard_cgrav*b% CE_initial_Mdonor*(b% CE_initial_Mdonor - s% m(1))) &
     275            0 :                /(Ebind*b% CE_initial_radius)
     276              :          else
     277            0 :             lambda = b% CE_fixed_lambda
     278              :             Ebind = -(standard_cgrav*b% CE_initial_Mdonor*(b% CE_initial_Mdonor - s% m(1))) &
     279            0 :                /(lambda*b% CE_initial_radius)
     280              :          end if
     281              : 
     282            0 :          if (b% d_i == 1) then
     283            0 :             b% CE_Ebind1 = Ebind
     284            0 :             b% CE_lambda1 = lambda
     285              :          else
     286            0 :             b% CE_Ebind2 = Ebind
     287            0 :             b% CE_lambda2 = lambda
     288              :          end if
     289              : 
     290            0 :          initial_Eorb = -standard_cgrav*b% CE_initial_Mdonor*b% CE_initial_Maccretor/(2*b% CE_initial_separation)
     291              : 
     292              :          separation = -b% CE_alpha*standard_cgrav*s% m(1)*b% CE_initial_Maccretor &
     293            0 :             /(2*(Ebind+b% CE_alpha*initial_Eorb))
     294              : 
     295            0 :          b% m(b% d_i) = b% s_donor% mstar
     296            0 :          b% time_step = b% s_donor% time_step
     297            0 :          if (b% point_mass_i == 0) then
     298            0 :             b% m(b% a_i) = b% s_accretor% mstar
     299              :          end if
     300              : 
     301            0 :          if (b% point_mass_i /= 1) then
     302            0 :             b% r(1) = Rsun*b% s1% photosphere_r
     303              :          else
     304            0 :             b% r(1) = 0
     305              :          end if
     306            0 :          if (b% point_mass_i /= 2) then
     307            0 :             b% r(2) = Rsun*b% s2% photosphere_r
     308              :          else
     309            0 :             b% r(2) = 0
     310              :          end if
     311              : 
     312              :          !only change separation if its reduced from the initial value
     313              :          call set_separation_eccentricity(b% binary_id, &
     314            0 :             min(b% CE_initial_separation, separation), b% eccentricity, ierr)
     315              : 
     316            0 :          b% model_number = b% model_number + 1
     317            0 :          b% time_step = b% s_donor% time_step
     318            0 :          b% binary_age = b% binary_age + b% time_step
     319              : 
     320            0 :          if (b% r(b% d_i)-b% rl(b% d_i) < 0d0) then
     321            0 :             b% CE_years_detached = b% CE_years_detached + b% time_step
     322              :          else
     323            0 :             b% CE_years_detached = 0d0
     324              :          end if
     325              : 
     326            0 :          CE_binary_evolve_step = keep_going
     327              : 
     328            0 :       end function CE_binary_evolve_step
     329              : 
     330            0 :       integer function CE_binary_finish_step(b)
     331            0 :          use binary_utils, only: eval_rlobe
     332              :          type (binary_info), pointer :: b
     333            0 :          real(dp) :: h_diff, he_diff, rlobe
     334              :          logical :: terminate_CE
     335              :          integer :: k
     336            0 :          CE_binary_finish_step = keep_going
     337              : 
     338            0 :          if (b% use_other_CE_binary_finish_step) then
     339            0 :             CE_binary_finish_step = b% other_CE_binary_finish_step(b% binary_id)
     340            0 :             return
     341              :          end if
     342              : 
     343            0 :          terminate_CE = .false.
     344            0 :          if ((b% r(b% d_i)-b% rl(b% d_i))/(b% rl(b% d_i)*b% CE_rel_rlo_for_detachment) < -1d0) then
     345            0 :             terminate_CE = .true.
     346            0 :             write(*,*) "Have reached CE_rel_rlo_for_detachment"
     347            0 :          else if (b% CE_years_detached > b% CE_years_detached_to_terminate) then
     348            0 :             terminate_CE = .true.
     349            0 :             write(*,*) "Have reached CE_years_detached_to_terminate"
     350              :          end if
     351              : 
     352              :          if (terminate_CE) then
     353            0 :             b% CE_flag = .false.
     354            0 :             b% mtransfer_rate = 0d0
     355            0 :             b% s_donor% mix_factor = 1d0
     356            0 :             b% s_donor% dxdt_nuc_factor = 1d0
     357            0 :             b% s_donor% timestep_hold = b% s_donor% model_number
     358            0 :             if (b% point_mass_i == 0) then
     359            0 :                b% s_accretor% mix_factor = 1d0
     360            0 :                b% s_accretor% dxdt_nuc_factor = 1d0
     361            0 :                b% s_accretor% timestep_hold = b% s_accretor% model_number
     362              :             end if
     363              : 
     364            0 :             b% keep_donor_fixed = .false.
     365            0 :             write(*,*) "TURNING OFF CE"
     366              :          end if
     367              : 
     368              :          ! termination conditions
     369              : 
     370            0 :          h_diff = abs(b% s_donor% center_h1 - b% s_donor% surface_h1)
     371            0 :          he_diff = abs(b% s_donor% center_he4 - b% s_donor% surface_he4)
     372              :          if (h_diff < b% CE_xa_diff_to_terminate &
     373            0 :             .and. he_diff < b% CE_xa_diff_to_terminate) then
     374            0 :             write(*,*) "Central and surface abundances below CE_xa_diff_to_terminate"
     375            0 :             write(*,*) "Terminating evolution"
     376            0 :             CE_binary_finish_step = terminate
     377            0 :             return
     378              :          end if
     379              : 
     380              :          !terminate if, for the current orbital separation, stripping the star down to the point
     381              :          !where CE_xa_diff_to_terminate would apply, the remaining layers would be Roche lobe
     382              :          !overflowing.
     383            0 :          if (b% CE_terminate_when_core_overflows) then
     384            0 :             do k = 1, b% s_donor% nz
     385            0 :                h_diff = abs(b% s_donor% center_h1 - b% s_donor% X(k))
     386            0 :                he_diff = abs(b% s_donor% center_he4 - b% s_donor% Y(k))
     387              :                if (h_diff < b% CE_xa_diff_to_terminate &
     388            0 :                   .and. he_diff < b% CE_xa_diff_to_terminate) then
     389            0 :                   rlobe = eval_rlobe(b% s_donor% m(k), b% m(b% a_i), b% separation)
     390            0 :                   if (b% s_donor% r(k) > rlobe) then
     391            0 :                      write(*,*) "Terminate due to CE_terminate_when_core_overflows"
     392            0 :                      write(*,*) "Terminating evolution"
     393            0 :                      CE_binary_finish_step = terminate
     394            0 :                      return
     395              :                   end if
     396              :                   exit
     397              :                end if
     398              :             end do
     399              :          end if
     400              : 
     401            0 :          if (b% period < b% CE_min_period_in_minutes*60d0) then
     402            0 :             write(*,*) "Orbital period is below CE_min_period_in_minutes"
     403            0 :             write(*,*) "Terminating evolution"
     404            0 :             CE_binary_finish_step = terminate
     405            0 :             return
     406              :          end if
     407              : 
     408            0 :       end function CE_binary_finish_step
     409              : 
     410              :       end module binary_ce
     411              : 
        

Generated by: LCOV version 2.0-1