Adding New Input File Parameters

This guide explains how to add new parameters to metalquicha’s input file format.

Overview

Input parameters flow through three stages:

  1. Parsing - mqc_config_parser.f90 reads the .mqc file into mqc_config_t

  2. Adaptation - mqc_config_adapter.f90 converts to driver_config_t

  3. Usage - Driver and workflows use the configuration

Key files:

  • src/io/mqc_config_parser.f90 - Parser and mqc_config_t type

  • src/io/mqc_config_adapter.f90 - Adapter and driver_config_t type

  • src/core/mqc_calculation_keywords.f90 - Keyword group types (hessian, aimd, scf)

Step-by-Step Guide

1. Add Field to mqc_config_t

Edit src/io/mqc_config_parser.f90 and add your field to mqc_config_t:

type :: mqc_config_t
   ! ... existing fields ...

   ! Your new field with a sensible default
   real(dp) :: my_new_parameter = 1.0_dp

end type mqc_config_t

Conventions:

  • Always provide a default value

  • Group related fields together with comments

  • Use appropriate types: integer, real(dp), logical, character(len=:), allocatable

2. Parse the Parameter

Find or create the appropriate section parser in the same file. Parameters are organized by section (%model, %driver, %hessian, etc.).

For example, to add a parameter to the %driver section:

subroutine parse_driver_section(unit, config, error)
   ! ... existing parsing code ...

   do
      read(unit, '(A)', iostat=io_stat) line
      if (io_stat /= 0) exit

      line = strip_comment(line)
      if (len_trim(line) == 0) cycle
      if (trim(line) == 'end') exit

      ! Parse key = value
      eq_pos = index(line, '=')
      if (eq_pos > 0) then
         key = to_lower(trim(adjustl(line(1:eq_pos-1))))
         value = trim(adjustl(line(eq_pos+1:)))

         select case (key)
         ! ... existing cases ...

         case ('my_new_parameter')
            read(value, *, iostat=io_stat) config%my_new_parameter
            if (io_stat /= 0) then
               call error%set(ERROR_PARSE, "Invalid my_new_parameter value: "//trim(value))
               return
            end if

         case default
            call error%set(ERROR_PARSE, "Unknown driver option: "//trim(key))
            return
         end select
      end if
   end do
end subroutine

3. Add to driver_config_t (if needed at runtime)

If your parameter is needed during calculation (not just for parsing), add it to driver_config_t in src/io/mqc_config_adapter.f90:

type :: driver_config_t
   ! ... existing fields ...

   real(dp) :: my_new_parameter = 1.0_dp

end type driver_config_t

Then copy it in config_to_driver:

subroutine config_to_driver(mqc_config, driver_config, molecule_index)
   ! ... existing code ...

   driver_config%my_new_parameter = mqc_config%my_new_parameter

end subroutine

4. Use in Keyword Groups (for structured settings)

For related parameters (like Hessian settings), use the keyword group types in src/core/mqc_calculation_keywords.f90:

type :: hessian_keywords_t
   real(dp) :: displacement = 0.001_dp
   real(dp) :: temperature = 298.15_dp
   real(dp) :: pressure = 1.0_dp
   ! Add your new field here
   logical :: project_rotations = .true.
end type

These are accessed via driver_config%hessian%project_rotations.

5. Update Documentation

Add your parameter to mqc_docs/source/input_files.rst in the appropriate section.

Example: Adding a New Section

To add an entirely new section (e.g., %optimization):

1. Add fields to mqc_config_t:

type :: mqc_config_t
   ! ... existing fields ...

   ! Optimization settings
   integer :: opt_maxiter = 100
   real(dp) :: opt_convergence = 1.0e-6_dp
   character(len=:), allocatable :: opt_algorithm

end type

2. Create a section parser:

subroutine parse_optimization_section(unit, config, error)
   integer, intent(in) :: unit
   type(mqc_config_t), intent(inout) :: config
   type(error_t), intent(out) :: error

   character(len=MAX_LINE_LEN) :: line
   character(len=:), allocatable :: key, value
   integer :: io_stat, eq_pos

   do
      read(unit, '(A)', iostat=io_stat) line
      if (io_stat /= 0) exit

      line = strip_comment(line)
      if (len_trim(line) == 0) cycle
      if (trim(line) == 'end') exit

      eq_pos = index(line, '=')
      if (eq_pos > 0) then
         key = to_lower(trim(adjustl(line(1:eq_pos-1))))
         value = trim(adjustl(line(eq_pos+1:)))

         select case (key)
         case ('maxiter')
            read(value, *, iostat=io_stat) config%opt_maxiter
         case ('convergence')
            read(value, *, iostat=io_stat) config%opt_convergence
         case ('algorithm')
            config%opt_algorithm = trim(value)
         case default
            call error%set(ERROR_PARSE, "Unknown optimization option: "//trim(key))
            return
         end select
      end if
   end do
end subroutine

3. Register the section in read_mqc_file:

subroutine read_mqc_file(filename, config, error)
   ! ... existing code ...

   select case (trim(line))
   ! ... existing cases ...

   case ('%optimization')
      call parse_optimization_section(unit, config, parse_error)
      if (parse_error%has_error()) then
         error = parse_error
         close(unit)
         return
      end if

   case default
      ! Unknown section handling
   end select

end subroutine

4. Create keyword type if needed:

In src/core/mqc_calculation_keywords.f90:

type :: optimization_keywords_t
   integer :: maxiter = 100
   real(dp) :: convergence = 1.0e-6_dp
   character(len=32) :: algorithm = "bfgs"
end type optimization_keywords_t

Input File Format Reference

Sections use the format:

%section_name
key1 = value1
key2 = value2
end

Supported value types:

  • Integers: maxiter = 100

  • Reals: tolerance = 1.0e-6

  • Strings: method = gfn2 (no quotes needed)

  • Booleans: use_symmetry = true or use_symmetry = false

  • Arrays: cutoffs = 5.0, 4.0, 3.0 (comma-separated)

Comments: Use # or ! for comments:

%driver
calc_type = gradient  # This is a comment
end

Testing Your Changes

  1. Create a test input file with your new parameter

  2. Build and run:

    cmake --build build -j
    ./build/mqc test_input.mqc
    
  3. Add a unit test in test/ if appropriate