LCOV - code coverage report
Current view: top level - star/private - hydro_rotation.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 4.9 % 405 20
Test Date: 2025-11-11 15:19:17 Functions: 4.5 % 22 1

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2010-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              : ! Routine eval_fp_ft for computing rotation corrections to the stellar structure equations.
      21              : ! Following the method of Kippenhahn & Thomas, 1970; Endal & Sofia 1976, as implemented in
      22              : ! Paxton et al., 2019 (MESA V), using the updated fits from Fabry, Marchant & Sana, 2022, A&A 661:A123.
      23              : 
      24              :       module hydro_rotation
      25              : 
      26              :       use const_def, only: pi, pi4, ln10, two_thirds, one_third, one_sixth
      27              :       use star_utils, only: get_r_from_xh
      28              :       use star_private_def
      29              : 
      30              :       implicit none
      31              : 
      32              :       private
      33              :       public :: log_term_power
      34              :       public :: a
      35              :       public :: c
      36              :       public :: w_div_w_roche_jrot
      37              :       public :: w_div_w_roche_omega
      38              :       public :: set_rotation_info
      39              :       public :: compute_j_fluxes_and_extra_jdot
      40              :       public :: set_surf_avg_rotation_info
      41              :       public :: use_xh_to_update_i_rot_and_j_rot
      42              :       public :: set_i_rot_from_omega_and_j_rot
      43              :       public :: use_xh_to_update_i_rot
      44              :       public :: update1_i_rot_from_xh
      45              :       public :: get_rotation_sigmas
      46              :       public :: set_omega
      47              :       public :: set_uniform_omega
      48              :       public :: set_i_rot
      49              :       public :: eval_i_rot
      50              : 
      51              :       real(dp), parameter :: log_term_power = 5.626d0
      52              : 
      53              :       contains
      54              : 
      55              :       ! compute w_div_w_roche for a known angular frequency omega, rpsi, and Mpsi
      56            0 :       real(dp) function w_div_w_roche_omega(rpsi, Mphi, omega, cgrav, max_w, max_w2, w_div_wc_flag) result(w_roche)
      57              :          real(dp), intent(in) :: rpsi, Mphi, omega, cgrav, max_w, max_w2
      58              :          logical, intent(in) :: w_div_wc_flag
      59            0 :          real(dp) :: wr, wr_high, wr_low, dimless_rpsi, new_dimless_rpsi, rpsi_lim1, rpsi_lim2
      60              : 
      61            0 :          if (omega == 0d0) then
      62            0 :             w_roche = 0d0
      63              :             return
      64              :          end if
      65              : 
      66            0 :          dimless_rpsi = rpsi * pow(abs(omega), two_thirds) / pow(cgrav * Mphi, one_third)
      67            0 :          if (.not. w_div_wc_flag) then
      68              :             ! verify if w_div_w_roche is not above max, otherwise limit it to that
      69            0 :             wr = max_w
      70            0 :             new_dimless_rpsi = pow(wr, two_thirds) * rpsi_from_re_factor(pow2(wr), pow4(wr), pow6(wr))
      71            0 :             if (dimless_rpsi > new_dimless_rpsi) then
      72            0 :                w_roche = wr
      73              :                return
      74              :             end if
      75              :          else
      76              :             ! smoothly cap to max_w2 to get a continuous function
      77              :             ! nothing is done when we are below max_w, but between max_w and max_w2 we smoothly
      78              :             ! produce an asymptote that would result in w_div_wc=max_w2 for jrot->infinity
      79            0 :             wr = max_w
      80            0 :             rpsi_lim1 = pow(wr, two_thirds) * rpsi_from_re_factor(pow2(wr), pow4(wr), pow6(wr))
      81              : 
      82            0 :             wr = max_w2
      83            0 :             rpsi_lim2 = pow(wr, two_thirds) * rpsi_from_re_factor(pow2(wr), pow4(wr), pow6(wr))
      84              : 
      85            0 :             if (abs(dimless_rpsi) > rpsi_lim1) then
      86            0 :                dimless_rpsi = sigmoid(abs(dimless_rpsi), rpsi_lim1, rpsi_lim2)
      87              :             end if
      88              :          end if
      89              : 
      90              :          ! otherwise, bisect result
      91            0 :          wr_high = wr
      92            0 :          wr_low = 0
      93            0 :          do while (wr_high - wr_low > 1d-6)
      94            0 :             wr = 0.5d0 * (wr_high + wr_low)
      95            0 :             new_dimless_rpsi = pow(wr, two_thirds) * rpsi_from_re_factor(pow2(wr), pow4(wr), pow6(wr))
      96            0 :             if (dimless_rpsi > new_dimless_rpsi) then
      97              :                wr_low = wr
      98              :             else
      99            0 :                wr_high = wr
     100              :             end if
     101              :          end do
     102            0 :          w_roche = 0.5d0*(wr_high+wr_low)
     103              : 
     104            0 :          if (omega < 0d0) then
     105            0 :             w_roche = -w_roche
     106              :          end if
     107              : 
     108              :       end function w_div_w_roche_omega
     109              : 
     110              :       ! compute w_div_w_roche for a known specific angular momentum jrot, rpsi, and Mphi
     111            0 :       real(dp) function w_div_w_roche_jrot(rpsi, Mphi, jrot, cgrav, max_w, max_w2, w_div_wc_flag) result(w_roche)
     112              :          real(dp), intent(in) :: rpsi, Mphi, jrot, cgrav, max_w, max_w2
     113              :          logical, intent(in) :: w_div_wc_flag
     114            0 :          real(dp) :: wr, wr_high, wr_low, dimless_factor, new_dimless_factor
     115            0 :          real(dp) :: w2, w4, w6, w_log_term, jr_lim1, jr_lim2
     116              : 
     117            0 :          if (jrot == 0d0) then
     118            0 :             w_roche = 0d0
     119              :             return
     120              :          end if
     121              : 
     122            0 :          dimless_factor = abs(jrot) / sqrt(cgrav * Mphi * rpsi)
     123              : 
     124            0 :          if (.not. w_div_wc_flag) then
     125              :             ! verify if w_div_w_roche is not above max, otherwise limit it to that
     126            0 :             wr = max_w
     127            0 :             w2 = pow2(wr)
     128            0 :             w4 = pow4(wr)
     129            0 :             w6 = pow6(wr)
     130            0 :             w_log_term = log(1d0 - pow(wr, log_term_power))
     131            0 :             new_dimless_factor = two_thirds * wr * C(w2, w4, w6, w_log_term) / A(w4, w6, w_log_term)
     132            0 :             if (dimless_factor > new_dimless_factor) then
     133            0 :                w_roche = wr
     134              :                return
     135              :             end if
     136              :          else
     137              :             ! smoothly cap to max_w2 to get a continuous function
     138              :             ! nothing is done when we are below max_w, but between max_w and max_w2 we smoothly
     139              :             ! produce an asymptote that would result in w_div_wc=max_w2 for jrot->infinity
     140            0 :             wr = max_w
     141            0 :             w4 = pow4(wr)
     142            0 :             w6 = pow6(wr)
     143            0 :             w_log_term = log(1 - pow(wr, log_term_power))
     144            0 :             jr_lim1 = two_thirds * wr * C(pow2(wr), w4, w6, w_log_term) / A(w4, w6, w_log_term)
     145              : 
     146            0 :             wr = max_w2
     147            0 :             w4 = pow4(wr)
     148            0 :             w6 = pow6(wr)
     149            0 :             w_log_term = log(1 - pow(wr, log_term_power))
     150            0 :             jr_lim2 = two_thirds * wr * C(pow2(wr), w4, w6, w_log_term) / A(w4, w6, w_log_term)
     151              : 
     152            0 :             if (abs(dimless_factor) > jr_lim1) then
     153            0 :                dimless_factor = sigmoid(abs(dimless_factor), jr_lim1, jr_lim2)
     154              :             end if
     155              :          end if
     156              : 
     157              :          ! otherwise, bisect result
     158            0 :          wr_high = wr
     159            0 :          wr_low = 0
     160            0 :          do while (wr_high - wr_low > 1d-6)
     161            0 :             wr = 0.5d0 * (wr_high + wr_low)
     162            0 :             w2 = pow2(wr)
     163            0 :             w4 = pow4(wr)
     164            0 :             w6 = pow6(wr)
     165            0 :             w_log_term = log(1d0 - pow(wr, log_term_power))
     166            0 :             new_dimless_factor = two_thirds * wr * C(w2, w4, w6, w_log_term) / A(w4, w6, w_log_term)
     167            0 :             if (dimless_factor > new_dimless_factor) then
     168              :                wr_low = wr
     169              :             else
     170            0 :                wr_high = wr
     171              :             end if
     172              :          end do
     173            0 :          w_roche = 0.5d0 * (wr_high + wr_low)
     174              : 
     175            0 :          if (jrot < 0d0) then
     176            0 :             w_roche = -w_roche
     177              :          end if
     178              : 
     179              :       end function w_div_w_roche_jrot
     180              : 
     181            0 :       subroutine eval_i_rot(id, r00, w_div_w_crit_roche, i_rot)
     182              :          use auto_diff_support
     183              : 
     184              :          integer, intent(in) :: id
     185              :          real(dp), intent(in) :: r00,w_div_w_crit_roche
     186              :          type (auto_diff_real_star_order1), intent(out) :: i_rot
     187              : 
     188              :          type (star_info), pointer :: s
     189              :          type (auto_diff_real_2var_order1) :: ir, r, re, w, w2, w4, w6, lg_one_sub_w4, B, A
     190              :          integer :: ierr
     191              : 
     192              :          include 'formats'
     193              : 
     194            0 :          call star_ptr(id, s, ierr)
     195            0 :          if (ierr /= 0) return
     196              : 
     197            0 :          i_rot = 0d0
     198            0 :          if (s% simple_i_rot_flag) then
     199            0 :             i_rot = two_thirds*r00*r00
     200            0 :             i_rot% d1Array(i_lnR_00) = 2*i_rot% val
     201            0 :             i_rot% d1Array(i_w_div_wc_00) = 0d0
     202              :          else
     203              :             ! Compute i_rot following Paxton et al. 2019 (ApJs, 243, 10)
     204            0 :             w = w_div_w_crit_roche
     205            0 :             w%d1val1 = 1d0
     206              : 
     207            0 :             r = r00
     208            0 :             r%d1val2 = r00 ! Makes the independent variable lnR
     209              : 
     210            0 :             w2 = pow2(w)
     211            0 :             w4 = pow4(w)
     212            0 :             w6 = pow6(w)
     213            0 :             lg_one_sub_w4 = log(1d0-w4)
     214            0 :             re = r*(1d0+w2/6d0-0.0002507d0*w4+0.06075d0*w6)
     215            0 :             B = (1d0+w2/5d0-0.2735d0*w4-0.4327d0*w6-3d0/2d0*0.5583d0*lg_one_sub_w4)
     216            0 :             A = (1d0-0.1076d0*w4-0.2336d0*w6-0.5583d0*lg_one_sub_w4)
     217              : 
     218            0 :             ir =  two_thirds*pow2(re)*B/A
     219              : 
     220            0 :             i_rot = 0d0
     221            0 :             i_rot = ir%val
     222            0 :             i_rot% d1Array(i_w_div_wc_00) = ir%d1val1
     223            0 :             i_rot% d1Array(i_lnR_00) = ir%d1val2
     224              :          end if
     225              : 
     226            0 :       end subroutine eval_i_rot
     227              : 
     228              : 
     229            0 :       subroutine set_i_rot(s, skip_w_div_w_crit_roche)
     230              :          type (star_info), pointer :: s
     231              :          logical, intent(in) :: skip_w_div_w_crit_roche
     232              :          integer :: k
     233              :          include 'formats'
     234              : 
     235            0 : !$OMP PARALLEL DO PRIVATE(k) SCHEDULE(dynamic,2)
     236              :          do k=1,s% nz
     237              :             if (.not. skip_w_div_w_crit_roche) then
     238              :                s% w_div_w_crit_roche(k) = &
     239              :                   w_div_w_roche_jrot(s% r(k), s% m(k), s% j_rot(k), s% cgrav(k), &
     240              :                      s% w_div_wcrit_max, s% w_div_wcrit_max2, s% w_div_wc_flag)
     241              :             end if
     242              :             call set1_i_rot(s, k, s% r(k))
     243              :          end do
     244              : !$OMP END PARALLEL DO
     245            0 :       end subroutine set_i_rot
     246              : 
     247            0 :       subroutine set_i_rot_from_omega_and_j_rot(s)
     248              :          use auto_diff_support
     249              :          type (star_info), pointer :: s
     250              :          integer :: k
     251              :          include 'formats'
     252              : 
     253            0 : !$OMP PARALLEL DO PRIVATE(k) SCHEDULE(dynamic, 2)
     254              :          do k=1,s% nz
     255              :             if (s% omega(k) /= 0d0) then
     256              :                ! we can directly compute i_rot using j_rot and omega
     257              :                s% i_rot(k) = 0d0
     258              :                s% i_rot(k)% val = s% j_rot(k)/s% omega(k)
     259              :             else
     260              :                call update1_i_rot_from_xh(s, k)
     261              :             end if
     262              :          end do
     263              : !$OMP END PARALLEL DO
     264            0 :       end subroutine set_i_rot_from_omega_and_j_rot
     265              : 
     266            0 :       subroutine set_j_rot(s)
     267              :          type (star_info), pointer :: s
     268              :          integer :: k
     269              :          include 'formats'
     270            0 : !$OMP PARALLEL DO PRIVATE(k) SCHEDULE(dynamic, 2)
     271              :          do k=1,s% nz
     272              :             s% j_rot(k) = s% i_rot(k)% val*s% omega(k)
     273              :          end do
     274              : !$OMP END PARALLEL DO
     275            0 :       end subroutine set_j_rot
     276              : 
     277            0 :       subroutine set_omega(s, str)
     278              :          type (star_info), pointer :: s
     279              :          character (len=*) :: str
     280              :          integer :: k
     281              :          include 'formats'
     282            0 : !$OMP PARALLEL DO PRIVATE(k) SCHEDULE(dynamic, 2)
     283              :          do k=1,s% nz
     284              :             s% omega(k) = s% j_rot(k)/s% i_rot(k)% val
     285              :          end do
     286              : !$OMP END PARALLEL DO
     287            0 :       end subroutine set_omega
     288              : 
     289              :       subroutine check_omega(s, str)
     290              :          type (star_info), pointer :: s
     291              :          character (len=*) :: str
     292              :          integer :: k
     293              :          logical :: okay
     294              :          include 'formats'
     295              :          okay = .true.
     296              :          do k=1,s% nz
     297              :             if (abs(s% omega(k) - s% j_rot(k)/s% i_rot(k)% val) > 1d-14) then
     298              :                write(*,2) 'omega error', k, s% omega(k) - s% j_rot(k)/s% i_rot(k)% val
     299              :                okay = .false.
     300              :                exit
     301              :             end if
     302              :          end do
     303              :          if (okay) return
     304              :          write(*,*) trim(str)
     305              :          call mesa_error(__FILE__,__LINE__,'check_omega')
     306              :       end subroutine check_omega
     307              : 
     308            0 :       subroutine update1_i_rot_from_xh(s, k)
     309              :          type (star_info), pointer :: s
     310              :          integer, intent(in) :: k
     311              :          real(dp) :: r00
     312              :          include 'formats'
     313            0 :          r00 = get_r_from_xh(s, k)
     314            0 :          call set1_i_rot(s, k, r00)
     315            0 :       end subroutine update1_i_rot_from_xh
     316              : 
     317            0 :       subroutine set1_i_rot(s, k, r)
     318              :          use auto_diff
     319              :          type (star_info), pointer :: s
     320              :          real(dp), intent(in) :: r
     321              :          integer, intent(in) :: k
     322              : 
     323              :          type (auto_diff_real_star_order1) :: i_rot_single, i_rot_tidal
     324              : 
     325            0 :          if (s% use_other_eval_i_rot) then
     326            0 :             call s% other_eval_i_rot(s% id, k, r, s% w_div_w_crit_roche(k), s% i_rot(k))
     327              :          else
     328            0 :             call eval_i_rot(s% id, r, s% w_div_w_crit_roche(k), i_rot_single)
     329            0 :             if (associated(s% binary_other_irot)) then
     330            0 :                call s% binary_other_irot(s% id, r, i_rot_tidal)
     331            0 :                call blend_tidal_values(s, k, i_rot_single, i_rot_tidal, s% omega(k), s% i_rot(k))
     332              :             else
     333            0 :                s% i_rot(k) = i_rot_single
     334              :             end if
     335              :          end if
     336            0 :       end subroutine set1_i_rot
     337              : 
     338            0 :       subroutine blend_tidal_values(s, k, single, tidal, omega, final)
     339            0 :          use auto_diff
     340              : 
     341              :          type (star_info), pointer :: s
     342              :          integer, intent(in) :: k
     343              :          real(dp), intent(in) :: omega
     344              :          type (auto_diff_real_star_order1), intent(in) :: single, tidal
     345              :          type (auto_diff_real_star_order1), intent(out) :: final
     346              : 
     347            0 :          real(dp) :: tidal_frac
     348              :          integer :: ierr
     349              : 
     350              :          include 'formats'
     351              : 
     352              :          ! do blending with tidal case according to synchronicity
     353            0 :          if (associated(s% binary_deformation_switch_fraction)) then
     354            0 :             call s% binary_deformation_switch_fraction(s% id, k, omega, tidal_frac, ierr)  ! tells how much of the tidal value we should use
     355            0 :             if (ierr /= 0) then
     356            0 :                if (s% report_ierr) write(*, 1) "failed in blend_tidal_values"
     357              :             end if
     358              :          end if
     359            0 :          final = tidal_frac * tidal + (1d0 - tidal_frac) * single
     360            0 :       end subroutine blend_tidal_values
     361              : 
     362            0 :       subroutine use_xh_to_update_i_rot(s)
     363              :          type (star_info), pointer :: s
     364              :          integer :: k
     365            0 :          real(dp) :: r00
     366              : 
     367            0 : !$OMP PARALLEL DO PRIVATE(k, r00) SCHEDULE(dynamic,2)
     368              :          do k=1,s% nz
     369              :             if (s% j_rot(k) /= 0d0) then
     370              :                r00 = get_r_from_xh(s,k)
     371              :                s% w_div_w_crit_roche(k) = &
     372              :                   w_div_w_roche_jrot(r00, s% m(k), s% j_rot(k), s% cgrav(k), &
     373              :                      s% w_div_wcrit_max, s% w_div_wcrit_max2, s% w_div_wc_flag)
     374              :             else
     375              :                s% w_div_w_crit_roche(k) = 0d0
     376              :             end if
     377              :             call update1_i_rot_from_xh(s, k)
     378              :          end do
     379              : !$OMP END PARALLEL DO
     380            0 :       end subroutine use_xh_to_update_i_rot
     381              : 
     382            0 :       subroutine use_xh_to_update_i_rot_and_j_rot(s)
     383              :          type (star_info), pointer :: s
     384              :          integer :: k
     385            0 :          real(dp) :: r00
     386              : 
     387            0 : !$OMP PARALLEL DO PRIVATE(k, r00) SCHEDULE(dynamic,2)
     388              :          do k=1,s% nz
     389              :             r00 = get_r_from_xh(s,k)
     390              :             s% w_div_w_crit_roche(k) = &
     391              :                w_div_w_roche_omega(r00, s% m(k), s% omega(k), s% cgrav(k), &
     392              :                   s% w_div_wcrit_max, s% w_div_wcrit_max2, s% w_div_wc_flag)
     393              :             call update1_i_rot_from_xh(s,k)
     394              :          end do
     395              : !$OMP END PARALLEL DO
     396            0 :          call set_j_rot(s)
     397            0 :       end subroutine use_xh_to_update_i_rot_and_j_rot
     398              : 
     399            0 :       subroutine get_rotation_sigmas(s, nzlo, nzhi, dt, ierr)
     400              :          type (star_info), pointer :: s
     401              :          integer, intent(in) :: nzlo, nzhi
     402              :          real(dp), intent(in) :: dt
     403              :          integer, intent(out) :: ierr
     404              : 
     405              :          integer :: k, nz
     406              :          real(dp), allocatable :: am_nu(:), am_sig(:)
     407              : 
     408              :          include 'formats'
     409              : 
     410            0 :          ierr = 0
     411            0 :          nz = s% nz
     412              : 
     413            0 :          allocate(am_nu(nz), am_sig(nz))
     414              : 
     415            0 :          call get1_am_sig(s, nzlo, nzhi, s% am_nu_j, s% am_sig_j, dt, ierr)
     416            0 :          if (ierr /= 0) then
     417            0 :             if (s% report_ierr) write(*,1) 'failed in get_rotation_sigmas'
     418            0 :             return
     419              :          end if
     420              : 
     421            0 :          do k=1,nz
     422            0 :             am_nu(k) = s% am_nu_j(k) + s% am_nu_omega(k)
     423              :          end do
     424              :          ! do it this way so apply limit to sum; sum is used as diffusion coeff for omega
     425            0 :          call get1_am_sig(s, nzlo, nzhi, am_nu, am_sig, dt, ierr)
     426            0 :          if (ierr /= 0) then
     427            0 :             if (s% report_ierr) write(*,1) 'failed in get_rotation_sigmas'
     428            0 :             return
     429              :          end if
     430              : 
     431            0 :          do k=1,nz
     432            0 :             s% am_sig_omega(k) = max(0d0, am_sig(k) - s% am_sig_j(k))
     433              :          end do
     434              : 
     435            0 :       end subroutine get_rotation_sigmas
     436              : 
     437            0 :       subroutine get1_am_sig(s, nzlo, nzhi, am_nu, am_sig, dt, ierr)
     438              :          type (star_info), pointer :: s
     439              :          integer, intent(in) :: nzlo, nzhi
     440              :          real(dp), intent(in) :: dt
     441              :          real(dp), dimension(:) :: am_nu, am_sig
     442              :          integer, intent(out) :: ierr
     443              : 
     444              :          integer :: k, nz, nz2
     445            0 :          real(dp) :: r, D, am_nu_E00, am_nu_Ep1, dmbar, s1, &
     446            0 :             sig_term_limit, xmstar, siglim
     447              : 
     448              :          include 'formats'
     449              : 
     450            0 :          ierr = 0
     451            0 :          xmstar = s% xmstar
     452            0 :          sig_term_limit = s% am_sig_term_limit
     453            0 :          nz = s% nz
     454              :          ! note: am_sig is cell centered, so combine adjacent am_nu face values.
     455            0 :          am_nu_E00 = 0; am_nu_Ep1 = 0
     456            0 :          nz2 = nzhi
     457            0 :          if (nzhi == nz) then
     458            0 :             k = nz
     459            0 :             D = am_nu_E00
     460            0 :             r = 0.5d0*s% r(k)
     461            0 :             s1 = pi4*r*r*s% rho(k)
     462            0 :             am_sig(k) = s1*s1*D/s% dm(k)
     463            0 :             nz2 = nz-1
     464              :          end if
     465            0 :          do k = nzlo, nz2
     466            0 :             am_nu_E00 = max(0d0, am_nu(k))
     467            0 :             am_nu_Ep1 = max(0d0, am_nu(k+1))
     468              :             ! Meynet, Maeder, & Mowlavi, A&A 416, 1023-1036, 2004, eqn 51 with f = 1/2.
     469            0 :             D = 2*(am_nu_E00*am_nu_Ep1)/max(1d-99, am_nu_E00 + am_nu_Ep1)
     470            0 :             r = 0.5d0*(s% r(k) + s% r(k+1))  ! consistent with f = 1/2
     471            0 :             s1 = pi4*r*r*s% rho(k)
     472            0 :             am_sig(k) = s1*s1*D/s% dm(k)
     473              :          end do
     474              : 
     475              :          ! can get numerical problems unless limit am_sig
     476              :          ! adjust am_sig to make sure am_sig*dt/dmbar is < allowed limit
     477            0 :          do k = nzlo, nzhi
     478            0 :             if (k < nz) then
     479            0 :                dmbar = xmstar*min(s% dq(k),s% dq(k+1))
     480            0 :                siglim = sig_term_limit*dmbar/dt
     481              :             else
     482            0 :                dmbar = xmstar*s% dq(k)
     483            0 :                siglim = sig_term_limit*dmbar/dt
     484              :             end if
     485            0 :             if (am_sig(k) > siglim) then
     486            0 :                am_sig(k) = siglim
     487              :             end if
     488              :          end do
     489              : 
     490            0 :       end subroutine get1_am_sig
     491              : 
     492            0 :       subroutine set_uniform_omega(id, omega, ierr)
     493              :          use auto_diff_support
     494              :          integer, intent(in) :: id
     495              :          real(dp), intent(in) :: omega
     496              :          integer, intent(out) :: ierr
     497              :          type (star_info), pointer :: s
     498              :          integer :: k, nz
     499              :          include 'formats'
     500            0 :          call get_star_ptr(id, s, ierr)
     501            0 :          if (ierr /= 0) return
     502            0 :          nz = s% nz
     503            0 :          do k=1, nz
     504            0 :             s% omega(k) = omega
     505            0 :             s% fp_rot(k) = 0d0
     506            0 :             s% ft_rot(k) = 0d0
     507              :          end do
     508            0 :          call use_xh_to_update_i_rot_and_j_rot(s)
     509            0 :          s% w_div_w_crit_roche(1:nz) = 0d0
     510            0 :          s% r_polar(1:nz) = 0d0
     511            0 :          s% r_equatorial(1:nz) = 0d0
     512            0 :          s% am_nu_rot(1:nz) = 0d0
     513            0 :          s% am_nu_non_rot(1:nz) = 0d0
     514            0 :          s% am_nu_omega(1:nz) = 0d0
     515            0 :          s% am_nu_j(1:nz) = 0d0
     516            0 :          s% am_sig_omega(1:nz) = 0d0
     517            0 :          s% am_sig_j(1:nz) = 0d0
     518            0 :          s% domega_dlnR(1:nz) = 0d0
     519            0 :          s% richardson_number(1:nz) = 0d0
     520            0 :          s% D_mix_non_rotation(1:nz) = 0d0
     521            0 :          s% D_visc(1:nz) = 0d0
     522            0 :          s% D_DSI(1:nz) = 0d0
     523            0 :          s% D_SH(1:nz) = 0d0
     524            0 :          s% D_SSI(1:nz) = 0d0
     525            0 :          s% D_ES(1:nz) = 0d0
     526            0 :          s% D_GSF(1:nz) = 0d0
     527            0 :          s% D_ST(1:nz) = 0d0
     528            0 :          s% nu_ST(1:nz) = 0d0
     529            0 :          s% omega_shear(1:nz) = 0d0
     530            0 :          s% dynamo_B_r(1:nz) = 0d0
     531            0 :          s% dynamo_B_phi(1:nz) = 0d0
     532            0 :          call set_rotation_info(s, .false., ierr)
     533            0 :          if (ierr /= 0) return
     534            0 :          s% need_to_setvars = .true.
     535            0 :       end subroutine set_uniform_omega
     536              : 
     537            0 :       subroutine set_rotation_info(s, skip_w_div_w_crit_roche, ierr)
     538            0 :          use auto_diff
     539              :          type (star_info), pointer :: s
     540              :          logical, intent(in) :: skip_w_div_w_crit_roche
     541              :          integer, intent(out) :: ierr
     542              : 
     543              :          type (auto_diff_real_star_order1) :: fp_single, fp_tidal, ft_single, ft_tidal
     544              :          integer :: k
     545              :          include 'formats'
     546            0 :          ierr = 0
     547              : 
     548            0 :          if (.not. s% rotation_flag) return
     549              : 
     550            0 :          call set_i_rot(s, skip_w_div_w_crit_roche)
     551            0 :          call set_omega(s, 'set_rotation_info')
     552              : 
     553            0 : !$OMP PARALLEL DO PRIVATE(k, fp_single, ft_single, fp_tidal, ft_tidal, ierr) SCHEDULE(dynamic,2)
     554              :          do k=1, s% nz
     555              :             if (s% use_other_eval_fp_ft) then
     556              :                call s% other_eval_fp_ft( &
     557              :                      s% id, k, s% m(k), s% r(k), s% rho(k), s% omega(k), s% fp_rot(k), s% ft_rot(k), &
     558              :                      s% r_polar(k), s% r_equatorial(k), s% report_ierr, ierr)
     559              :             else
     560              :                call eval_fp_ft( &
     561              :                      s% id, s% r(k), s% w_div_w_crit_roche(k), fp_single, ft_single, &
     562              :                      s% r_polar(k), s% r_equatorial(k), s% report_ierr, ierr)
     563              :                !if (k == s% solver_test_partials_k) then
     564              :                !   s% solver_test_partials_val = fp_single
     565              :                !   s% solver_test_partials_var = s% i_w_div_wc
     566              :                !   s% solver_test_partials_dval_dx = s% dfp_rot_dw_div_wc(k)
     567              :                !end if
     568              :                if (associated(s% binary_other_fp_ft)) then
     569              :                   call s% binary_other_fp_ft(&
     570              :                         s% id, s% r(k), fp_tidal, ft_tidal, s% r_polar(k), s% r_equatorial(k), &
     571              :                         s% report_ierr, ierr)
     572              :                   call blend_tidal_values(s, k, fp_single, fp_tidal, s% omega(k), s% fp_rot(k))
     573              :                   call blend_tidal_values(s, k, ft_single, ft_tidal, s% omega(k), s% ft_rot(k))
     574              :                else
     575              :                   s% fp_rot(k) = fp_single
     576              :                   s% ft_rot(k) = ft_single
     577              :                end if
     578              : 
     579              :                if (ierr /= 0) then
     580              :                   write(*,1) 'failed in eval_fp_ft'
     581              :                end if
     582              : 
     583              :             end if
     584              :          end do
     585              : !$OMP END PARALLEL DO
     586            0 :       end subroutine set_rotation_info
     587              : 
     588           33 :       subroutine set_surf_avg_rotation_info(s)
     589            0 :          use star_utils, only: get_Lrad_div_Ledd
     590              :          type (star_info), pointer :: s
     591              :          real(dp) :: &
     592           33 :             dm, dmsum, omega_sum, omega_crit_sum, omega_div_omega_crit_sum, &
     593           33 :             v_rot_sum, v_crit_sum, v_div_v_crit_sum, Lrad_div_Ledd_sum, &
     594           33 :             gamma_factor, omega_crit, omega, kap_sum, &
     595           33 :             j_rot_sum, j_rot, v_rot, v_crit, Lrad_div_Ledd, dtau, tau, &
     596           33 :             cgrav, kap, mmid, Lmid, rmid, logT_sum, logRho_sum
     597              :          integer :: k, ierr
     598              :          logical, parameter :: dbg = .false.
     599              :          include 'formats'
     600              : 
     601           33 :          if (.not. s% rotation_flag) then
     602           33 :             s% omega_avg_surf = 0
     603           33 :             s% omega_crit_avg_surf = 0
     604           33 :             s% w_div_w_crit_avg_surf = 0
     605           33 :             s% j_rot_avg_surf = 0
     606           33 :             s% v_rot_avg_surf = 0
     607           33 :             s% v_crit_avg_surf = 0
     608           33 :             s% v_div_v_crit_avg_surf = 0
     609           33 :             s% Lrad_div_Ledd_avg_surf = 0
     610           33 :             s% opacity_avg_surf = 0
     611           33 :             s% logT_avg_surf = 0
     612           33 :             s% logRho_avg_surf = 0
     613              :             return
     614              :          end if
     615              : 
     616              :          ierr = 0
     617            0 :          call set_rotation_info(s,.true.,ierr)
     618            0 :          if (ierr /= 0) then
     619            0 :             write(*,*) 'got ierr from call set_rotation_info in set_surf_avg_rotation_info'
     620            0 :             write(*,*) 'just ignore it'
     621              :          end if
     622              : 
     623            0 :          tau = s% tau_factor*s% tau_base
     624            0 :          dmsum = 0d0
     625            0 :          Lrad_div_Ledd_sum = 0d0
     626            0 :          rmid = 0d0
     627              : 
     628            0 :          do k = 1, s% nz - 1
     629            0 :             kap = s% opacity(k)
     630            0 :             rmid = s% rmid(k)
     631            0 :             mmid = 0.5d0*(s% m_grav(k) + s% m_grav(k+1))
     632            0 :             Lmid = 0.5d0*(s% L(k) + s% L(k+1))
     633            0 :             cgrav = 0.5d0*(s% cgrav(k) + s% cgrav(k+1))
     634            0 :             dm = s% dm(k)
     635            0 :             dtau = dm*kap/(pi4*rmid*rmid)
     636              : 
     637            0 :             if (tau + dtau <= s% surf_avg_tau_min) then
     638              :                tau = tau + dtau
     639              :                cycle
     640              :             end if
     641              : 
     642              :             ! check for partial contribution from cell
     643              :             ! the tau < s% surf_avg_tau is meant for the case in which the surface tau is set
     644              :             ! equal or larger to surf_avg_tau. In that case we just use the values of the surface cell.
     645            0 :             if (tau < s% surf_avg_tau) then
     646            0 :                if (tau < s% surf_avg_tau_min) then  ! only use part of this cell
     647            0 :                   dm = dm*(tau + dtau - s% surf_avg_tau_min)/dtau
     648            0 :                else if (tau + dtau > s% surf_avg_tau) then  ! only use part of this cell
     649            0 :                   dm = dm*(s% surf_avg_tau - tau)/dtau
     650              :                   !write(*,2) 'tau limit', k, (s% surf_avg_tau - tau)/dtau
     651              :                end if
     652              :             end if
     653            0 :             dmsum = dmsum + dm
     654            0 :             Lrad_div_Ledd = get_Lrad_div_Ledd(s,k)
     655            0 :             Lrad_div_Ledd_sum = Lrad_div_Ledd_sum + dm*Lrad_div_Ledd
     656            0 :             tau = tau + dtau
     657            0 :             if (tau >= s% surf_avg_tau) exit
     658              :          end do
     659              : 
     660            0 :          s% Lrad_div_Ledd_avg_surf = Lrad_div_Ledd_sum/dmsum
     661            0 :          gamma_factor = 1d0 - min(s% Lrad_div_Ledd_avg_surf, 0.9999d0)
     662              : 
     663            0 :          tau = s% tau_factor*s% tau_base
     664            0 :          dmsum = 0
     665            0 :          j_rot_sum = 0
     666            0 :          omega_sum = 0
     667            0 :          omega_crit_sum = 0
     668            0 :          omega_div_omega_crit_sum = 0
     669            0 :          v_rot_sum = 0
     670            0 :          v_crit_sum = 0
     671            0 :          v_div_v_crit_sum = 0
     672            0 :          kap_sum = 0
     673            0 :          logT_sum = 0
     674            0 :          logRho_sum = 0
     675              : 
     676            0 :          do k = 1, s% nz - 1
     677              : 
     678            0 :             kap = s% opacity(k)
     679              :             ! TODO: better explain
     680              :             ! Use equatorial radius
     681            0 :             rmid = 0.5d0*(s% r_equatorial(k) + s% r_equatorial(k+1))
     682            0 :             dm = s% dm(k)
     683            0 :             dtau = dm*kap/(pi4*rmid*rmid)
     684              : 
     685            0 :             if (tau + dtau <= s% surf_avg_tau_min) then
     686              :                tau = tau + dtau
     687              :                cycle
     688              :             end if
     689              : 
     690              :             ! check for partial contribution from cell
     691              :             ! the tau < s% surf_avg_tau is meant for the case in which the surface tau is set
     692              :             ! equal or larger to surf_avg_tau. In this case we just use the values of the surface cell.
     693            0 :             if (tau < s% surf_avg_tau) then
     694            0 :                if (tau < s% surf_avg_tau_min) then  ! only use part of this cell
     695            0 :                   dm = dm*(tau + dtau - s% surf_avg_tau_min)/dtau
     696            0 :                else if (tau + dtau > s% surf_avg_tau) then  ! only use part of this cell
     697            0 :                   dm = dm*(s% surf_avg_tau - tau)/dtau
     698              :                end if
     699              :             end if
     700              : 
     701            0 :             dmsum = dmsum + dm
     702            0 :             cgrav = 0.5d0*(s% cgrav(k) + s% cgrav(k+1))
     703            0 :             mmid = 0.5d0*(s% m_grav(k) + s% m_grav(k+1))
     704            0 :             omega = 0.5d0*(s% omega(k) + s% omega(k+1))
     705            0 :             j_rot = 0.5d0*(s% j_rot(k) + s% j_rot(k+1))
     706              : 
     707            0 :             kap_sum = kap_sum + dm*kap
     708            0 :             j_rot_sum = j_rot_sum + dm*j_rot
     709              : 
     710            0 :             omega_crit = sqrt(gamma_factor*cgrav*mmid/pow3(rmid))
     711            0 :             omega_div_omega_crit_sum = omega_div_omega_crit_sum + dm*abs(omega/omega_crit)
     712              : 
     713            0 :             v_rot = omega*rmid
     714            0 :             v_crit = omega_crit*rmid
     715            0 :             omega_sum = omega_sum + dm*omega
     716            0 :             omega_crit_sum = omega_crit_sum + dm*omega_crit
     717            0 :             v_rot_sum = v_rot_sum + dm*v_rot
     718            0 :             v_crit_sum = v_crit_sum + dm*v_crit
     719            0 :             v_div_v_crit_sum = v_div_v_crit_sum + dm*abs(v_rot/v_crit)
     720            0 :             logT_sum = logT_sum + dm*s% lnT(k)/ln10
     721            0 :             logRho_sum = logRho_sum + dm*s% lnd(k)/ln10
     722            0 :             kap_sum = kap_sum + dm*kap
     723            0 :             tau = tau + dtau
     724            0 :             if (tau >= s% surf_avg_tau) exit
     725              : 
     726              :          end do
     727              : 
     728            0 :          s% logT_avg_surf = logT_sum/dmsum
     729            0 :          s% logRho_avg_surf = logRho_sum/dmsum
     730            0 :          s% opacity_avg_surf = kap_sum/dmsum
     731            0 :          s% j_rot_avg_surf = j_rot_sum/dmsum
     732            0 :          s% omega_avg_surf = omega_sum/dmsum
     733            0 :          s% omega_crit_avg_surf = omega_crit_sum/dmsum
     734            0 :          s% w_div_w_crit_avg_surf = omega_div_omega_crit_sum/dmsum
     735            0 :          s% v_rot_avg_surf = v_rot_sum/dmsum
     736            0 :          s% v_crit_avg_surf = v_crit_sum/dmsum
     737            0 :          s% v_div_v_crit_avg_surf = v_div_v_crit_sum/dmsum
     738              : 
     739           33 :       end subroutine set_surf_avg_rotation_info
     740              : 
     741              :       ! Input variables:
     742              :       !  r     Radius coordinate [cm]
     743              :       !  aw    fractional critical angular velocity Omega/Omega_crit
     744              :       ! Output variables:
     745              :       !  Correction factor Fp at each meshpoint
     746              :       !  Correction factor Ft at each meshpoint
     747              :       !  r_polar, r_equatorial at each meshpoint
     748            0 :       subroutine eval_fp_ft(id, r, aw, fp, ft, r_polar, r_equatorial, report_ierr, ierr)
     749           33 :          use num_lib
     750              :          use auto_diff_support
     751              :          integer, intent(in) :: id
     752              :          real(dp), intent(in) :: r, aw
     753              :          type(auto_diff_real_star_order1), intent(out) :: fp, ft
     754              :          real(dp), intent(inout) :: r_polar, r_equatorial
     755              :          logical, intent(in) :: report_ierr
     756              :          integer, intent(out) :: ierr
     757              : 
     758              :          type (star_info), pointer :: s
     759              :          integer :: j
     760              : 
     761              :          logical :: dbg
     762              : 
     763              :          type(auto_diff_real_1var_order1) :: A_omega, fp_numerator, ft_numerator, &
     764              :             w, w2, w3, w4, w5, w6, w_log_term, fp_temp, ft_temp
     765              : 
     766              :          include 'formats'
     767              : 
     768              :          ierr = 0
     769            0 :          call star_ptr(id, s, ierr)
     770            0 :          if (ierr /= 0) return
     771              : 
     772            0 :          dbg = .false.  ! (s% model_number >= 5)
     773              : 
     774              :          !Compute fp, ft, re and rp using fits to the Roche geometry of a single star.
     775            0 :          w = abs(aw)  ! single rotation deformation is symmetric to prograde/retrograde
     776            0 :          w% d1val1 = 1d0
     777              : 
     778            0 :          w2 = pow2(w)
     779            0 :          w4 = pow4(w)
     780            0 :          w6 = pow6(w)
     781            0 :          w_log_term = log(1d0 - pow(w, log_term_power))
     782              :          ! cannot use real function below because these are auto_diff variables
     783            0 :          A_omega = 1d0 + 0.3293d0 * w4 - 0.4926d0 * w6 - 0.5560d0 * w_log_term
     784              : 
     785              :          ! fits for fp, ft; Fabry+2022, Eqs. A.10, A.11
     786            0 :          fp_temp = (1d0 - two_thirds * w2 - 0.2133d0 * w4 - 0.1068d0 * w6) / A_omega
     787            0 :          ft_temp = (1d0 - 0.07955d0 * w4 - 0.2322d0 * w6) / A_omega
     788              : 
     789              :          ! re and rp can be derived analytically from w_div_wcrit
     790            0 :          r_equatorial = r * re_from_rpsi_factor(w2% val, w4% val, w6% val)
     791            0 :          r_polar = r_equatorial / (1d0 + 0.5d0 * w2% val)
     792              : 
     793              :          ! Be sure they are consistent with r_Psi
     794            0 :          r_equatorial = max(r_equatorial, r)
     795            0 :          r_polar = min(r_polar, r)
     796              : 
     797            0 :          fp = 0d0
     798            0 :          ft = 0d0
     799            0 :          fp% val = fp_temp% val
     800            0 :          ft% val = ft_temp% val
     801            0 :          if (s% w_div_wc_flag) then
     802            0 :             fp% d1Array(i_w_div_wc_00) = fp_temp% d1val1
     803            0 :             ft% d1Array(i_w_div_wc_00) = ft_temp% d1val1
     804              :          end if
     805              : 
     806            0 :       end subroutine eval_fp_ft
     807              : 
     808            0 :       subroutine compute_j_fluxes_and_extra_jdot(id, ierr)
     809            0 :          use auto_diff_support
     810              :          integer, intent(in) :: id
     811              :          integer, intent(out) :: ierr
     812              :          type (star_info), pointer :: s
     813              :          type(auto_diff_real_star_order1) :: omega00, omegap1, r00, rp1, i_rot00, i_rotp1, rho00, part1
     814              : 
     815              :          integer :: k
     816              :          logical :: dbg
     817              : 
     818            0 :          real(dp) :: pi2_div4
     819              :          ierr = 0
     820            0 :          dbg = .false.
     821              : 
     822            0 :          call star_ptr(id, s, ierr)
     823            0 :          if (ierr /= 0) return
     824            0 :          if (s% am_D_mix_factor==0d0) then
     825            0 :             s% am_nu_omega(:) = 0d0
     826              :          end if
     827              : 
     828            0 :          s% extra_jdot(:) = 0d0
     829            0 :          if (s% use_other_torque) then
     830            0 :             call s% other_torque(s% id, ierr)
     831            0 :             if (ierr /= 0) then
     832            0 :                if (s% report_ierr .or. dbg) &
     833            0 :                   write(*, *) 'solve_omega_mix: other_torque returned ierr', ierr
     834            0 :                return
     835              :             end if
     836              :          end if
     837            0 :          if (associated(s% binary_other_torque)) then
     838            0 :             call s% binary_other_torque(s% id, ierr)
     839            0 :             if (ierr /= 0) then
     840            0 :                if (s% report_ierr .or. dbg) &
     841            0 :                   write(*, *) 'solve_omega_mix: binary_other_torque returned ierr', ierr
     842            0 :                return
     843              :             end if
     844              :          end if
     845              : 
     846            0 :          pi2_div4 = pow2(pi)/4d0
     847            0 :          do k=1, s% nz-1
     848              : 
     849            0 :             r00 = wrap_r_00(s, k)
     850            0 :             rp1 = wrap_r_p1(s, k)
     851            0 :             rho00 = wrap_d_00(s, k)
     852              : 
     853            0 :             omega00 = wrap_omega_00(s, k)
     854            0 :             omegap1 = wrap_omega_p1(s, k)
     855              : 
     856            0 :             i_rot00 = s% i_rot(k)
     857            0 :             i_rotp1 = shift_p1(s% i_rot(k+1))
     858              : 
     859            0 :             part1 = -pi2_div4*pow4(r00+rp1)*pow2(rho00)*(i_rot00+i_rotp1)*(s% am_nu_omega(k)+s% am_nu_omega(k+1))
     860            0 :             s% j_flux(k) = part1*(omega00-omegap1)/s% dm(k)
     861              : 
     862              :             !! this is to test partials
     863              :             !if (k==188) then
     864              :             !   part1 = (omega00-omegap1)/s% dm(k)
     865              :             !   s% solver_test_partials_val = part1% val
     866              :             !   s% solver_test_partials_var = s% i_lnR !s% i_w_div_wc
     867              :             !   s% solver_test_partials_dval_dx =  part1% d1Array(i_lnR_00) !part1% d1Array(i_w_div_wc_00)
     868              :             !end if
     869              : 
     870              :          end do
     871            0 :          s% j_flux(s% nz) = 0d0
     872            0 :       end subroutine compute_j_fluxes_and_extra_jdot
     873              : 
     874            0 :       real(dp) function rpsi_from_re_factor(o2, o4, o6)
     875              :          real(dp), intent(in) :: o2
     876              :          real(dp), intent(in) :: o4
     877              :          real(dp), intent(in) :: o6
     878              :          ! Fabry+2022, Eq. A.2
     879            0 :          rpsi_from_re_factor = 1d0 - one_sixth * o2 + 0.02025d0 * o4 - 0.03870d0 * o6
     880            0 :       end function rpsi_from_re_factor
     881              : 
     882            0 :       real(dp) function re_from_rpsi_factor(o2, o4, o6)
     883              :          real(dp), intent(in) :: o2
     884              :          real(dp), intent(in) :: o4
     885              :          real(dp), intent(in) :: o6
     886              :          ! Fabry+2022, Eq. A.3
     887            0 :          re_from_rpsi_factor = 1d0 + one_sixth * o2 - 0.005124d0 * o4 + 0.06562d0 * o6
     888              :       end function re_from_rpsi_factor
     889              : 
     890            0 :       real(dp) function A(o4, o6, o_log_term)
     891              :          real(dp), intent(in) :: o4
     892              :          real(dp), intent(in) :: o6
     893              :          real(dp), intent(in) :: o_log_term
     894              :          ! Fabry+2022, Eq. A.6
     895            0 :          A = 1d0 + 0.3293d0 * o4 - 0.4926d0 * o6 - 0.5560d0 * o_log_term
     896            0 :       end function A
     897              : 
     898              :       real(dp) function B(o2, o4, o6, o_log_term)
     899              :          real(dp), intent(in) :: o2
     900              :          real(dp), intent(in) :: o4
     901              :          real(dp), intent(in) :: o6
     902              :          real(dp), intent(in) :: o_log_term
     903              :          ! Fabry+2022, Eq. A.7
     904              :          B = 1d0 + o2 / 5d0 + 0.4140d0 * o4 - 0.8650d0 * o6 - 0.8370d0 * o_log_term
     905              :       end function B
     906              : 
     907            0 :       real(dp) function C(o2, o4, o6, o_log_term)
     908              :          real(dp), intent(in) :: o2
     909              :          real(dp), intent(in) :: o4
     910              :          real(dp), intent(in) :: o6
     911              :          real(dp), intent(in) :: o_log_term
     912              :          ! Fabry+2022, Eq. A.12
     913            0 :          C = 1d0 + 17d0 / 60d0 * o2 + 0.4010d0 * o4 - 0.8606d0 * o6 - 0.9305d0 * o_log_term
     914            0 :       end function C
     915              : 
     916            0 :       real(dp) function sigmoid(x, xmax1, xmax2)
     917              :          real(dp), intent(in) :: x, xmax1, xmax2
     918              :          ! smoothly maps abs(x) = [xmax1, infty] to sigmoid = [xmax1, xmax2]
     919            0 :          sigmoid = 2d0 * (xmax2 - xmax1) / (1d0 + exp(-2d0 * (abs(x) - xmax1) / (xmax2 - xmax1))) - xmax2 + 2d0 * xmax1
     920            0 :       end function sigmoid
     921              : 
     922              :       end module hydro_rotation
        

Generated by: LCOV version 2.0-1