LCOV - code coverage report
Current view: top level - binary/private - binary_tides.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 % 8 0

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2013-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              :       module binary_tides
      21              : 
      22              :       use star_lib
      23              :       use star_def
      24              :       use const_def, only: dp, pi, pi4, secyer, rsun, msun, one_third, standard_cgrav, convective_mixing
      25              :       use utils_lib
      26              :       use math_lib
      27              :       use binary_def
      28              : 
      29              :       implicit none
      30              : 
      31              : 
      32              :       contains
      33              : 
      34              : 
      35            0 :       subroutine sync_spin_orbit_torque(id, ierr)
      36              :          integer, intent(in) :: id
      37              :          integer, intent(out) :: ierr
      38              :          type (star_info), pointer :: s
      39            0 :          real(dp) :: osep  ! orbital separation (cm)
      40            0 :          real(dp) :: qratio  ! mass_other_star/mass_this_star
      41            0 :          real(dp) :: rlr  ! roche lobe radius (cm)
      42            0 :          real(dp) :: dt_next  ! next timestep
      43            0 :          real(dp) :: Ftid  ! efficiency of tidal synchronization. (time scale × FSYNC).
      44              :          character (len=strlen) :: sync_type
      45              :          character (len=strlen) :: sync_mode
      46              :          type (binary_info), pointer :: b
      47              :          ierr = 0
      48              : 
      49            0 :          call star_ptr(id, s, ierr)
      50            0 :          if (ierr /= 0) then
      51            0 :             write(*,*) 'failed in star_ptr'
      52            0 :             return
      53              :          end if
      54              : 
      55            0 :          call binary_ptr(s% binary_id, b, ierr)
      56            0 :          if (ierr /= 0) then
      57            0 :             write(*,*) 'failed in binary_ptr'
      58            0 :             return
      59              :          end if
      60              : 
      61            0 :          if (.not. b% do_tidal_sync) return
      62              : 
      63            0 :          call star_ptr(id, s, ierr)
      64              : 
      65            0 :          osep = b% separation
      66            0 :          qratio = b% m(b% a_i) / b% m(b% d_i)
      67            0 :          if (is_donor(b, s)) then
      68            0 :             rlr = b% rl(b% d_i)
      69              :          else
      70            0 :             qratio = 1d0/qratio
      71            0 :             rlr = b% rl(b% a_i)
      72              :          end if
      73            0 :          dt_next = s% dt
      74              : 
      75            0 :          if (b% point_mass_i /= 1 .and. id == b% s1% id) then
      76            0 :             Ftid = b% Ftid_1
      77            0 :             sync_type = b% sync_type_1
      78            0 :             sync_mode = b% sync_mode_1
      79              :          else
      80            0 :             Ftid = b% Ftid_2
      81            0 :             sync_type = b% sync_type_2
      82            0 :             sync_mode = b% sync_mode_2
      83              :          end if
      84              : 
      85            0 :          if (b% use_other_sync_spin_to_orbit) then
      86            0 :             call b% other_sync_spin_to_orbit(s% id, s% nz, osep, qratio, rlr, dt_next, Ftid, sync_type, sync_mode, ierr)
      87              :          else
      88            0 :             call sync_spin_to_orbit(s% id, s% nz, osep, qratio, rlr, dt_next, Ftid, sync_type, sync_mode, ierr)
      89              :          end if
      90              : 
      91              :       end subroutine sync_spin_orbit_torque
      92              : 
      93            0 :       subroutine sync_spin_to_orbit(id, nz, osep, qratio, rl, dt_next, Ftid, sync_type, sync_mode, ierr)
      94              :          ! initially based on spiba.f kindly provided by Norbert Langer and group.
      95              :          integer, intent(in) :: id
      96              :          integer, intent(in) :: nz
      97              :          real(dp), intent(in) :: osep  ! orbital separation (cm)
      98              :          real(dp), intent(in) :: qratio  ! mass_other_star/mass_this_star
      99              :          real(dp), intent(in) :: rl  ! roche lobe radius (cm)
     100              :          real(dp), intent(in) :: dt_next  ! next timestep
     101              :          real(dp), intent(in) :: Ftid  ! efficiency of tidal synchronization. (time scale / Ftid ).
     102              : 
     103              :          character (len=strlen), intent(in) :: sync_type  ! synchronization timescale
     104              :          character (len=strlen), intent(in) :: sync_mode  ! where to put/take angular momentum
     105              :          integer, intent(out) :: ierr
     106              : 
     107              :          type (star_info), pointer :: s
     108            0 :          real(dp) :: G, m, t_sync, r_phot, delta_total_J, &
     109            0 :             sum_J_sync, sum_J_non_sync, tdyn, tkh, rho_face, cv_face, &
     110            0 :             T_face, csound_face, ff, omega_orb
     111              : 
     112            0 :          real(dp), dimension(nz) :: j_sync, delta_j, tdyn_div_tkh
     113            0 :          integer, dimension(nz) :: layers_in_sync
     114              :          integer :: k, num_sync_layers
     115              :          type (binary_info), pointer :: b
     116              : 
     117            0 :          real(dp) :: a1,a2
     118              : 
     119              :          include 'formats'
     120              : 
     121              :          ierr = 0
     122              : 
     123            0 :          call star_ptr(id, s, ierr)
     124            0 :          if (ierr /= 0) then
     125            0 :             write(*,*) 'failed in star_ptr'
     126            0 :             return
     127              :          end if
     128              : 
     129            0 :          if (osep <= 0) return
     130            0 :          if (qratio <= 0) return
     131            0 :          if (rl <= 0) return
     132            0 :          if (dt_next <= 0) return
     133            0 :          if (Ftid <= 0) return
     134            0 :          if (sync_type == "None") return
     135              : 
     136            0 :          call binary_ptr(s% binary_id, b, ierr)
     137            0 :          if (ierr /= 0) then
     138            0 :             write(*,*) 'failed in binary_ptr'
     139            0 :             return
     140              :          end if
     141              : 
     142            0 :          t_sync = 0
     143              : 
     144            0 :          G = standard_cgrav
     145              : 
     146            0 :          if (is_donor(b, s)) then
     147            0 :             m = b% m(b% d_i)
     148            0 :             r_phot = b% r(b% d_i)
     149              :          else
     150            0 :             m = b% m(b% a_i)
     151            0 :             r_phot = b% r(b% a_i)
     152              :          end if
     153              : 
     154            0 :          omega_orb = 2d0*pi/b% period
     155            0 :          do k=1,nz
     156            0 :             j_sync(k) = omega_orb*s% i_rot(k)% val
     157              :          end do
     158              : 
     159            0 :          if (sync_type == "Instantaneous") then  ! instantaneous synchronisation
     160            0 :             do k=1,nz
     161            0 :                delta_j(k) = s% j_rot(k) - j_sync(k)
     162              :             end do
     163              :          else
     164              :             ! get synchronization timescale, i.e.
     165              :             ! 1/t_sync = \dot(omega_star)/(omega_orb-omega_star)
     166              :             ! to order e (e=eccentricity).
     167            0 :             if (.not. b% use_other_tsync) then
     168            0 :                call get_tsync(s% id, sync_type, Ftid, qratio, m, r_phot, osep, t_sync, ierr)
     169            0 :                if (ierr/=0) return
     170              :             else
     171            0 :                call b% other_tsync(s% id, sync_type, Ftid, qratio, m, r_phot, osep, t_sync, ierr)
     172            0 :                if (ierr/=0) return
     173              :             end if
     174            0 :             if (sync_mode == "Uniform") then
     175            0 :                a1 = f2(b% eccentricity)
     176            0 :                a2 = pow(1-pow2(b% eccentricity), 1.5d0)*f5(b% eccentricity)
     177            0 :                do k=1,nz
     178            0 :                   delta_j(k) = (1d0 - exp(-a2*dt_next/t_sync))*(s% j_rot(k) - a1/a2*j_sync(k))
     179              :                end do
     180            0 :             else if (sync_mode == "tdyn_div_tkh") then
     181              :                ! set local timescale following Gautschy & Glatzel 1990 MNRAS 245, 597-613.
     182            0 :                do k=1,nz
     183              :                   !tdyn_div_tkh := local dynamical time-scale / local thermal time-scale
     184            0 :                   rho_face = star_interp_val_to_pt(s% rho,k,nz,s% dq,'binary_tides')
     185            0 :                   cv_face = star_interp_val_to_pt(s% cv,k,nz,s% dq,"binary_tides")
     186            0 :                   T_face = star_interp_val_to_pt(s% T,k,nz,s% dq,"binary_tides")
     187            0 :                   csound_face = star_interp_val_to_pt(s% csound,k,nz,s% dq,"binary_tides")
     188            0 :                   tkh = pi4*s% r(k)*s% r(k)*rho_face*cv_face*T_face/s% L(k)  ! (4.4)
     189            0 :                   tdyn = 1/csound_face
     190            0 :                   tdyn_div_tkh(k) = tdyn/tkh
     191              :                end do
     192              : 
     193              :                ! j_sync = synchronized specific angular momentum
     194            0 :                delta_total_J = 0
     195            0 :                do k=1,nz
     196            0 :                   delta_total_J = delta_total_J + abs(s% j_rot(k) - j_sync(k))*s% dm_bar(k)
     197            0 :                   delta_j(k) = 0
     198            0 :                   layers_in_sync(k) = 0
     199              :                end do
     200            0 :                delta_total_J = delta_total_J*(1d0 - exp(-dt_next/t_sync))
     201              : 
     202              :                ! Iteratively solve the scaling factor ff to add (or remove) delta_total_J.
     203              :                ! At each iteration, ff is solved such that each zone k has a change on
     204              :                ! its angular momentum J_k of the form:
     205              :                !
     206              :                ! Delta J_k = (J_k-J_{k,sync})*tdyn_div_tkh(k)**2*ff,
     207              :                !
     208              :                ! and the total change adds up to delta_total_J.
     209              :                ! Since tides can at most drive a layer into sync, ff cannot be
     210              :                ! solved right away. Then, each iteration solves ff ignoring layers
     211              :                ! going over sync, spreads the angular momentum to see which ones
     212              :                ! become synced, and then recalculates taking this into account
     213              :                ! until it converges.
     214            0 :                num_sync_layers = 0
     215              :                !write(*,*) "Entering tide iteration", delta_total_J
     216              :                do while (.true.)
     217            0 :                   sum_J_sync = 0
     218            0 :                   sum_J_non_sync = 0
     219            0 :                   do k=1,nz
     220            0 :                      delta_j(k) = s% j_rot(k) - j_sync(k)
     221            0 :                      if (layers_in_sync(k) /= 1) then
     222              :                         sum_J_non_sync = sum_J_non_sync + &
     223            0 :                             s% dm_bar(k) * abs(delta_j(k)) * pow2(tdyn_div_tkh(k))
     224              :                      else
     225            0 :                         sum_J_sync = sum_J_sync + s% dm_bar(k) * abs(delta_j(k))
     226              :                      end if
     227              :                   end do
     228              : 
     229            0 :                   ff = (delta_total_J - sum_J_sync) / sum_J_non_sync
     230              : 
     231            0 :                   do k=1,nz
     232            0 :                      if (layers_in_sync(k) == 1) cycle
     233            0 :                      if (delta_j(k) >= 0) then
     234            0 :                         delta_j(k) = min(delta_j(k),delta_j(k)*pow2(tdyn_div_tkh(k))*ff)
     235              :                      else
     236            0 :                         delta_j(k) = max(delta_j(k),delta_j(k)*pow2(tdyn_div_tkh(k))*ff)
     237              :                      end if
     238            0 :                      if (delta_j(k) == s% j_rot(k) - j_sync(k)) layers_in_sync(k) = 1
     239              :                   end do
     240            0 :                   if (num_sync_layers == sum(layers_in_sync)) exit
     241            0 :                   num_sync_layers = sum(layers_in_sync)
     242              : 
     243              :                end do
     244              :                !write(*,*) "tide iteration", num_sync_layers, &
     245              :                !    delta_total_J, sum_J_sync, sum_J_non_sync, ff
     246              :             else
     247            0 :                write(*,*) "sync_mode = " , sync_mode , " not recognized"
     248            0 :                write(*,*) "not doing tides"
     249            0 :                do k=1,nz
     250            0 :                   delta_j(k) = 0d0
     251              :                end do
     252              :             end if
     253              : 
     254              :          end if
     255              : 
     256            0 :          if (b% point_mass_i /= 1 .and. b% s1% id == s% id) then
     257            0 :             b% t_sync_1 = t_sync
     258            0 :             if (b% model_twins_flag) then
     259            0 :                b% t_sync_2 = t_sync
     260              :             end if
     261              :          else
     262            0 :             b% t_sync_2 = t_sync
     263              :          end if
     264              : 
     265            0 :          if (.not. b% doing_first_model_of_run) then
     266            0 :             do k=1,nz
     267            0 :                s% extra_jdot(k) = s% extra_jdot(k) - delta_j(k)/dt_next
     268              :             end do
     269              :          end if
     270              : 
     271              :       end subroutine sync_spin_to_orbit
     272              : 
     273              : 
     274            0 :       real(dp) function f2(e)
     275              :          real(dp), intent(in) :: e
     276              : 
     277            0 :          f2 = 1d0
     278              : 
     279              :          ! Hut 1981, A&A, 99, 126, definition of f2 after eq. 11
     280            0 :          if (e > 0d0) then
     281            0 :              f2 = 1d0 + 15d0/2d0*pow2(e) + 45d0/8d0*pow4(e) + 5d0/16d0*pow6(e)
     282              :          end if
     283              : 
     284            0 :       end function f2
     285              : 
     286            0 :       real(dp) function f3(e)
     287              :          real(dp), intent(in) :: e
     288              : 
     289            0 :          f3 = 1d0
     290              : 
     291              :          ! Hut 1981, A&A, 99, 126, definition of f3 after eq. 11
     292            0 :          if (e > 0d0) then
     293            0 :              f3 = 1d0 + 15d0/4d0*pow2(e) + 15d0/8d0*pow4(e) + 5d0/64d0*pow6(e)
     294              :          end if
     295              : 
     296            0 :       end function f3
     297              : 
     298              : 
     299            0 :       real(dp) function f4(e)
     300              :          real(dp), intent(in) :: e
     301              : 
     302            0 :          f4 = 1d0
     303              : 
     304              :          ! Hut 1981, A&A, 99, 126, definition of f4 after eq. 11
     305            0 :          if (e > 0d0) then
     306            0 :              f4 = 1d0 + 3d0/2d0*pow2(e) + 1d0/8d0*pow4(e)
     307              :          end if
     308              : 
     309            0 :       end function f4
     310              : 
     311              : 
     312            0 :       real(dp) function f5(e)
     313              :          real(dp), intent(in) :: e
     314              : 
     315            0 :          f5 = 1d0
     316              : 
     317              :          ! Hut 1981, A&A, 99, 126, definition of f5 after eq. 11
     318            0 :          if (e > 0d0) then
     319            0 :              f5 = 1d0 + 3d0*pow2(e) + 3d0/8d0*pow4(e)
     320              :          end if
     321              : 
     322            0 :       end function f5
     323              : 
     324            0 :       subroutine get_tsync(id, sync_type, Ftid, qratio, m, r_phot, osep, t_sync, ierr)
     325              :          integer, intent(in) :: id
     326              :          character (len=strlen), intent(in) :: sync_type  ! synchronization timescale
     327              :          real(dp), intent(in) :: Ftid  ! efficiency of tidal synchronization. (time scale / Ftid ).
     328              :          real(dp), intent(in) :: qratio  ! mass_other_star/mass_this_star
     329              :          real(dp), intent(in) :: m
     330              :          real(dp), intent(in) :: r_phot
     331              :          real(dp), intent(in) :: osep  ! orbital separation (cm)
     332              :          real(dp), intent(out) :: t_sync
     333              :          integer, intent(out) :: ierr
     334            0 :          real(dp) :: rGyr_squared, moment_of_inertia, kdivt
     335              :          type (binary_info), pointer :: b
     336              :          type (star_info), pointer :: s
     337              :          integer :: k
     338              : 
     339              :          include 'formats'
     340              : 
     341              :          ierr = 0
     342              : 
     343            0 :          call star_ptr(id, s, ierr)
     344            0 :          if (ierr /= 0) then
     345            0 :             write(*,*) 'failed in star_ptr'
     346            0 :             return
     347              :          end if
     348              : 
     349            0 :          call binary_ptr(s% binary_id, b, ierr)
     350            0 :          if (ierr /= 0) then
     351            0 :             write(*,*) 'failed in binary_ptr'
     352            0 :             return
     353              :          end if
     354              :          ! calculate the gyration radius squared
     355            0 :          moment_of_inertia = 0d0
     356            0 :          do k=1, s% nz
     357            0 :             moment_of_inertia = moment_of_inertia + s% i_rot(k)% val*s% dm_bar(k)
     358              :          end do
     359            0 :          rGyr_squared = (moment_of_inertia/(m*r_phot*r_phot))
     360            0 :          if (sync_type == "Hut_conv") then
     361              :             ! eq. (11) of Hut, P. 1981, A&A, 99, 126
     362              : 
     363            0 :             kdivt = k_div_T(b, s, .true., ierr)
     364            0 :             if(ierr/=0) return
     365              : 
     366            0 :             t_sync = 3d0*kdivt*(qratio*qratio/rGyr_squared)*pow6(r_phot/osep)
     367              :             ! invert it.
     368            0 :             t_sync = 1d0/t_sync
     369            0 :          else if (sync_type == "Hut_rad") then
     370              :             ! eq. (11) of Hut, P. 1981, A&A, 99, 126
     371              : 
     372            0 :             kdivt = k_div_T(b, s, .false., ierr)
     373            0 :             if(ierr/=0) return
     374              : 
     375            0 :             t_sync = 3d0*kdivt*(qratio*qratio/rGyr_squared)*pow6(r_phot/osep)
     376              :             ! invert it.
     377            0 :             t_sync = 1d0/t_sync
     378            0 :          else if (sync_type == "Orb_period") then  ! sync on timescale of orbital period
     379            0 :             t_sync = b% period  ! synchronize on timescale of orbital period
     380              :          else
     381            0 :             ierr = -1
     382            0 :             write(*,*) 'unrecognized sync_type', sync_type
     383            0 :             return
     384              :          end if
     385            0 :          t_sync = t_sync / Ftid
     386              :       end subroutine get_tsync
     387              : 
     388            0 :       real(dp) function k_div_T(b, s, has_convective_envelope, ierr)
     389              :          type(binary_info), pointer :: b
     390              :          type(star_info), pointer :: s
     391              :          logical, intent(in) :: has_convective_envelope
     392              :          integer, intent(out) :: ierr
     393              : 
     394              :          integer :: k
     395            0 :          real(dp) :: osep, qratio, m, r_phot,porb, m_env, r_env, tau_conv, P_tid, f_conv
     396            0 :          real(dp) :: e2
     397              : 
     398            0 :          ierr = 0
     399              : 
     400              :          ! k/T computed as in Hurley, J., Tout, C., Pols, O. 2002, MNRAS, 329, 897
     401              :          ! Kudos to Francesca Valsecchi for help implementing and testing this
     402              : 
     403            0 :          k_div_T = 0d0
     404              : 
     405            0 :          osep = b% separation
     406            0 :          qratio = b% m(b% a_i) / b% m(b% d_i)
     407            0 :          if (is_donor(b, s)) then
     408            0 :             m = b% m(b% d_i)
     409            0 :             r_phot = b% r(b% d_i)
     410              :          else
     411            0 :             qratio = 1d0/qratio
     412            0 :             m = b% m(b% a_i)
     413            0 :             r_phot = b% r(b% a_i)
     414              :          end if
     415            0 :          porb = b% period
     416              : 
     417            0 :          if (has_convective_envelope) then
     418            0 :             m_env = s% m(1) / Msun  ! assume fully convective
     419            0 :             r_env = s% r(1) / Rsun
     420            0 :             do k=1, s% nz  ! search if _not_ fully convective
     421            0 :                if (s% mixing_type(k) /= convective_mixing .and. &
     422            0 :                    s% rho(k) > 1d5*s% rho(1)) then
     423            0 :                   r_env = (r_phot - s% r(k))/Rsun
     424            0 :                   m_env = (s% m(1) - s% m(k))/Msun
     425            0 :                   exit
     426              :                end if
     427              :             end do
     428              :             tau_conv = 0.431d0*pow(m_env*r_env* &
     429            0 :                (r_phot/Rsun-r_env/2d0)/3d0/s% L_phot,one_third) * secyer
     430            0 :             if (1d0/porb /= s% omega_avg_surf/(2d0*pi)) then
     431            0 :                P_tid = 1d0/abs(1d0/porb-s% omega_avg_surf/(2d0*pi))
     432            0 :                f_conv = min(1.0d0, pow(P_tid/(2d0*tau_conv),b% tidal_reduction))
     433              :             else
     434              :                f_conv = 1d0
     435              :             end if
     436              : 
     437            0 :             k_div_T = 2d0/21d0*f_conv/tau_conv*m_env/(m/Msun)
     438              :          else
     439              :             !NOTE:There is a typo in eq. (42) of Hurley+ 2002,
     440              :             !correct expression is given in footnote 3 of
     441              :             !Sepinsky+ 2007
     442            0 :             k_div_T = 1.9782d4*sqrt(m*r_phot*r_phot/pow5(osep)/(Msun/pow3(Rsun)))
     443            0 :             k_div_T = k_div_T*pow(1d0+qratio,5d0/6d0)
     444              : 
     445            0 :             if (b% use_other_e2) then
     446            0 :                call b% other_e2(s% id, e2, ierr)
     447            0 :                if(ierr/=0) then
     448            0 :                   write(*,*) "Error when computing other_e2 ",ierr
     449            0 :                   return
     450              :                end if
     451              :             else
     452              :                ! E2 from Hurley 2002 eq 43 based on Zahn 1975
     453            0 :                e2 = 1.592d-9*pow(m/Msun,2.84d0)
     454              :             end if
     455            0 :             k_div_T = k_div_T*e2/secyer
     456              :          end if
     457              : 
     458              :       end function k_div_T
     459              : 
     460              :       end module binary_tides
     461              : 
        

Generated by: LCOV version 2.0-1