LCOV - code coverage report
Current view: top level - star/private - conv_premix.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 438 0
Test Date: 2025-05-08 18:23:42 Functions: 0.0 % 19 0

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2017-2018  Rich Townsend & 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 conv_premix
      21              : 
      22              :   use const_def, only: dp, i8, msun, convective_mixing
      23              :   use star_private_def
      24              :   use chem_def
      25              :   use chem_lib
      26              :   use num_lib
      27              : 
      28              :   implicit none
      29              : 
      30              :   private
      31              :   public :: do_conv_premix
      32              : 
      33              :   integer, parameter :: BURN_TYPE_NONE = 1
      34              :   integer, parameter :: BURN_TYPE_H = 2
      35              :   integer, parameter :: BURN_TYPE_HE = 3
      36              :   integer, parameter :: BURN_TYPE_Z = 4
      37              : 
      38              :   integer, parameter :: FIXED_PT_MODE = 5
      39              :   integer, parameter :: FIXED_DT_MODE = 6
      40              : 
      41              :   logical, parameter :: TRACE_ALL = .FALSE.
      42              : 
      43              :   ! The zone_info type stores information about the extent &
      44              :   ! properties of each convective zone
      45              : 
      46              :   type zone_info
      47              :      integer               :: kc_t = 0                   ! Cell index of top boundary
      48              :      integer               :: kc_b = 0                   ! Cell index of bot boundary
      49              :      real(dp)              :: vc_t = 0._dp               ! Convective velocity near top boundary
      50              :      real(dp)              :: vc_b = 0._dp               ! Convective velocity near bot boundary
      51              :      real(dp)              :: vp_t = 0._dp               ! Penetration velocity at top boundary
      52              :      real(dp)              :: vp_b = 0._dp               ! Penetration velocity at bot boundary
      53              :      real(dp)              :: dt_t = 0._dp               ! Clock for top boundary
      54              :      real(dp)              :: dt_b = 0._dp               ! Clock for bottom boundary
      55              :      integer               :: burn_type = BURN_TYPE_NONE  ! Type of burning in zone
      56              :      logical               :: sel_t = .FALSE.            ! Flag to select top boundary for modification
      57              :      logical               :: sel_b = .FALSE.            ! Flag to select bot boundary for modification
      58              :      logical               :: split_retreat = .FALSE.    ! Flag to indicate the zone has split, or the trailing bdy retreated
      59              :      real(dp), allocatable :: avg_xa(:)                  ! Initial average abundances
      60              :      real(dp), allocatable :: davg_xa_dt(:)              ! Initial rate of change of average abundances (due to burning)
      61              :      integer               :: avoid_inc_iso = 0          ! Isotope id for which increases should be avoided
      62              :   end type zone_info
      63              : 
      64              :   ! The saved_data type saves cell and face data from star_info, to
      65              :   ! enable us to restore the latter to an earlier state. Only data in
      66              :   ! cells kc_t:kc_b are used, but the arrays are full-size
      67              : 
      68              :   type saved_data
      69              :      real(dp), allocatable :: xa(:,:)
      70              :      real(dp), allocatable :: lnd(:)
      71              :      real(dp), allocatable :: rho(:)
      72              :      real(dp), allocatable :: lnPgas(:)
      73              :      real(dp), allocatable :: Pgas(:)
      74              :      real(dp), allocatable :: gradL_composition_term(:)
      75              :      integer, allocatable  :: update_mode(:)
      76              :      integer               :: kc_t = 0
      77              :      integer               :: kc_b = 0
      78              :   end type saved_data
      79              : 
      80              : contains
      81              : 
      82            0 :   subroutine do_conv_premix (s, ierr)
      83              : 
      84              :     use star_utils, only: start_time, update_time
      85              : 
      86              :     type(star_info), pointer       :: s
      87              :     integer, intent(out)           :: ierr
      88              : 
      89              :     logical, parameter :: TRACE_CONV_PREMIX = TRACE_ALL
      90              : 
      91            0 :     integer                      :: update_mode(s%nz)
      92            0 :     type(saved_data)             :: sd
      93            0 :     type(zone_info), allocatable :: zi(:)
      94              :     integer                      :: j_it
      95            0 :     real(dp), allocatable        :: dr(:)
      96            0 :     real(dp), allocatable        :: dt_t(:)
      97            0 :     real(dp), allocatable        :: dt_b(:)
      98              :     integer                      :: i_bdy
      99              :     logical                      :: t_bdy
     100              : 
     101              :     integer(i8)                   :: time0
     102            0 :     real(dp)                     :: total
     103              : 
     104            0 :     ierr = 0
     105              : 
     106            0 :     if (s% doing_timing) call start_time(s, time0, total)
     107              : 
     108              :     ! Initialize the update_mode values. These control how cells will
     109              :     ! be updated (i.e., how the thermodynamic state will be
     110              :     ! re-evaluated after abundances have changed). For untouched
     111              :     ! cells, update_mode is determined by the lnPgas_flag setting
     112              : 
     113              :     !if (s%lnPgas_flag) then
     114              :     !   update_mode = FIXED_PT_MODE
     115              :     !else
     116            0 :        update_mode = FIXED_DT_MODE
     117              :     !end if
     118              : 
     119              :     ! Allocate the saved data arrays
     120              : 
     121            0 :     call alloc_saved_data_(s, sd)
     122              : 
     123              :     ! Initialize the zone info table
     124              : 
     125            0 :     call init_zone_info_(s, zi)
     126              : 
     127              :     ! Loop until there are no boundaries left to advance
     128              : 
     129            0 :     j_it = 0
     130              : 
     131            0 :     iter_loop : do
     132              : 
     133            0 :        j_it = j_it + 1
     134              : 
     135              :        ! Validate the current zone info table
     136              : 
     137            0 :        call validate_zone_info_(s, zi)
     138              : 
     139              :        ! Check for completion
     140              : 
     141            0 :        if (.NOT. (ANY(zi%sel_t .OR. zi%sel_b))) exit iter_loop
     142              : 
     143              :        ! Calculate mixing timescales for each boundary (the explicit
     144              :        ! allocations of dt_t and dt_b are required to workaround a
     145              :        ! gfortran bug, where allocate-on-assign will raise bogus array
     146              :        ! bounds violations)
     147              : 
     148            0 :        dr = s%r(zi%kc_t+1) - s%r(zi%kc_b)
     149              : 
     150            0 :        allocate(dt_t(SIZE(zi)))
     151            0 :        allocate(dt_b(SIZE(zi)))
     152              : 
     153            0 :        dt_t = MERGE(dr/zi%vc_t, HUGE(0._dp), MASK=zi%sel_t)
     154            0 :        dt_b = MERGE(dr/zi%vc_b, HUGE(0._dp), MASK=zi%sel_b)
     155              : 
     156              :        ! Select the boundary with the shortest timescale
     157              : 
     158            0 :        i_bdy = MINLOC(MIN(dt_t, dt_b), DIM=1)
     159            0 :        t_bdy = dt_t(i_bdy) < dt_b(i_bdy)
     160              : 
     161              :        if (TRACE_CONV_PREMIX) then
     162              :           write(*,*) 'Selected i_bdy, t_bdy:', i_bdy, t_bdy
     163              :        end if
     164              : 
     165              :        ! Advance the selected boundary, modifying the model and the
     166              :        ! zone info table
     167              : 
     168            0 :        call advance_bdy_(s, update_mode, zi, i_bdy, t_bdy, sd, j_it)
     169              : 
     170              :        ! Loop around
     171              : 
     172            0 :        deallocate(dt_t)
     173            0 :        deallocate(dt_b)
     174              : 
     175              :     end do iter_loop
     176            0 :     s% need_to_setvars = .true.
     177              : 
     178            0 :     if (s% doing_timing) call update_time(s, time0, total, s% time_conv_premix)
     179              : 
     180            0 :     return
     181              : 
     182            0 :   end subroutine do_conv_premix
     183              : 
     184              : 
     185            0 :   subroutine advance_bdy_ (s, update_mode, zi, i_bdy, t_bdy, sd, j_it)
     186              : 
     187              :     type(star_info), pointer                    :: s
     188              :     integer, intent(inout)                      :: update_mode(:)
     189              :     type(zone_info), allocatable, intent(inout) :: zi(:)
     190              :     integer, intent(inout)                      :: i_bdy
     191              :     logical, intent(in)                         :: t_bdy
     192              :     type(saved_data), intent(inout)             :: sd
     193              :     integer, intent(in)                         :: j_it
     194              : 
     195              :     logical, parameter :: TRACE_ADVANCE_BDY = TRACE_ALL
     196              : 
     197              :     integer        :: j_ad
     198              :     character(256) :: filename
     199              : 
     200              :     ! Advance the top (bottom) boundary of the zone zi(i_bdy). t_bdy
     201              :     ! is .true. for the top boundary, .false. for the bottom. Because
     202              :     ! the zone info table can potentially change during the process
     203              :     ! (as zones split/merge), so can i_bdy
     204              : 
     205              :     if (TRACE_ADVANCE_BDY) then
     206              :        write(*,*) 'Advancing zone boundary: i_bdy, t_bdy, j_it=', i_bdy, t_bdy, j_it
     207              :     end if
     208              : 
     209            0 :     if (s% conv_premix_dump_snapshots) then
     210              : 
     211            0 :        write(filename, 100) 'cpm-snap.', s%model_number, '.it_', j_it, '.ad_', 0, '.dat'
     212              : 100    format(A,I5.5,A,I5.5,A,I5.5,A)
     213              : 
     214            0 :        call dump_snapshot_(s, filename)
     215              : 
     216              :     end if
     217              : 
     218            0 :     j_ad = 0
     219              : 
     220              :     advance_loop : do
     221              : 
     222            0 :        j_ad = j_ad + 1
     223              : 
     224              :        ! Check whether we've reached the edge of the grid
     225              : 
     226            0 :        if (t_bdy .AND. zi(i_bdy)%kc_t == 1) then
     227            0 :           zi(i_bdy)%sel_t = .FALSE.
     228            0 :        elseif (.NOT. t_bdy .AND. zi(i_bdy)%kc_b == s%nz) then
     229            0 :           zi(i_bdy)%sel_b = .FALSE.
     230              :        end if
     231              : 
     232              :        ! Check whether the advancing face is still selected
     233              : 
     234            0 :        if ((t_bdy .AND. .NOT. zi(i_bdy)%sel_t) .OR. &
     235              :             (.NOT. t_bdy .AND. .NOT. zi(i_bdy)%sel_b)) exit advance_loop
     236              : 
     237              :        ! Advance the boundary by one cell
     238              : 
     239            0 :        call advance_bdy_by_one_cell_(s, update_mode, zi, i_bdy, t_bdy, sd)
     240              : 
     241              :        ! If necessary, dump a snapshot
     242              : 
     243            0 :        if (s% conv_premix_dump_snapshots .AND. MOD(j_ad, 50) == 0) then
     244              : 
     245            0 :           write(filename, 100) 'cpm-snap.', s%model_number, '.it_', j_it, '.ad_', j_ad, '.dat'
     246              : 
     247            0 :           call dump_snapshot_(s, filename)
     248              : 
     249              :        end if
     250              : 
     251              :        ! Loop around
     252              : 
     253              :     end do advance_loop
     254              : 
     255            0 :     if (s% conv_premix_dump_snapshots) then
     256              : 
     257            0 :        write(filename, 100) 'cpm-snap.', s%model_number, '.it_', j_it, '.ad_', j_ad-1, '.dat'
     258              : 
     259            0 :        call dump_snapshot_(s, filename)
     260              : 
     261              :     end if
     262              : 
     263              :     if (TRACE_ADVANCE_BDY) then
     264              :        write(*,*) 'Done advancing zone boundary'
     265              :     end if
     266              : 
     267            0 :     return
     268              : 
     269            0 :   end subroutine advance_bdy_
     270              : 
     271              : 
     272            0 :   subroutine advance_bdy_by_one_cell_ (s, update_mode, zi, i_bdy, t_bdy, sd)
     273              : 
     274              :     type(star_info), pointer                    :: s
     275              :     integer, intent(inout)                      :: update_mode(:)
     276              :     type(zone_info), allocatable, intent(inout) :: zi(:)
     277              :     integer, intent(inout)                      :: i_bdy
     278              :     logical, intent(in)                         :: t_bdy
     279              :     type(saved_data), intent(inout)             :: sd
     280              : 
     281              :     integer, parameter :: N_SUB_INI = 1
     282              :     integer, parameter :: N_SUB_MAX = 1024
     283              :     logical, parameter :: TRACE_MIX_CELL = TRACE_ALL
     284              :     logical, parameter :: TRACE_MIX_SUBCELL = TRACE_ALL
     285              : 
     286            0 :     real(dp)              :: dr
     287            0 :     real(dp)              :: vp
     288            0 :     real(dp)              :: delta_dt
     289            0 :     real(dp)              :: dt_limit
     290              :     integer               :: kc_new
     291              :     integer               :: n_sub
     292              :     integer               :: kc_t
     293              :     integer               :: kc_b
     294              :     integer               :: kf_t
     295              :     integer               :: kf_b
     296            0 :     real(dp), allocatable :: xa_sub(:,:)
     297            0 :     real(dp)              :: m_sub
     298              :     logical               :: has_split
     299              :     integer               :: kc_sub
     300            0 :     real(dp)              :: m_zone
     301              :     integer               :: l
     302            0 :     real(dp)              :: avg_xa(s%species)
     303              :     integer               :: n_nc
     304              :     integer               :: kf
     305            0 :     real(dp)              :: davg_xa_dt(s%species)
     306              : 
     307              :     ! Advance the top (bottom) boundary of the zone zi(i_bdy) by one
     308              :     ! cell
     309              : 
     310              :     ! Calculate the clock increment for adding the new cell
     311              : 
     312            0 :     if (t_bdy) then
     313            0 :        kc_t = zi(i_bdy)%kc_t
     314            0 :        dr = s%r(kc_t) - s%r(kc_t+1)
     315            0 :        vp = zi(i_bdy)%vp_t
     316              :     else
     317            0 :        kc_b = zi(i_bdy)%kc_b
     318            0 :        if (kc_b < s%nz) then
     319            0 :           dr = s%r(kc_b) - s%r(kc_b+1)
     320              :        else
     321            0 :           dr = s%r(kc_b)
     322              :        end if
     323            0 :        vp = zi(i_bdy)%vp_b
     324              :     end if
     325              : 
     326            0 :     delta_dt = dr/vp
     327              : 
     328              :     ! Check whether we have enough time; if not, return
     329              : 
     330            0 :     if (s%conv_premix_time_factor > 0._dp) then
     331            0 :        dt_limit = s%conv_premix_time_factor*s%dt
     332              :     else
     333              :        dt_limit = HUGE(0._dp)
     334              :     end if
     335              : 
     336            0 :     if (t_bdy) then
     337              : 
     338            0 :        if (zi(i_bdy)%dt_t + delta_dt > dt_limit) then
     339              : 
     340            0 :           zi(i_bdy)%sel_t = .FALSE.
     341              : 
     342              :           if (TRACE_MIX_CELL) then
     343              :              write(*,*) '  Outcome: out of time', zi(i_bdy)%dt_t, delta_dt, dt_limit
     344              :           end if
     345              : 
     346            0 :           return
     347              : 
     348              :        else
     349              : 
     350            0 :           zi(i_bdy)%dt_t = zi(i_bdy)%dt_t + delta_dt
     351              : 
     352              :        end if
     353              : 
     354              :     else
     355              : 
     356            0 :        if (zi(i_bdy)%dt_b + delta_dt > dt_limit) then
     357              : 
     358            0 :           zi(i_bdy)%sel_b = .FALSE.
     359              : 
     360              :           if (TRACE_MIX_CELL) then
     361              :              write(*,*) '  Outcome: out of time', zi(i_bdy)%dt_b, delta_dt, dt_limit
     362              :           end if
     363              : 
     364            0 :           return
     365              : 
     366              :        else
     367              : 
     368            0 :           zi(i_bdy)%dt_b = zi(i_bdy)%dt_b + delta_dt
     369              : 
     370              :        end if
     371              : 
     372              :     end if
     373              : 
     374              :     ! Determine the index of the new cell we're going to add
     375              : 
     376            0 :     if (t_bdy) then
     377            0 :        kc_new = zi(i_bdy)%kc_t - 1
     378              :     else
     379            0 :        kc_new = zi(i_bdy)%kc_b + 1
     380              :     end if
     381              : 
     382              :     ! Save the current model state over all cells we *might* modify
     383              : 
     384            0 :     if (t_bdy) then
     385            0 :        call save_model_(s, update_mode, kc_new, zi(i_bdy)%kc_b, sd)
     386              :     else
     387            0 :        call save_model_(s, update_mode, zi(i_bdy)%kc_t, kc_new, sd)
     388              :     end if
     389              : 
     390              :     ! Loop over subcell refinement levels
     391              : 
     392              :     n_sub = N_SUB_INI
     393              : 
     394            0 :     refine_loop : do
     395              : 
     396              :        ! Set initial cell and face indices
     397              : 
     398            0 :        kc_t = zi(i_bdy)%kc_t
     399            0 :        kc_b = zi(i_bdy)%kc_b
     400              : 
     401            0 :        kf_t = kc_t
     402            0 :        kf_b = kc_b + 1
     403              : 
     404              :        if (TRACE_MIX_CELL) then
     405              :           write(*,*) 'Attempting to add cell via n_sub subcells:', n_sub
     406              :           write(*,*) '  kc_t   :', kc_t
     407              :           write(*,*) '  kc_b   :', kc_b
     408              :           write(*,*) '  kc_new :', kc_new
     409              :        end if
     410              : 
     411              :        ! Set update_mode over the cells being mixed
     412              : 
     413            0 :        if (s%conv_premix_fix_pgas) then
     414            0 :           update_mode(kc_t:kc_b) = FIXED_PT_MODE
     415              :        else
     416            0 :           update_mode(kc_t:kc_b) = FIXED_DT_MODE
     417              :        end if
     418              : 
     419              :        ! Set gradL_composition_term to zero on interior faces plus the
     420              :        ! advancing face
     421              : 
     422            0 :        if (t_bdy) then
     423            0 :           s%gradL_composition_term(kf_t:kf_b-1) = 0._dp
     424              :        else
     425            0 :           s%gradL_composition_term(kf_t+1:kf_b) = 0._dp
     426              :        end if
     427              : 
     428              :        ! Divide the new cell into n_sub virtual subcells, and store the
     429              :        ! (initially uniform) abundances of these subcells
     430              : 
     431            0 :        if (ALLOCATED(xa_sub)) deallocate(xa_sub)
     432            0 :        allocate(xa_sub(s%species,n_sub))
     433              : 
     434            0 :        do l = 1, s%species
     435            0 :           xa_sub(l,:) = s%xa(l,kc_new)
     436              :        end do
     437              : 
     438            0 :        m_sub = s%dm(kc_new)/n_sub
     439              : 
     440              :        ! Mix subcells into the zone, one by one
     441              : 
     442            0 :        has_split = .FALSE.
     443              : 
     444            0 :        subcell_loop : do kc_sub = 1, n_sub
     445              : 
     446              :           if (TRACE_MIX_SUBCELL) then
     447              :              write(*,*) 'In subcell loop:', kc_sub, n_sub
     448              :           end if
     449              : 
     450              :           ! Determine average abundances across the zone and the 1:k_s
     451              :           ! subcells
     452              : 
     453            0 :           m_zone = SUM(s%dm(kc_t:kc_b))
     454              : 
     455            0 :           do l = 1, s%species
     456              :              avg_xa(l) = (SUM(s%dm(kc_t:kc_b)*s%xa(l,kc_t:kc_b)) + &
     457            0 :                           SUM(m_sub*xa_sub(l,1:kc_sub))) / (m_zone + m_sub*kc_sub)
     458              :           end do
     459              : 
     460            0 :           avg_xa = MAX(MIN(avg_xa, 1._dp), 0._dp)
     461            0 :           avg_xa = avg_xa/SUM(avg_xa)
     462              : 
     463              :           ! Update abundances using the average
     464              : 
     465            0 :           do l = 1, s%species
     466            0 :              s%xa(l,kc_t:kc_b) = avg_xa(l)
     467            0 :              xa_sub(l,1:kc_sub) = avg_xa(l)
     468              :           end do
     469              : 
     470            0 :           call update_model_(s, update_mode, kc_t, kc_b)
     471              : 
     472              :           ! Determine how many interior faces are now non-convective
     473              : 
     474            0 :           n_nc = COUNT(s%mlt_mixing_type(kf_t+1:kf_b-1) /= convective_mixing)
     475              : 
     476            0 :           if (n_nc > 1 .AND. n_sub < N_SUB_MAX) then
     477              : 
     478              :              ! More than a single one: double the number of subcells
     479              :              ! and restart (as long as we're below the subcell limit)
     480              : 
     481            0 :              n_sub = 2*n_sub
     482              : 
     483            0 :              call restore_model_(s, update_mode, sd)
     484              : 
     485              :              cycle refine_loop
     486              : 
     487            0 :           elseif (n_nc > 0) then
     488              : 
     489              :              ! A single one (or more, if we're over the subcell
     490              :              ! limit): move the trailing boundary to its new position
     491              :              ! (defined as the closest non-convective face to the
     492              :              ! advancing boundary)
     493              : 
     494            0 :              has_split = .TRUE.
     495              : 
     496            0 :              if (t_bdy) then
     497              : 
     498            0 :                 search_down_loop : do kf = kf_t+1, kf_b-1
     499            0 :                    if (s%mlt_mixing_type(kf) /= convective_mixing) exit search_down_loop
     500              :                 end do search_down_loop
     501              : 
     502            0 :                 kc_b = kf - 1
     503            0 :                 kf_b = kc_b + 1
     504              : 
     505              :                 if (TRACE_MIX_SUBCELL) then
     506              :                    write(*,*) '  Moved lower boundary to', kc_b
     507              :                 end if
     508              : 
     509              :              else
     510              : 
     511            0 :                 search_up_loop : do kf = kf_b-1, kf_t+1, -1
     512            0 :                    if (s%mlt_mixing_type(kf) /= convective_mixing) exit search_up_loop
     513              :                 end do search_up_loop
     514              : 
     515            0 :                 kc_t = kf
     516            0 :                 kf_t = kc_t
     517              : 
     518              :                 if (TRACE_MIX_SUBCELL) then
     519              :                    write(*,*) '  Moved upper boundary to', kc_t
     520              :                 end if
     521              : 
     522              :              end if
     523              : 
     524              :           end if
     525              : 
     526              :        end do subcell_loop
     527              : 
     528              :        ! If we reach this point, all went well; exit
     529              : 
     530              :        if (TRACE_MIX_SUBCELL) then
     531              :           write(*,*) '  Completed mixing subcell'
     532              :        end if
     533              : 
     534              :        exit refine_loop
     535              : 
     536              :     end do refine_loop
     537              : 
     538              :     ! Update the abundances in the new cell, and adjust the cell/face
     539              :     ! indices
     540              : 
     541            0 :     s%xa(:,kc_new) = avg_xa
     542              : 
     543            0 :     if (s%conv_premix_fix_pgas) then
     544            0 :        update_mode(kc_new) = FIXED_PT_MODE
     545              :     else
     546            0 :        update_mode(kc_new) = FIXED_DT_MODE
     547              :     end if
     548              : 
     549            0 :     if (t_bdy) then
     550            0 :        kc_t = kc_new
     551            0 :        kf_t = kc_t
     552              :     else
     553            0 :        kc_b = kc_new
     554            0 :        kf_b = kc_b + 1
     555              :     end if
     556              : 
     557            0 :     call update_model_(s, update_mode, kc_t, kc_b)
     558              : 
     559              :     ! Check whether an abundance increase has occurred; if so, revert
     560              :     ! back to the starting model and return
     561              : 
     562            0 :     if (zi(i_bdy)%avoid_inc_iso /= 0) then
     563              : 
     564              :        ! Evaluate the rate of change of the zone-average abundances
     565              : 
     566            0 :        davg_xa_dt = (avg_xa - zi(i_bdy)%avg_xa)/s%dt
     567              : 
     568              :        ! Check whether an increase will occur (davg_xa_dt is positive,
     569              :        ! and greater in magnitude than the rate of decrease due to
     570              :        ! burning)
     571              : 
     572              :        associate (iso => zi(i_bdy)%avoid_inc_iso)
     573              : 
     574            0 :          if (davg_xa_dt(iso) > MAX(-zi(i_bdy)%davg_xa_dt(iso), 0._dp)) then
     575              : 
     576            0 :             call restore_model_(s, update_mode, sd)
     577              : 
     578            0 :             if (t_bdy) then
     579            0 :                zi(i_bdy)%sel_t = .FALSE.
     580              :             else
     581            0 :                zi(i_bdy)%sel_b = .FALSE.
     582              :             end if
     583              : 
     584              :             if (TRACE_MIX_CELL) then
     585              :                write(*,*) '  Outcome: abundance reversal for isotope', iso
     586              :             end if
     587              : 
     588            0 :             return
     589              : 
     590              :          end if
     591              : 
     592              :        end associate
     593              : 
     594              :     end if
     595              : 
     596              :     ! Determine whether the face just inside the advancing boundary
     597              :     ! has become/remained convective; if not, revert back to the
     598              :     ! starting model and return
     599              : 
     600            0 :     if (t_bdy) then
     601              : 
     602            0 :        if (s%mlt_mixing_type(kf_t+1) /= convective_mixing) then
     603              : 
     604            0 :           call restore_model_(s, update_mode, sd)
     605              : 
     606            0 :           zi(i_bdy)%sel_t = .FALSE.
     607              : 
     608              :           if (TRACE_MIX_CELL) then
     609              :              write(*,*) '  Outcome: non-convective at top'
     610              :           end if
     611              : 
     612            0 :           return
     613              : 
     614              :        end if
     615              : 
     616              :     else
     617              : 
     618            0 :        if (s%mlt_mixing_type(kf_b-1) /= convective_mixing) then
     619              : 
     620            0 :           call restore_model_(s, update_mode, sd)
     621              : 
     622            0 :           zi(i_bdy)%sel_b = .FALSE.
     623              : 
     624              :           if (TRACE_MIX_CELL) then
     625              :              write(*,*) '  Outcome: non-convective at bottom'
     626              :           end if
     627              : 
     628            0 :           return
     629              : 
     630              :        end if
     631              : 
     632              :     end if
     633              : 
     634              :     ! Update the zone info table to reflect all the changes we made
     635              : 
     636            0 :     call update_zone_info_(s, i_bdy, t_bdy, has_split, zi)
     637              : 
     638            0 :     return
     639              : 
     640            0 :   end subroutine advance_bdy_by_one_cell_
     641              : 
     642              : 
     643            0 :   subroutine init_zone_info_ (s, zi)
     644              : 
     645              :     type(star_info), pointer                  :: s
     646              :     type(zone_info), allocatable, intent(out) :: zi(:)
     647              : 
     648              :     integer :: i
     649              : 
     650              :     ! Initialize the zone info table for the whole star
     651              : 
     652            0 :     call create_zone_info_(s, 1, s%nz, zi)
     653              : 
     654              :     ! Perform additional initializations
     655              : 
     656            0 :     zone_loop : do i = 1, SIZE(zi)
     657              : 
     658              :        ! Set penetration velocities
     659              : 
     660            0 :        call set_penetration_velocities_(s, zi(i))
     661              : 
     662              :        ! Set burn types and initial average abundances
     663              : 
     664            0 :        call set_burn_data_(s, zi(i))
     665            0 :        call set_abund_data_(s, zi(i))
     666              : 
     667              :        ! Initialize clocks
     668              : 
     669            0 :        zi(i)%dt_t = 0._dp
     670            0 :        zi(i)%dt_b = 0._dp
     671              : 
     672              :        ! Set flags
     673              : 
     674            0 :        zi(i)%sel_t = zi(i)%kc_t /= 1
     675            0 :        zi(i)%sel_b = zi(i)%kc_b /= s%nz
     676              : 
     677            0 :        zi(i)%split_retreat = .FALSE.
     678              : 
     679              :     end do zone_loop
     680              : 
     681            0 :     return
     682              : 
     683              :   end subroutine init_zone_info_
     684              : 
     685              : 
     686            0 :   subroutine create_zone_info_ (s, kc_t, kc_b, zi)
     687              : 
     688              :     type(star_info), pointer                  :: s
     689              :     integer, intent(in)                       :: kc_t
     690              :     integer, intent(in)                       :: kc_b
     691              :     type(zone_info), allocatable, intent(out) :: zi(:)
     692              : 
     693              :     logical         :: in_conv
     694            0 :     type(zone_info) :: zi_new
     695              :     integer         :: kf
     696              : 
     697              :     ! Create a zone info table spanning the cells kc_t:kc_b. For the
     698              :     ! purposes of convective premixing, a zone is defined as a regions
     699              :     ! of two or more cells with (i) convective faces inside the
     700              :     ! region, (ii) non-convective faces at the top and bottom
     701              :     ! boundaries of the region.  The possible exceptions to these
     702              :     ! rules apply to zones which begin (end) at kc_t (kc_b); their
     703              :     ! upper (lower) boundary faces are not checked for being
     704              :     ! non-convective.
     705              : 
     706            0 :     if (kc_t >= kc_b) then
     707            0 :        write(*,*) 'conv_premix: Invalid cell range in create_zone_info_'
     708            0 :        stop
     709              :     end if
     710              : 
     711              :     ! Create the empty zone info table
     712              : 
     713            0 :     allocate(zi(0))
     714              : 
     715              :     ! Loop through faces, looking for zone boundaries and adding zones
     716              :     ! (cf. locate_convection_boundaries in mix_info.f90)
     717              : 
     718            0 :     kf = kc_b
     719              : 
     720            0 :     in_conv = s%mlt_mixing_type(kf) == convective_mixing
     721              : 
     722            0 :     if (in_conv) then
     723              : 
     724            0 :        zi_new%kc_b = kf
     725            0 :        zi_new%vc_b = s%mlt_vc(kf)
     726              : 
     727              :     end if
     728              : 
     729            0 :     face_loop : do kf = kc_b-1, kc_t+1, -1
     730              : 
     731            0 :        if (in_conv .AND. s%mlt_mixing_type(kf) /= convective_mixing) then
     732              : 
     733              :           ! Transitioning out of a convective zone; complete
     734              :           ! definition of new zone info and add to to the table
     735              : 
     736            0 :           zi_new%kc_t = kf
     737            0 :           zi_new%vc_t = s%mlt_vc(kf+1)
     738              : 
     739            0 :           zi = [zi,zi_new]
     740              : 
     741            0 :           in_conv = .FALSE.
     742              : 
     743            0 :        elseif (.NOT. in_conv .AND. s%mlt_mixing_type(kf) == convective_mixing) then
     744              : 
     745              :           ! Transitioning into a convective zone; begin definition of
     746              :           ! new zone info
     747              : 
     748            0 :           zi_new%kc_b = kf
     749            0 :           zi_new%vc_b = s%mlt_vc(kf)
     750              : 
     751            0 :           in_conv = .TRUE.
     752              : 
     753              :        end if
     754              : 
     755              :     end do face_loop
     756              : 
     757            0 :     kf = kc_t
     758              : 
     759            0 :     if (in_conv) then
     760              : 
     761            0 :        zi_new%kc_t = kf
     762            0 :        zi_new%vc_t = s%mlt_vc(kf+1)
     763              : 
     764            0 :        zi = [zi,zi_new]
     765              : 
     766              :     end if
     767              : 
     768            0 :     return
     769              : 
     770              :   end subroutine create_zone_info_
     771              : 
     772              : 
     773            0 :   subroutine update_zone_info_ (s, i_bdy, t_bdy, has_split, zi)
     774              : 
     775              :     type(star_info), pointer                    :: s
     776              :     integer, intent(inout)                      :: i_bdy
     777              :     logical, intent(in)                         :: t_bdy
     778              :     logical, intent(in)                         :: has_split
     779              :     type(zone_info), allocatable, intent(inout) :: zi(:)
     780              : 
     781              :     logical, parameter :: TRACE_UPDATE_ZONE = TRACE_ALL
     782              : 
     783            0 :     type(zone_info), allocatable :: zi_new(:)
     784              :     integer                      :: i
     785              : 
     786              :     ! Update the zone info table to account for a single-cell
     787              :     ! advancement in zone i_bdy, direction t_bdy
     788              : 
     789              :     ! Advance the zone by one cell
     790              : 
     791            0 :     if (t_bdy) then
     792            0 :        zi(i_bdy)%kc_t = zi(i_bdy)%kc_t - 1
     793            0 :        zi(i_bdy)%vc_t = s%mlt_vc(zi(i_bdy)%kc_t+1)
     794              :     else
     795            0 :        zi(i_bdy)%kc_b = zi(i_bdy)%kc_b + 1
     796            0 :        zi(i_bdy)%vc_b = s%mlt_vc(zi(i_bdy)%kc_b)
     797              :     end if
     798              : 
     799            0 :     call set_penetration_velocities_(s, zi(i_bdy))
     800              : 
     801              :     ! If necessary, truncate (or even delete) the adjacent zone we've
     802              :     ! encroached into
     803              : 
     804            0 :     if (t_bdy .AND. i_bdy < SIZE(zi)) then
     805              : 
     806            0 :        if (zi(i_bdy)%kc_t == zi(i_bdy+1)%kc_b) then
     807              : 
     808              :           ! Encroached into zone above
     809              : 
     810            0 :           if (zi(i_bdy+1)%kc_b-zi(i_bdy+1)%kc_t > 1) then
     811              : 
     812              :              ! Truncate the zone
     813              : 
     814            0 :              zi(i_bdy+1)%kc_b = zi(i_bdy+1)%kc_b - 1
     815            0 :              zi(i_bdy+1)%vc_b = s%mlt_vc(zi(i_bdy+1)%kc_b)
     816              : 
     817              :              if (TRACE_UPDATE_ZONE) then
     818              :                 write(*,*) 'Truncated zone above to', zi(i_bdy+1)%kc_b, zi(i_bdy+1)%vc_b, &
     819              :                      s%mlt_mixing_type(zi(i_bdy+1)%kc_b), zi(i_bdy+1)%kc_b-zi(i_bdy+1)%kc_t+1
     820              :              end if
     821              : 
     822              :           else
     823              : 
     824              :              ! Delete the zone
     825              : 
     826            0 :              zi = [zi(:i_bdy),zi(i_bdy+2:)]
     827              : 
     828              :              if (TRACE_UPDATE_ZONE) then
     829              :                 write(*,*) 'Deleted zone above'
     830              :              end if
     831              : 
     832              :           end if
     833              : 
     834              :        end if
     835              : 
     836            0 :     elseif (.NOT. t_bdy .AND. i_bdy > 1) then
     837              : 
     838            0 :        if (zi(i_bdy)%kc_b == zi(i_bdy-1)%kc_t) then
     839              : 
     840              :           ! Encroached into zone below
     841              : 
     842            0 :           if (zi(i_bdy-1)%kc_b-zi(i_bdy-1)%kc_t > 1) then
     843              : 
     844              :              ! Truncate the zone
     845              : 
     846            0 :              zi(i_bdy-1)%kc_t = zi(i_bdy-1)%kc_t + 1
     847            0 :              zi(i_bdy-1)%vc_t = s%mlt_vc(zi(i_bdy-1)%kc_t+1)
     848              : 
     849              :              if (TRACE_UPDATE_ZONE) then
     850              :                 write(*,*) 'Truncated zone below to', zi(i_bdy-1)%kc_t
     851              :              end if
     852              : 
     853              :           else
     854              : 
     855              :              ! Delete the zone
     856              : 
     857            0 :              zi = [zi(:i_bdy-2),zi(i_bdy:)]
     858            0 :              i_bdy = i_bdy - 1
     859              : 
     860              :              if (TRACE_UPDATE_ZONE) then
     861              :                 write(*,*) 'Deleted zone below'
     862              :              end if
     863              : 
     864              :           end if
     865              : 
     866              :        end if
     867              : 
     868              :     end if
     869              : 
     870              :     ! If the zone has split (or its trailing boundary has retreated),
     871              :     ! then replace the zone with as many new zones as necessary
     872              : 
     873            0 :     if (has_split) then
     874              : 
     875              :        ! Create new zones to replace the zone
     876              : 
     877            0 :        call create_zone_info_(s, zi(i_bdy)%kc_t, zi(i_bdy)%kc_b, zi_new)
     878              : 
     879              :        ! Set up parameters for the new zones
     880              : 
     881            0 :        new_zone_loop : do i = 1, SIZE(zi_new)
     882              : 
     883              :           ! Clocks & selection flags
     884              : 
     885            0 :           if (zi_new(i)%kc_t == zi(i_bdy)%kc_t) then
     886            0 :              zi_new(i)%dt_t = zi(i_bdy)%dt_t
     887            0 :              zi_new(i)%sel_t = zi(i_bdy)%sel_t
     888              :           else
     889            0 :              if (t_bdy) then
     890            0 :                 zi_new(i)%dt_t = zi(i_bdy)%dt_t
     891            0 :                 zi_new(i)%sel_t = .FALSE.
     892              :              else
     893            0 :                 zi_new(i)%dt_t = zi(i_bdy)%dt_b
     894            0 :                 zi_new(i)%sel_t = .FALSE.
     895              :              end if
     896              :           end if
     897              : 
     898            0 :           if (zi_new(i)%kc_b == zi(i_bdy)%kc_b) then
     899            0 :              zi_new(i)%dt_b = zi(i_bdy)%dt_b
     900            0 :              zi_new(i)%sel_b = zi(i_bdy)%sel_b
     901              :           else
     902            0 :              if (t_bdy) then
     903            0 :                 zi_new(i)%dt_b = zi(i_bdy)%dt_t
     904            0 :                 zi_new(i)%sel_b = .FALSE.
     905              :              else
     906            0 :                 zi_new(i)%dt_b = zi(i_bdy)%dt_b
     907            0 :                 zi_new(i)%sel_b = .FALSE.
     908              :              end if
     909              :           end if
     910              : 
     911              :           ! Penetration velocities
     912              : 
     913            0 :           call set_penetration_velocities_(s, zi_new(i))
     914              : 
     915              :           ! Burn data
     916              : 
     917            0 :           call set_burn_data_(s, zi_new(i))
     918              : 
     919              :           ! Initial abundances
     920              : 
     921            0 :           zi_new(i)%avg_xa = zi(i_bdy)%avg_xa
     922            0 :           zi_new(i)%davg_xa_dt = zi(i_bdy)%davg_xa_dt
     923              : 
     924              :        end do new_zone_loop
     925              : 
     926              :        ! Replace the zone with the new zones
     927              : 
     928            0 :        zi = [zi(:i_bdy-1),zi_new,zi(i_bdy+1:)]
     929              : 
     930              :        if (TRACE_UPDATE_ZONE) then
     931              :           write(*,*) 'Replaced zone with new zones', SIZE(zi), SIZE(zi_new)
     932              :        end if
     933              : 
     934              :     else
     935              : 
     936              :        if (TRACE_UPDATE_ZONE) then
     937              :           write(*,*) 'Zone did not split, updating indices',&
     938              :                COUNT(s%mlt_mixing_type(zi(i_bdy)%kc_t+1:zi(i_bdy)%kc_b) /= convective_mixing)
     939              :        end if
     940              : 
     941              :     end if
     942              : 
     943            0 :     return
     944              : 
     945            0 :   end subroutine update_zone_info_
     946              : 
     947              : 
     948            0 :   subroutine set_penetration_velocities_ (s, zi)
     949              : 
     950              :     type(star_info), pointer       :: s
     951              :     type(zone_info), intent(inout) :: zi
     952              : 
     953            0 :     real(dp) :: mu_i
     954            0 :     real(dp) :: mu_e
     955              :     integer  :: kf
     956            0 :     real(dp) :: z_s
     957              : 
     958              :     ! Set the penetration velocities for the zone info. These are
     959              :     ! calculated by evaluating the molecular weight contrast between
     960              :     ! the boundary cell and the adjacent (radiative) cell, and
     961              :     ! calculating a characteristic penetration velocity using the
     962              :     ! approach by Castellani (1971)
     963              : 
     964              :     ! Bottom boundary
     965              : 
     966            0 :     if (zi%kc_b < s%nz) then
     967              : 
     968            0 :        mu_i = s%mu(zi%kc_b)
     969            0 :        mu_e = s%mu(zi%kc_b+1)
     970              : 
     971            0 :        kf = zi%kc_b
     972              : 
     973            0 :        if (mu_e - mu_i > EPSILON(0._dp)) then
     974            0 :           z_s = MIN(zi%vc_b*zi%vc_b/(2._dp*s%grav(kf)*(1._dp - mu_i/mu_e)), s%mlt_mixing_length(kf))
     975              :        else
     976            0 :           z_s = s%mlt_mixing_length(kf)
     977              :        end if
     978              : 
     979            0 :        zi%vp_b = zi%vc_b*z_s/s%mlt_mixing_length(kf)
     980              : 
     981              :     else
     982              : 
     983            0 :        zi%vp_b = zi%vc_b
     984              : 
     985              :     end if
     986              : 
     987            0 :     if (zi%vp_b == 0._dp) print *,'Bottom bdy has zero vp', zi%kc_b, zi%vp_b, zi%vc_b, z_s
     988              : 
     989              :     ! Top boundary
     990              : 
     991            0 :     if (zi%kc_t > 1) then
     992              : 
     993            0 :        mu_i = s%mu(zi%kc_t)
     994            0 :        mu_e = s%mu(zi%kc_t-1)
     995              : 
     996            0 :        kf = zi%kc_t + 1
     997              : 
     998            0 :        if (mu_i - mu_e > EPSILON(0._dp)) then
     999            0 :           z_s = MIN(zi%vc_t*zi%vc_t/(2._dp*s%grav(kf)*(1._dp - mu_e/mu_i)), s%mlt_mixing_length(kf))
    1000              :        else
    1001            0 :           z_s = s%mlt_mixing_length(kf)
    1002              :        end if
    1003              : 
    1004            0 :        zi%vp_t = zi%vc_t*z_s/s%mlt_mixing_length(kf)
    1005              : 
    1006              :     else
    1007              : 
    1008            0 :        zi%vp_t = zi%vc_t
    1009              : 
    1010              :     end if
    1011              : 
    1012            0 :     if (zi%vp_t == 0._dp) print *,'Top bdy has zero vp', zi%kc_t, zi%vp_t, zi%vc_t, z_s
    1013              : 
    1014            0 :     return
    1015              : 
    1016              :   end subroutine set_penetration_velocities_
    1017              : 
    1018              : 
    1019            0 :   subroutine set_burn_data_ (s, zi)
    1020              : 
    1021              :     type(star_info), pointer       :: s
    1022              :     type(zone_info), intent(inout) :: zi
    1023              : 
    1024              :     real(dp), parameter :: EPS_THRESHOLD = 1._dp  ! Threshold eps for a zone to be classified as burning
    1025              : 
    1026              :     integer  :: iso_h1
    1027              :     integer  :: iso_he4
    1028              :     integer  :: iso_c12
    1029            0 :     real(dp) :: eps_max
    1030            0 :     real(dp) :: eps_h_max
    1031            0 :     real(dp) :: eps_he_max
    1032              :     integer  :: kc
    1033              : 
    1034              :     ! Set burning data for the zone info
    1035              : 
    1036            0 :     iso_h1 = s%net_iso(chem_get_iso_id('h1'))
    1037            0 :     iso_he4 = s%net_iso(chem_get_iso_id('he4'))
    1038            0 :     iso_c12 = s%net_iso(chem_get_iso_id('c12'))
    1039              : 
    1040              :     associate (kc_t => zi%kc_t, &
    1041              :                kc_b => zi%kc_b)
    1042              : 
    1043              :       ! Find the maximum burning rate, and the H/He burning rate at
    1044              :       ! the same location
    1045              : 
    1046            0 :       eps_max = -HUGE(0._dp)
    1047            0 :       eps_h_max = -HUGE(0._dp)
    1048            0 :       eps_he_max = -HUGE(0._dp)
    1049              : 
    1050            0 :       cell_loop : do kc = kc_t, kc_b
    1051              : 
    1052            0 :          if (s%eps_nuc(kc) > eps_max) then
    1053              : 
    1054            0 :             eps_max = s%eps_nuc(kc)
    1055              : 
    1056              :             eps_h_max = s%eps_nuc_categories(ipp, kc) + &
    1057            0 :                         s%eps_nuc_categories(icno, kc)
    1058              :             eps_he_max = s%eps_nuc_categories(i3alf, kc) + &
    1059            0 :                          s%eps_nuc_categories(i_burn_c, kc)
    1060              : 
    1061              :          end if
    1062              : 
    1063              :       end do cell_loop
    1064              : 
    1065              :       ! Classify the principal burning type
    1066              : 
    1067            0 :       if (eps_max > EPS_THRESHOLD) then
    1068              : 
    1069            0 :          if (eps_h_max > 0.5_dp*eps_max) then
    1070              : 
    1071              :             ! Hydrogen burning zone
    1072              : 
    1073            0 :             zi%burn_type = BURN_TYPE_H
    1074              : 
    1075            0 :          elseif (eps_he_max > 0.5_dp*eps_max) then
    1076              : 
    1077              :             ! Helium burning zone
    1078              : 
    1079            0 :             zi%burn_type = BURN_TYPE_HE
    1080              : 
    1081              :          else
    1082              : 
    1083              :             ! Metal burning zone
    1084              : 
    1085            0 :             zi%burn_type = BURN_TYPE_Z
    1086              : 
    1087              :          end if
    1088              : 
    1089              :       else
    1090              : 
    1091              :          ! Non-burning zone
    1092              : 
    1093            0 :          zi%burn_type = BURN_TYPE_NONE
    1094              : 
    1095              :       end if
    1096              : 
    1097              :     end associate
    1098              : 
    1099              :     ! Based on the burning type, decide which isotope (if any)
    1100              :     ! should be monitored for abundance reversal avoidance
    1101              : 
    1102            0 :     if (s%conv_premix_avoid_increase) then
    1103              : 
    1104            0 :        select case (zi%burn_type)
    1105              :        case (BURN_TYPE_H)
    1106            0 :           zi%avoid_inc_iso = iso_h1
    1107              :        case (BURN_TYPE_HE)
    1108            0 :           zi%avoid_inc_iso = iso_he4
    1109              :        case default
    1110            0 :           zi%avoid_inc_iso = 0
    1111              :        end select
    1112              : 
    1113              :     end if
    1114              : 
    1115            0 :     return
    1116              : 
    1117              :   end subroutine set_burn_data_
    1118              : 
    1119              : 
    1120            0 :   subroutine set_abund_data_ (s, zi)
    1121              : 
    1122              :     type(star_info), pointer       :: s
    1123              :     type(zone_info), intent(inout) :: zi
    1124              : 
    1125            0 :     real(dp) :: m_zone
    1126              :     integer  :: l
    1127              : 
    1128              :     ! Set abundance data for the zone info
    1129              : 
    1130            0 :     allocate(zi%avg_xa(s%species))
    1131            0 :     allocate(zi%davg_xa_dt(s%species))
    1132              : 
    1133              :     associate (kc_t => zi%kc_t, &
    1134              :                kc_b => zi%kc_b, &
    1135              :                avg_xa => zi%avg_xa, &
    1136              :                davg_xa_dt => zi%davg_xa_dt)
    1137              : 
    1138              :       ! Calculate average abundances and their rates of change
    1139              : 
    1140            0 :       m_zone = SUM(s%dm(kc_t:kc_b))
    1141              : 
    1142            0 :       do l = 1, s%species
    1143            0 :          avg_xa(l) = SUM(s%dm(kc_t:kc_b)*s%xa(l,kc_t:kc_b))/m_zone
    1144            0 :          davg_xa_dt(l) = SUM(s%dm(kc_t:kc_b)*s%dxdt_nuc(l,kc_t:kc_b))/m_zone
    1145              :       end do
    1146              : 
    1147            0 :       avg_xa = MAX(MIN(avg_xa, 1._dp), 0._dp)
    1148            0 :       avg_xa = avg_xa/SUM(avg_xa)
    1149              : 
    1150              :     end associate
    1151              : 
    1152            0 :     return
    1153              : 
    1154              :   end subroutine set_abund_data_
    1155              : 
    1156              : 
    1157            0 :   subroutine validate_zone_info_ (s, zi)
    1158              : 
    1159              :     type(star_info), pointer    :: s
    1160              :     type(zone_info), intent(in) :: zi(:)
    1161              : 
    1162              :     logical, parameter :: TRACE_VALIDATE_INFO = TRACE_ALL
    1163              : 
    1164              :     logical :: valid
    1165              :     integer :: n_zi
    1166              :     integer :: i
    1167              : 
    1168              :     integer :: kf
    1169              : 
    1170              :     ! Validate zone info data
    1171              : 
    1172            0 :     n_zi = SIZE(zi)
    1173              : 
    1174            0 :     valid = .TRUE.
    1175              : 
    1176            0 :     zone_info_loop : do i = 1, n_zi
    1177              : 
    1178              :        if (TRACE_VALIDATE_INFO) then
    1179              :           write(*,*) 'Validating info for zone', i
    1180              :           call print_zone_info_(s, zi(i))
    1181              :        end if
    1182              : 
    1183              :        ! (i) Degenerate zone (just one cell)
    1184              : 
    1185            0 :        if (zi(i)%kc_t == zi(i)%kc_b) then
    1186            0 :           write(*,*) 'conv_premix: Degenerate zone'
    1187            0 :           valid = .FALSE.
    1188              :        end if
    1189              : 
    1190              :        ! (iii) Out of bounds zone (cells outside grid)
    1191              : 
    1192            0 :        if (zi(i)%kc_t < 1) then
    1193            0 :           write(*,*) 'conv_premix: Zone top outside grid'
    1194            0 :           valid = .FALSE.
    1195              :        end if
    1196              : 
    1197            0 :        if (zi(i)%kc_b > s%nz) then
    1198            0 :           write(*,*) 'conv_premix: Zone bottom outside grid'
    1199            0 :           valid = .FALSE.
    1200              :        end if
    1201              : 
    1202              :        ! (iv) Overlapping zone (cells inside previous/next zone)
    1203              : 
    1204            0 :        if (i > 1) then
    1205            0 :           if (zi(i)%kc_b >= zi(max(1,i-1))%kc_t) then
    1206              :              ! bp: max(1,i-1) to prevent bogus warning from gfortran
    1207            0 :              write(*,*) 'conv_premix: Zone bottom inside previous zone'
    1208            0 :              valid = .FALSE.
    1209              :           end if
    1210              :        end if
    1211              : 
    1212            0 :        if (i < n_zi) then
    1213            0 :           if (zi(i)%kc_t <= zi(i+1)%kc_b) then
    1214            0 :              write(*,*) 'conv_premix: Zone top inside next zone'
    1215            0 :              valid = .FALSE.
    1216              :           end if
    1217              :        end if
    1218              : 
    1219              :        ! (vi) Convective velocities
    1220              : 
    1221            0 :        if (zi(i)%vc_t <= 0._dp) then
    1222            0 :           write(*,*) 'conv_premix: Convective velocity is not greater than zero at zone top'
    1223            0 :           valid = .FALSE.
    1224              :        end if
    1225              : 
    1226            0 :        if (zi(i)%vc_b <= 0._dp) then
    1227            0 :           write(*,*) 'conv_premix: Convective velocity is not greater than zero at zone bottom'
    1228            0 :           valid = .FALSE.
    1229              :        end if
    1230              : 
    1231              :        ! (vii) Mixing type
    1232              : 
    1233            0 :        if (.NOT. ALL(s%mlt_mixing_type(zi(i)%kc_t+1:zi(i)%kc_b) == convective_mixing)) then
    1234            0 :           do kf = zi(i)%kc_t+1, zi(i)%kc_b
    1235            0 :              if (s%mlt_mixing_type(kf) /= convective_mixing) then
    1236            0 :                 write(*,*) 'Mix type at kf=',kf, s%mlt_mixing_type(kf)
    1237              :              end if
    1238              :           end do
    1239            0 :           write(*,*) 'conv_premix: Zone contains cells with mixing_type /= convective_mixing'
    1240            0 :           valid = .FALSE.
    1241              :        end if
    1242              : 
    1243              :     end do zone_info_loop
    1244              : 
    1245            0 :     if (.NOT. valid) stop
    1246              : 
    1247            0 :     return
    1248              : 
    1249              :   end subroutine validate_zone_info_
    1250              : 
    1251              : 
    1252              :   subroutine print_zone_info_ (s, zi)
    1253              : 
    1254              :     type(star_info), pointer    :: s
    1255              :     type(zone_info), intent(in) :: zi
    1256              : 
    1257              :     ! Print out zone info
    1258              : 
    1259              :     write(*,*) '  nz            :', s%nz
    1260              :     write(*,*) '  kc_t          :', zi%kc_t
    1261              :     write(*,*) '  kc_b          :', zi%kc_b
    1262              :     write(*,*) '  mass(kf_t)    :', (s%M_center + s%xmstar*s%q(zi%kc_t))/Msun
    1263              :     if (zi%kc_b < s%nz) then
    1264              :        write(*,*) '  mass(kf_b)    :', (s%M_center + s%xmstar*s%q(zi%kc_b+1))/Msun
    1265              :     else
    1266              :        write(*,*) '  mass(kf_b)    :', (s%M_center)/Msun
    1267              :     end if
    1268              :     write(*,*) '  vc_t          :', zi%vc_t
    1269              :     write(*,*) '  vc_b          :', zi%vc_b
    1270              :     write(*,*) '  dt_t          :', zi%dt_t
    1271              :     write(*,*) '  dt_b          :', zi%dt_b
    1272              :     write(*,*) '  burn_type     :', zi%burn_type
    1273              :     write(*,*) '  sel_t         :', zi%sel_t
    1274              :     write(*,*) '  sel_b         :', zi%sel_b
    1275              :     write(*,*) '  avoid_inc_iso :', zi%avoid_inc_iso
    1276              :     write(*,'(A)')
    1277              : 
    1278              :     return
    1279              : 
    1280              :   end subroutine print_zone_info_
    1281              : 
    1282              : 
    1283            0 :   subroutine update_model_ (s, update_mode, kc_t, kc_b)
    1284              : 
    1285              :     use turb_info, only: set_mlt_vars
    1286              :     use micro
    1287              : 
    1288              :     type(star_info), pointer :: s
    1289              :     integer, intent(in)      :: update_mode(:)
    1290              :     integer, intent(in)      :: kc_t
    1291              :     integer, intent(in)      :: kc_b
    1292              : 
    1293              :     logical, parameter :: TRACE_UPDATE_MODEL = .FALSE.
    1294              : 
    1295              :     integer  :: ierr
    1296              :     integer  :: kf_t
    1297              :     integer  :: kf_b
    1298              : 
    1299              :     ! Update the model to reflect changes in the abundances across
    1300              :     ! cells kc_t:kc_b
    1301              : 
    1302              :     if (TRACE_UPDATE_MODEL) then
    1303              :        write(*,*) '  Updating model'
    1304              :     end if
    1305              : 
    1306              :     ! Perform the EOS updates, in accordance with the update_mode
    1307              :     ! values. We make two passes -- the first to handle cells with
    1308              :     ! update_mode == FIXED_PT_MODE, the second to handle cells with
    1309              :     ! update_mode == FIXED_DT_MODE.
    1310              : 
    1311            0 :     s%fix_Pgas = .TRUE.
    1312              : 
    1313            0 :     call set_eos_with_mask(s, kc_t, kc_b, update_mode==FIXED_PT_MODE, ierr)
    1314            0 :     if (ierr /= 0) then
    1315            0 :        write(*,*) 'conv_premix: error from call to set_eos_with_mask'
    1316            0 :        stop
    1317              :     end if
    1318              : 
    1319            0 :     s%fix_Pgas = .FALSE.
    1320              : 
    1321            0 :     call set_eos_with_mask(s, kc_t, kc_b, update_mode==FIXED_DT_MODE, ierr)
    1322            0 :     if (ierr /= 0) then
    1323            0 :        write(*,*) 'conv_premix: error from call to set_eos_with_mask'
    1324            0 :        stop
    1325              :     end if
    1326              : 
    1327              :     ! Update opacities across cells kc_t:kc_b (this also sets rho_face
    1328              :     ! and related quantities on faces kc_t:kc_b)
    1329              : 
    1330              :     call set_micro_vars(s, kc_t, kc_b, &
    1331            0 :          skip_eos=.TRUE., skip_net=.TRUE., skip_neu=.TRUE., skip_kap=.FALSE., ierr=ierr)
    1332            0 :     if (ierr /= 0) then
    1333            0 :        write(*,*) 'conv_premix: error from call to set_micro_vars'
    1334            0 :        stop
    1335              :     end if
    1336              : 
    1337              :     ! Finally update MLT for interior faces
    1338              : 
    1339            0 :     kf_t = kc_t
    1340            0 :     kf_b = kc_b + 1
    1341              : 
    1342            0 :     call set_mlt_vars(s, kf_t+1, kf_b-1, ierr)
    1343            0 :     if (ierr /= 0) then
    1344            0 :        write(*,*) 'conv_premix: failed in call to set_mlt_vars during update_model_'
    1345            0 :        stop
    1346              :     end if
    1347              : 
    1348            0 :     return
    1349              : 
    1350            0 :   end subroutine update_model_
    1351              : 
    1352              : 
    1353            0 :   subroutine dump_snapshot_ (s, filename)
    1354              : 
    1355            0 :     use brunt
    1356              :     use hydro_vars, only: set_grads
    1357              : 
    1358              :     type(star_info), pointer :: s
    1359              :     character(*), intent(in) :: filename
    1360              : 
    1361            0 :     real(dp), allocatable :: gradL_composition_term(:)
    1362              :     integer               :: ierr
    1363              :     integer               :: unit
    1364              :     integer               :: iso_h1
    1365              :     integer               :: iso_he4
    1366              :     integer               :: iso_c12
    1367              :     integer               :: iso_o16
    1368              :     integer               :: k
    1369              : 
    1370              :     ! Dump a snapshot of the current model state to file
    1371              : 
    1372              :     ! First, re-calculate gradL_composition_term
    1373            0 :     allocate(gradL_composition_term(1:s%nz))
    1374              : 
    1375            0 :     gradL_composition_term = s%gradL_composition_term(1:s%nz)
    1376              : 
    1377            0 :     call do_brunt_B(s, 1, s%nz, ierr)
    1378            0 :     if (ierr /= 0) then
    1379            0 :        write(*,*) 'conv_premix: error from call to do_brunt_B'
    1380            0 :        stop
    1381              :     end if
    1382              : 
    1383            0 :     call set_grads(s, ierr)
    1384            0 :     if (ierr /= 0) then
    1385            0 :        write(*,*) 'conv_premix: error from call to set_grads'
    1386            0 :        stop
    1387              :     end if
    1388              : 
    1389              :     ! Dump the snapshot
    1390              : 
    1391            0 :     open(NEWUNIT=unit, FILE=filename, STATUS='REPLACE')
    1392              : 
    1393            0 :     iso_h1 = s%net_iso(chem_get_iso_id('h1'))
    1394            0 :     iso_he4 = s%net_iso(chem_get_iso_id('he4'))
    1395            0 :     iso_c12 = s%net_iso(chem_get_iso_id('c12'))
    1396            0 :     iso_o16 = s%net_iso(chem_get_iso_id('o16'))
    1397              : 
    1398            0 :     write(*,*) '  Dumping to file:', TRIM(filename)
    1399              : 
    1400            0 :     write(unit, *) '1 2'
    1401            0 :     write(unit, *) 'star_age model_number'
    1402            0 :     write(unit, *) s%star_age, s%model_number
    1403            0 :     write(unit, *) ''
    1404            0 :     write(unit, *) '1 2 3 4 5 6 7 8'
    1405            0 :     write(unit, *) 'k mass gradr grada gradL_composition_term mixing_type x y'
    1406              : 
    1407            0 :     do k = 1, s%nz
    1408            0 :        write(unit, *) k, s%m(k)/msun, s%gradr(k), s%grada_face(k), &
    1409            0 :             s%gradL_composition_term(k), s%mlt_mixing_type(k), s%xa(iso_h1,k), s%xa(iso_he4,k)
    1410              :     end do
    1411              : 
    1412            0 :     close(unit)
    1413              : 
    1414              :     ! Restore gradL_composition_term
    1415              : 
    1416            0 :     s%gradL_composition_term(1:s%nz) = gradL_composition_term
    1417              : 
    1418            0 :     return
    1419              : 
    1420            0 :   end subroutine dump_snapshot_
    1421              : 
    1422              : 
    1423            0 :   subroutine alloc_saved_data_ (s, sd)
    1424              : 
    1425              :     type(star_info), pointer :: s
    1426              :     type(saved_data)         :: sd
    1427              : 
    1428              :     ! Allocate cell data arrays
    1429              : 
    1430            0 :     allocate(sd%xa(s%species,s%nz))
    1431              : 
    1432            0 :     allocate(sd%lnd(s%nz))
    1433            0 :     allocate(sd%rho(s%nz))
    1434              : 
    1435            0 :     allocate(sd%lnPgas(s%nz))
    1436            0 :     allocate(sd%Pgas(s%nz))
    1437              : 
    1438            0 :     allocate(sd%update_mode(s%nz))
    1439              : 
    1440            0 :     allocate(sd%gradL_composition_term(s%nz))
    1441              : 
    1442            0 :   end subroutine alloc_saved_data_
    1443              : 
    1444              : 
    1445            0 :   subroutine save_model_ (s, update_mode, kc_t, kc_b, sd)
    1446              : 
    1447              :     type(star_info), pointer        :: s
    1448              :     integer, intent(in)             :: update_mode(:)
    1449              :     integer, intent(in)             :: kc_t
    1450              :     integer, intent(in)             :: kc_b
    1451              :     type(saved_data), intent(inout) :: sd
    1452              : 
    1453              :     logical, parameter :: TRACE_SAVE_MODEL = .FALSE.
    1454              : 
    1455              :     integer :: kf_t
    1456              :     integer :: kf_b
    1457              : 
    1458              :     ! Save data for cells kc_t:kc_b and interior faces
    1459              : 
    1460              :     if (TRACE_SAVE_MODEL) then
    1461              :        write(*,*) '  Saving model'
    1462              :     end if
    1463              : 
    1464              :     ! Save cell data
    1465              : 
    1466              :     if (TRACE_SAVE_MODEL) then
    1467              :        write(*,*) '    kc_t:', kc_t
    1468              :        write(*,*) '    kc_b:', kc_b
    1469              :     end if
    1470              : 
    1471            0 :     sd%update_mode(kc_t:kc_b) = update_mode(kc_t:kc_b)
    1472              : 
    1473            0 :     sd%xa(:,kc_t:kc_b) = s%xa(:,kc_t:kc_b)
    1474              : 
    1475            0 :     sd%lnd(kc_t:kc_b) = s%lnd(kc_t:kc_b)
    1476            0 :     sd%Rho(kc_t:kc_b) = s%Rho(kc_t:kc_b)
    1477              : 
    1478            0 :     sd%lnPgas(kc_t:kc_b) = s%lnPgas(kc_t:kc_b)
    1479            0 :     sd%Pgas(kc_t:kc_b) = s%Pgas(kc_t:kc_b)
    1480              : 
    1481              :     ! Save face data
    1482              : 
    1483            0 :     kf_t = kc_t
    1484            0 :     kf_b = kc_b + 1
    1485              : 
    1486              :     if (TRACE_SAVE_MODEL) then
    1487              :        write(*,*) '    kf_t:', kf_t
    1488              :        write(*,*) '    kf_b:', kf_b
    1489              :     end if
    1490              : 
    1491            0 :     sd%gradL_composition_term(kf_t+1:kf_b-1) = s%gradL_composition_term(kf_t+1:kf_b-1)
    1492              : 
    1493              :     ! Store indices (used when we call restore_model_)
    1494              : 
    1495            0 :     sd%kc_t = kc_t
    1496            0 :     sd%kc_b = kc_b
    1497              : 
    1498            0 :     return
    1499              : 
    1500              :   end subroutine save_model_
    1501              : 
    1502              : 
    1503            0 :   subroutine restore_model_ (s, update_mode, sd)
    1504              : 
    1505              :     type(star_info), pointer        :: s
    1506              :     integer, intent(inout)          :: update_mode(:)
    1507              :     type(saved_data), intent(inout) :: sd
    1508              : 
    1509              :     logical, parameter :: TRACE_RESTORE_MODEL = .FALSE.
    1510              : 
    1511              :     integer :: kc_t
    1512              :     integer :: kc_b
    1513              :     integer :: kf_t
    1514              :     integer :: kf_b
    1515              : 
    1516              :     ! Restore data from previous call to save_model_
    1517              : 
    1518              :     if (TRACE_RESTORE_MODEL) then
    1519              :        write(*,*) '  Restoring model'
    1520              :     end if
    1521              : 
    1522              :     ! Restore cell data
    1523              : 
    1524            0 :     kc_t = sd%kc_t
    1525            0 :     kc_b = sd%kc_b
    1526              : 
    1527              :     if (TRACE_RESTORE_MODEL) then
    1528              :        write(*,*) '    kc_t:', kc_t
    1529              :        write(*,*) '    kc_b:', kc_b
    1530              :     end if
    1531              : 
    1532            0 :     update_mode(kc_t:kc_b) = sd%update_mode(kc_t:kc_b)
    1533              : 
    1534            0 :     s%xa(:,kc_t:kc_b) = sd%xa(:,kc_t:kc_b)
    1535              : 
    1536            0 :     s%lnd(kc_t:kc_b) = sd%lnd(kc_t:kc_b)
    1537            0 :     s%rho(kc_t:kc_b) = sd%rho(kc_t:kc_b)
    1538              : 
    1539            0 :     s%lnPgas(kc_t:kc_b) = sd%lnPgas(kc_t:kc_b)
    1540            0 :     s%Pgas(kc_t:kc_b) = sd%Pgas(kc_t:kc_b)
    1541              : 
    1542              :     ! Restore face data
    1543              : 
    1544            0 :     kf_t = kc_t
    1545            0 :     kf_b = kc_b + 1
    1546              : 
    1547              :     if (TRACE_RESTORE_MODEL) then
    1548              :        write(*,*) '    kf_t:', kf_t
    1549              :        write(*,*) '    kf_b:', kf_b
    1550              :     end if
    1551              : 
    1552            0 :     s%gradL_composition_term(kf_t+1:kf_b-1) = sd%gradL_composition_term(kf_t+1:kf_b-1)
    1553              : 
    1554              :     ! Update the model set those quantities that are not stored
    1555              :     ! explicitly in sd
    1556              : 
    1557            0 :     call update_model_(s, update_mode, kc_t, kc_b)
    1558              : 
    1559            0 :   end subroutine restore_model_
    1560              : 
    1561            0 : end module conv_premix
        

Generated by: LCOV version 2.0-1