! Module to handle reading a configuration file based on key/value pairs
!
module MW_configuration
   use MW_configuration_line, only: MW_configuration_line_t, &
         MW_configuration_line_seek_next_data => seek_next_data, &
         MW_configuration_line_get_word => get_word, &
         max_word_length
   use MW_system, only: MW_system_t
   use MW_errors, only: &
         MW_errors_runtime_error => runtime_error, &
         MW_errors_allocate_error => allocate_error, &
         MW_errors_deallocate_error => deallocate_error
   use MW_kinds, only: wp
   use MW_external_field, only: MW_external_field_t
   implicit none
   private

   ! Public subroutines
   ! ------------------
   public :: read_system_parameters
contains

   !================================================================================
   ! Read system parameters from configuration file
   !
   ! File format
   !
   !   num_steps *int*
   !   timestep  *real*
   !   temperature  *real*
   !   box *block*
   !   thermostat *block*
   !   barostat *block*
   !   species *block*
   !   molecules *block*
   !   electrodes *block*
   !   interactions *block*
   !   output *block*
   !   velocity *block*
   !
   subroutine read_system_parameters(system, algorithms, configfile)
      use MW_algorithms, only: MW_algorithms_t, &
            MW_algorithms_void_type => void_type
      use MW_system, only: MW_system_t, &
            MW_system_void_parameters => void_parameters, &
            MW_system_check_electrodes => check_electrodes, &
            MW_system_check_polarizability => check_polarizability, &
            MW_system_check_deformability => check_deformability
      use MW_errors, only: MW_errors_open_error => open_error, &
            MW_errors_close_error => close_error, &
            MW_errors_parameter_error => parameter_error, &
            MW_errors_runtime_warning => runtime_warning
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_ion, only: &
            MW_ion_define_type => define_type, &
            MW_ion_void_type => void_type
      use MW_electrode, only: &
            MW_electrode_define_type => define_type, &
            MW_electrode_void_type => void_type
      use MW_molecule, only: &
            MW_molecule_define_type => define_type, &
            MW_molecule_void_type => void_type, &
            MW_molecule_set_index => set_index, &
            MW_molecule_set_sites_index => set_sites_index
      use MW_electrode_parameters, only: MW_electrode_parameters_t, &
            MW_electrode_parameters_set_index => set_index, &
            MW_electrode_parameters_set_species_index => set_species_index
      use MW_species, only: MW_species_t
      use MW_species_parameters, only: CHARGE_TYPE_GAUSSIAN
      use MW_cg, only: MW_cg_allocate_arrays => allocate_arrays
      use MW_thomas_fermi, only: MW_thomas_fermi_t, &
            MW_thomas_fermi_void_type => void_type, &
            MW_thomas_fermi_define_type => define_type
!      use MW_cgaim, only: MW_cgaim_allocate_arrays => allocate_arrays
      use MW_maze, only: MW_maze_set_dimensionality => set_dimensionality, &
            MW_maze_allocate_arrays => allocate_arrays
      use MW_matrix_inversion, only: MW_matrix_inversion_allocate_arrays => allocate_arrays
      use MW_ewald, only: MW_ewald_set_num_pbc => set_num_pbc
      use MW_external_field, only: FIELD_TYPE_ELECTRIC, FIELD_TYPE_DISPLACEMENT
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_algorithms_t), intent(inout) :: algorithms
      character(*), intent(in) :: configfile

      ! Local
      ! -----
      integer :: funit, ierr
      integer :: line_num
      character(max_word_length) :: keyword
      integer :: i
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      integer :: temperature_defined, numstep_defined, timestep_defined, numPBC_defined, &
            box_defined, species_defined, molecules_defined, electrodes_defined, &
            interactions_defined, output_defined, thermostat_defined, barostat_defined, &
            velocity_defined, plumed_defined, dipoles_electrodes_defined, &
            external_field_defined
      integer :: charges_defined, charge_neutrality_defined, dipoles_minimization_defined
      integer :: itype, num_elec, num_species, num_ion_types, species_index
      type(MW_species_t), allocatable :: species(:)
      type(MW_electrode_parameters_t), allocatable :: electrodes(:)

      ! Reset all system parameters
      call MW_system_void_parameters(system)
      call MW_algorithms_void_type(algorithms)

      ! Open file
      call MW_fileunit_get_new_unit(funit)
      open(unit=funit, file=configfile, access="SEQUENTIAL", action="READ", &
            position="REWIND", form="FORMATTED", status="OLD", iostat=ierr)
      if (ierr /= 0) then
         call MW_errors_open_error("read_system_parameters", "configuration.f90", configfile, ierr)
      end if
      line_num = 0

      ! Flag each section as undefined (line number will be stored)
      temperature_defined = 0
      numstep_defined = 0
      timestep_defined = 0
      numPBC_defined = 0
      box_defined = 0
      thermostat_defined = 0
      barostat_defined = 0
      species_defined = 0
      molecules_defined = 0
      electrodes_defined = 0
      dipoles_electrodes_defined = 0
      interactions_defined = 0
      output_defined = 0
      velocity_defined = 0
      external_field_defined = 0
      plumed_defined = 0

      charges_defined = 0
      charge_neutrality_defined = 0
      dipoles_minimization_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("temperature")
            if (temperature_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |temperature|. First definition at line ",i4,".")') &
                     line_num, temperature_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            temperature_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for temperature parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_system_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, system%temperature)

         case("num_steps")
            if (numstep_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |num_steps|. First definition at line ",i4,".")') &
                     line_num, numstep_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            numstep_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for num_steps parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_system_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, system%num_steps)

         case("timestep")
            if (timestep_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |timestep|. First definition at line ",i4,".")') &
                     line_num, timestep_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            timestep_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for timestep parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_system_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, system%dt)

         case("num_pbc")
            if (numPBC_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |num_pbc|. First definition at line ",i4,".")') &
                     line_num, numPBC_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            numPBC_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for num PBC parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_system_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, system%num_PBC)

         case("box")
            if (box_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |box|. First definition at line ",i4,".")') &
                     line_num, box_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            box_defined = line_num
            call read_box_parameters(system, funit, line_num)

         case("thermostat")
            if (thermostat_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |thermostat|. First definition at line ",i4,".")') &
                     line_num, thermostat_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            thermostat_defined = line_num
            call read_thermostat_parameters(system, funit, line_num)

         case("barostat")
            if (barostat_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |barostat|. First definition at line ",i4,".")') &
                     line_num, barostat_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            barostat_defined = line_num
            call read_barostat_parameters(system, funit, line_num)

#ifdef MW_USE_PLUMED
         case("plumed")
            if (plumed_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |plumed|. First definition at line ",i4,".")') &
               line_num, plumed_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            plumed_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for plumed parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_system_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, system%plumed_file)
            system%plumedlog=.true.
#else
         case("plumed")
            write(errmsg,'("At line ",i4,", definition of |plumed| needs compilation with PLUMED interface.")') line_num
            call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
#endif

         case("species")
            if (species_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |species|. First definition at line ",i4,".")') &
                     line_num, species_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            species_defined = line_num
            call read_species_parameters(species, algorithms, system%charged_species, system%four_site_model, funit, line_num, &
                  dipoles_minimization_defined)

         case("molecules")
            if (molecules_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |molecules|. First definition at line ",i4,".")') &
                     line_num, molecules_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if

            ! Molecules must be defined after ions
            if (species_defined == 0) then
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                     "molecules definition block must come after species definition block")
            end if

            molecules_defined = line_num
            call read_molecules_parameters(system, funit, line_num)

         case("electrodes")
            if (electrodes_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |electrodes|. First definition at line ",i4,".")') &
                     line_num, electrodes_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if

            electrodes_defined = line_num
            call read_electrodes_parameters(electrodes, algorithms, funit, line_num, system%electrode_compute_force, &
                  system%electrode_charge_neutrality, system%global_charge_neutrality, charges_defined, charge_neutrality_defined)

         case("dipoles_and_electrodes")
            if (dipoles_electrodes_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |dipoles_and_electrodes|. First definition at line ",i4,".")') &
                     line_num, dipoles_electrodes_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if

            if (dipoles_minimization_defined > 0) then
               write(errmsg,'("At line ",i4,", |dipoles_minimization| incompatible with |dipoles_and_electrodes| block.")') &
                     dipoles_minimization_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if

            if (charges_defined > 0) then
               write(errmsg,'("At line ",i4,", |electrode_charges| incompatible with |dipoles_and_electrodes| block.")') &
                     charges_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if

            if (charge_neutrality_defined > 0) then
               write(errmsg,'("At line ",i4,", |charge_neutrality| incompatible with |dipoles_and_electrodes| block.")') &
                     charge_neutrality_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if

            dipoles_electrodes_defined = line_num
            call read_dipoles_electrodes_parameters(electrodes, algorithms, funit, line_num, &
                  system%electrode_charge_neutrality, system%global_charge_neutrality)

         case("interactions")
            if (interactions_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |interactions|. First definition at line ",i4,".")') &
                     line_num, interactions_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            interactions_defined = line_num

            if (species_defined == 0) then
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                     "interactions definition block must come after species definition block")
            end if
            call read_interactions_parameters(system, species, electrodes, funit, line_num)

         case("output")
            if (output_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |output|. First definition at line ",i4,".")') &
                     line_num, output_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            output_defined = line_num
            call read_output_parameters(system, funit, line_num)

         case("velocity")
            if (velocity_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |velocity|. First definition at line ",i4,".")') &
                     line_num, velocity_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            velocity_defined = line_num
            call read_velocity_parameters(system, funit, line_num)

         case("external_field")
            if (external_field_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |external_field|. First definition at line ",i4,".")') &
                     line_num, external_field_defined
               call MW_errors_runtime_error("read_system_parameters", "configuration.f90", errmsg)
            end if
            external_field_defined = line_num
            call read_external_field_parameters(system%field, funit, line_num)

         case default
            write(errmsg, '("Invalid keyword |",a,"| at line ",i3)') keyword, line_num
            call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                  errmsg)
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)

      end do keywordLoop

      close(funit, iostat=ierr)
      if (ierr /= 0) then
         call MW_errors_close_error("read_system_parameters", "configuration.f90", &
               funit, ierr)
      end if

      ! Define ions and electrodes from species and electrode_parameters
      ! ions = fixed or mobile point charge species
      ! electrodes = fixed gaussian charge species
      ! mobile gaussian charge species are not supported
      ! all gaussian charge species must be mapped to an electrode
      ! point charge species must be defined before gaussian charge species
      num_species = 0
      if (allocated(species)) num_species = size(species,1)
      num_elec = 0
      if (allocated(electrodes)) num_elec = size(electrodes,1)
      num_ion_types = num_species - num_elec

      if (num_ion_types < 0) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "You defined more electrode_type(s) than species_type(s)")
      end if

      ! Define ions
      ! -----------
      if (allocated(system%ions)) then
         do itype = 1, size(system%ions,1)
            call MW_ion_void_type(system%ions(itype))
         end do
         deallocate(system%ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("read_system_parameters", "configuration.f90", ierr)
         end if
      end if
      allocate(system%ions(num_ion_types), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("read_system_parameters", "configuration.f90", ierr)
      end if
      system%num_ion_types = num_ion_types

      system%num_ions = 0
      system%total_ions_charge = 0.0_wp
      do itype = 1, num_ion_types
         if (species(itype)%charge_type == CHARGE_TYPE_GAUSSIAN) then
            call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                  "Ion type cannot have a gaussian charge distribution")
         end if
         call MW_ion_define_type(system%ions(itype), &
               species(itype)%name, &
               species(itype)%count, &
               species(itype)%charge, &
               species(itype)%polarizability, &
               species(itype)%selfdaimD, &
               species(itype)%selfdaimbeta, &
               species(itype)%selfdaimzeta, &
               species(itype)%mass, &
               species(itype)%is_mobile, &
               species(itype)%is_fourth_site, &
               species(itype)%dump_efg, &
               species(itype)%dump_lammps, &
               species(itype)%dump_xyz, &
               species(itype)%dump_trajectories, &
               species(itype)%dump_pdb)
         system%ions(itype)%offset = system%num_ions
         system%num_ions = system%num_ions + species(itype)%count
         system%total_ions_charge = system%total_ions_charge + species(itype)%count * species(itype)%charge
         system%efgrun = ((system%efgrun) .or. species(itype)%dump_efg) ! .true. if at least one species needs efg
      end do

      ! check whether we use the polarizable ion model
      call MW_system_check_polarizability(system)
      ! check whether we use the aspherical ion model
      call MW_system_check_deformability(system)

      if (abs(system%total_ions_charge).gt.1.0e-12_wp) then
         write(errmsg,'("The electrolyte is not neutral, has charge ",f5.3)') system%total_ions_charge
         call MW_errors_runtime_warning("read_box_parameters", "configuration.f90", errmsg)
      end if

      ! Define electrodes
      ! -----------------
      if (allocated(system%electrodes)) then
         do itype = 1, size(system%electrodes,1)
            call MW_electrode_void_type(system%electrodes(itype))
         end do
         deallocate(system%electrodes, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("read_system_parameters", "configuration.f90", ierr)
         end if
      end if
      allocate(system%electrodes(num_elec), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("read_system_parameters", "configuration.f90", ierr)
      end if

      if (allocated(system%thomas_fermi)) then
         do itype = 1, size(system%thomas_fermi,1)
            call MW_thomas_fermi_void_type(system%thomas_fermi(itype))
         end do
         deallocate(system%thomas_fermi, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("read_system_parameters", "configuration.f90", ierr)
         end if
      end if
      allocate(system%thomas_fermi(num_elec), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("read_system_parameters", "configuration.f90", ierr)
      end if

      system%num_elec = num_elec

      system%num_atoms = 0
      do itype = 1, num_elec
         call MW_electrode_parameters_set_index(electrodes(itype), itype)
         call MW_electrode_parameters_set_species_index(electrodes(itype), species)
         species_index = electrodes(itype)%species_index
         if (species_index /= (num_ion_types+itype)) then
            call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                  "Electrode type species must be specified after electrolyte type species " // &
                  "and in the same order as electrode types")
         end if
         if (species(species_index)%charge_type /= CHARGE_TYPE_GAUSSIAN) then
            call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                  "Electrode type species must have a gaussian charge distribution")
         end if
         if (species(species_index)%is_mobile) then
            call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                  "Electrode type species must not be mobile.")
         end if

         call MW_electrode_define_type(system%electrodes(itype), &
               species(species_index)%name, &
               species(species_index)%count, &
               species(species_index)%eta, &
               species(species_index)%charge, &
               electrodes(itype)%potential, &
               electrodes(itype)%piston, &
               electrodes(itype)%pressure, &
               species(species_index)%count*species(species_index)%mass, &
               electrodes(itype)%thomas_fermi_length, &
               electrodes(itype)%voronoi_volume, &
               species(species_index)%dump_lammps, &
               species(species_index)%dump_xyz, &
               species(species_index)%dump_trajectories, &
               species(species_index)%dump_pdb)
         if (electrodes(itype)%piston) then
            system%pistonrun = .true.
         end if

         call MW_thomas_fermi_define_type(system%thomas_fermi(itype), &
               electrodes(itype)%thomas_fermi_length, &
               electrodes(itype)%voronoi_volume)
         if (electrodes(itype)%thomas_fermi_length > 1.0e-12_wp) then
            system%tfrun = .true.
         end if

         system%electrodes(itype)%offset = system%num_atoms
         system%num_atoms = system%num_atoms + species(species_index)%count
      end do

      !check whether electrodes minimization is needed
      call MW_system_check_electrodes(algorithms%constant_charge, system)
      !Check if charge neutrality is to be enforced
      !Done directly in read_electrodes_parameters or read_dipoles_electrodes_parameters
      !call check_charge_neutrality(system, algorithms)

      if (abs(system%total_ions_charge).gt.1.0e-12_wp &
            .and. system%electrode_charge_neutrality &
            .and. (.not. system%global_charge_neutrality)) then
         write(errmsg,'("The global system is not neutral (only the electrodes are)")')
         call MW_errors_runtime_warning("read_box_parameters", "configuration.f90", errmsg)
      end if

      ! Set algorithms for charge calculation
      if (system%pimrun.and.system%elecrun) then
         if (algorithms%maze%calc_dip.and.algorithms%maze%calc_elec) then
            system%mazerun = .true.
            call MW_maze_set_dimensionality(algorithms%maze, 3*system%num_ions+system%num_atoms)
            call MW_maze_allocate_arrays(algorithms%maze)
         else if (algorithms%matrix_inversion%calc_dip.and.algorithms%matrix_inversion%calc_elec) then
            call MW_matrix_inversion_allocate_arrays(algorithms%matrix_inversion, 3*system%num_ions+system%num_atoms)
         else
            if (algorithms%constant_charge) then
               call MW_cg_allocate_arrays(algorithms%cg, 3*system%num_ions)
            else
               call MW_cg_allocate_arrays(algorithms%cg, 3*system%num_ions+system%num_atoms)
            end if
         end if
      else if (system%pimrun) then
         if (algorithms%maze%calc_dip) then
            system%mazerun = .true.
            call MW_maze_set_dimensionality(algorithms%maze, 3*system%num_ions)
            call MW_maze_allocate_arrays(algorithms%maze)
         else if (algorithms%matrix_inversion%calc_dip) then
            call MW_matrix_inversion_allocate_arrays(algorithms%matrix_inversion, 3*system%num_ions)
         else
            call MW_cg_allocate_arrays(algorithms%cg, 3*system%num_ions)
         end if
      else if (system%elecrun) then
         if (algorithms%maze%calc_elec) then
            system%mazerun = .true.
            call MW_maze_set_dimensionality(algorithms%maze, system%num_atoms)
            call MW_maze_allocate_arrays(algorithms%maze)
         else if (algorithms%matrix_inversion%calc_elec) then
            call MW_matrix_inversion_allocate_arrays(algorithms%matrix_inversion, system%num_atoms)
         else
            call MW_cg_allocate_arrays(algorithms%cg, system%num_atoms)
         end if
      end if

      call MW_ewald_set_num_pbc(system%ewald, system%num_pbc)

      ! Define molecules
      system%num_molecules = 0
      do i = 1, system%num_molecule_types
         call MW_molecule_set_index(system%molecules(i), i)
         call MW_molecule_set_sites_index(system%molecules(i), species)
         system%num_molecules = system%num_molecules + system%molecules(i)%n
      end do

      if (.not. allocated(system%molecules)) then
         allocate(system%molecules(0), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("read_system_parameters", "configuration.f90", ierr)
         end if
         system%num_molecules = 0
         system%num_molecule_types = 0
      end if

      allocate(system%species_names(num_species))
      do species_index = 1, num_species
         system%species_names(species_index) = species(species_index)%name
      end do

      ! Raise errors for uncompatibilities
      ! Constant charge and charge neutrality cannot be enforced together
      if (algorithms%constant_charge .and. &
          (system%electrode_charge_neutrality.or.system%global_charge_neutrality)) then
          call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                "Charge neutrality is not compatible with constant charges on electrodes")
      end if

      ! MaZe and charge neutrality only works with maze inversion
      if (system%electrode_charge_neutrality .and. &
          algorithms%maze%calc_elec          .and. &
          .not.algorithms%maze%invert) then
          call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
                "Charge neutrality with MaZe algorithm is only compatible with `maze inversion`")
      end if

      ! External fields need charge neutrality
      if ((system%field%field_type.eq.FIELD_TYPE_ELECTRIC      .or.  &
           system%field%field_type.eq.FIELD_TYPE_DISPLACEMENT) .and. &
           .not.system%electrode_charge_neutrality) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use external fields without charge neutrality")
      end if

      ! External fields needs 3D periodic boundary conditions
      if ((system%field%field_type.eq.FIELD_TYPE_ELECTRIC      .or.  &
           system%field%field_type.eq.FIELD_TYPE_DISPLACEMENT) .and. &
           system%num_pbc.ne.3) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use external fields without 3D PBC")
      end if

      ! Dipoles needs 3D periodic boundary conditions
      ! if (system%pimrun .and. system%num_pbc.ne.3) then
      !    call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
      !          "cannot use dipoles without 3D PBC")
      ! end if

      ! Computing forces on electrodes does not support PIM and AIM force fields
      if (system%electrode_compute_force .and. system%pimrun) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use compute_force with pim")
      end if
      if (system%electrode_compute_force .and. system%aimrun) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use compute_force with aim")
      end if

      ! Electrodes should not move with piston if the charges are computed through maze or matrix inversion methods, or for 3D periodic systems
      if (system%pistonrun .and. algorithms%maze%calc_elec) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use piston with maze")
      end if
      if (system%pistonrun .and. algorithms%matrix_inversion%calc_elec) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use piston with matrix_inversion")
      end if
      if (system%pistonrun .and. system%num_pbc > 2) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use piston without 2DPBC")
      end if

      ! External fields are not compatible with Thomas-Fermi
      if ((system%field%field_type.eq.FIELD_TYPE_ELECTRIC      .or.  &
           system%field%field_type.eq.FIELD_TYPE_DISPLACEMENT) .and. &
           system%tfrun) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use external fields with Thomas-Fermi length > 0.0")
      end if

      ! NPT is not compatible with external fields
      if ((system%field%field_type.eq.FIELD_TYPE_ELECTRIC      .or.  &
           system%field%field_type.eq.FIELD_TYPE_DISPLACEMENT) .and. &
           (barostat_defined > 0)) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use NPT with external field")
      end if

      ! NPT is not compatible with electrodes and/or fixed atoms
      if ((electrodes_defined > 0)  .and. &
           (barostat_defined > 0)) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use NPT with electrodes")
      end if

      ! NPT is not compatible with 2D PBCs
      if ((system%num_pbc.ne.3) .and. &
           (barostat_defined > 0)) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use NPT with 2D PBCs")
      end if

      ! Do not compute the efg if its dump frequency is zero
      if (system%output%efg_dump_frequency.eq.0) then
         system%efgrun = .false.
      end if

      ! The efg computation is not compatible with electrodes
      if ((electrodes_defined > 0) .and. (system%efgrun)) then
         call MW_errors_runtime_error("read_system_parameters", "configuration.f90", &
               "cannot use EFG computation with 2D PBCs")
      end if

   end subroutine read_system_parameters

   ! ================================================================================
   ! Read box parameters
   !
   ! box
   !   length a b c
   subroutine read_box_parameters(system, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_box, only: MW_box_define_type => define_type, &
            MW_box_void_type => void_type
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< line number in configuration file

      ! Local
      ! -----
      real(wp) :: a, b, c
      character(256) :: errmsg
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      integer :: length_defined

      ! Void data structure
      call MW_box_void_type(system%box)

      ! Set default values
      a = 0.0_wp
      b = 0.0_wp
      c = 0.0_wp

      ! Flag each section as undefined (line number will be stored)
      length_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("length")
            if (length_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |length|. First definition at line ",i4,".")') &
                     line_num, length_defined
               call MW_errors_runtime_error("read_box_parameters", "configuration.f90", errmsg)
            end if
            length_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 4) then
               write(errmsg,'("Invalid format for box length parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_box_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, a)
            call MW_configuration_line_get_word(config_line, 3, b)
            call MW_configuration_line_get_word(config_line, 4, c)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      ! Define box
      call MW_box_define_type(system%box, a, b, c)

   end subroutine read_box_parameters

   ! ================================================================================
   ! Read thermostat parameters
   !
   ! thermostat *block*
   !   relaxation_time *real*
   !   max_iteration *int*
   !   tolerance *real*
   !   chain_length *int*
   subroutine read_thermostat_parameters(system, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp
      use MW_thermostat, only: MW_thermostat_t, &
            MW_thermostat_void_type => void_type, &
            MW_thermostat_define_type => define_type
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< line number in configuration file

      ! Local
      ! -----
      real(wp) :: relaxation_time, tolerance
      integer :: max_iteration, chain_length
      character(256) :: errmsg
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      integer :: relaxation_time_defined, tolerance_defined, &
            max_iteration_defined, chain_length_defined

      ! Void data structure
      call MW_thermostat_void_type(system%thermostat)

      ! Set default values
      relaxation_time = 4134.1_wp
      tolerance = 1.0e-15_wp
      max_iteration = 50
      chain_length = 5

      ! Flag each section as undefined (line number will be stored)
      relaxation_time_defined = 0
      tolerance_defined = 0
      max_iteration_defined = 0
      chain_length_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("relaxation_time")
            if (relaxation_time_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |relaxation_time|. First definition at line ",i4,".")') &
                     line_num, relaxation_time_defined
               call MW_errors_runtime_error("read_thermostat_parameters", "configuration.f90", errmsg)
            end if
            relaxation_time_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for relaxation_time parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_thermostat_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, relaxation_time)

         case("max_iteration")
            if (max_iteration_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |max_iteration|. First definition at line ",i4,".")') &
                     line_num, max_iteration_defined
               call MW_errors_runtime_error("read_thermostat_parameters", "configuration.f90", errmsg)
            end if
            max_iteration_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for max_iteration parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_thermostat_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, max_iteration)

         case("tolerance")
            if (tolerance_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |tolerance|. First definition at line ",i4,".")') &
                     line_num, tolerance_defined
               call MW_errors_runtime_error("read_thermostat_parameters", "configuration.f90", errmsg)
            end if
            tolerance_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for tolerance parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_thermostat_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, tolerance)

         case("chain_length")
            if (chain_length_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |chain_length|. First definition at line ",i4,".")') &
                     line_num, chain_length_defined
               call MW_errors_runtime_error("read_thermostat_parameters", "configuration.f90", errmsg)
            end if
            chain_length_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for chain_length parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_thermostat_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, chain_length)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      ! Define box
      call MW_thermostat_define_type(system%thermostat, system%temperature, relaxation_time, &
            max_iteration, tolerance, chain_length)

   end subroutine read_thermostat_parameters

   ! ================================================================================
   ! Read barostat parameters
   !
   ! barostat *block*
   !   pressure *real*
   !   relaxation_time *real*
   !   chain_length *int*
   subroutine read_barostat_parameters(system, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp
      use MW_thermostat, only: MW_thermostat_t, &
            MW_thermostat_void_type => void_type
      use MW_barostat, only: MW_barostat_t, &
            MW_barostat_void_type => void_type, &
            MW_barostat_define_type => define_type
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< line number in configuration file

      ! Local
      ! -----
      real(wp) :: relaxation_time
      real(wp) :: pressure
      integer :: chain_length
      character(256) :: errmsg
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      integer :: pressure_defined, relaxation_time_defined, chain_length_defined

      ! Void data structure
      call MW_barostat_void_type(system%barostat)

      ! Set default values
      pressure = 0.0_wp
      relaxation_time = 20670.5_wp
      chain_length = 5

      ! Flag each section as undefined (line number will be stored)
      pressure_defined = 0
      relaxation_time_defined = 0
      chain_length_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("pressure")
            if (pressure_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |pressure|. First definition at line ",i4,".")') &
                     line_num, pressure_defined
               call MW_errors_runtime_error("read_barostat_parameters", "configuration.f90", errmsg)
            end if
            pressure_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for pressure parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_barostat_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, pressure)

         case("relaxation_time")
            if (relaxation_time_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |relaxation_time|. First definition at line ",i4,".")') &
                     line_num, relaxation_time_defined
               call MW_errors_runtime_error("read_barostat_parameters", "configuration.f90", errmsg)
            end if
            relaxation_time_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for relaxation_time parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_barostat_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, relaxation_time)

         case("chain_length")
            if (chain_length_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |chain_length|. First definition at line ",i4,".")') &
                     line_num, chain_length_defined
               call MW_errors_runtime_error("read_barostat_parameters", "configuration.f90", errmsg)
            end if
            chain_length_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for chain_length parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_barostat_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, chain_length)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      ! Define box
      call MW_barostat_define_type(system%barostat, system%thermostat,  &
             pressure, relaxation_time, chain_length)

      ! Void thermostat data structure
      call MW_thermostat_void_type(system%thermostat)

   end subroutine read_barostat_parameters

   ! ================================================================================
   ! Read rescale parameters
   !
   ! velocity *block*
   !   create        *True/false*
   !   com_threshold *threshold*
   !   scale         *method* *period*
   subroutine read_velocity_parameters(system, funit, line_num)
      use MW_velocity, only: RESCALE_METHOD_GLOBAL, RESCALE_METHOD_SPECIES, &
            RESCALE_METHOD_MOLECULES_GLOBAL, RESCALE_METHOD_MOLECULES_INDEPENDENT
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< line number in configuration file

      ! Local
      ! -----
      character(256) :: errmsg
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      integer :: scale_defined, com_reset_defined, create_defined
      integer :: scale_period
      character(max_word_length) :: scale_method
      logical :: create
      real(wp) :: com_threshold

      ! Set default values
      scale_period = 100
      scale_method = "global"
      create = .false.
      com_threshold = 1.0e+6 ! very large so com is not reset

      ! Flag each section as undefined (line number will be stored)
      scale_defined = 0
      com_reset_defined = 0
      create_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("create")
            if (create_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |create|. First definition at line ",i4,".")') &
                     line_num, create_defined
               call MW_errors_runtime_error("read_velocity_parameters", "configuration.f90", errmsg)
            end if
            create_defined = line_num

            ! Read parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for create parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_velocity_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, create)

         case("com_threshold")
            if (com_reset_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |com_reset|. First definition at line ",i4,".")') &
                     line_num, com_reset_defined
               call MW_errors_runtime_error("read_velocity_parameters", "configuration.f90", errmsg)
            end if
            com_reset_defined = line_num

            ! Read parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for com_reset parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_velocity_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, com_threshold)

         case("scale")
            if (scale_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |scale|. First definition at line ",i4,".")') &
                     line_num, scale_defined
               call MW_errors_runtime_error("read_velocity_parameters", "configuration.f90", errmsg)
            end if
            scale_defined = line_num

            ! Read parameters
            if (config_line%num_words /= 3) then
               write(errmsg,'("Invalid format for scale parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_velocity_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, scale_method)
            call MW_configuration_line_get_word(config_line, 3, scale_period)

         case default
            ! Keyword not expected in velocity section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      if (scale_defined > 0) then
         select case(scale_method)
         case("global")
            system%velocity_scale_method = RESCALE_METHOD_GLOBAL
         case("species")
            system%velocity_scale_method = RESCALE_METHOD_SPECIES
         case("molecules_global")
            system%velocity_scale_method = RESCALE_METHOD_MOLECULES_GLOBAL
         case("molecules_independent")
            system%velocity_scale_method = RESCALE_METHOD_MOLECULES_INDEPENDENT
         case default
            write(errmsg,'("Invalid rescale method at line ",i4,": ",a32)') scale_defined, scale_method
            call MW_errors_runtime_error("read_velocity_parameters","configuration.f90", &
                  errmsg)
         end select
         system%velocity_scale = .true.
         system%velocity_scale_period = scale_period
         system%velocity_scale_temperature = system%temperature
      else
         system%velocity_scale = .false.
         system%velocity_scale_method = RESCALE_METHOD_GLOBAL
         system%velocity_scale_period = 0
         system%velocity_scale_temperature = system%temperature
      end if

      system%velocity_com_threshold = com_threshold
      system%velocity_create = create

   end subroutine read_velocity_parameters

   ! ================================================================================
   !> Read species block definition
   !!
   !! species *block list*
   !!   species_type *block*
   !!     name *string*
   !!     count *int*
   !!     mass  *real*
   !!     + charge neutral
   !!     + charge point q (*real*)
   !!     + charge gaussian eta (*real*) [q] (*real*)
   !!     polarizability  *real*
   !!     deformability D(*real*) beta(*real*) zeta(*real*)
   !!     mobile *bool*
   !!     fourth_site_atom
   !!     needs_efg *bool*
   !!
   subroutine read_species_parameters(species, algorithms, charged_species, four_site_model, funit, line_num, &
         dipoles_minimization_defined)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_cg, only: &
            MW_cg_set_max_iterations => set_max_iterations, &
            MW_cg_set_tolerance => set_tolerance, &
            MW_cg_set_charge_neutrality => set_charge_neutrality, &
            MW_cg_set_preconditioner => set_preconditioner, &
            MW_cg_reset_statistics => reset_statistics
      use MW_cgaim, only: &
            MW_cgaim_set_max_iterations => set_max_iterations, &
            MW_cgaim_set_tolerance => set_tolerance, &
            MW_cgaim_reset_statistics => reset_statistics
      use MW_maze, only: &
            MW_maze_set_max_iterations => set_max_iterations, &
            MW_maze_set_tolerance => set_tolerance, &
            MW_maze_select_algorithm => select_algorithm, &
            MW_maze_set_charge_neutrality => set_charge_neutrality, &
            MW_maze_set_write_matrix => set_write_matrix, &
            MW_maze_set_sor_param => set_sor_param, &
            MW_maze_reset_statistics => reset_statistics
      use MW_matrix_inversion, only: &
            MW_matrix_inversion_set_tolerance => set_tolerance, &
            MW_matrix_inversion_set_charge_neutrality => set_charge_neutrality, &
            MW_matrix_inversion_set_write_matrix => set_write_matrix, &
            MW_matrix_inversion_reset_statistics => reset_statistics
      use MW_errors, only: MW_errors_runtime_error => runtime_error, &
            MW_errors_allocate_error => allocate_error, &
            MW_errors_deallocate => deallocate_error
      use MW_species, only: MW_species_t, &
            MW_species_read_parameters => read_parameters, &
            MW_species_void_type => void_type
      use MW_algorithms, only: MW_algorithms_t
      implicit none
      ! Parameters
      ! ----------
      type(MW_algorithms_t), intent(inout) :: algorithms
      type(MW_species_t), allocatable, intent(inout) :: species(:)
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number
      logical, intent(inout) :: charged_species
      logical, intent(inout) :: four_site_model
      integer, intent(inout) :: dipoles_minimization_defined

      ! Local
      ! -----
      type(MW_configuration_line_t) :: config_line
      character(max_word_length) :: keyword
      integer :: ierr
      character(256) :: errmsg
      integer :: line_start
      integer :: line_last
      integer :: num_species_types, itype
      integer :: max_iteration, max_iterationaim, n_blocks
      real(wp) :: tolerance, toleranceaim, sor_param
      integer :: write_matrix_defined
      type(MW_species_t) :: dummy
      character(max_word_length) :: algorithm, algorithmaim, maze_algorithm, maze_cg_store, maze_cg_prec, cg_preconditioner
      logical :: charge_neutrality, write_matrix, cg_preconditioned

      ! keep line number to be able to come back at beginning of section
      line_start = line_num

      num_species_types = 0
      dipoles_minimization_defined = 0
      write_matrix_defined = 0

      ! Set default values
      algorithm = "cg"
      cg_preconditioned = .false.
      cg_preconditioner = "jacobi"
      algorithms%maze%calc_dip = .false.
      tolerance = 1.0e-12_wp
      max_iteration = 100
      n_blocks = 0
      sor_param = 1.0_wp
      maze_cg_store = "nostore"
      maze_cg_prec = "nopreconditioned"
      charge_neutrality = .false.
      write_matrix = .false.

      algorithmaim = "cg"
      toleranceaim = 1.0e-8_wp
      max_iterationaim = 100

      charged_species = .false.
      four_site_model = .false.

      ! Read once to get num_species_types
      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("species_type")
            call MW_species_read_parameters(dummy, funit, line_num)
            num_species_types = num_species_types + 1

         case("dipoles_minimization")
            if (dipoles_minimization_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |dipoles_minimization|. First definition at line ",i4,".")') &
                     line_num, dipoles_minimization_defined
               call MW_errors_runtime_error("read_species_parameters", "configuration.f90", errmsg)
            end if
            dipoles_minimization_defined = line_num

            if ((config_line%num_words < 2) .or. (config_line%num_words > 5)) then
               write(errmsg,'("Invalid format for algorithm parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, algorithm)
            select  case(algorithm)
               case("matrix_inversion")
                  if (config_line%num_words >= 3) then
                     call MW_configuration_line_get_word(config_line, 3, tolerance)
                  end if
               case("cg")
                  if (config_line%num_words >= 3) then
                     call MW_configuration_line_get_word(config_line, 3, tolerance)
                  end if
                  if (config_line%num_words >= 4) then
                     call MW_configuration_line_get_word(config_line, 4, max_iteration)
                  end if

               case("maze")
                  if (config_line%num_words >= 3) then
                     call MW_configuration_line_get_word(config_line, 3, maze_algorithm)
                  else
                     write(errmsg,'("Invalid format for maze algorithm at line ",i4)') line_num
                     call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                           errmsg)
                  end if
                  select case (maze_algorithm)
                     case("inversion")
                        if (config_line%num_words >= 4) then
                           call MW_configuration_line_get_word(config_line, 4, tolerance)
                        end if
                     case("iterative_shake")
                        if (config_line%num_words >= 4) then
                           call MW_configuration_line_get_word(config_line, 4, tolerance)
                        end if
                        if (config_line%num_words >= 5) then
                           call MW_configuration_line_get_word(config_line, 5, max_iteration)
                        end if
                        if (config_line%num_words >= 6) then
                           call MW_configuration_line_get_word(config_line, 6, n_blocks)
                        end if
                        if (config_line%num_words >= 7) then
                           call MW_configuration_line_get_word(config_line, 7, sor_param)
                        end if
                     case("iterative_cg")
                        if (config_line%num_words >= 4) then
                           call MW_configuration_line_get_word(config_line, 4, tolerance)
                        end if
                        if (config_line%num_words >= 5) then
                           call MW_configuration_line_get_word(config_line, 5, max_iteration)
                        end if
                        if (config_line%num_words >= 6) then
                           call MW_configuration_line_get_word(config_line, 6, maze_cg_store)
                        end if
                        if (config_line%num_words >= 7) then
                           call MW_configuration_line_get_word(config_line, 7, maze_cg_prec)
                        end if
                     case default
                        write(errmsg,'("Invalid keyword for maze algorithm at line ",i4)') line_num
                        call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                              errmsg)
                  end select
               case default
                  write(errmsg,'("Invalid keyword for dipoles_minimization algorithm at line ",i4)') line_num
                  call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                        errmsg)
            end select

         case("preconditioner")
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for |preconditioner| parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                     errmsg)
            end if
            cg_preconditioned = .true.

            if (algorithm /= "cg") then
               write(errmsg,'("Invalid |preconditioner| keyword for chosen algorithm at line ",i4)') line_num
               call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, cg_preconditioner)

         case("radius_minimization")

            if ((config_line%num_words < 2) .or. (config_line%num_words > 4)) then
               write(errmsg,'("Invalid format for algorithm parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, algorithmaim)
            if (config_line%num_words >= 3) then
               call MW_configuration_line_get_word(config_line, 3, toleranceaim)
            end if
            if (config_line%num_words >= 4) then
               call MW_configuration_line_get_word(config_line, 4, max_iterationaim)
            end if

         case("write_matrix")

            if (write_matrix_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |write_matrix|. First definition at line ",i4,".")') &
                     line_num, write_matrix_defined
               call MW_errors_runtime_error("read_species_parameters", "configuration.f90", errmsg)
            end if
            write_matrix_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for write_matrix parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, write_matrix)

         case default
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop
      line_last = line_num

      if (allocated(species)) then
         do itype = 1, size(species,1)
            call MW_species_void_type(species(itype))
         end do
         deallocate(species, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("read_species_parameters", "configuration.f90", ierr)
         end if
      end if
      allocate(species(num_species_types), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("read_species_parameters", "configuration.f90", ierr)
      end if

      do while (line_num > line_start)
         backspace(funit, iostat=ierr)
         if (ierr /= 0) then
            call MW_errors_runtime_error("read_species_parameters", "configuration.f90", &
                  "An error while backspace back to beginning of |species| section.")
         end if
         line_num = line_num-1
      end do

      ! Read a second time to store the values to get num_ion_types
      do itype = 1, num_species_types
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
         call MW_configuration_line_get_word(config_line, 1, keyword)
         if (keyword /= "species_type") then
            write(errmsg,'("Expected |species_type| keyword in species parameters at line ",i4)') line_num
            call MW_errors_runtime_error("read_species_parameters","configuration.f90", &
                  errmsg)
         end if
         call MW_species_read_parameters(species(itype), funit, line_num)
         if (species(itype)%charge > 0.0_wp) then
            charged_species = .true.
         end if
         if (species(itype)%is_fourth_site) then
            four_site_model = .true.
         end if
      end do
      ! move back to the end of the species block
      do while (line_num < line_last)
         read(funit,*)
         line_num = line_num + 1
      end do

      ! Define algorithm parameters
      select case (algorithm)
      case("cg")
         call MW_cg_set_max_iterations(algorithms%cg, max_iteration)
         call MW_cg_set_tolerance(algorithms%cg, tolerance)
         call MW_cg_set_charge_neutrality(algorithms%cg, charge_neutrality)
         call MW_cg_set_preconditioner(algorithms%cg, cg_preconditioned, cg_preconditioner)
         call MW_cg_reset_statistics(algorithms%cg)
      case("maze")
         algorithms%maze%calc_dip = .true.
         call MW_maze_set_max_iterations(algorithms%maze, max_iteration)
         call MW_maze_set_tolerance(algorithms%maze, tolerance)
         call MW_maze_select_algorithm(algorithms%maze, maze_algorithm, maze_cg_store, maze_cg_prec, n_blocks)
         call MW_maze_set_charge_neutrality(algorithms%maze, charge_neutrality, 1.0_wp)
         call MW_maze_set_write_matrix(algorithms%maze, write_matrix)
         call MW_maze_set_sor_param(algorithms%maze, sor_param)
         call MW_maze_reset_statistics(algorithms%maze)
      case("matrix_inversion")
         algorithms%matrix_inversion%calc_dip = .true.
         call MW_matrix_inversion_set_tolerance(algorithms%matrix_inversion, tolerance)
         call MW_matrix_inversion_set_charge_neutrality(algorithms%matrix_inversion, charge_neutrality)
         call MW_matrix_inversion_set_write_matrix(algorithms%matrix_inversion, write_matrix)
         call MW_matrix_inversion_reset_statistics(algorithms%matrix_inversion)
      case default
         write(errmsg,'("Unknown dipole minimization algorithm ",a)') &
               algorithm
         call MW_errors_runtime_error("read_dipoles_parameters","configuration.f90", &
               errmsg)
      end select
      select case (algorithmaim)
      case("cg")
         call MW_cgaim_set_max_iterations(algorithms%cgaim, max_iterationaim)
         call MW_cgaim_set_tolerance(algorithms%cgaim, toleranceaim)
         call MW_cgaim_reset_statistics(algorithms%cgaim)
      case default
         write(errmsg,'("Unknown radius minimization algorithm ",a)') &
               algorithmaim
         call MW_errors_runtime_error("read_radius_parameters","configuration.f90", &
               errmsg)
      end select

   end subroutine read_species_parameters

   ! ================================================================================
   ! Read molecules block parameters
   !
   ! molecules
   !   molecule_type *block*
   !     name *string*
   !     count *int*
   !     sites *string* [*string* ...]
   !     [constraints *block*
   !        constraint *string* *string* *real*
   !        [constraint *string* *string* *real*
   !         ...]
   !     [constraints_algorithm *string* *parameters depend on algorithm name*]
   !
   subroutine read_molecules_parameters(system, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ion, only: MW_ion_identify => identify
      use MW_molecule, only: MW_molecule_t, &
            MW_molecule_define_type => define_type, &
            MW_molecule_void_type => void_type, &
            MW_molecule_read_parameters => read_parameters
      use MW_errors, only: MW_errors_allocate_error => allocate_error, &
            MW_errors_deallocate_error => deallocate_error, &
            MW_errors_runtime_error => runtime_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      integer :: ierr
      character(256) :: errmsg
      type(MW_configuration_line_t) :: config_line
      character(max_word_length) :: keyword
      type(MW_molecule_t) :: dummy
      integer :: num_molecule_types
      integer :: imol, itype
      integer :: line_start
      integer :: harmonic_bonds_defined, harmonic_angles_defined, dihedrals_defined

      harmonic_bonds_defined = 0
      harmonic_angles_defined = 0
      dihedrals_defined = 0

      ! Void current values
      if (allocated(system%molecules)) then
         do imol = 1, size(system%molecules,1)
            call MW_molecule_void_type(system%molecules(imol))
         end do
         deallocate(system%molecules, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("read_molecules_parameters", "configuration.f90", ierr)
         end if
      end if
      num_molecule_types = 0
      line_start = line_num

      ! Read once to get num_molecule_types
      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)
         select case(keyword)

         case("molecule_type")
            call MW_molecule_read_parameters(dummy, funit, line_num)
            num_molecule_types = num_molecule_types + 1
            call MW_molecule_void_type(dummy)
         case default
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      system%num_molecule_types = num_molecule_types
      allocate(system%molecules(num_molecule_types), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("read_molecules_parameters", "configuration.f90", ierr)
      end if

      do while (line_num > line_start)
         backspace(funit, iostat=ierr)
         if (ierr /= 0) then
            call MW_errors_runtime_error("read_molecules_parameters", "configuration.f90", &
                  "An error while backspace back to beginning of |molecules| section.")
         end if
         line_num = line_num-1
      end do

      ! Read a second time to store the values
      do itype = 1, num_molecule_types
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
         call MW_configuration_line_get_word(config_line, 1, keyword)

         if (keyword /= "molecule_type") then
            write(errmsg,'("Expected |molecule_type| keyword in molecules parameters at line ",i4)') line_num
            call MW_errors_runtime_error("read_molecules_parameters","configuration.f90", &
                  errmsg)
         end if
         call MW_molecule_read_parameters(system%molecules(itype), funit, line_num)
      end do

   end subroutine read_molecules_parameters


   ! ================================================================================
   !> Read electrode parameters
   !! electrodes *block list*
   !!   electrode_type *block*
   !!     name *string*
   !!     species *string*
   !!     potential *real*
   !!     piston *real* *int*
   !!
   !!   electrode_charges *string* *real* *int*
   !!   charge_neutrality *true/false* *global/electrodes* *real*
   !!   compute_force *true/false*
   !!
   subroutine read_electrodes_parameters(electrodes, algorithms, funit, line_num, compute_force, &
         charge_neutrality, global_neutrality, charges_defined, charge_neutrality_defined)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_electrode_parameters, only: MW_electrode_parameters_t, &
            MW_electrode_parameters_void_type => void_type, &
            MW_electrode_parameters_read_parameters => read_parameters
      use MW_cg, only: &
            MW_cg_set_max_iterations => set_max_iterations, &
            MW_cg_set_tolerance => set_tolerance, &
            MW_cg_set_charge_neutrality => set_charge_neutrality, &
            MW_cg_set_preconditioner => set_preconditioner, &
            MW_cg_reset_statistics => reset_statistics
      use MW_maze, only: &
            MW_maze_set_max_iterations => set_max_iterations, &
            MW_maze_set_tolerance => set_tolerance, &
            MW_maze_select_algorithm => select_algorithm, &
            MW_maze_set_charge_neutrality => set_charge_neutrality, &
            MW_maze_set_write_matrix => set_write_matrix, &
            MW_maze_set_sor_param => set_sor_param, &
            MW_maze_reset_statistics => reset_statistics
      use MW_matrix_inversion, only: &
            MW_matrix_inversion_set_tolerance => set_tolerance, &
            MW_matrix_inversion_set_charge_neutrality => set_charge_neutrality, &
            MW_matrix_inversion_set_write_matrix => set_write_matrix, &
            MW_matrix_inversion_reset_statistics => reset_statistics
      use MW_kinds, only: wp
      use MW_errors, only: MW_errors_allocate_error => allocate_error, &
            MW_errors_deallocate_error => deallocate_error, &
            MW_errors_runtime_error => runtime_error
      use MW_algorithms, only: MW_algorithms_t
      implicit none
      ! Parameters
      ! ----------
      type(MW_electrode_parameters_t), allocatable, intent(inout) :: electrodes(:)
      type(MW_algorithms_t), intent(inout) :: algorithms
      integer,           intent(in)    :: funit !< configuration file unit
      integer,           intent(inout) :: line_num !< configuration file line number
      logical,           intent(inout) :: compute_force
      logical,           intent(out)   :: global_neutrality
      logical,           intent(out)   :: charge_neutrality
      integer,           intent(out)   :: charges_defined
      integer,           intent(out)   :: charge_neutrality_defined

      ! Locals
      ! ------
      character(256) :: errmsg
      character(max_word_length) :: keyword, global_or_electrodes
      type(MW_configuration_line_t) :: config_line
      integer :: ierr
      integer :: line_last
      integer :: num_elec, ielec
      character(max_word_length) :: algorithm, cg_preconditioner, maze_algorithm, maze_cg_store, maze_cg_prec
      integer :: max_iteration, n_blocks
      real(wp) :: tolerance, sor_param
      integer :: types_defined, preconditioner_defined
      integer :: compute_force_defined, write_matrix_defined
      type(MW_electrode_parameters_t) :: dummy
      logical :: write_matrix, cg_preconditioned, dummy_log
      real(wp) :: uscale
      ierr = 0

      ! Void current values
      if (allocated(electrodes)) then
         do ielec = 1, size(electrodes,1)
            call MW_electrode_parameters_void_type(electrodes(ielec))
         end do
         deallocate(electrodes, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("read_electrodes_parameters", "configuration.f90", ierr)
         end if
      end if
      num_elec = 0

      ! Flag each section as undefined (line number will be stored)
      types_defined = 0
      charges_defined = 0
      preconditioner_defined = 0
      charge_neutrality_defined = 0
      write_matrix_defined = 0
      compute_force_defined = 0

      ! Set default values
      algorithm = "cg"
      cg_preconditioned = .false.
      cg_preconditioner = "jacobi"
      algorithms%maze%calc_elec = .false.
      tolerance = 1.0e-12_wp
      max_iteration = 100
      n_blocks = 0
      sor_param = 1.0_wp
      maze_cg_store = "nostore"
      maze_cg_prec = "nopreconditioned"
      charge_neutrality = .true.
      global_neutrality = .false.
      global_or_electrodes = "global"
      uscale = 1.0_wp
      write_matrix = .false.
      compute_force = .false.

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case (keyword)

         case("electrode_type")
            if (types_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |electrode_type|. First definition at line ",i4,".")') &
                     line_num, types_defined
               call MW_errors_runtime_error("read_electrodes_parameters", "configuration.f90", errmsg)
            end if
            types_defined = line_num

            call MW_electrode_parameters_read_parameters(dummy, funit, line_num, dummy_log)
            num_elec = num_elec + 1

            call MW_configuration_line_seek_next_data(config_line, funit, line_num)
            keyword = ""
            if (config_line%num_words > 0) call MW_configuration_line_get_word(config_line, 1, keyword)
            do while (keyword == "electrode_type")
               call MW_electrode_parameters_read_parameters(dummy, funit, line_num, dummy_log)
               num_elec = num_elec + 1

               call MW_configuration_line_seek_next_data(config_line, funit, line_num)
               keyword = ""
               if (config_line%num_words > 0) call MW_configuration_line_get_word(config_line, 1, keyword)
               call MW_configuration_line_get_word(config_line, 1, keyword)
            end do

            ! Backspace 1 record
            backspace(funit)
            line_num = line_num - 1

         case("electrode_charges")
            if (charges_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |electrode_charges|. First definition at line ",i4,".")') &
                     line_num, charges_defined
               call MW_errors_runtime_error("read_electrodes_parameters", "configuration.f90", errmsg)
            end if
            charges_defined = line_num

            if ((config_line%num_words < 2) .or. (config_line%num_words > 7)) then
               write(errmsg,'("Invalid format for algorithm parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, algorithm)
            select  case(algorithm)
               case("constant_charge")
               case("matrix_inversion")
                  if (config_line%num_words >= 3) then
                     call MW_configuration_line_get_word(config_line, 3, tolerance)
                  end if
               case("cg")
                  if (config_line%num_words >= 3) then
                     call MW_configuration_line_get_word(config_line, 3, tolerance)
                  end if
                  if (config_line%num_words >= 4) then
                     call MW_configuration_line_get_word(config_line, 4, max_iteration)
                  end if

               case("maze")
                  if (config_line%num_words >= 3) then
                     call MW_configuration_line_get_word(config_line, 3, maze_algorithm)
                  else
                     write(errmsg,'("Invalid format for maze algorithm at line ",i4)') line_num
                     call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                           errmsg)
                  end if
                  select case (maze_algorithm)
                     case("inversion")
                        if (config_line%num_words >= 4) then
                           call MW_configuration_line_get_word(config_line, 4, tolerance)
                        end if
                     case("iterative_shake")
                        if (config_line%num_words >= 4) then
                           call MW_configuration_line_get_word(config_line, 4, tolerance)
                        end if
                        if (config_line%num_words >= 5) then
                           call MW_configuration_line_get_word(config_line, 5, max_iteration)
                        end if
                        if (config_line%num_words >= 6) then
                           call MW_configuration_line_get_word(config_line, 6, n_blocks)
                        end if
                        if (config_line%num_words >= 7) then
                           call MW_configuration_line_get_word(config_line, 7, sor_param)
                        end if
                     case("iterative_cg")
                        if (config_line%num_words >= 4) then
                           call MW_configuration_line_get_word(config_line, 4, tolerance)
                        end if
                        if (config_line%num_words >= 5) then
                           call MW_configuration_line_get_word(config_line, 5, max_iteration)
                        end if
                        if (config_line%num_words >= 6) then
                           call MW_configuration_line_get_word(config_line, 6, maze_cg_store)
                        end if
                        if (config_line%num_words >= 7) then
                           call MW_configuration_line_get_word(config_line, 7, maze_cg_prec)
                        end if
                     case default
                        write(errmsg,'("Invalid keyword for maze algorithm at line ",i4)') line_num
                        call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                              errmsg)
                  end select
               case default
                  write(errmsg,'("Invalid keyword for electrode_charges algorithm at line ",i4)') line_num
                  call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                        errmsg)
            end select

         case("preconditioner")
            if (preconditioner_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |preconditioner|. First definition at line ",i4,".")') &
                     line_num, preconditioner_defined
               call MW_errors_runtime_error("read_electrodes_parameters", "configuration.f90", errmsg)
            end if
            preconditioner_defined = line_num

            if (algorithm /= "cg") then
               write(errmsg,'("Invalid |preconditioner| keyword for chosen algorithm at line ",i4)') line_num
               call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                     errmsg)
            end if

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for |preconditioner| parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            cg_preconditioned = .true.
            call MW_configuration_line_get_word(config_line, 2, cg_preconditioner)

         case("charge_neutrality")
            if (charge_neutrality_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |charge_neutrality|. First definition at line ",i4,".")') &
                     line_num, charge_neutrality_defined
               call MW_errors_runtime_error("read_electrodes_parameters", "configuration.f90", errmsg)
            end if
            charge_neutrality_defined = line_num

            if ((config_line%num_words < 2) .or. (config_line%num_words > 4)) then
               write(errmsg,'("Invalid format for charge_neutrality parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, charge_neutrality)
            if (charge_neutrality) global_neutrality = .true.
            if (config_line%num_words >= 3) then
               call MW_configuration_line_get_word(config_line, 3, global_or_electrodes)
               select case (global_or_electrodes)
               case("global")
                  global_neutrality = .true.
               case("electrodes")
                  global_neutrality = .false.
               case default
                  write(errmsg,'("Invalid keyword for charge_neutrality parameters ",a," at line ",i4)') &
                        global_or_electrodes, line_num
                  call MW_errors_runtime_error("read_electrodes_parameters","configuration.f90", &
                        errmsg)
               end select
            end if
            if (config_line%num_words >= 4) then
               call MW_configuration_line_get_word(config_line, 4, uscale)
            end if

         case("write_matrix")
            if (write_matrix_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |write_matrix|. First definition at line ",i4,".")') &
                     line_num, write_matrix_defined
               call MW_errors_runtime_error("read_electrodes_parameters", "configuration.f90", errmsg)
            end if
            write_matrix_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for write_matrix parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, write_matrix)

         case("compute_force")
            if (compute_force_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |compute_force|. First definition at line ",i4,".")') &
                     line_num, compute_force_defined
               call MW_errors_runtime_error("read_electrodes_parameters", "configuration.f90", errmsg)
            end if
            compute_force_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for compute_force parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, compute_force)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop
      line_last = line_num

      ! Set up electrode types
      allocate(electrodes(num_elec), stat=ierr) ! allocate even if num_elec = 0!
      if (ierr /= 0) then
         call MW_errors_allocate_error("read_electrodes_parameters","configuration.f90", &
               ierr)
      end if
      if (num_elec > 0) then
         ! Read a second time to store the values
         do while (line_num >= types_defined)
            backspace(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_runtime_error("read_electrodes_parameters","configuration.f90", &
                     "An error while backspace back to beginning of |electrode| section.")
            end if
            line_num = line_num - 1
         end do

         do ielec = 1, num_elec

            call MW_configuration_line_seek_next_data(config_line, funit, line_num)
            call MW_configuration_line_get_word(config_line, 1, keyword)

            if (keyword /= "electrode_type") then
               write(errmsg,'("Expected |electrode_type| keyword in electrodes parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_electrodes_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_electrode_parameters_read_parameters(electrodes(ielec), funit, line_num, compute_force)
         end do

         ! move back to the end of the electrodes block
         do while (line_num < line_last)
            read(funit,*)
            line_num = line_num + 1
         end do
      end if

      ! Define algorithm parameters
      select case (algorithm)
      case("cg")
         call MW_cg_set_max_iterations(algorithms%cg, max_iteration)
         call MW_cg_set_tolerance(algorithms%cg, tolerance)
         call MW_cg_set_charge_neutrality(algorithms%cg, charge_neutrality)
         call MW_cg_set_preconditioner(algorithms%cg, cg_preconditioned, cg_preconditioner)
         call MW_cg_reset_statistics(algorithms%cg)
      case("constant_charge")
         algorithms%constant_charge = .true.
         if (charge_neutrality_defined>0 .and. charge_neutrality) then
            write(errmsg,'("Charge neutrality cannot be enforced for constant charge electrodes")')
            call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", errmsg)
         end if
         charge_neutrality = .false.
         global_neutrality = .false.
      case("maze")
         algorithms%maze%calc_elec =.true.
         algorithms%maze%constant_constr_grad =.true.
         call MW_maze_set_max_iterations(algorithms%maze, max_iteration)
         call MW_maze_set_tolerance(algorithms%maze, tolerance)
         call MW_maze_select_algorithm(algorithms%maze, maze_algorithm, maze_cg_store, maze_cg_prec, n_blocks)
         call MW_maze_set_charge_neutrality(algorithms%maze, charge_neutrality, uscale)
         call MW_maze_set_write_matrix(algorithms%maze, write_matrix)
         call MW_maze_set_sor_param(algorithms%maze, sor_param)
         call MW_maze_reset_statistics(algorithms%maze)
      case("matrix_inversion")
         algorithms%matrix_inversion%calc_elec =.true.
         algorithms%matrix_inversion%constant_matrix =.true.
         call MW_matrix_inversion_set_tolerance(algorithms%matrix_inversion, tolerance)
         call MW_matrix_inversion_set_charge_neutrality(algorithms%matrix_inversion, charge_neutrality)
         call MW_matrix_inversion_set_write_matrix(algorithms%matrix_inversion, write_matrix)
         call MW_matrix_inversion_reset_statistics(algorithms%matrix_inversion)
      case default
         write(errmsg,'("Unknown charge computation algorithm ",a," at line ",i4)') &
               algorithm, charges_defined
         call MW_errors_runtime_error("read_electrodes_parameters","configuration.f90", &
               errmsg)
      end select

   end subroutine read_electrodes_parameters

   ! ================================================================================
   !> Read dipoles and electrode parameters
   !! dipoles_and_electrodes *block list*
   !!
   !!   algorithm *string* *real* *int*
   !!   preconditioner *string*
   !!   charge_neutrality *true/false* *global/electrodes* *real*
   !!

   subroutine read_dipoles_electrodes_parameters(electrodes, algorithms, funit, line_num, &
         charge_neutrality, global_neutrality)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_electrode_parameters, only: MW_electrode_parameters_t
      use MW_cg, only: &
            MW_cg_set_max_iterations => set_max_iterations, &
            MW_cg_set_tolerance => set_tolerance, &
            MW_cg_set_charge_neutrality => set_charge_neutrality, &
            MW_cg_set_preconditioner => set_preconditioner, &
            MW_cg_reset_statistics => reset_statistics
      use MW_kinds, only: wp
      use MW_errors, only: MW_errors_allocate_error => allocate_error, &
            MW_errors_deallocate_error => deallocate_error, &
            MW_errors_runtime_error => runtime_error
      use MW_algorithms, only: MW_algorithms_t

      implicit none
      ! Parameters
      ! ----------
      type(MW_electrode_parameters_t), allocatable, intent(inout) :: electrodes(:)
      type(MW_algorithms_t), intent(inout) :: algorithms
      integer,           intent(in)    :: funit !< configuration file unit
      integer,           intent(inout) :: line_num !< configuration file line number
      logical,           intent(out)   :: global_neutrality
      logical,           intent(out)   :: charge_neutrality

      ! Locals
      ! ------
      character(256) :: errmsg
      character(max_word_length) :: keyword, global_or_electrodes
      type(MW_configuration_line_t) :: config_line
      integer :: ierr
      integer :: line_last
      integer :: num_elec
      character(max_word_length) :: algorithm, cg_preconditioner
      integer :: max_iteration
      real(wp) :: tolerance
      integer :: algorithm_defined, preconditioner_defined
      integer :: charge_neutrality_defined
      logical :: cg_preconditioned
      ierr = 0

      ! Void current values
      if (.not. allocated(electrodes)) then
         write(errmsg,'("|dipoles_and_electrodes| definition must be after |electrodes|")') &
               line_num, algorithm_defined
         call MW_errors_runtime_error("read_electrodes_parameters", "configuration.f90", errmsg)
      end if
      num_elec = size(electrodes,1)

      ! Flag each section as undefined (line number will be stored)
      algorithm_defined = 0
      preconditioner_defined = 0
      charge_neutrality_defined = 0

      ! Set default values
      algorithm = "cg"
      cg_preconditioned = .false.
      cg_preconditioner = "jacobi"
      algorithms%maze%calc_elec = .false.
      algorithms%maze%calc_dip = .false.
      tolerance = 1.0e-12_wp
      max_iteration = 100
      charge_neutrality = .true.
      global_neutrality = .false.
      global_or_electrodes = "global"

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case (keyword)

         case("algorithm")
            if (algorithm_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |algorithm|. First definition at line ",i4,".")') &
                     line_num, algorithm_defined
               call MW_errors_runtime_error("read_dipoles_electrodes_parameters", "configuration.f90", errmsg)
            end if
            algorithm_defined = line_num

            if ((config_line%num_words < 2) .or. (config_line%num_words > 7)) then
               write(errmsg,'("Invalid format for algorithm parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_dipoles_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, algorithm)
            select case(algorithm)
               case("cg_and_constant_charge")
               case("cg")
                  if (config_line%num_words >= 3) then
                     call MW_configuration_line_get_word(config_line, 3, tolerance)
                  end if
                  if (config_line%num_words >= 4) then
                     call MW_configuration_line_get_word(config_line, 4, max_iteration)
                  end if

               case default
                  write(errmsg,'("Invalid keyword for algorithm at line ",i4)') line_num
                  call MW_errors_runtime_error("read_dipoles_electrode_parameters","configuration.f90", &
                        errmsg)
            end select

         case("preconditioner")
            if (preconditioner_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |preconditioner|. First definition at line ",i4,".")') &
                     line_num, preconditioner_defined
               call MW_errors_runtime_error("read_dipoles_electrodes_parameters", "configuration.f90", errmsg)
            end if
            preconditioner_defined = line_num

            if (algorithm /= "cg" .and. algorithm /= "cg_and_constant_charge") then
               write(errmsg,'("Invalid |preconditioner| keyword for chosen algorithm at line ",i4)') line_num
               call MW_errors_runtime_error("read_dipoles_electrodes_parameters","configuration.f90", &
                     errmsg)
            end if

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for |preconditioner| parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_dipoles_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            cg_preconditioned = .true.
            call MW_configuration_line_get_word(config_line, 2, cg_preconditioner)

         case("charge_neutrality")
            if (charge_neutrality_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |charge_neutrality|. First definition at line ",i4,".")') &
                     line_num, charge_neutrality_defined
               call MW_errors_runtime_error("read_dipoles_electrodes_parameters", "configuration.f90", errmsg)
            end if
            charge_neutrality_defined = line_num

            if ((config_line%num_words < 2) .or. (config_line%num_words > 4)) then
               write(errmsg,'("Invalid format for charge_neutrality parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_dipoles_electrode_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, charge_neutrality)
            if (charge_neutrality) global_neutrality = .true.
            if (config_line%num_words >= 3) then
               call MW_configuration_line_get_word(config_line, 3, global_or_electrodes)
               select case (global_or_electrodes)
               case("global")
                  global_neutrality = .true.
               case("electrodes")
                  global_neutrality = .false.
               case default
                  write(errmsg,'("Invalid keyword for charge_neutrality parameters ",a," at line ",i4)') &
                        global_or_electrodes, line_num
                  call MW_errors_runtime_error("read_dipoles_electrodes_parameters","configuration.f90", &
                        errmsg)
               end select
            end if

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop
      line_last = line_num

      ! Define algorithm parameters
      select case (algorithm)
      case("cg")
         call MW_cg_set_max_iterations(algorithms%cg, max_iteration)
         call MW_cg_set_tolerance(algorithms%cg, tolerance)
         call MW_cg_set_charge_neutrality(algorithms%cg, charge_neutrality)
         call MW_cg_set_preconditioner(algorithms%cg, cg_preconditioned, cg_preconditioner)
         call MW_cg_reset_statistics(algorithms%cg)
      case("cg_and_constant_charge")
         algorithms%constant_charge = .true.
         if (charge_neutrality_defined>0 .and. charge_neutrality) then
            write(errmsg,'("Charge neutrality cannot be enforced for constant charge electrodes")')
            call MW_errors_runtime_error("read_electrode_parameters","configuration.f90", errmsg)
         end if
         charge_neutrality = .false.
         global_neutrality = .false.
         call MW_cg_set_max_iterations(algorithms%cg, max_iteration)
         call MW_cg_set_tolerance(algorithms%cg, tolerance)
         call MW_cg_set_charge_neutrality(algorithms%cg, charge_neutrality)
         call MW_cg_set_preconditioner(algorithms%cg, cg_preconditioned, cg_preconditioner)
         call MW_cg_reset_statistics(algorithms%cg)
      case default
         write(errmsg,'("Unknown dipoles and charge computation algorithm ",a," at line ",i4)') &
               algorithm, algorithm_defined
         call MW_errors_runtime_error("read_dipoles_electrodes_parameters","configuration.f90", &
               errmsg)
      end select

   end subroutine read_dipoles_electrodes_parameters

   ! ==========================================================================================
   ! Read interaction parameters
   ! interactions *block*
   !   coulomb *block*
   !     coulomb_rtol *real*
   !     coulomb_rcut *real*
   !     coulomb_ktol *real*
   !
   !   lennard-jones *block*
   !     lj_rcut *real*
   !     lj_pair *string* *string* *real* *real*
   !     [lj_pair *string* *string* *real* *real*]
   !
   !   fumi-tosi *block*
   !     ft_rcut *real*
   !     ft_pair *string* *string* *real* *real* *real* *real* *real* *real*
   !     [ft_pair *string* *string* *real* *real* *real* *real* *real* *real*]
   !
   !   xft *block*
   !     xft_rcut *real*
   !     xft_pair *string* *string* *real* *real* *real* *real* *int* *real* *real* *real* *real*
   !     [xft_pair *string* *string* *real* *real* *real* *real* *int* *real* *real* *real* *real*]
   !
   !   daim *block*
   !     daim_rcut *real*
   !     daim_pair *string* *string* *real* x10
   !     [daim_pair *string* *string* *real* x10]
   !
   !   damping *block*
   !     tt_pair *string* *string* *real* *int* *real*
   !     [tt_pair *string* *string* *real* *int* *real*]
   !
   !   steele *int*
   !     steele_pair *string* *string* *real* *real* *real*
   !     [steele_pair *string* *string* *real* *real* *real*]

   subroutine read_interactions_parameters(system, species, electrodes, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_species, only: MW_species_t
      use MW_electrode_parameters, only: MW_electrode_parameters_t

      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_species_t), intent(in) :: species(:)
      type(MW_electrode_parameters_t), allocatable :: electrodes(:)
      integer, intent(in) :: funit
      integer, intent(inout) :: line_num
      ! Locals
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      integer :: coulomb_defined, lj_defined, ft_defined, xft_defined
      integer :: tt_defined, daim_defined, steele_defined
      integer :: num_walls=0

      coulomb_defined = 0
      lj_defined = 0
      daim_defined = 0
      ft_defined = 0
      xft_defined = 0
      tt_defined = 0
      steele_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("coulomb")
            if (coulomb_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |coulomb|. First definition at line ",i4,".")') &
                     line_num, coulomb_defined
               call MW_errors_runtime_error("read_interactions_parameters", "configuration.f90", errmsg)
            end if
            coulomb_defined = line_num

            ! Read coulomb parameters
            call read_coulomb_parameters(system, funit, line_num)

         case("lennard-jones")
            if (lj_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |lennard-jones|. First definition at line ",i4,".")') &
                     line_num, lj_defined
               call MW_errors_runtime_error("read_interactions_parameters", "configuration.f90", errmsg)
            end if
            lj_defined = line_num

            ! Read lennard-jones parameters
            call read_lj_parameters(system, species, funit, line_num)

         case("fumi-tosi")
            if (ft_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |fumi-tosi|. First definition at line ",i4,".")') &
                     line_num, ft_defined
               call MW_errors_runtime_error("read_interactions_parameters", "configuration.f90", errmsg)
            end if
            ft_defined = line_num

            ! Read fumi-tosi parameters
            call read_ft_parameters(system, species, funit, line_num)

         case("xft")
            if (xft_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |xft|. First definition at line ",i4,".")') &
                     line_num, xft_defined
               call MW_errors_runtime_error("read_interactions_parameters", "configuration.f90", errmsg)
            end if
            xft_defined = line_num

            ! Read xft parameters
            call read_xft_parameters(system, species, funit, line_num)

         case("daim")
            if (daim_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |daim|. First definition at line ",i4,".")') &
                     line_num, daim_defined
               call MW_errors_runtime_error("read_interactions_parameters", "configuration.f90", errmsg)
            end if
            daim_defined = line_num

            ! Read daim parameters
            call read_daim_parameters(system, species, funit, line_num)

         case("damping")
            if (tt_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |damping|. First definition at line ",i4,".")') &
                     line_num, tt_defined
               call MW_errors_runtime_error("read_interactions_parameters", "configuration.f90", errmsg)
            end if
            tt_defined = line_num

            ! Read damping parameters
            call read_tt_parameters(system, species, funit, line_num)

         case("steele")
            if (steele_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |steele|. First definition at line ",i4,".")') &
                     line_num, steele_defined
               call MW_errors_runtime_error("read_interactions_parameters", "configuration.f90", errmsg)
            end if
            steele_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid |steele| specification at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_interactions_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, num_walls)

            ! Read steele parameters
            call read_steele_parameters(system, species, electrodes, num_walls, funit, line_num)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

   end subroutine read_interactions_parameters

   ! ================================================================================
   ! Read Ewald summation parameters for coulomb interactions
   !   coulomb *block*
   !     coulomb_rtol *real*
   !     coulomb_rcut *real*
   !     coulomb_ktol *real*
   !     ewald_zrange *real*
   !     ewald_zscale *real*
   subroutine read_coulomb_parameters(system, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ewald, only: MW_ewald_define_type => define_type, &
            MW_ewald_void_type => void_type, &
            MW_ewald_define_zscale => define_zscale
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      integer,           intent(in)    :: funit !< configuration file unit
      integer,           intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      real(wp) :: rtol, rcut, ktol, zrange, zweight
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      integer :: rtol_defined, rcut_defined, ktol_defined, zrange_defined, zweight_defined

      call MW_ewald_void_type(system%ewald)

      ! set default
      rtol = 1.0e-15
      ktol = 1.0e-15
      rcut = 0.0_wp
      zrange = 1.0_wp
      zweight = 1.0_wp

      ! Flag for each section definition
      rtol_defined = 0
      rcut_defined = 0
      ktol_defined = 0
      zrange_defined = 0
      zweight_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("coulomb_rtol")
            if (rtol_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |coulomb_rtol|. First definition at line ",i4,".")') &
                     line_num, rtol_defined
               call MW_errors_runtime_error("read_coulomb_parameters", "configuration.f90", errmsg)
            end if
            rtol_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid rtol specification for ewald parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_coulomb_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, rtol)

         case("coulomb_rcut")
            if (rcut_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |coulomb_rcut|. First definition at line ",i4,".")') &
                     line_num, rcut_defined
               call MW_errors_runtime_error("read_coulomb_parameters", "configuration.f90", errmsg)
            end if
            rcut_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid rcut specification for ewald parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_coulomb_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, rcut)

         case("coulomb_ktol")
            if (ktol_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |coulomb_ktol|. First definition at line ",i4,".")') &
                     line_num, ktol_defined
               call MW_errors_runtime_error("read_coulomb_parameters", "configuration.f90", errmsg)
            end if
            ktol_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid ktol specification for ewald parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_coulomb_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, ktol)

         case("ewald_zrange")
            if (zrange_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |ewald_zrange|. First definition at line ",i4,".")') &
                     line_num, zrange_defined
               call MW_errors_runtime_error("read_coulomb_parameters", "configuration.f90", errmsg)
            end if
            zrange_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid zrange specification for ewald parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_coulomb_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, zrange)

         case("ewald_zweight")
            if (zweight_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |ewald_zweight|. First definition at line ",i4,".")') &
                     line_num, zweight_defined
               call MW_errors_runtime_error("read_coulomb_parameters", "configuration.f90", errmsg)
            end if
            zweight_defined = line_num
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid zweight specification for ewald parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_coulomb_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, zweight)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      call MW_ewald_define_type(system%ewald, rtol, rcut, ktol)
      call MW_ewald_define_zscale(system%ewald, zrange, zweight)
   end subroutine read_coulomb_parameters

   ! ================================================================================
   ! Read lennard-jones potential parameters
   !
   !   lennard-jones *block*
   !     lj_rcut *real*
   !     lj_pair *string* *string* *real* *real*
   !     [lj_pair *string* *string* *real* *real*]
   subroutine read_lj_parameters(system, species, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ion, only: MW_ion_identify => identify
      use MW_electrode, only: MW_electrode_identify => identify
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp, PARTICLE_NAME_LEN
      use MW_lennard_jones, only: MW_lennard_jones_define_type => define_type, &
            MW_lennard_jones_void_type => void_type, &
            MW_lennard_jones_set_rcut => set_rcut, &
            MW_lennard_jones_set_parameters => set_parameters, &
            MW_lennard_jones_lorentz_berthelot_mixing_rule => lorentz_berthelot_mixing_rule
      use MW_species, only: MW_species_t, &
            MW_species_identify => identify
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_species_t), intent(in) :: species(:)
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      character(max_word_length) :: keyword, rule
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      character(PARTICLE_NAME_LEN) :: siteA, siteB
      integer :: itypeA, itypeB
      integer :: num_species
      real(wp) :: rcut, lj_eps, lj_sig
      integer :: rcut_defined

      num_species = size(species,1)

      call MW_lennard_jones_void_type(system%lj)
      call MW_lennard_jones_define_type(system%lj, num_species, 0.0_wp)
      rcut_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)
         case("lj_3D_tail_correction")
            system%lj%lj_3D_tail_correction=.true.
            if (system%num_PBC==2) then
              write(errmsg,'("At line ",i4,", setting 3D LJ correction with 2D PBC does not make sense")') &
               line_num
              call MW_errors_runtime_error("read_lj_parameters", "configuration.f90", errmsg)
              stop
            end if

         case("lj_rcut")

            if (rcut_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |lj_rcut|. First definition at line ",i4,".")') &
                     line_num, rcut_defined
               call MW_errors_runtime_error("read_lj_parameters", "configuration.f90", errmsg)
            end if
            rcut_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid rcut specification for lennard-jones parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_lj_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, rcut)
            call MW_lennard_jones_set_rcut(system%lj, rcut)

         case("lj_pair")
            if (config_line%num_words /= 5) then
               write(errmsg,'("Invalid pair specification for lennard-jones parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_lj_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, siteA)
            call MW_species_identify(siteA, species, itypeA)
            if (itypeA == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for lj parameters at line ",i4)') &
                     siteA, line_num
               call MW_errors_runtime_error("read_lj_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 3, siteB)
            call MW_species_identify(siteB, species, itypeB)
            if (itypeB == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for lj parameters at line ",i4)') &
                     siteB, line_num
               call MW_errors_runtime_error("read_lj_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 4, lj_eps)
            call MW_configuration_line_get_word(config_line, 5, lj_sig)

            call MW_lennard_jones_set_parameters(system%lj, itypeA, itypeB, lj_eps, lj_sig)

         case("lj_rule")
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid lj_rule specification for lennard-jones parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_lj_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, rule)
            select case(rule)
            case("Lorentz_Berthelot")
               call MW_lennard_jones_lorentz_berthelot_mixing_rule(system%lj)

            case default
               write(errmsg,'("Unknown lj_rule specification at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_lj_parameters","configuration.f90", &
                     errmsg)
            end select

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

   end subroutine read_lj_parameters


   ! ================================================================================
   ! Read fumi-tosi potential parameters
   !
   !   fumi-tosi *block*
   !     ft_rcut *real*
   !     ft_pair *string* *string* *real* *real* *real* *real* *real* *real*
   !     [ft_pair *string* *string* *real* *real* *real* *real* *real* *real*]
   subroutine read_ft_parameters(system, species, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ion, only: MW_ion_identify => identify
      use MW_electrode, only: MW_electrode_identify => identify
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp, PARTICLE_NAME_LEN
      use MW_fumi_tosi, only: MW_fumi_tosi_define_type => define_type, &
            MW_fumi_tosi_void_type => void_type, &
            MW_fumi_tosi_set_rcut => set_rcut, &
            MW_fumi_tosi_set_parameters => set_parameters, &
            MW_fumi_tosi_mixing_rule => mixing_rule
      use MW_species, only: MW_species_t, &
            MW_species_identify => identify
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_species_t), intent(in) :: species(:)
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      character(max_word_length) :: keyword, rule
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      character(PARTICLE_NAME_LEN) :: siteA, siteB
      integer :: itypeA, itypeB
      integer :: num_species
      real(wp) :: rcut, ft_eta, ft_B, ft_C, ft_D, ft_damp_dd, ft_damp_dq
      integer :: rcut_defined

      num_species = size(species,1)

      call MW_fumi_tosi_void_type(system%ft)
      call MW_fumi_tosi_define_type(system%ft, num_species, 0.0_wp)
      rcut_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)
         case("ft_3D_pressure_tail_correction")
            system%ft%ft_3D_tail_correction=.true.
            if (system%num_PBC==2) then
              write(errmsg,'("At line ",i4,", setting 3D FT correction with 2D PBC does not make sense")') &
               line_num
              call MW_errors_runtime_error("read_ft_parameters", "configuration.f90", errmsg)
              stop
            end if

         case("ft_rcut")

            if (rcut_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |ft_rcut|. First definition at line ",i4,".")') &
                     line_num, rcut_defined
               call MW_errors_runtime_error("read_ft_parameters", "configuration.f90", errmsg)
            end if
            rcut_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid rcut specification for fumi-tosi parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_ft_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, rcut)
            call MW_fumi_tosi_set_rcut(system%ft, rcut)

         case("ft_pair")

            if (config_line%num_words /= 9) then
               write(errmsg,'("Invalid pair specification for fumi-tosi parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_ft_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, siteA)
            call MW_species_identify(siteA, species, itypeA)
            if (itypeA == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for ft parameters at line ",i4)') &
                     siteA, line_num
               call MW_errors_runtime_error("read_ft_parameters","configuration.f90", &
                     errmsg)
            end if


            call MW_configuration_line_get_word(config_line, 3, siteB)
            call MW_species_identify(siteB, species, itypeB)
            if (itypeB == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for ft parameters at line ",i4)') &
                     siteB, line_num
               call MW_errors_runtime_error("read_ft_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 4, ft_eta)
            call MW_configuration_line_get_word(config_line, 5, ft_B)
            call MW_configuration_line_get_word(config_line, 6, ft_C)
            call MW_configuration_line_get_word(config_line, 7, ft_D)
            call MW_configuration_line_get_word(config_line, 8, ft_damp_dd)
            call MW_configuration_line_get_word(config_line, 9, ft_damp_dq)

            call MW_fumi_tosi_set_parameters(system%ft, itypeA, itypeB, ft_eta, ft_B, ft_C, ft_D, ft_damp_dd, ft_damp_dq)

         case("ft_rule")
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid ft_rule specification for fumi-tosi parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_ft_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, rule)
            select case(rule)
            case("Mixing")
               call MW_fumi_tosi_mixing_rule(system%ft)

            case default
               write(errmsg,'("Unknown ft_rule specification at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_ft_parameters","configuration.f90", &
                     errmsg)
            end select

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

   end subroutine read_ft_parameters

   ! ================================================================================
   ! Read xft potential parameters
   !
   !   xft *block*
   !     xft_rcut *real*
   !     xft_pair *string* *string* *real* *real* *real* *real* *int* *real* *real* *real* *real*
   !     [xft_pair *string* *string* *real* *real* *real* *real* *int* *real* *real* *real* *real*]
   subroutine read_xft_parameters(system, species, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ion, only: MW_ion_identify => identify
      use MW_electrode, only: MW_electrode_identify => identify
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp, PARTICLE_NAME_LEN
      use MW_xft, only: MW_xft_define_type => define_type, &
            MW_xft_void_type => void_type, &
            MW_xft_set_rcut => set_rcut, &
            MW_xft_set_parameters => set_parameters, &
            MW_xft_mixing_rule => mixing_rule
      use MW_species, only: MW_species_t, &
            MW_species_identify => identify
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_species_t), intent(in) :: species(:)
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      character(max_word_length) :: keyword, rule
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      character(PARTICLE_NAME_LEN) :: siteA, siteB
      integer :: itypeA, itypeB
      integer :: num_species
      integer :: xft_n
      real(wp) :: rcut, xft_eta, xft_B, xft_C, xft_D, xft_damp_dd, xft_damp_dq
      real(wp) :: xft_etax,xft_Bx
      integer :: rcut_defined

      num_species = size(species,1)

      call MW_xft_void_type(system%xft)
      call MW_xft_define_type(system%xft, num_species, 0.0_wp)
      rcut_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)
         case("xft_3D_pressure_tail_correction")
            system%xft%xft_3D_tail_correction=.true.
            if (system%num_PBC==2) then
              write(errmsg,'("At line ",i4,", setting 3D XFT correction with 2D PBC does not make sense")') &
               line_num
              call MW_errors_runtime_error("read_xft_parameters", "configuration.f90", errmsg)
              stop
            end if

         case("xft_rcut")

            if (rcut_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |xft_rcut|. First definition at line ",i4,".")') &
                     line_num, rcut_defined
               call MW_errors_runtime_error("read_xft_parameters", "configuration.f90", errmsg)
            end if
            rcut_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid rcut specification for xft parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_xft_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, rcut)
            call MW_xft_set_rcut(system%xft, rcut)

         case("xft_pair")

            if (config_line%num_words /= 12) then
               write(errmsg,'("Invalid pair specification for xft parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_xft_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, siteA)
            call MW_species_identify(siteA, species, itypeA)
            if (itypeA == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for xft parameters at line ",i4)') &
                     siteA, line_num
               call MW_errors_runtime_error("read_xft_parameters","configuration.f90", &
                     errmsg)
            end if


            call MW_configuration_line_get_word(config_line, 3, siteB)
            call MW_species_identify(siteB, species, itypeB)
            if (itypeB == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for xft parameters at line ",i4)') &
                     siteB, line_num
               call MW_errors_runtime_error("read_xft_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 4, xft_eta)
            call MW_configuration_line_get_word(config_line, 5, xft_B)
            call MW_configuration_line_get_word(config_line, 6, xft_n)
            call MW_configuration_line_get_word(config_line, 7, xft_etax)
            call MW_configuration_line_get_word(config_line, 8, xft_Bx)
            call MW_configuration_line_get_word(config_line, 9, xft_C)
            call MW_configuration_line_get_word(config_line, 10, xft_D)
            call MW_configuration_line_get_word(config_line, 11, xft_damp_dd)
            call MW_configuration_line_get_word(config_line, 12, xft_damp_dq)

            call MW_xft_set_parameters(system%xft, itypeA, itypeB, xft_eta, xft_B, &
                   xft_n, xft_etax, xft_Bx, xft_C, xft_D, xft_damp_dd, xft_damp_dq)

         case("xft_rule")
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid xft_rule specification for xft parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_xft_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, rule)
            select case(rule)
            case("Mixing")
               call MW_xft_mixing_rule(system%xft)

            case default
               write(errmsg,'("Unknown xft_rule specification at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_xft_parameters","configuration.f90", &
                     errmsg)
            end select

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

   end subroutine read_xft_parameters

   ! ================================================================================
   ! Read daim potential parameters
   !
   !   daim *block*
   !     daim_rcut *real*
   !     daim_pair *string* *string* *real* x10
   !     [daim_pair *string* *string* *real* x10]
   subroutine read_daim_parameters(system, species, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ion, only: MW_ion_identify => identify
      use MW_electrode, only: MW_electrode_identify => identify
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp, PARTICLE_NAME_LEN
      use MW_daim, only: MW_daim_define_type => define_type, &
            MW_daim_void_type => void_type, &
            MW_daim_set_rcut => set_rcut
      use MW_species, only: MW_species_t, &
            MW_species_identify => identify
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_species_t), intent(in) :: species(:)
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      character(PARTICLE_NAME_LEN) :: siteA, siteB
      integer :: itypeA, itypeB
      integer :: num_species
      real(wp) :: rcut
      real(wp) :: daim_eta1, daim_B1
      real(wp) :: daim_eta2, daim_B2
      real(wp) :: daim_eta3, daim_B3
      real(wp) :: daim_C, daim_D, daim_damp_dd, daim_damp_dq
      integer :: rcut_defined

      num_species = size(species,1)

      call MW_daim_void_type(system%daim)
      call MW_daim_define_type(system%daim, num_species, 0.0_wp)
      rcut_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)
         case("daim_3D_pressure_tail_correction")
            system%daim%daim_3D_tail_correction=.true.
            if (system%num_PBC==2) then
              write(errmsg,'("At line ",i4,", setting 3D DAIM correction with 2D PBC does not make sense")') &
               line_num
              call MW_errors_runtime_error("read_daim_parameters", "configuration.f90", errmsg)
              stop
            end if

         case("daim_rcut")

            if (rcut_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |daim_rcut|. First definition at line ",i4,".")') &
                     line_num, rcut_defined
               call MW_errors_runtime_error("read_daim_parameters", "configuration.f90", errmsg)
            end if
            rcut_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid rcut specification for daim parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_daim_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, rcut)
            call MW_daim_set_rcut(system%daim, rcut)

         case("daim_pair")

            if (config_line%num_words /= 13) then
               write(errmsg,'("Invalid pair specification for daim parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_daim_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, siteA)
            call MW_species_identify(siteA, species, itypeA)
            if (itypeA == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for daim parameters at line ",i4)') &
                     siteA, line_num
               call MW_errors_runtime_error("read_daim_parameters","configuration.f90", &
                     errmsg)
            end if


            call MW_configuration_line_get_word(config_line, 3, siteB)
            call MW_species_identify(siteB, species, itypeB)
            if (itypeB == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for daim parameters at line ",i4)') &
                     siteB, line_num
               call MW_errors_runtime_error("read_daim_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 4, daim_eta1)
            call MW_configuration_line_get_word(config_line, 5, daim_eta2)
            call MW_configuration_line_get_word(config_line, 6, daim_eta3)
            call MW_configuration_line_get_word(config_line, 7, daim_B1)
            call MW_configuration_line_get_word(config_line, 8, daim_B2)
            call MW_configuration_line_get_word(config_line, 9, daim_B3)
            call MW_configuration_line_get_word(config_line, 10, daim_C)
            call MW_configuration_line_get_word(config_line, 11, daim_D)
            call MW_configuration_line_get_word(config_line, 12, daim_damp_dd)
            call MW_configuration_line_get_word(config_line, 13, daim_damp_dq)


            system%daim%eta1(itypeA,itypeB) = daim_eta1
            system%daim%eta1(itypeB,itypeA) = daim_eta1

            system%daim%eta2(itypeA,itypeB) = daim_eta2
            system%daim%eta2(itypeB,itypeA) = daim_eta2

            system%daim%eta3(itypeA,itypeB) = daim_eta3
            system%daim%eta3(itypeB,itypeA) = daim_eta3

            system%daim%B1(itypeA,itypeB) = daim_B1
            system%daim%B1(itypeB,itypeA) = daim_B1

            system%daim%B2(itypeA,itypeB) = daim_B2
            system%daim%B2(itypeB,itypeA) = daim_B2

            system%daim%B3(itypeA,itypeB) = daim_B3
            system%daim%B3(itypeB,itypeA) = daim_B3

            system%daim%C(itypeA,itypeB) = daim_C
            system%daim%C(itypeB,itypeA) = daim_C

            system%daim%D(itypeA,itypeB) = daim_D
            system%daim%D(itypeB,itypeA) = daim_D

            system%daim%damp_dd(itypeA,itypeB) = daim_damp_dd
            system%daim%damp_dd(itypeB,itypeA) = daim_damp_dd

            system%daim%damp_dq(itypeA,itypeB) = daim_damp_dq
            system%daim%damp_dq(itypeB,itypeA) = daim_damp_dq

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

   end subroutine read_daim_parameters

   ! ================================================================================
   ! Read damping potential parameters (tt stands for Tang-Toennies)
   !
   !   damping *block*
   !     tt_pair *string* *string* *real* *int* *real*
   !     [tt_pair *string* *string* *real* *int* *real*]
   !     first atom is the charge that damps, second one carries the dipole which is damped
   subroutine read_tt_parameters(system, species, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ion, only: MW_ion_identify => identify
      use MW_electrode, only: MW_electrode_identify => identify
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp, PARTICLE_NAME_LEN
      use MW_damping, only: MW_damping_define_type => define_type, &
            MW_damping_void_type => void_type
      use MW_species, only: MW_species_t, &
            MW_species_identify => identify
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_species_t), intent(in) :: species(:)
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      character(PARTICLE_NAME_LEN) :: siteA, siteB
      integer :: itypeA, itypeB
      integer :: num_species
      real(wp) :: tt_b, tt_c, tt_order

      num_species = size(species,1)

      call MW_damping_void_type(system%tt)
      call MW_damping_define_type(system%tt, num_species)

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)
         case("tt_pair")

            if (config_line%num_words /= 6) then
               write(errmsg,'("Invalid pair specification for damping parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_tt_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, siteA)
            call MW_species_identify(siteA, species, itypeA)
            if (itypeA == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for damping parameters at line ",i4)') &
                     siteA, line_num
               call MW_errors_runtime_error("read_tt_parameters","configuration.f90", &
                     errmsg)
            end if


            call MW_configuration_line_get_word(config_line, 3, siteB)
            call MW_species_identify(siteB, species, itypeB)
            if (itypeB == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for damping parameters at line ",i4)') &
                     siteB, line_num
               call MW_errors_runtime_error("read_tt_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 4, tt_b)
            call MW_configuration_line_get_word(config_line, 5, tt_order)
            call MW_configuration_line_get_word(config_line, 6, tt_c)

            system%tt%b(itypeA,itypeB) = tt_b

            if (tt_order/int(tt_order) == 1.0_wp) then
               system%tt%order(itypeA,itypeB) = int(tt_order)
            else
               write(errmsg,'("Tang-Tonnies order for pair ",a,"-",a," should be integer at line ",i4)') &
                     siteA, siteB, line_num
               call MW_errors_runtime_error("read_tt_parameters","configuration.f90", &
                     errmsg)
            end if

            system%tt%c(itypeA,itypeB) = tt_c

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

   end subroutine read_tt_parameters

   ! ================================================================================
   ! Read steele potential parameters
   !
   !   steele *block*
   !     steele_rcut *real*
   !     wall *string* *string* *real* *real* *real* *real*
   !     [wall *string* *string* *real* *real* *real* *real*]
   subroutine read_steele_parameters(system, species, electrodes, num_walls, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_ion, only: MW_ion_identify => identify
      use MW_electrode, only: MW_electrode_identify => identify
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_kinds, only: wp, PARTICLE_NAME_LEN
      use MW_steele, only: MW_steele_define_type => define_type, &
            MW_steele_void_type => void_type, &
            MW_steele_set_rcut => set_rcut, &
            MW_steele_set_parameters => set_parameters
      use MW_species, only: MW_species_t, &
            MW_species_identify => identify
      use MW_electrode_parameters, only: MW_electrode_parameters_t
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      type(MW_species_t), intent(in) :: species(:)
      type(MW_electrode_parameters_t), allocatable :: electrodes(:)
      integer, intent(in) :: num_walls
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< configuration file line number

      ! Local
      ! -----
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      character(PARTICLE_NAME_LEN) :: siteA, siteB
      integer :: iwallA, itypeB, i
      integer :: num_species
      real(wp) :: rcut, rho_w, eps_wf, sig_wf, delta_w
      integer :: rcut_defined

      num_species = size(species,1)

      call MW_steele_void_type(system%steele)
      call MW_steele_define_type(system%steele, num_walls, num_species, 0.0_wp)
      rcut_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)
         case("steele_rcut")

            if (rcut_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |steele_rcut|. First definition at line ",i4,".")') &
                     line_num, rcut_defined
               call MW_errors_runtime_error("read_steele_parameters", "configuration.f90", errmsg)
            end if
            rcut_defined = line_num

            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid rcut specification for steele parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_steele_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, rcut)
            call MW_steele_set_rcut(system%steele, rcut)

         case("wall")
            if (config_line%num_words /= 7) then
               write(errmsg,'("Invalid pair specification for steele parameters at line ",i4)') &
                     line_num
               call MW_errors_runtime_error("read_steele_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 2, siteA)

            iwallA = 0
            do i = 1, size(electrodes, 1)
               if (siteA == electrodes(i)%name) then
                  iwallA = i
                  exit
               end if
            end do

            if (iwallA == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for steele parameters at line ",i4)') &
                     siteA, line_num
               call MW_errors_runtime_error("read_steele_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 3, siteB)
            call MW_species_identify(siteB, species, itypeB)
            if (itypeB == 0) then
               write(errmsg,'("Unknown particle type ",a," in pair specification for steele parameters at line ",i4)') &
                     siteB, line_num
               call MW_errors_runtime_error("read_steele_parameters","configuration.f90", &
                     errmsg)
            end if

            call MW_configuration_line_get_word(config_line, 4, rho_w)
            call MW_configuration_line_get_word(config_line, 5, eps_wf)
            call MW_configuration_line_get_word(config_line, 6, sig_wf)
            call MW_configuration_line_get_word(config_line, 7, delta_w)

            call MW_steele_set_parameters(system%steele, iwallA, itypeB, rho_w, eps_wf, sig_wf, delta_w)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

   end subroutine read_steele_parameters
   ! ================================================================================
   ! Read output parameters
   !
   ! output *block*
   !   default *int*
   !   trajectories *int*
   !   xyz *int*
   !   pdb *int*
   !   lammps *int*
   !   charges *int*
   !   forces *int*
   !   dipoles *int*
   !   radius *int*
   !   restart *int* (alternate)
   !   step    *int*
   !   total_charges *int*
   !   density *int* *int* (*real* *real*)
   subroutine read_output_parameters(system, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_output, only: &
            MW_output_trajectories_set_dump_frequency => trajectories_set_dump_frequency, &
            MW_output_xyz_set_dump_frequency => xyz_set_dump_frequency, &
            MW_output_pdb_set_dump_frequency => pdb_set_dump_frequency, &
            MW_output_lammps_set_dump_frequency => lammps_set_dump_frequency, &
            MW_output_charges_set_dump_frequency => charges_set_dump_frequency, &
            MW_output_forces_set_dump_frequency => forces_set_dump_frequency, &
            MW_output_elec_forces_set_dump_frequency => elec_forces_set_dump_frequency, &
            MW_output_dipoles_set_dump_frequency => dipoles_set_dump_frequency, &
            MW_output_efg_set_dump_frequency => efg_set_dump_frequency, &
            MW_output_radius_set_dump_frequency => radius_set_dump_frequency, &
            MW_output_potential_shift_set_dump_frequency => potential_shift_set_dump_frequency, &
            MW_output_energies_set_dump_frequency => energies_set_dump_frequency, &
            MW_output_restart_set_dump_frequency => restart_set_dump_frequency, &
            MW_output_temperature_set_dump_frequency => temperature_set_dump_frequency, &
            MW_output_pressure_set_dump_frequency => pressure_set_dump_frequency, &
            MW_output_stress_tensor_set_dump_frequency => stress_tensor_set_dump_frequency, &
            MW_output_box_set_dump_frequency => box_set_dump_frequency, &
            MW_output_step_set_dump_frequency => step_set_dump_frequency, &
            MW_output_total_charges_set_dump_frequency => total_charges_set_dump_frequency, &
            MW_output_polarization_set_dump_frequency => polarization_set_dump_frequency, &
            MW_output_diagnostic_set_dump_frequency => diagnostic_set_dump_frequency
      use MW_diagnostic, only: MW_diagnostic_set_num_bins => set_num_bins
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: system
      integer, intent(in) :: funit
      integer, intent(inout) :: line_num

      ! Local
      ! -----
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      character(256) :: errmsg
      character(max_word_length) :: alternate, unwrap
      integer :: default, trajectories, xyz, pdb, lammps, charges, forces, elec_forces, &
            dipoles, efg, radius, potential_shift, energies, &
            restart, temperature, pressure, stress_tensor, box, &
            step, total_charges, density, density_num_bins, polarization
      integer :: default_defined, trajectories_defined, xyz_defined, pdb_defined, lammps_defined, &
            charges_defined, forces_defined, elec_forces_defined, dipoles_defined, efg_defined, &
            radius_defined, potential_shift_defined, energies_defined, restart_defined, &
            temperature_defined, pressure_defined, stress_tensor_defined, box_defined, &
            step_defined, total_charges_defined, density_defined, &
            polarization_defined
      integer :: term
      logical :: density_limits
      real(wp) :: density_min, density_max

      ! Set default values
      default = 0
      trajectories = 0
      xyz = 0
      pdb = 0
      lammps = 0
      charges = 0
      forces = 0
      elec_forces = 0
      dipoles = 0
      efg = 0
      radius = 0
      potential_shift = 0
      energies = 0
      restart = 0
      temperature = 0
      pressure = 0
      stress_tensor = 0
      box = 0
      step = 0
      total_charges = 0
      polarization = 0
      density = 0
      density_num_bins = 1
      density_limits = .false.
      density_min = 0.0_wp
      density_max = 0.0_wp

      ! Flag each section as undefined (line number will be stored)
      default_defined = 0
      trajectories_defined = 0
      xyz_defined = 0
      pdb_defined = 0
      lammps_defined = 0
      charges_defined = 0
      forces_defined = 0
      elec_forces_defined = 0
      dipoles_defined = 0
      efg_defined = 0
      radius_defined = 0
      potential_shift_defined = 0
      energies_defined = 0
      restart_defined = 0
      temperature_defined = 0
      pressure_defined = 0
      stress_tensor_defined = 0
      box_defined = 0
      step_defined = 0
      total_charges_defined = 0
      polarization_defined =0
      density_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("default")
            if (default_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |default|. First definition at line ",i4,".")') &
                     line_num, default_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            default_defined = line_num

            ! Read default parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for default parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, default)

         case("trajectories")
            if (trajectories_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |trajectories|. First definition at line ",i4,".")') &
                     line_num, trajectories_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            trajectories_defined = line_num

            ! Read trajectories parameters
            if (config_line%num_words /= 2 .and. config_line%num_words /= 3) then
               write(errmsg,'("Invalid format for trajectories parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, trajectories)
            if (config_line%num_words == 3) then
               call MW_configuration_line_get_word(config_line, 3, unwrap)
               if (unwrap == "unwrap") then
                  system%trajectories_unwrapped = 1
               else
                  write(errmsg,'("Invalid keyword for trajectories parameters at line ",i4)') line_num
                  call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                        errmsg)
               end if
            end if

         case("xyz")
            if (xyz_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |xyz|. First definition at line ",i4,".")') &
                     line_num, xyz_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            xyz_defined = line_num

            ! Read xyz parameters
            if (config_line%num_words /= 2 .and. config_line%num_words /= 3) then
               write(errmsg,'("Invalid format for xyz parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, xyz)
            if (config_line%num_words == 3) then
               call MW_configuration_line_get_word(config_line, 3, unwrap)
               if (unwrap == "unwrap") then
                  system%xyz_unwrapped = 1
               else
                  write(errmsg,'("Invalid keyword for xyz parameters at line ",i4)') line_num
                  call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                        errmsg)
               end if
            end if

         case("pdb")
            if (pdb_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |pdb|. First definition at line ",i4,".")') &
                     line_num, pdb_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            pdb_defined = line_num

            ! Read pdb parameters
            if (config_line%num_words /= 2 .and. config_line%num_words /= 3) then
               write(errmsg,'("Invalid format for pdb parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, pdb)
            if (config_line%num_words == 3) then
               call MW_configuration_line_get_word(config_line, 3, unwrap)
               if (unwrap == "unwrap") then
                  system%pdb_unwrapped = 1
               else
                  write(errmsg,'("Invalid keyword for pdb parameters at line ",i4)') line_num
                  call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                        errmsg)
               end if
            end if

         case("lammps")
            if (lammps_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |lammps|. First definition at line ",i4,".")') &
                     line_num, lammps_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            lammps_defined = line_num

            ! Read lammps parameters
            if (config_line%num_words /= 2 .and. config_line%num_words /= 3) then
               write(errmsg,'("Invalid format for lammps parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, lammps)
            if (config_line%num_words == 3) then
               call MW_configuration_line_get_word(config_line, 3, unwrap)
               if (unwrap == "unwrap") then
                  system%lammps_unwrapped = 1
               else
                  write(errmsg,'("Invalid keyword for lammps parameters at line ",i4)') line_num
                  call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                        errmsg)
               end if
            end if

         case("temperature")
            if (temperature_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |temperature|. First definition at line ",i4,".")') &
                     line_num, temperature_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            temperature_defined = line_num

            ! Read temperature parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for temperature parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, temperature)

         case("pressure")
            if (pressure_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |pressure|. First definition at line ",i4,".")') &
                     line_num, pressure_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            pressure_defined = line_num

            ! Read pressure parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for pressure parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, pressure)

         case("stress_tensor")
            if (stress_tensor_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |stress_tensor|. First definition at line ",i4,".")') &
                     line_num, stress_tensor_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            stress_tensor_defined = line_num

            ! Read stress_tensor parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for stress_tensor parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, stress_tensor)

         case("box_parameters")
            if (box_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |box_parameters|. First definition at line ",i4,".")') &
                     line_num, box_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            box_defined = line_num

            ! Read pressure parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for pressure parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, box)

         case("charges")
            if (charges_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |charges|. First definition at line ",i4,".")') &
                     line_num, charges_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            charges_defined = line_num

            ! Read charges parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for charges parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, charges)

         case("total_charges")
            if (total_charges_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |total_charges|. First definition at line ",i4,".")') &
                     line_num, total_charges_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            total_charges_defined = line_num

            ! Read total_charges parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for total_charges parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, total_charges)

         case("forces")
            if (forces_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |forces|. First definition at line ",i4,".")') &
                     line_num, forces_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            forces_defined = line_num

            ! Read forces parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for forces parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, forces)

         case("elec_forces")
            if (elec_forces_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |elec_forces|. First definition at line ",i4,".")') &
                     line_num, elec_forces_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            elec_forces_defined = line_num

            ! Read elec_forces parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for elec_forces parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, elec_forces)

         case("dipoles")
            if (dipoles_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |dipoles|. First definition at line ",i4,".")') &
                     line_num, dipoles_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            dipoles_defined = line_num

            ! Read dipoles parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for dipoles parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, dipoles)

         case("efg")
            if (efg_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |efg|. First definition at line ",i4,".")') &
                     line_num, efg_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            efg_defined = line_num

            ! Read dipoles parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for efg parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, efg)

         case("radius")
            if (radius_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |radius|. First definition at line ",i4,".")') &
                     line_num, radius_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            radius_defined = line_num

            ! Read radius parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for dipoles parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, radius)

         case("potential_shift")
            if (potential_shift_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |potential_shift|. First definition at line ",i4,".")') &
                     line_num, potential_shift_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            potential_shift_defined = line_num

            ! Read potential_shift parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for potential shift parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, potential_shift)

         case("energies")
            if (energies_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |energies|. First definition at line ",i4,".")') &
                     line_num, energies_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            energies_defined = line_num

            ! Read energies parameters
            if (config_line%num_words < 2) then
               write(errmsg,'("Invalid format for energies parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, energies)

            if (config_line%num_words > 2) then
               system%energy_terms_to_dump = config_line%num_words - 2
               if (system%energy_terms_to_dump > size(system%energies_to_dump)) then
                  call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                        errmsg)
               end if

               do term = 1, system%energy_terms_to_dump
                  call MW_configuration_line_get_word(config_line, 2+term, system%energies_to_dump(term))
               end do
            end if

         case("restart")
            if (restart_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |restart|. First definition at line ",i4,".")') &
                     line_num, restart_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            restart_defined = line_num

            ! Read restart parameters
            if (config_line%num_words /= 2 .and. config_line%num_words /= 3) then
               write(errmsg,'("Invalid format for restart parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, restart)
            if (config_line%num_words == 3) then
               call MW_configuration_line_get_word(config_line, 3, alternate)
               if (alternate == "alternate") then
                  system%alternate_restart = 1
               else
                  write(errmsg,'("Invalid keyword for restart parameters at line ",i4)') line_num
                  call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                        errmsg)
               end if
            end if

         case("step")
            if (step_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |step|. First definition at line ",i4,".")') &
                     line_num, step_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            step_defined = line_num

            ! Read step parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for step parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, step)

         case("polarization")
            if (polarization_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |polarization|. First definition at line ",i4,".")') &
                     line_num, polarization_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            polarization_defined = line_num

            ! Read polarization parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for forces parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, polarization)

         case("density")
            if (density_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |density|. First definition at line ",i4,".")') &
                     line_num, density_defined
               call MW_errors_runtime_error("read_output_parameters", "configuration.f90", errmsg)
            end if
            density_defined = line_num

            ! Read density parameters
            if (config_line%num_words /= 3 .and. config_line%num_words /= 5) then
               write(errmsg,'("Invalid format for density parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_output_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, density_num_bins)
            call MW_configuration_line_get_word(config_line, 3, density)
            if (config_line%num_words == 5) then
               density_limits = .true.
               call MW_configuration_line_get_word(config_line, 4, density_min)
               call MW_configuration_line_get_word(config_line, 5, density_max)
            end if

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)

      end do keywordLoop

      ! Set default value if not defined
      if (trajectories_defined == 0) trajectories = default
      call MW_output_trajectories_set_dump_frequency(system%output, trajectories)

      if (xyz_defined == 0) xyz = default
      call MW_output_xyz_set_dump_frequency(system%output, xyz)

      if (pdb_defined == 0) pdb = default
      call MW_output_pdb_set_dump_frequency(system%output, pdb)

      if (lammps_defined == 0) lammps = default
      call MW_output_lammps_set_dump_frequency(system%output, lammps)

      if (charges_defined == 0) charges = default
      call MW_output_charges_set_dump_frequency(system%output, charges)

      if (total_charges_defined == 0) total_charges = default
      call MW_output_total_charges_set_dump_frequency(system%output, total_charges)

      if (forces_defined == 0) forces = default
      call MW_output_forces_set_dump_frequency(system%output, forces)

      if (elec_forces_defined == 0) elec_forces = default
      call MW_output_elec_forces_set_dump_frequency(system%output, elec_forces)

      if (dipoles_defined == 0) dipoles = default
      call MW_output_dipoles_set_dump_frequency(system%output, dipoles)

      if (efg_defined == 0) efg = default
      call MW_output_efg_set_dump_frequency(system%output, efg)

      if (radius_defined == 0) radius = default
      call MW_output_radius_set_dump_frequency(system%output, radius)

      if (potential_shift_defined == 0) potential_shift = default
      call MW_output_potential_shift_set_dump_frequency(system%output, potential_shift)

      if (energies_defined == 0) energies = default
      call MW_output_energies_set_dump_frequency(system%output, energies)

      if (restart_defined == 0) restart = default
      call MW_output_restart_set_dump_frequency(system%output, restart)

      if (temperature_defined == 0) temperature = default
      call MW_output_temperature_set_dump_frequency(system%output, temperature)

      if (pressure_defined == 0) pressure = default
      call MW_output_pressure_set_dump_frequency(system%output, pressure)

      if (stress_tensor_defined == 0) stress_tensor = default
      call MW_output_stress_tensor_set_dump_frequency(system%output, stress_tensor)

      if (box_defined == 0) box = default
      call MW_output_box_set_dump_frequency(system%output, box)

      if (step_defined == 0) step = default
      call MW_output_step_set_dump_frequency(system%output, step)

      if (polarization_defined == 0) polarization = default
      call MW_output_polarization_set_dump_frequency(system%output, polarization)

      if (density_defined == 0) density = default
      call MW_output_diagnostic_set_dump_frequency(system%output, density)
      call MW_diagnostic_set_num_bins(system%diagnostic, density_num_bins, density_limits, density_min, density_max)

      ! Compute and store unwrapped ion coordinates if requested
      if ((system%trajectories_unwrapped == 1) .or. &
          (system%xyz_unwrapped == 1)          .or. &
          (system%pdb_unwrapped == 1)          .or. &
          (system%lammps_unwrapped) == 1) then
         system%store_xyz_ions_unwrapped = .true.
      end if

   end subroutine read_output_parameters

   ! ================================================================================
   ! Read external field parameters
   !
   ! external_field *block*
   !   type *string*
   !   direction *real* *real* *real*
   !   amplitude *real*
   subroutine read_external_field_parameters(field, funit, line_num)
      use MW_configuration_line, only: MW_configuration_line_t, &
            MW_configuration_line_seek_next_data => seek_next_data, &
            MW_configuration_line_get_word => get_word, &
            max_word_length
      use MW_system, only: MW_system_t
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      use MW_external_field, only: MW_external_field_t, &
            MW_external_field_define_type => define_type, &
            MW_external_field_void_type => void_type

      implicit none
      ! Parameters
      ! ----------
      type(MW_external_field_t), intent(inout) :: field
      integer, intent(in) :: funit !< configuration file unit
      integer, intent(inout) :: line_num !< line number in configuration file

      ! Local
      ! -----
      real(wp) :: amplitude
      real(wp),dimension(3) :: direction
      character(max_word_length) :: field_type
      character(256) :: errmsg
      character(max_word_length) :: keyword
      type(MW_configuration_line_t) :: config_line
      integer :: field_type_defined, field_direction_defined, field_amplitude_defined

      ! Set default values
      field_type = "E"
      direction(:) = 0.0_wp
      amplitude = 0.0_wp

      ! Flag for each section definition
      field_type_defined = 0
      field_direction_defined = 0
      field_amplitude_defined = 0

      call MW_external_field_void_type(field)


      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("type")
            if (field_type_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |field type|. First definition at line ",i4,".")') &
                     line_num, field_type_defined
               call MW_errors_runtime_error("read_external_field_parameters", "configuration.f90", errmsg)
            end if
            field_type_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for field type parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_external_field_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, field_type)


         case("direction")
            if (field_direction_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |field direction|. First definition at line ",i4,".")') &
                     line_num, field_direction_defined
               call MW_errors_runtime_error("read_external_field_parameters", "configuration.f90", errmsg)
            end if
            field_direction_defined = line_num

            ! Read direction parameters
            if (config_line%num_words /= 4) then
               write(errmsg,'("Invalid format for field direction parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_external_field_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, direction(1))
            call MW_configuration_line_get_word(config_line, 3, direction(2))
            call MW_configuration_line_get_word(config_line, 4, direction(3))


         case("amplitude")
            if (field_amplitude_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |field amplitude|. First definition at line ",i4,".")') &
                     line_num, field_amplitude_defined
               call MW_errors_runtime_error("read_external_field_parameters", "configuration.f90", errmsg)
            end if
            field_amplitude_defined = line_num

            ! Read amplitude parameter
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for field amplitude parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_external_field_parameters","configuration.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, amplitude)


         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop
         end select

         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      call MW_external_field_define_type(field, field_type, amplitude, direction)

   end subroutine read_external_field_parameters

   ! ================================================================================

   ! Check whether we are using charge neutrality
   subroutine check_charge_neutrality(system, algorithms)
      use MW_algorithms, only: MW_algorithms_t
      implicit none
      type(MW_system_t), intent(inout) :: system
      type(MW_algorithms_t), intent(in) :: algorithms

      system%electrode_charge_neutrality=.false.
      if (algorithms%maze%calc_elec) then
         system%electrode_charge_neutrality=algorithms%maze%charge_neutrality
      else if (algorithms%matrix_inversion%calc_elec) then
         system%electrode_charge_neutrality=algorithms%matrix_inversion%charge_neutrality
      else if (algorithms%cg%charge_neutrality) then
         system%electrode_charge_neutrality=algorithms%cg%charge_neutrality
      end if

   end subroutine check_charge_neutrality

end module MW_configuration
