run_parameters.f90 Source File


Contents

Source Code


Source Code

!> This module is basically a store for the input parameters that are
!> specified in the namelists knobs and parameters.
!> In general, the names of the public variables in this module are
!> the same as the name of the input parameter they correspond to.
module run_parameters
  use abstract_config, only: abstract_config_type, CONFIG_MAX_NAME_LEN
  
  implicit none

  private

  public :: init_run_parameters, finish_run_parameters, check_run_parameters
  public :: wnml_run_parameters, write_trinity_parameters
  public :: beta, zeff, tite, reset, immediate_reset, delt, ieqzip, k0, user_comments
  public :: fphi, fapar, fbpar, has_phi, has_apar, has_bpar
  public :: nstep, max_sim_time, wstar_units, rhostar, neo_test, ncheck_stop
  public :: delt_option_switch, delt_option_auto, avail_cpu_time, margin_cpu_time
  public :: do_eigsolve, save_timer_statistics, save_init_times
  !> If true and nonlinear_mode is "off", return
  !> simple diffusive estimates of fluxes to trinity
  public :: trinity_linear_fluxes
  !> If true and nonlinear_mode is "off", 
  !> and trinity_linear_fluxes is true, return
  !> quasilinear estimates of fluxes to trinity
  public :: trinity_ql_fluxes
  !> If true use old diagnostics. Included for testing
  !> only and will eventually be removed. Please use
  !> the new diagnostics module!
  public :: use_old_diagnostics
  public :: set_overrides, get_knobs_config, knobs_config_type, set_run_parameters_config
  
  real :: beta, zeff, tite, fphi, fapar, fbpar, k0, rhostar
  logical :: has_phi, has_apar, has_bpar, neo_test
  real :: delt, avail_cpu_time, margin_cpu_time, max_sim_time
  integer :: ncheck_stop, nstep, seed
  logical :: reset = .false., initialized = .false.
  logical :: immediate_reset, wstar_units, save_timer_statistics, save_init_times
  integer :: delt_option_switch
  integer, parameter :: delt_option_hand = 1, delt_option_auto = 2
  logical :: knexist
  logical :: trinity_ql_fluxes, trinity_linear_fluxes, do_eigsolve, use_old_diagnostics
  character(len=100000) :: user_comments

  !> Used to indicate which wavenumbers we do not want to evolve.
  !> This choice is controlled by the [[eqzip_option]]
  !> input. Primarily intended for secondary mode analysis.
  logical, allocatable :: ieqzip(:,:)
  integer :: eqzip_option_switch
  integer, parameter :: &
       eqzip_option_none = 1, &
       eqzip_option_secondary = 2, &
       eqzip_option_tertiary = 3, &
       eqzip_option_equilibrium = 4

  !> Used to represent the input configuration of run_parameters through "knobs"
  type, extends(abstract_config_type) :: knobs_config_type
     ! namelist : knobs
     ! indexed : false     
     !> The maximum wall clock time available to GS2
     real :: avail_cpu_time = 1.0e10
     !> \(\beta\) is the ratio of the reference pressure to the reference
     !> magnetic energy density, \(\beta=2\mu_0n_{ref}T_{ref}/B^2_{ref}\).
     real :: beta = 0.0
     !> Initial timestep
     real :: delt = 0.1
     !> Determines how initial timestep is set. Possible options are:
     !>
     !>  - "default" or "set_by_hand": use timestep from input file
     !>  - "check_restart": read timestep(s) from restart file
     !>
     !> If "check_restart" is used but the restart files aren't
     !> present for any reason (for instance, the current run is not a
     !> restart), GS2 will error when trying to read the
     !> timesteps. You can run the [[ingen]] tool on your input file
     !> to check this issue before running your job
     character(len = 20) :: delt_option = 'default'
     !> If true then use eigensolver instead of initial value solver.
     logical :: do_eigsolve = .false.
     !> Advanced option to freeze evolution of certain modes. Possible values are:
     !>
     !> - "secondary": don't evolve `ik == 2, it == 1`;
     !> - "tertiary": dont' evolve `ik == 1, it == 2 or ntheta0`;
     !> - "harris": don't evolve `ik == 1`.
     !>
     !> Only used in [[dist_fn]], should be moved to [[dist_fn_knobs]].
     character(len = 20) :: eqzip_option = 'none'
     !> Multiplies \(A_\|\) throughout.
     real :: fapar = 0.0
     !> Multiplies \(B_\|\) throughout.
     real :: fbpar = -1.0
     !> Multiplies \(\phi\) throughout.
     real :: fphi = 1.0
     !> Determines the behaviour when the CFL condition is broken in the nonlinear term:
     logical :: immediate_reset = .true.
     !> Used in [[init_g_knobs:ginit_option]] "harris" and "convect". Should be
     !> moved to init_g namelist.
     real :: k0 = 1.0
     !> How close to avail_cpu_time can we go before trying to stop the run cleanly
     real :: margin_cpu_time = 300.0
     !> The simulation time after which the run should stop.
     real :: max_sim_time = 1.0e6
     !> Sets the time step interval at which check for the existence
     !> of the <runname>.stop file.
     integer :: ncheck_stop = 5
     !> Lowflow only. If true, GS2 exits after writing out neoclassical
     !> quantities of interest
     logical :: neo_test = .false.
     !> Maximum number of steps to take
     integer :: nstep = 100
     !> Normalised gyro-radius, only used for low flow builds
     real :: rhostar = 3.0e-3
     !> If true then save init timer information to
     !> <run_name>.init_times.
     logical :: save_init_times = .false.
     !> If true then save some extra timer information to
     !> <run_name>.timing_stats giving min/max/mean values.
     logical :: save_timer_statistics = .true.
     !> Used to set the random seed used in the random number
     !> generators provided by [[ran]]. If this input is 0 (the
     !> default) then we don't set the seed and use the system default
     !> instead.
     integer :: seed = 0
     !> Only used when there is an adiabatic species. Sets `n q ^2 / T` of the adiabatic
     !> species, normalised to the reference values.
     real :: tite = 1.0
     !> If true and running linearly, return linear diffusive flux estimates to Trinity.
     logical :: trinity_linear_fluxes = .false.
     !> Unknown
     logical :: trinity_ql_fluxes = .false.
     !> If true use original diagnostics rather than new form
     logical :: use_old_diagnostics = .false.
     !> Custom description of run to be added to netcdf output (new diagnostics only)
     character(len = 100000) :: user_comments = ''
     !> If true makes timestep proportional to ky*rho. Only sensible for linear runs.
     logical :: wstar_units = .false.
     !> Effective ionic charge appearing in electron collision frequency
     real :: zeff = 1.0
   contains
     procedure, public :: read => read_knobs_config
     procedure, public :: write => write_knobs_config
     procedure, public :: reset => reset_knobs_config
     procedure, public :: broadcast => broadcast_knobs_config
     procedure, public, nopass :: get_default_name => get_default_name_knobs_config
     procedure, public, nopass :: get_default_requires_index => get_default_requires_index_knobs_config
  end type knobs_config_type
  
  type(knobs_config_type) :: knobs_config

contains

  !> FIXME : Add documentation  
  subroutine check_run_parameters(report_unit)
    use species, only: has_hybrid_electron_species, spec
    implicit none
    integer, intent(in) :: report_unit
    if (fphi /= 1.) then
       write (report_unit, *) 
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, fmt="('fphi in the knobs namelist = ',e11.4)") fphi
       write (report_unit, fmt="('fphi is a scale factor of all instances of Phi (the electrostatic potential).')")
       write (report_unit, fmt="('THIS IS PROBABLY AN ERROR.')") 
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, *) 
    end if

    if (fapar == 0.) then
       write (report_unit, fmt="('A_parallel will not be included in the calculation.')")
    end if
    if (fapar == 1.) then
       write (report_unit, fmt="('A_parallel will be included in the calculation.')")
    end if
    if (fapar /= 0. .and. fapar /= 1.) then
       write (report_unit, *) 
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, fmt="('fapar in the knobs namelist = ',e11.4)") fapar
       write (report_unit, fmt="('fapar is a scale factor of all instances of A_parallel (the parallel vector potential).')")
       write (report_unit, fmt="('THIS IS PROBABLY AN ERROR.')") 
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, *) 
    end if

    if (fbpar == 0.) then
       write (report_unit, fmt="('B_parallel will not be included in the calculation.')")
    end if
    if (fbpar == 1.) then
       write (report_unit, fmt="('B_parallel will be included in the calculation.')")
    end if
    if (fbpar /= 0. .and. fbpar /= 1.) then
       write (report_unit, *) 
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, fmt="('fbpar in the knobs namelist = ',e11.4)") fbpar
       write (report_unit, fmt="('fbpar is a scale factor of all instances of B_parallel &
           & (the perturbed parallel magnetic field).')")
       write (report_unit, fmt="('THIS IS PROBABLY AN ERROR.')") 
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, *) 
    end if

    write (report_unit, *) 
    if(immediate_reset)then
       write (report_unit, fmt="('The time step will be reset immediately after cfl violation detected.')") 
    else
       write (report_unit, fmt="('The time step will be reset just before the next time step after cfl violation detected.')") 
    endif

    write (report_unit, *)
    if(seed .ne. 0)then
       write (report_unit, fmt="('The random number generator will use a seed derived from use input, ',I0,'.')") seed
    else
       write (report_unit, fmt="('The random number generator will use the default seed.')")
    endif
    write (report_unit, *)

    if ( has_hybrid_electron_species(spec) .and. beta /= 0.0 &
         .and. (fapar /= 0 .or. fbpar /=0) ) then
       write (report_unit, *)
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, fmt="('You are using a hybrid electron species in an electromagnetic simulation.')")
       write (report_unit, fmt="('This is probably not physical.')")
       write (report_unit, fmt="('################# WARNING #######################')")
       write (report_unit, *)
    end if

  end subroutine check_run_parameters

  !> FIXME : Add documentation    
  subroutine wnml_run_parameters(unit,electrons,collisions)
    implicit none
    integer, intent(in) :: unit
    logical, intent(in) :: electrons, collisions
    if (knexist) then
       write (unit, *)
       write (unit, fmt="(' &',a)") "knobs"
       write (unit, fmt="(' fphi   = ',f6.3)") fphi
       write (unit, fmt="(' fapar  = ',f6.3)") fapar
       write (unit, fmt="(' fbpar = ',f6.3)") fbpar
       write (unit, fmt="(' delt = ',e17.10)") delt
       write (unit, fmt="(' max_sim_time = ',e17.10)") max_sim_time
       write (unit, fmt="(' nstep = ',i8)") nstep
       write (unit, fmt="(' wstar_units = ',L1)") wstar_units
       select case (delt_option_switch)
       case (delt_option_auto)
          write (unit, fmt="(' delt_option = ',a)") '"check_restart"'
       case (delt_option_hand)
          ! nothing
       end select
       write (unit, fmt="(' immediate_reset = ',L1)") immediate_reset
       write (unit, fmt="(' beta = ',e17.10)") beta       ! if zero, fapar, fbpar should be zero
       if (collisions) write (unit, fmt="(' zeff = ',e17.10)") zeff
       if (.not. electrons)  write (unit, fmt="(' tite = ',e17.10)") tite
       write (unit, fmt="(' /')")
    endif
  end subroutine wnml_run_parameters

  !> FIXME : Add documentation  
  subroutine init_run_parameters(knobs_config_in)
    use kt_grids, only: init_kt_grids, naky, nakx => ntheta0
    implicit none
    type(knobs_config_type), intent(in), optional :: knobs_config_in

    if (initialized) return
    initialized = .true.

    call read_parameters(knobs_config_in)

    call init_kt_grids

    if(.not.allocated(ieqzip)) allocate(ieqzip(nakx,naky))
    ieqzip = .false.
    select case (eqzip_option_switch)
    case (eqzip_option_secondary)
       ! suppress evolution of secondary mode
       ieqzip(1,2) = .true.
    case (eqzip_option_tertiary)
       ! suppress evolution of tertiary mode
       ieqzip(2,1) = .true.
       ieqzip(nakx,1) = .true.
    case (eqzip_option_equilibrium)
       ! suppress evolution of 1D equilibrium (x dependent)
       ieqzip(1:nakx,1) = .true.
    end select
  end subroutine init_run_parameters

  !> FIXME : Add documentation    
  subroutine read_parameters(knobs_config_in)
    use file_utils, only: input_unit, error_unit, input_unit_exist
    use mp, only: proc0, broadcast
    use text_options, only: text_option, get_option_value
    use kt_grids, only: gryfx
    use ran, only: set_seed_from_single_integer
    implicit none
    type(knobs_config_type), intent(in), optional :: knobs_config_in
    
    type (text_option), dimension (4), parameter :: eqzipopts = &
         (/ text_option('none', eqzip_option_none), &
            text_option('secondary', eqzip_option_secondary), &
            text_option('tertiary', eqzip_option_tertiary), &
            text_option('equilibrium', eqzip_option_equilibrium) /)
    character (len=20) :: eqzip_option
    type (text_option), dimension (3), parameter :: deltopts = &
         (/ text_option('default', delt_option_hand), &
            text_option('set_by_hand', delt_option_hand), &
            text_option('check_restart', delt_option_auto) /)
    character(20) :: delt_option
    integer :: ierr, in_file
    logical :: rpexist
    !> Identify if we need to warn about removal of parameters
    if (proc0) then
       in_file = input_unit_exist('parameters', rpexist)
       if (rpexist) write(*, '("Warning: Input file contains namelist ",A," but this has been removed. Inputs have been migrated to ",A)') "'parameters'", "'knobs'"
    end if
    
    if (present(knobs_config_in)) knobs_config = knobs_config_in
    call knobs_config%init(name = 'knobs', requires_index = .false.)

    ! Copy out internal values into module level parameters
    avail_cpu_time = knobs_config%avail_cpu_time
    beta = knobs_config%beta
    delt = knobs_config%delt
    delt_option = knobs_config%delt_option
    do_eigsolve = knobs_config%do_eigsolve
    eqzip_option = knobs_config%eqzip_option
    fapar = knobs_config%fapar
    fbpar = knobs_config%fbpar
    fphi = knobs_config%fphi
    immediate_reset = knobs_config%immediate_reset
    k0 = knobs_config%k0
    margin_cpu_time = knobs_config%margin_cpu_time
    max_sim_time = knobs_config%max_sim_time
    ncheck_stop = knobs_config%ncheck_stop
    neo_test = knobs_config%neo_test
    nstep = knobs_config%nstep
    rhostar = knobs_config%rhostar
    save_init_times = knobs_config%save_init_times
    save_timer_statistics = knobs_config%save_timer_statistics
    seed = knobs_config%seed
    tite = knobs_config%tite
    trinity_linear_fluxes = knobs_config%trinity_linear_fluxes
    trinity_ql_fluxes = knobs_config%trinity_ql_fluxes
    use_old_diagnostics = knobs_config%use_old_diagnostics
    user_comments = knobs_config%user_comments
    wstar_units = knobs_config%wstar_units
    zeff = knobs_config%zeff
  
    knexist = knobs_config%exist

    ! Now handle inputs to calculate derived quantities

    ! Override fapar, fbpar and beta if set inconsistently:
    
    !> If we have zero beta then disable apar and bpar fields
    !! as these contribute nothing to result but slow down calculation.
    if(beta.eq.0) then
       if(((fapar.ne.0) .or. (fbpar.ne.0)).and. proc0) then
          ierr = error_unit()
          write(ierr,'("Warning: Disabling apar and bpar as beta = 0.")')
       endif
       fapar = 0.
       fbpar = 0.
    endif

    has_phi = fphi > epsilon(0.0)
    has_apar = fapar > epsilon(0.0)
    has_bpar = fbpar > epsilon(0.0)

    !> If we have non-zero beta then alert the user that they have some
    !! of the perturbed magnetic fields disabled. This may be intended
    !! behaviour, so we throw a warning. However, if both are disabled,
    !! we set beta=0. This will make the simulation electrostatic, as 
    !! probably intended if both apar and bpar are set to zero.
    if( beta /= 0.0 .and. .not. (has_apar .or. has_bpar)) then
       if(proc0) then
          ierr = error_unit()
          write(ierr,'("Warning: Both fapar and fbpar are zero: setting beta = 0.")')
       endif
       beta = 0.0
    endif
    if((beta.ne.0).and.proc0) then
       ierr = error_unit()
       if(.not. has_apar) write(ierr,'("Warning: Running with finite beta but fapar=0.")')
       if(.not. has_bpar) write(ierr,'("Warning: Running with finite beta but fbpar=0.")')
    endif

    ierr = error_unit()
    call get_option_value &
         (delt_option, deltopts, delt_option_switch, ierr, &
         "delt_option in knobs",.true.)
    
    call get_option_value ( &
         eqzip_option, eqzipopts, eqzip_option_switch, error_unit(), &
         "eqzip_option in knobs",.true.)

    ! FOR GRYFX
    if (gryfx()) then
       delt = delt/sqrt(2.0)
       max_sim_time = max_sim_time / sqrt(2.0)
       nstep = nstep*2
    end if

    if (seed .ne. 0 ) call set_seed_from_single_integer(seed)
  end subroutine read_parameters

  !> FIXME : Add documentation    
  subroutine write_trinity_parameters(trinpars_unit)
    integer, intent(in) :: trinpars_unit
    write(trinpars_unit, "(A15)") '&run_parameters'
    write (trinpars_unit, *) ' beta = ', beta
    write (trinpars_unit, "(A1)") '/'

  end subroutine write_trinity_parameters

  !> FIXME : Add documentation    
  subroutine set_overrides(tstep_ov)
    use overrides, only: timestep_overrides_type
    type(timestep_overrides_type), intent(in) :: tstep_ov
    if (tstep_ov%override_immediate_reset) immediate_reset=tstep_ov%immediate_reset
  end subroutine set_overrides

  !> FIXME : Add documentation    
  subroutine finish_run_parameters
    implicit none

    if (allocated(ieqzip)) deallocate (ieqzip)

    initialized = .false.
    call knobs_config%reset()
  end subroutine finish_run_parameters


  !> Set the module level config type
  !> Will abort if the module has already been initialised to avoid
  !> inconsistencies.
  subroutine set_run_parameters_config(knobs_config_in)
    use mp, only: mp_abort
    type(knobs_config_type), intent(in), optional :: knobs_config_in
    
    if (initialized) then
       call mp_abort("Trying to set run_parameters config when already initialized.", to_screen = .true.)
    end if
    if (present(knobs_config_in)) then
       knobs_config = knobs_config_in
    end if
  end subroutine set_run_parameters_config

  !---------------------------------------
  ! Following is for the knobs config_type
  !---------------------------------------

  !> Reads in the knobs namelist and populates the member variables
  subroutine read_knobs_config(self)
    use file_utils, only: input_unit_exist, get_indexed_namelist_unit
    use mp, only: proc0
    implicit none
    class(knobs_config_type), intent(in out) :: self
    logical :: exist
    integer :: in_file

    ! Note: When this routine is in the module where these variables live
    ! we shadow the module level variables here. This is intentional to provide
    ! isolation and ensure we can move this routine to another module easily.    
    character(len = 100000) :: user_comments
    character(len = 20) :: delt_option, eqzip_option
    integer :: ncheck_stop, nstep, seed
    logical :: do_eigsolve, immediate_reset, neo_test, save_init_times, save_timer_statistics, &
         trinity_linear_fluxes, trinity_ql_fluxes, use_old_diagnostics, wstar_units
    real :: avail_cpu_time, beta, delt, fapar, fbpar, fphi, k0, margin_cpu_time, max_sim_time, rhostar, tite, zeff

    namelist /knobs/ avail_cpu_time, beta, delt, delt_option, do_eigsolve, eqzip_option, fapar, fbpar, fphi, immediate_reset, k0, margin_cpu_time, max_sim_time, ncheck_stop, neo_test, nstep, rhostar, &
         save_init_times, save_timer_statistics, seed, tite, trinity_linear_fluxes, trinity_ql_fluxes, use_old_diagnostics, user_comments, wstar_units, zeff

    ! Only proc0 reads from file
    if (.not. proc0) return

    ! First set local variables from current values
    avail_cpu_time = self%avail_cpu_time
    beta = self%beta
    delt = self%delt
    delt_option = self%delt_option
    do_eigsolve = self%do_eigsolve
    eqzip_option = self%eqzip_option
    fapar = self%fapar
    fbpar = self%fbpar
    fphi = self%fphi
    immediate_reset = self%immediate_reset
    k0 = self%k0
    margin_cpu_time = self%margin_cpu_time
    max_sim_time = self%max_sim_time
    ncheck_stop = self%ncheck_stop
    neo_test = self%neo_test
    nstep = self%nstep
    rhostar = self%rhostar
    save_init_times = self%save_init_times
    save_timer_statistics = self%save_timer_statistics
    seed = self%seed
    tite = self%tite
    trinity_linear_fluxes = self%trinity_linear_fluxes
    trinity_ql_fluxes = self%trinity_ql_fluxes
    use_old_diagnostics = self%use_old_diagnostics
    user_comments = self%user_comments
    wstar_units = self%wstar_units
    zeff = self%zeff

    ! Now read in the main namelist
    in_file = input_unit_exist(self%get_name(), exist)
    if (exist) read(in_file, nml = knobs)

    ! Now copy from local variables into type members
    self%avail_cpu_time = avail_cpu_time
    self%beta = beta
    self%delt = delt
    self%delt_option = delt_option
    self%do_eigsolve = do_eigsolve
    self%eqzip_option = eqzip_option
    self%fapar = fapar
    self%fbpar = fbpar
    self%fphi = fphi
    self%immediate_reset = immediate_reset
    self%k0 = k0
    self%margin_cpu_time = margin_cpu_time
    self%max_sim_time = max_sim_time
    self%ncheck_stop = ncheck_stop
    self%neo_test = neo_test
    self%nstep = nstep
    self%rhostar = rhostar
    self%save_init_times = save_init_times
    self%save_timer_statistics = save_timer_statistics
    self%seed = seed
    self%tite = tite
    self%trinity_linear_fluxes = trinity_linear_fluxes
    self%trinity_ql_fluxes = trinity_ql_fluxes
    self%use_old_diagnostics = use_old_diagnostics
    self%user_comments = user_comments
    self%wstar_units = wstar_units
    self%zeff = zeff

    self%exist = exist
  end subroutine read_knobs_config

  !> Writes out a namelist representing the current state of the config object
  subroutine write_knobs_config(self, unit)
    implicit none
    class(knobs_config_type), intent(in) :: self
    integer, intent(in) , optional:: unit
    integer :: unit_internal

    unit_internal = 6 ! @todo -- get stdout from file_utils
    if (present(unit)) then
       unit_internal = unit
    endif

    call self%write_namelist_header(unit_internal)
    call self%write_key_val("avail_cpu_time", self%avail_cpu_time, unit_internal)
    call self%write_key_val("beta", self%beta, unit_internal)
    call self%write_key_val("delt", self%delt, unit_internal)
    call self%write_key_val("delt_option", self%delt_option, unit_internal)
    call self%write_key_val("do_eigsolve", self%do_eigsolve, unit_internal)
    call self%write_key_val("eqzip_option", self%eqzip_option, unit_internal)
    call self%write_key_val("fapar", self%fapar, unit_internal)
    call self%write_key_val("fbpar", self%fbpar, unit_internal)
    call self%write_key_val("fphi", self%fphi, unit_internal)
    call self%write_key_val("immediate_reset", self%immediate_reset, unit_internal)
    call self%write_key_val("k0", self%k0, unit_internal)
    call self%write_key_val("margin_cpu_time", self%margin_cpu_time, unit_internal)
    call self%write_key_val("max_sim_time", self%max_sim_time, unit_internal)
    call self%write_key_val("ncheck_stop", self%ncheck_stop, unit_internal)
    call self%write_key_val("neo_test", self%neo_test, unit_internal)
    call self%write_key_val("nstep", self%nstep, unit_internal)
    call self%write_key_val("rhostar", self%rhostar, unit_internal)
    call self%write_key_val("save_init_times", self%save_init_times, unit_internal)
    call self%write_key_val("save_timer_statistics", self%save_timer_statistics, unit_internal)
    call self%write_key_val("seed", self%seed, unit_internal)
    call self%write_key_val("tite", self%tite, unit_internal)
    call self%write_key_val("trinity_linear_fluxes", self%trinity_linear_fluxes, unit_internal)
    call self%write_key_val("trinity_ql_fluxes", self%trinity_ql_fluxes, unit_internal)
    call self%write_key_val("use_old_diagnostics", self%use_old_diagnostics, unit_internal)
    call self%write_key_val("user_comments", self%user_comments, unit_internal)
    call self%write_key_val("wstar_units", self%wstar_units, unit_internal)
    call self%write_key_val("zeff", self%zeff, unit_internal)
    call self%write_namelist_footer(unit_internal)
  end subroutine write_knobs_config

  !> Resets the config object to the initial empty state
  subroutine reset_knobs_config(self)
    class(knobs_config_type), intent(in out) :: self
    type(knobs_config_type) :: empty
    select type (self)
    type is (knobs_config_type)
       self = empty
    end select
  end subroutine reset_knobs_config

  !> Broadcasts all config parameters so object is populated identically on
  !! all processors
  subroutine broadcast_knobs_config(self)
    use mp, only: broadcast
    implicit none
    class(knobs_config_type), intent(in out) :: self
    call broadcast(self%avail_cpu_time)
    call broadcast(self%beta)
    call broadcast(self%delt)
    call broadcast(self%delt_option)
    call broadcast(self%do_eigsolve)
    call broadcast(self%eqzip_option)
    call broadcast(self%fapar)
    call broadcast(self%fbpar)
    call broadcast(self%fphi)
    call broadcast(self%immediate_reset)
    call broadcast(self%k0)
    call broadcast(self%margin_cpu_time)
    call broadcast(self%max_sim_time)
    call broadcast(self%ncheck_stop)
    call broadcast(self%neo_test)
    call broadcast(self%nstep)
    call broadcast(self%rhostar)
    call broadcast(self%save_init_times)
    call broadcast(self%save_timer_statistics)
    call broadcast(self%seed)
    call broadcast(self%tite)
    call broadcast(self%trinity_linear_fluxes)
    call broadcast(self%trinity_ql_fluxes)
    call broadcast(self%use_old_diagnostics)
    call broadcast(self%user_comments)
    call broadcast(self%wstar_units)
    call broadcast(self%zeff)

    call broadcast(self%exist)
  end subroutine broadcast_knobs_config

  !> Gets the default name for this namelist
  function get_default_name_knobs_config()
    implicit none
    character(len = CONFIG_MAX_NAME_LEN) :: get_default_name_knobs_config
    get_default_name_knobs_config = "knobs"
  end function get_default_name_knobs_config

  !> Gets the default requires index for this namelist
  function get_default_requires_index_knobs_config()
    implicit none
    logical :: get_default_requires_index_knobs_config
    get_default_requires_index_knobs_config = .false.
  end function get_default_requires_index_knobs_config

  !> Get the module level config instance
  pure function get_knobs_config()
    type(knobs_config_type) :: get_knobs_config
    get_knobs_config = knobs_config
  end function get_knobs_config
end module run_parameters