module MW_system

   use MW_kinds, only: wp, PARTICLE_NAME_LEN
   use MW_ewald, only: MW_ewald_t
   use MW_box, only: MW_box_t
   use MW_ion, only: MW_ion_t
   use MW_species, only: MW_species_t
   use MW_electrode, only: MW_electrode_t
   use MW_external_field, only: MW_external_field_t
   use MW_fumi_tosi, only: MW_fumi_tosi_t
   use MW_xft, only: MW_xft_t
   use MW_daim, only: MW_daim_t
   use MW_damping, only: MW_damping_t
   use MW_lennard_jones, only: MW_lennard_jones_t
   use MW_steele, only: MW_steele_t
   use MW_localwork, only: MW_localwork_t
   use MW_molecule, only: MW_molecule_t
   use MW_rattle, only: MW_rattle_t
   use MW_thermostat, only: MW_thermostat_t
   use MW_barostat, only: MW_barostat_t
   use MW_thomas_fermi, only: MW_thomas_fermi_t
   use MW_output, only: MW_output_t
   use MW_random, only: &
         MW_random_seed => seed, &
         MW_random_normal_random_number => normal_random_number
   use MW_stdio, only: MW_stdout
   use MW_diagnostic, only: MW_diagnostic_t
   use MW_lookup_Table, only: MW_lut_t, lut_init, lut_push, lut_get, lut_check, &
                              lut_set, lut_void, lut_push_energies, lut_set_all, &
                              CHARLENGTH
   implicit none
   private

   ! Public type
   ! -----------
   public :: MW_system_t

   ! Public subroutines
   ! ------------------
   public :: void_parameters
   public :: deallocate_data_arrays
   public :: allocate_data_arrays
   public :: setup_history
   public :: print_type
   public :: print_step
   public :: update_history
   public :: forces_compute_coulomb
   public :: forces_compute_lj
   public :: forces_compute_lj_soft_core
   public :: forces_compute_ft
   public :: forces_compute_xft
   public :: forces_compute_daim
   public :: forces_compute_intramolecular
   public :: forces_compute_steele
   public :: forces_compute_external_field
   public :: coulomb_melt_efg
#ifdef MW_USE_PLUMED
   public :: forces_compute_plumed
#endif
   public :: forces_gather
   public :: ions_forces_compute
   public :: ions_positions_update
   public :: ions_velocities_update
   public :: piston_positions_update
   public :: piston_velocities_update
   public :: ewald_setup_cossin_ions
   public :: ewald_setup_cossin_elec
   public :: output_setup
   public :: output_dump
   public :: distribute_work
   public :: setup_sr_interaction
   public :: setup_num_dof
   public :: energy_coulomb
   public :: energy_lj
   public :: energy_lj_soft_core
   public :: energy_ft
   public :: energy_xft
   public :: energy_daim
   public :: energy_intramolecular
   public :: energy_steele
   public :: energy_thermostat
   public :: energy_barostat
   public :: energy_piston
   public :: energy_tf
   public :: energy
   public :: setup_velocities
   public :: setup_thermostats_barostats_constraints
   public :: setup_polarization
   public :: temperature_compute
   public :: pressure_compute
   public :: stress_tensor_compute
   public :: read_data
   public :: assign_ion_types
   public :: assign_electrode_atom_types
   public :: compute_diagnostics
   public :: check_electrodes
   public :: check_polarizability
   public :: check_deformability
   public :: write_data
   public :: redistribute_work

   integer, parameter :: XYZ_NUM_STEPS = 2          !< number of steps stored for xyz_ions
   integer, parameter :: VELOCITY_NUM_STEPS = 2     !< number of steps stored for velocity_ions
   integer, parameter :: FORCES_NUM_STEPS = 2       !< number of steps stored for forces_ions
   integer, parameter :: DIPOLES_NUM_STEPS = 7      !< number of steps stored for dipoles_ions
   integer, parameter :: RADIUS_NUM_STEPS = 2       !< number of steps stored for deltas_ions, epsilons_ions
   integer, parameter :: QATOMS_NUM_STEPS = 7       !< number of steps stored for q_elec

   integer, parameter :: COULOMB_POTENTIAL_NUM_TERMS = 4          !< number of terms stored for coulomb potential
   integer, parameter :: COULOMB_FORCES_NUM_TERMS = 5             !< number of terms stored for coulomb forces
   integer, parameter :: LJ_FORCES_NUM_TERMS = 3                  !< number of terms stored for lennard-jones potential
   integer, parameter :: FT_FORCES_NUM_TERMS = 3                  !< number of terms stored for tosi-fumi potential
   integer, parameter :: XFT_FORCES_NUM_TERMS = 3                  !< number of terms stored for xft potential
   integer, parameter :: DAIM_FORCES_NUM_TERMS = 3                 !< number of terms stored for daim potential
   integer, parameter :: INTRAMOLECULAR_FORCES_NUM_TERMS = 5      !< number of terms stored for intra molecular potential
   integer, parameter :: INTERMOLECULAR_FORCES_NUM_TERMS = 1      !< number of terms stored for inter molecular potential
   integer, parameter :: EXTERNAL_FIELD_NUM_TERMS = 3             !< number of terms stored for external field
   integer, parameter :: PLUMED_FORCES_NUM_TERMS = 1             !< number of terms stored for external field
   integer, parameter :: QUANTUM_POTENTIAL_NUM_TERMS = 1          !< number of terms stored for quantum contributions to the potential
   integer, parameter :: ENERGY_NUM_TERMS = 30                     !< number of terms stored for energy
   type MW_system_t

      ! Time parameters
      integer :: step = 0      !< global step index
      integer :: num_steps = 0 !< number of time steps
      real(wp) :: dt = 0.0_wp  !< time step

      integer :: num_PBC !< number of periodic boundary conditions
      type(MW_box_t) :: box !< simulation box

      type(MW_thermostat_t) :: thermostat !< Thermostat parameters

      type(MW_barostat_t) :: barostat !< Barostat parameters

      integer :: num_molecules = 0                           !< total number of molecules
      integer :: num_molecule_types = 0                      !< number of molecule types
      type(MW_molecule_t), allocatable :: molecules(:) !< molecule parameters

      integer :: num_ions = 0                      !< total number of ions
      integer :: num_ion_types= 0                  !< number of ion types
      type(MW_ion_t), allocatable :: ions(:) !< ions parameters
      real(wp) :: total_ions_charge !< total charge of the ions

      integer :: num_atoms = 0                           !< total number of electrode atoms
      integer :: num_elec = 0                            !< number of electrodes
      type(MW_electrode_t), allocatable :: electrodes(:) !< electrode parameters
      logical :: elecrun = .false.            !< flag for electrode minimization
      logical :: electrode_charges_initialized = .false. !< flag if electrode charge are given at t=0
      logical :: electrode_use_predictor = .false.       !< flag if electrode charge should be initialized with a predictor
      logical :: electrode_constant_charge = .false.   !< flag if constant charge on electrode
      logical :: electrode_charge_neutrality = .false.   !< flag if charge neutrality must be enforced
      logical :: global_charge_neutrality = .false.   !< flag if global charge neutrality must be enforced
      logical :: electrode_compute_force = .false.   !< flag if forces on electrodes must be computed
      logical :: pistonrun = .false.
      logical :: four_site_model = .false. !< flag if there is a 4 site model

      type(MW_ewald_t) :: ewald !< ewald summation parameters

      ! inter-molecular potentials
      type(MW_lennard_jones_t) :: lj !< Lennard-Jones potential parameters
      type(MW_fumi_tosi_t) :: ft !< Fumi-Tosi potential parameters
      type(MW_xft_t) :: xft !< XFT potential parameters
      type(MW_daim_t) :: daim    !< daim potential parameters
      type(MW_damping_t) :: tt   !< Damping potential parameters
      type(MW_steele_t) :: steele !< Steele potential parameters

      ! quantum corrections to the potential
      type(MW_thomas_fermi_t), allocatable :: thomas_fermi(:)
      real(wp), allocatable :: quantum_potential_atoms(:,:)
      logical :: tfrun = .false.

      ! External field
      type(MW_external_field_t) :: field !< electric field

      integer  :: num_dof = 0              !< number of degrees of freedom in the system
      real(wp) :: kinetic_energy = 0.0_wp !< kinetic energy of the system
      real(wp) :: polarization_energy = 0.0_wp !< polarization energy of the system
      real(wp) :: temperature = 0.0_wp    !< temperature of the system
      real(wp) :: pressure = 0.0_wp    !< pressure of the system (defined only in 3D)
      logical  :: velocity_initialized = .false. !< flag if velocity are initialized from data.inpt
      logical  :: velocity_create = .false.           !< flag if velocity should be generated
      logical  :: velocity_scale = .false.             !< flag if the user wants to rescale the velocity
      real(wp) :: velocity_scale_temperature = 0.0_wp  !< Temperature used for velocity rescaling
      integer  :: velocity_scale_method = 0            !< Method used for velocity rescaling
      integer  :: velocity_scale_period = 0            !< Rescale velocity period (in number of steps)
      real(wp) :: velocity_com_threshold = 0.0_wp      !< threshold to use for com reset
      logical  :: linear_present = .false. !< flag if some linear molecules are present
      logical  :: constraints_present = .false. !< flag if some constraints are present

      real(wp) :: pressure_tensor(3,3)= 0.0_wp    !< pressure tensor of the system (defined only in 3D)
      real(wp) :: stress_tensor(3,3) = 0.0_wp    !< stress tensor of the system (defined only in 3D)

      real(wp) :: pressure_tail_correction = 0.0_wp   ! Correction to the pressure due to the truncation of potential
      real(wp) :: lj_pressure_tail_correction = 0.0_wp   ! Correction to the pressure due to the truncation of LJ potential
      real(wp) :: ft_pressure_tail_correction = 0.0_wp   ! Correction to the pressure due to the truncation of dispersion
      real(wp) :: xft_pressure_tail_correction = 0.0_wp   ! Correction to the pressure due to the truncation of dispersion
      real(wp) :: daim_pressure_tail_correction = 0.0_wp   ! Correction to the pressure due to the truncation of dispersion
      logical  :: pressure_tail_correction_computed = .false.
      real(wp) :: coulomb_stress_tensor(3,3,5) = 0.0_wp
      real(wp) :: lj_stress_tensor(3,3,3) = 0.0_wp
      real(wp) :: ft_stress_tensor(3,3,3) = 0.0_wp
      real(wp) :: xft_stress_tensor(3,3,3) = 0.0_wp
      real(wp) :: daim_stress_tensor(3,3,2) = 0.0_wp
      real(wp) :: intramolecular_stress_tensor(3,3,5) = 0.0_wp
      real(wp) :: steele_stress_tensor(3,3) = 0.0_wp
      real(wp) :: constraint_stress_tensor(3,3) = 0.0_wp
      real(wp) :: four_site_stress_tensor(3,3) = 0.0_wp

      integer, allocatable :: type_ions(:)              !< ions type

      real(wp), allocatable :: xyz_ions(:,:,:)          !< ions xyz positions
      real(wp), allocatable :: velocity_ions(:,:,:)     !< ions xyz velocities
      real(wp), allocatable :: forces_ions(:,:,:)       !< ions xyz forces
      real(wp), allocatable :: dipoles_ions(:,:,:)      !< ions xyz dipoles
      real(wp), allocatable :: efg_ions(:,:)            !< ions efg xx,xy,xz,yy,yz,zz
      real(wp), allocatable :: deltas_ions(:,:)         !< ions xyz deltas
      real(wp), allocatable :: epsilons_ions(:,:,:)     !< ions xyz epsilons
      logical :: forces_initialized = .false. !< flag if forces are given at t=0
      logical :: dipoles_initialized = .false. !< flag if dipoles are given at t=0
      logical :: dipoles_use_predictor = .false.       !< flag if ion dipoles should be initialized with a predictor
      logical :: radius_initialized = .false. !< flag if radius are given at t=0
      logical :: pimrun = .false.             !< flags for dipolar method to use
      logical :: aimrun = .false.
      logical :: mazerun = .false.
      logical :: efgrun = .false.             !< flags for the efg computation
      logical :: charged_species = .false.

      real(wp), allocatable :: xyz_ions_unwrapped(:,:,:)  !< unwrapped ions xyz positions
      logical :: store_xyz_ions_unwrapped = .false. ! flag for computing and strong unwrapped ion positions 

      real(wp), allocatable :: coulomb_potential_atoms(:,:)
      real(wp), allocatable :: external_field_potential_atoms(:,:,:)

      real(wp), allocatable :: coulomb_forces_ions(:,:,:)
      real(wp), allocatable :: lj_forces_ions(:,:,:)
      real(wp), allocatable :: ft_forces_ions(:,:,:)
      real(wp), allocatable :: xft_forces_ions(:,:,:)
      real(wp), allocatable :: daim_forces_ions(:,:,:)
      real(wp), allocatable :: steele_forces_ions(:)
      real(wp), allocatable :: intermolecular_forces_ions(:,:,:)
      real(wp), allocatable :: intramolecular_forces_ions(:,:,:)
      real(wp), allocatable :: external_field_forces_ions(:,:,:)
#ifdef MW_USE_PLUMED
      real(wp), allocatable :: plumed_forces_ions(:,:,:)
#endif

      integer, allocatable :: type_electrode_atoms(:)              !< electrode atoms type

      real(wp), allocatable :: xyz_atoms(:,:)   !< electrode atoms xyz positions
      real(wp), allocatable :: q_atoms(:,:)     !< charge on electrode atoms
      real(wp), allocatable :: chi(:)           !< potential shift in simulations with enforced neutrality

      type(MW_lut_t) :: energy                  !< contribution to the system energy
      character(CHARLENGTH) :: energies_to_dump(ENERGY_NUM_TERMS)        !< energies to be printed in the breakdown
      integer :: energy_terms_to_dump = 0

      real(wp), allocatable :: polarization_field(:,:)
      logical :: polarization_initialized = .false. !< flag if polarization is given at t=0

      ! History pointer
      integer :: xyz_ions_step(XYZ_NUM_STEPS)
      integer :: velocity_ions_step(VELOCITY_NUM_STEPS)
      integer :: forces_ions_step(FORCES_NUM_STEPS)
      integer :: dipoles_ions_step(DIPOLES_NUM_STEPS)
      integer :: radius_ions_step(RADIUS_NUM_STEPS)
      integer :: q_atoms_step(QATOMS_NUM_STEPS)
      integer :: potential_shift_step(QATOMS_NUM_STEPS)

      ! Output files
      type(MW_output_t) :: output
      integer :: alternate_restart = 0
      integer :: trajectories_unwrapped = 0 
      integer :: xyz_unwrapped = 0
      integer :: pdb_unwrapped = 0
      integer :: lammps_unwrapped = 0

      ! work assignment
      type(MW_localwork_t) :: localwork

      ! diagnostic
      type(MW_diagnostic_t) :: diagnostic

      character(PARTICLE_NAME_LEN), allocatable :: species_names(:)

      logical :: plumedlog = .false.
      character(50) :: plumed_file

   end type MW_system_t

   type MW_restart_section_t
      integer :: coordinates, velocities, forces, dipoles
      integer :: charges, piston, radius, polarization
      integer :: thermostat, barostat, potential_shift
   end type MW_restart_section_t


contains

   !================================================================================
   ! Void system parameters
   subroutine void_parameters(this)
      use MW_box, only: MW_box_void_type => void_type
      use MW_electrode, only: MW_electrode_void_type => void_type
      use MW_errors, only: MW_errors_deallocate => deallocate_error
      use MW_ewald, only: MW_ewald_void_type => void_type
      use MW_ion, only: MW_ion_void_type => void_type
      use MW_fumi_tosi, only: MW_fumi_tosi_void_type => void_type
      use MW_xft, only: MW_xft_void_type => void_type
      use MW_daim, only: MW_daim_void_type => void_type
      use MW_damping, only: MW_damping_void_type => void_type
      use MW_lennard_jones, only: MW_lennard_jones_void_type => void_type
      use MW_molecule, only: MW_molecule_void_type => void_type
      use MW_thermostat, only: MW_thermostat_void_type => void_type
      use MW_barostat, only: MW_barostat_void_type => void_type
      use MW_output, only: MW_output_close_all => close_all
      use MW_localwork, only: MW_localwork_void_type => void_type
      use MW_external_field, only: MW_external_field_void_type => void_type
      implicit none

      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: ierr, i

      ! Void time parameters
      this%num_steps = 0
      this%dt = 0.0_wp

      ! void box parameter
      this%num_PBC = 0
      call MW_box_void_type(this%box)

      ! Molecule type parameters
      if (allocated(this%molecules)) then
         do i = 1, size(this%molecules,1)
            call MW_molecule_void_type(this%molecules(i))
         end do
         deallocate(this%molecules, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate("void_parameters", "system.f90",ierr)
         end if
      end if
      this%num_molecule_types = 0
      this%num_molecules = 0

      ! electrode parameters
      if (allocated(this%electrodes)) then
         do i = 1, size(this%electrodes,1)
            call MW_electrode_void_type(this%electrodes(i))
         end do
         deallocate(this%electrodes, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate("void_parameters", "system.f90",ierr)
         end if
      end if
      ! electrode parameters
      this%num_elec = 0
      this%num_atoms = 0
      this%electrode_use_predictor = .false.

      ! ion type parameters
      if (allocated(this%ions)) then
         do i = 1, size(this%ions,1)
            call MW_ion_void_type(this%ions(i))
         end do
         deallocate(this%ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate("void_parameters", "system.f90",ierr)
         end if
      end if
      this%num_ion_types = 0
      this%num_ions = 0

      ! ewald parameters
      call MW_ewald_void_type(this%ewald)

      ! Fumi-Tosi parameters
      call MW_fumi_tosi_void_type(this%ft)

      ! XFT parameters
      call MW_xft_void_type(this%xft)

      ! daim parameters
      call MW_daim_void_type(this%daim)

      ! Damping parameters
      call MW_damping_void_type(this%tt)

      ! Lennard-Jones parameters
      call MW_lennard_jones_void_type(this%lj)

      ! Thermostat
      call MW_thermostat_void_type(this%thermostat)

      ! Barostat
      call MW_barostat_void_type(this%barostat)

      ! Output
      call MW_output_close_all(this%output)

      ! Local work
      call MW_localwork_void_type(this%localwork)

      ! External_field
      call MW_external_field_void_type(this%field)

      if (allocated(this%species_names)) deallocate(this%species_names)
   end subroutine void_parameters

   ! ================================================================================
   ! Allocate data arrays and set up the enerigies to be output
   subroutine allocate_data_arrays(this)
      use MW_errors, only : MW_errors_allocate_error => allocate_error, &
            MW_errors_runtime_error => runtime_error
      use MW_constants, only: cache_line_size
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      ! Local
      ! -----
      integer :: ierr
      integer :: n, padding, term
      logical :: found
      character(256) :: errmsg

      ! Lookup that contains energy values
      call lut_init(this%energy, ENERGY_NUM_TERMS)
      call lut_push_energies(this%energy, 0.0_wp)

      if (this%energy_terms_to_dump == 0) then
         call fill_energy_terms_to_dump(this)
      end if

      do term = 1, this%energy_terms_to_dump
         call lut_check(this%energy, this%energies_to_dump(term), found)
         if (.not. found) then
            write(errmsg, '("Unknown energy term required ",a128,".")') this%energies_to_dump(term)
            call MW_errors_runtime_error("allocate_data_arrays", "system.f90", errmsg)
         end if
      end do

      padding = mod(this%num_ions, cache_line_size)
      n = this%num_ions
      if (padding > 0) n = n + (cache_line_size-padding)

      allocate(this%type_ions(n), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%type_ions(:) = 0

      allocate(this%xyz_ions(n,3,XYZ_NUM_STEPS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%xyz_ions(:,:,:) = 0.0_wp

      if (this%store_xyz_ions_unwrapped) then 
         allocate(this%xyz_ions_unwrapped(n,3,XYZ_NUM_STEPS), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%xyz_ions_unwrapped(:,:,:) = 0.0_wp
      end if 

      allocate(this%velocity_ions(n,3,VELOCITY_NUM_STEPS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%velocity_ions(:,:,:) = 0.0_wp

      allocate(this%forces_ions(n,3,FORCES_NUM_STEPS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%forces_ions(:,:,:) = 0.0_wp

      allocate(this%dipoles_ions(n,3,DIPOLES_NUM_STEPS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%dipoles_ions(:,:,:) = 0.0_wp

      if (this%efgrun) then
         allocate(this%efg_ions(n,6), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
      this%efg_ions(:,:) = 0.0_wp
      end if 

      if(this%aimrun) then
         allocate(this%deltas_ions(n,RADIUS_NUM_STEPS), stat=ierr)
         if (ierr /= 0) then
           call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%deltas_ions(:,:) = 0.0_wp

         allocate(this%epsilons_ions(n,3,RADIUS_NUM_STEPS), stat=ierr)
         if (ierr /= 0) then
           call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%epsilons_ions(:,:,:) = 0.0_wp
      end if

      allocate(this%coulomb_forces_ions(n,3, COULOMB_FORCES_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%coulomb_forces_ions(:,:,:) = 0.0_wp

      if (this%ft%num_species > 0) then
         allocate(this%ft_forces_ions(n, 3, FT_FORCES_NUM_TERMS), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%ft_forces_ions(:,:,:) = 0.0_wp
      end if

      if (this%xft%num_species > 0) then
         allocate(this%xft_forces_ions(n, 3, XFT_FORCES_NUM_TERMS), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%xft_forces_ions(:,:,:) = 0.0_wp
      end if

      if (this%daim%num_species > 0) then
         allocate(this%daim_forces_ions(n, 3, DAIM_FORCES_NUM_TERMS), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%daim_forces_ions(:,:,:) = 0.0_wp
      end if

      if (this%steele%num_walls > 0) then
         allocate(this%steele_forces_ions(n), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%steele_forces_ions(:) = 0.0_wp
      end if

      if (this%lj%num_species > 0) then
         allocate(this%lj_forces_ions(n, 3, LJ_FORCES_NUM_TERMS), stat=ierr)
         if (ierr /= 0) then
            call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
         end if
         this%lj_forces_ions(:,:,:) = 0.0_wp
      end if

      allocate(this%intermolecular_forces_ions(n, 3, INTERMOLECULAR_FORCES_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%intermolecular_forces_ions(:,:,:) = 0.0_wp

      allocate(this%intramolecular_forces_ions(n, 3, INTRAMOLECULAR_FORCES_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%intramolecular_forces_ions(:,:,:) = 0.0_wp

      allocate(this%external_field_forces_ions(n, 3, EXTERNAL_FIELD_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%external_field_forces_ions(:,:,:) = 0.0_wp

#ifdef MW_USE_PLUMED
      allocate(this%plumed_forces_ions(n, 3, PLUMED_FORCES_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%plumed_forces_ions(:,:,:) = 0.0_wp
#endif

      ! Compute padding for num_atoms size arrays
      padding = mod(this%num_atoms, cache_line_size)
      n = this%num_atoms
      if (padding > 0) n = n + (cache_line_size-padding)

      allocate(this%type_electrode_atoms(n), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%type_electrode_atoms(:) = 0

      allocate(this%xyz_atoms(n,3), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%xyz_atoms(:,:) = 0.0_wp

      allocate(this%q_atoms(n,QATOMS_NUM_STEPS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      if (this%electrode_charge_neutrality) then
         allocate(this%chi(QATOMS_NUM_STEPS), stat=ierr)
         this%chi(:) = 0.0_wp
      end if
      this%q_atoms(:,:) = 0.0_wp

      allocate(this%coulomb_potential_atoms(n, COULOMB_POTENTIAL_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%coulomb_potential_atoms(:,:) = 0.0_wp

      allocate(this%quantum_potential_atoms(n, QUANTUM_POTENTIAL_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%quantum_potential_atoms(:,:) = 0.0_wp

      allocate(this%polarization_field(3,4), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%polarization_field(:,:) = 0.0_wp

      allocate(this%external_field_potential_atoms(n,EXTERNAL_FIELD_NUM_TERMS,2), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "system.f90", ierr)
      end if
      this%external_field_potential_atoms(:,:,:) = 0.0_wp

   end subroutine allocate_data_arrays

   ! ================================================================================
   subroutine fill_energy_terms_to_dump(this)
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: imoltype

      ! Fill energies_to_dump smartly
      ! 'General' energies
      this%energy_terms_to_dump = this%energy_terms_to_dump + 3
      this%energies_to_dump(1) = "Total"
      this%energies_to_dump(2) = "Kinetic"
      this%energies_to_dump(3) = "Potential"

      ! Intramolecular energies
      if (this%num_molecule_types > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Intramolecular"
         do imoltype = 1, this%num_molecule_types
            if (this%molecules(imoltype)%num_harmonic_bonds > 0) then
               this%energy_terms_to_dump = this%energy_terms_to_dump + 1
               this%energies_to_dump(this%energy_terms_to_dump) = "Harmonic_bond"
            end if
            if (this%molecules(imoltype)%num_harmonic_angles > 0) then
               this%energy_terms_to_dump = this%energy_terms_to_dump + 1
               this%energies_to_dump(this%energy_terms_to_dump) = "Harmonic_angle"
            end if
            if (this% molecules(imoltype)%num_dihedrals > 0) then
               this%energy_terms_to_dump = this%energy_terms_to_dump + 1
               this%energies_to_dump(this%energy_terms_to_dump) = "Dihedrals"
            end if
         end do
      end if

      ! Intermolecular energies
      this%energy_terms_to_dump = this%energy_terms_to_dump + 1
      this%energies_to_dump(this%energy_terms_to_dump) = "Coulomb"
      this%energy_terms_to_dump = this%energy_terms_to_dump + 1
      this%energies_to_dump(this%energy_terms_to_dump) = "Coulomb_lr"
      this%energy_terms_to_dump = this%energy_terms_to_dump + 1
      this%energies_to_dump(this%energy_terms_to_dump) = "Coulomb_sr"
      if (this%num_PBC == 2) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Coulomb_keq0"
      end if
      this%energy_terms_to_dump = this%energy_terms_to_dump + 1
      this%energies_to_dump(this%energy_terms_to_dump) = "Coulomb_self"
      if (this%num_molecule_types > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Coulomb_molecule"
      end if
      this%energy_terms_to_dump = this%energy_terms_to_dump + 1
      this%energies_to_dump(this%energy_terms_to_dump) = "van_der_Waals"
      if (this%lj%num_species > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Lennard-Jones"
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Lennard-Jones_cutoff"
      end if
      if (this%ft%num_species > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Fumi-Tosi"
         if (this%num_molecule_types > 0) then
            this%energy_terms_to_dump = this%energy_terms_to_dump + 1
            this%energies_to_dump(this%energy_terms_to_dump) = "Fumi-Tosi_molecule"
         end if
      end if
      if (this%xft%num_species > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "XFT"
         if (this%num_molecule_types > 0) then
            this%energy_terms_to_dump = this%energy_terms_to_dump + 1
            this%energies_to_dump(this%energy_terms_to_dump) = "XFT_molecule"
         end if
      end if
      if (this%daim%num_species > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "DAIM"
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "DAIM_self"
      end if
      if (this%steele%num_walls > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Steele"
      end if

      ! Hamiltonian
      if (this%thermostat%chain_length > 0 .or. this%barostat%ions_th_chain_length > 0 &
             .or. this%plumedlog .or. this%pistonrun .or. this%tfrun) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "Hamiltonian"
         if (this%thermostat%chain_length > 0) then
            this%energy_terms_to_dump = this%energy_terms_to_dump + 1
            this%energies_to_dump(this%energy_terms_to_dump) = "Thermostat"
         end if
         if (this%barostat%ions_th_chain_length > 0) then
            this%energy_terms_to_dump = this%energy_terms_to_dump + 1
            this%energies_to_dump(this%energy_terms_to_dump) = "Barostat"
         end if
         if (this%plumedlog) then
            this%energy_terms_to_dump = this%energy_terms_to_dump + 1
            this%energies_to_dump(this%energy_terms_to_dump) = "Plumed_bias"
         end if
         if (this%pistonrun) then
            this%energy_terms_to_dump = this%energy_terms_to_dump + 1
            this%energies_to_dump(this%energy_terms_to_dump) = "Piston_work"
         end if
         if (this%tfrun) then
            this%energy_terms_to_dump = this%energy_terms_to_dump + 1
            this%energies_to_dump(this%energy_terms_to_dump) = "Thomas_Fermi"
         end if
      end if

      if (this%field%field_type > 0) then
         this%energy_terms_to_dump = this%energy_terms_to_dump + 1
         this%energies_to_dump(this%energy_terms_to_dump) = "External_field"
      endif

   end subroutine fill_energy_terms_to_dump

   ! ================================================================================
   ! Deallocate data arrays
   subroutine deallocate_data_arrays(this)
      use MW_errors, only : MW_errors_deallocate_error => deallocate_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      ! Locals
      ! ------
      integer :: ierr

      ! Make sure all data arrays are deallocated

      if (allocated(this%q_atoms)) then
         deallocate(this%q_atoms, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
         if(this%electrode_charge_neutrality) then
            deallocate(this%chi, stat=ierr)
            if (ierr /= 0) then
               call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
            end if
         end if
      end if

      if (allocated(this%xyz_atoms)) then
         deallocate(this%xyz_atoms, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%intramolecular_forces_ions)) then
         deallocate(this%intramolecular_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%intermolecular_forces_ions)) then
         deallocate(this%intermolecular_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%lj_forces_ions)) then
         deallocate(this%lj_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%ft_forces_ions)) then
         deallocate(this%ft_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%xft_forces_ions)) then
         deallocate(this%xft_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%daim_forces_ions)) then
         deallocate(this%daim_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%steele_forces_ions)) then
         deallocate(this%steele_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%coulomb_forces_ions)) then
         deallocate(this%coulomb_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%coulomb_potential_atoms)) then
         deallocate(this%coulomb_potential_atoms, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%forces_ions)) then
         deallocate(this%forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%dipoles_ions)) then
         deallocate(this%dipoles_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%efg_ions)) then
         deallocate(this%efg_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%deltas_ions)) then
         deallocate(this%deltas_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%epsilons_ions)) then
         deallocate(this%epsilons_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%velocity_ions)) then
         deallocate(this%velocity_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%type_ions)) then
         deallocate(this%type_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%type_electrode_atoms)) then
         deallocate(this%type_electrode_atoms, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%xyz_ions)) then
         deallocate(this%xyz_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%xyz_ions_unwrapped)) then
         deallocate(this%xyz_ions_unwrapped, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%polarization_field)) then
         deallocate(this%polarization_field, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%external_field_forces_ions)) then
         deallocate(this%external_field_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%external_field_potential_atoms)) then
         deallocate(this%external_field_potential_atoms, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
      if (allocated(this%quantum_potential_atoms)) then
         deallocate(this%quantum_potential_atoms, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
#ifdef MW_USE_PLUMED
      if (allocated(this%plumed_forces_ions)) then
         deallocate(this%plumed_forces_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "system.f90", ierr)
         end if
      end if
#endif

      call lut_void(this%energy)
   end subroutine deallocate_data_arrays

   ! ================================================================================
   ! Initialize history pointer
   subroutine setup_history(this)
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: i

      do i = 1, XYZ_NUM_STEPS
         this%xyz_ions_step(i) = i
      end do

      do i = 1, VELOCITY_NUM_STEPS
         this%velocity_ions_step(i) = i
      end do

      do i = 1, FORCES_NUM_STEPS
         this%forces_ions_step(i) = i
      end do

      do i = 1, DIPOLES_NUM_STEPS
         this%dipoles_ions_step(i) = i
      end do

      do i = 1, RADIUS_NUM_STEPS
         this%radius_ions_step(i) = i
      end do

      do i = 1, QATOMS_NUM_STEPS
         this%q_atoms_step(i) = i
      end do

      if (this%electrode_charge_neutrality) then
         do i = 1, QATOMS_NUM_STEPS
            this%potential_shift_step(i) = i
         end do
      end if

   end subroutine setup_history

   ! ================================================================================
   ! Determine the number of degrees of freedom in the system
   subroutine setup_num_dof(this)
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: itype
      integer :: imol
      integer :: num_mobile_ions, num_com_dof
      logical :: immobile_ions, immobile_electrodes, piston_electrodes

      num_mobile_ions = 0
      immobile_ions = .false.
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%mobile) then
            if (.not. this%ions(itype)%is_fourth_site) then
               num_mobile_ions = num_mobile_ions + this%ions(itype)%count
            end if
         else
            immobile_ions = .true.
         end if
      end do

      if (this%num_elec > 0) then
         piston_electrodes = .true.
         immobile_electrodes = .false.
         do itype = 1, size(this%electrodes, 1)
            if (.not. this%electrodes(itype)%piston) then
               piston_electrodes = .false.
               immobile_electrodes = .true.
            end if
         end do
      else
         piston_electrodes = .false.
         immobile_electrodes = .false.
      end if

      num_com_dof = 3
      if (immobile_ions) then
         num_com_dof = 0
      else if (immobile_electrodes) then
         num_com_dof = 0
      else if (piston_electrodes) then
         num_com_dof = 1
      end if

      this%num_dof = 3 * num_mobile_ions - num_com_dof

      ! intra molecular constraints remove some degrees of freedom
      do imol = 1, this%num_molecule_types
         if (this%molecules(imol)%linear) then
            ! A linear molecule has 5 degrees of freedom (3 translations + 2 rotations)
            this%num_dof = this%num_dof + (5 - 3*this%molecules(imol)%num_sites)*this%molecules(imol)%n
            this%linear_present =  .true.
         else
            ! Each constraints removes 1 degree of freedom
            this%num_dof = this%num_dof - &
                  this%molecules(imol)%n * this%molecules(imol)%num_constraints
            this%constraints_present =  .true.
         end if
      end do

   end subroutine setup_num_dof

   !================================================================================
   !> Setup velocities
   !!
   !! 1 - Initialize velocities with normal random distribution if not provided in data.inpt
   !! 2 - Rescale velocities if requested in runtime.inpt
   subroutine setup_velocities(this)
      use MW_velocity, only: &
            MW_velocity_initialize => initialize, &
            MW_velocity_rescale => rescale
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: velocity_now

      velocity_now = this%velocity_ions_step(1)
      if ((.not. this%velocity_initialized) .or. (this%velocity_create)) then
         call MW_velocity_initialize(this%temperature, this%num_dof, this%ions, &
               this%velocity_ions(:,:,velocity_now))
         this%velocity_create = .true.
      end if

      call MW_velocity_rescale(this%velocity_scale_method, this%velocity_scale_temperature, &
            this%num_dof, this%molecules, this%ions, this%velocity_ions(:,:,velocity_now))

   end subroutine setup_velocities

   !================================================================================
   !> Setup thermostats and barostats
   !!
   !! 1 - Initialize thermostat parameters if a thermostat is defined
   !! 2 - Initialize barostat parameters if a barostat is defined
   !! 3 - Compute the constraints contribution to virial at step 0
   subroutine setup_thermostats_barostats_constraints(this)
      use MW_thermostat, only: &
           MW_thermostat_allocate_velocity_work_array => allocate_velocity_work_array, &
           MW_thermostat_initialize => initialize
      use MW_barostat, only: &
           MW_barostat_allocate_velocity_work_array => allocate_velocity_work_array, &
           MW_barostat_initialize => initialize
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: velocity_now
      real(wp) :: configurational_pressure

      velocity_now = this%velocity_ions_step(1)

      if (this%thermostat%chain_length > 0) then
         call MW_thermostat_allocate_velocity_work_array(this%thermostat, size(this%velocity_ions,1))
         call MW_thermostat_initialize(this%thermostat, this%num_dof, this%ions, &
              this%velocity_ions(:,:,velocity_now))
      end if

      if(this%constraints_present)call initial_constraints_stress_tensor_compute(this)

      if (this%barostat%ions_th_chain_length > 0) then

         call configurational_pressure_compute(this,configurational_pressure)
         call MW_barostat_allocate_velocity_work_array(this%barostat, size(this%velocity_ions,1))
         call MW_barostat_initialize(this%barostat, this%num_dof, this%ions, &
              this%velocity_ions(:,:,velocity_now),this%box%volume,configurational_pressure)
      end if

   end subroutine setup_thermostats_barostats_constraints

   !===============================================================================
   !> Setup polarization
   subroutine setup_polarization(this)
      use MW_external_field, only:&
            MW_external_field_initialize_polarization => initialize_polarization
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      if (.not. this%polarization_initialized) then
         call MW_external_field_initialize_polarization(this%box, &
               this%xyz_ions(:,:,1), this%ions, this%polarization_field(:,:))
      endif

    end subroutine setup_polarization

   !================================================================================
   ! Print system parameters
   subroutine print_type(this, ounit)
      use MW_errors, only: &
            MW_errors_allocate_error => allocate_error, &
            MW_errors_deallocate_error => deallocate_error
      use MW_kinds, only: PARTICLE_NAME_LEN
      use MW_box, only: MW_box_print_type => print_type
      use MW_electrode, only: MW_electrode_print_type => print_type
      use MW_ewald, only: MW_ewald_print_type => print_type
      use MW_ion, only: MW_ion_print_type => print_type
      use MW_species, only: MW_species_print_type => print_type
      use MW_molecule, only: MW_molecule_print_type => print_type
      use MW_rattle, only: MW_rattle_print_type => print_type
      use MW_fumi_tosi, only: MW_fumi_tosi_print_type => print_type
      use MW_xft, only: MW_xft_print_type => print_type
      use MW_daim, only: MW_daim_print_type => print_type
      use MW_damping, only: MW_damping_print_type => print_type
      use MW_lennard_jones, only: MW_lennard_jones_print_type => print_type
      use MW_localwork, only: MW_localwork_print_type => print_type
      use MW_thermostat, only: MW_thermostat_print_type => print_type
      use MW_barostat, only: MW_barostat_print_type => print_type
      use MW_thomas_fermi, only: MW_thomas_fermi_print_type => print_type
      use MW_external_field, only: MW_external_field_print_type => print_type
      implicit none

      ! Parameters in
      ! -------------
      type(MW_system_t), intent(in) :: this
      integer, intent(in) :: ounit

      ! Local
      ! -----
      integer :: i, ierr
      integer :: num_species
      character(PARTICLE_NAME_LEN), allocatable :: species_names(:)

      ! Setup list of ion names for molecule print
      num_species = this%num_ion_types+this%num_elec
      allocate(species_names(num_species), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("print_type", "system.f90",ierr)
      end if
      do i = 1, this%num_ion_types
         species_names(i) = this%ions(i)%name
      end do
      do i = 1, this%num_elec
         species_names(i+this%num_ion_types) = this%electrodes(i)%name
      end do

      write(ounit, *)
      write(ounit, '("System parameters:")')
      write(ounit, '("==================")')

      write(ounit,*)
      write(ounit, '("Time step:             ", es12.5)') this%dt
      write(ounit, '("Number of steps:       ", i12)') this%num_steps
      write(ounit, '("Total simulation time: ", es12.5)') this%dt*this%num_steps

      write(ounit,*)
      write(ounit, '("Total number of molecules:          ",i8)') this%num_molecules
      write(ounit, '("Total number of electrolyte atoms:  ",i8)') this%num_ions
      write(ounit, '("Total number of electrode atoms:    ",i8)') this%num_atoms
      write(ounit, '("Total number of degrees of freedom: ",i8)') this%num_dof

      write(ounit,*)
      write(ounit, '("charges    at t=0 are provided: ",a3)') yes_or_no(this%electrode_charges_initialized)
      write(ounit, '("forces     at t=0 are provided: ",a3)') yes_or_no(this%forces_initialized)
      write(ounit, '("dipoles    at t=0 are provided: ",a3)') yes_or_no(this%dipoles_initialized)
      write(ounit, '("radius     at t=0 are provided: ",a3)') yes_or_no(this%radius_initialized)

      write(ounit,*)
      write(ounit, '("box")')
      write(ounit, '("---")')
      call MW_box_print_type(this%box, ounit)

      write(ounit,*)
      write(ounit,*)
      write(ounit, '("velocities")')
      write(ounit, '("----------")')
      write(ounit, '("velocities at t=0 are provided in data file: ",a3)') yes_or_no(this%velocity_initialized)
      write(ounit, '("velocities at t=0 are created with a random normal distribution:  ",a3)') yes_or_no(this%velocity_create)
      write(ounit, '("center of mass threshold:       ",es12.5)') this%velocity_com_threshold
      write(ounit, '("velocities are scaled:   ",a3)') yes_or_no(this%velocity_scale)
      if (this%velocity_scale) then
         write(ounit, '("  * scaling method:      ",i12)')    this%velocity_scale_method
         write(ounit, '("  * scaling period:      ",i12)')    this%velocity_scale_period
         write(ounit, '("  * scaling temperature: ",es12.5)') this%velocity_scale_temperature
      end if

      if (this%thermostat%chain_length > 0) then
         write(ounit,*)
         write(ounit, '("thermostat")')
         write(ounit, '("----------")')
         call MW_thermostat_print_type(this%thermostat, ounit)
      end if

      if (this%barostat%ions_th_chain_length > 0) then
         write(ounit,*)
         write(ounit, '("barostat")')
         write(ounit, '("----------")')
         call MW_barostat_print_type(this%barostat, ounit)
      end if

      write(ounit,*)
      write(ounit, '("electrolyte types (",i3,")")') this%num_ion_types
      write(ounit, '("---------------")')
      do i = 1, this%num_ion_types
         call MW_ion_print_type(this%ions(i), ounit)
      end do

      write(ounit,*)
      write(ounit,'("|electrolyte charge|: ",f5.3)') this%total_ions_charge

      write(ounit,*)
      write(ounit, '("electrodes (",i3,")")') this%num_elec
      write(ounit, '("----------------")')
      write(ounit, '("charge neutrality is enforced: ",a3)') yes_or_no(this%electrode_charge_neutrality)
      write(ounit, '("global charge neutrality is enforced: ",a3)') yes_or_no(this%global_charge_neutrality)
      do i = 1, this%num_elec
         call MW_electrode_print_type(this%electrodes(i), ounit)
         call MW_thomas_fermi_print_type(this%thomas_fermi(i), ounit)
      end do

      if (this%num_molecules > 0) then
         write(ounit,*)
         write(ounit, '("molecules (",i3,")")') this%num_molecule_types
         write(ounit, '("---------------")')
         do i = 1, this%num_molecule_types
            write(ounit,*)
            call MW_molecule_print_type(this%molecules(i), ounit, species_names)
         end do
      end if

      if (this%ft%num_species > 0) then
         write(ounit,*)
         write(ounit, '("Fumi-Tosi")')
         write(ounit, '("---------")')
         call MW_fumi_tosi_print_type(this%ft, species_names, ounit)
      end if

      if (this%xft%num_species > 0) then
         write(ounit,*)
         write(ounit, '("XFT")')
         write(ounit, '("---------")')
         call MW_xft_print_type(this%xft, species_names, ounit)
      end if

      if (this%daim%num_species > 0) then
         write(ounit,*)
         write(ounit, '("Daim")')
         write(ounit, '("---------")')
         call MW_daim_print_type(this%daim, species_names, ounit)
      end if

      if (this%tt%num_species > 0) then
         write(ounit,*)
         write(ounit, '("Damping")')
         write(ounit, '("---------")')
         call MW_damping_print_type(this%tt, species_names, ounit)
      end if

      if (this%lj%num_species > 0) then
         write(ounit,*)
         write(ounit, '("Lennard-Jones")')
         write(ounit, '("-------------")')
         call MW_lennard_jones_print_type(this%lj, species_names, ounit)
      end if

      deallocate(species_names, stat=ierr)
      if (ierr /= 0) then
         call MW_errors_deallocate_error("print_type", "system.f90",ierr)
      end if

      write(ounit,*)
      write(ounit, '("polarization")')
      write(ounit, '("----------")')
      write(ounit, '("polarization at t=0 is provided in data file: ",a3)') yes_or_no(this%polarization_initialized)

      write(ounit,*)
      write(ounit, '("ewald")')
      write(ounit, '("-----")')
      call MW_ewald_print_type(this%ewald, ounit)

      write(ounit,*)
      write(ounit, '("local work")')
      write(ounit, '("----------")')
      call MW_localwork_print_type(this%localwork, ounit)

      if (this%field%field_type > 0) then
         write(ounit,*)
         write(ounit, '("external field")')
         write(ounit, '("----------")')
         call MW_external_field_print_type(this%field, ounit)
      endif

   end subroutine print_type

   ! ==============================================================================
   ! Print statistic for a step
   subroutine print_step(this, istep, ounit, do_output)
      use MW_timers, only: MW_timers_lastElapsedTime, TIMER_STEP, TIMER_DIAG_IO, MW_timers_start, MW_timers_stop
      use MW_errors, only: MW_errors_runtime_error => runtime_error, &
                           MW_errors_temperature_warning => temperature_warning
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: istep
      integer, intent(in) :: ounit
      logical, intent(in) :: do_output !< flag if something needs to be printed


      ! Locals
      ! ------
      integer :: qatoms_now
      real(wp) :: electrode_charge, all_charges
      integer :: itype, i, offset
      real(wp) :: infinity = HUGE(wp)
      logical :: temperature_issue
      character(256) :: errmsg

      call MW_timers_start(TIMER_DIAG_IO)
      if (do_output) then
         write(ounit,*)
         write(ounit,'("|step| step ",i10)') this%step
         write(ounit,'("|step| step ",i10," / ",i10," in this run.")') istep, this%num_steps
         if (istep > 0) then
            write(ounit,'("|step| elapsed time:       ",es12.5)')  MW_timers_lastElapsedTime(TIMER_STEP)
         end if
      end if

      ! Temperature and kinetic energy

      if (do_output) then
        call temperature_compute(this, temperature_issue)
        ! if temperature too large or is NaN => error
        if(this%temperature > infinity .or. this%temperature .ne. this%temperature) then
          call MW_errors_runtime_error("print_step", "system.f90", &
            "Temperature is NaN or meaningless")
        end if
        write(ounit,'("|step| kinetic energy:     ",es12.5," kJ/mol")') this%kinetic_energy
        write(ounit,'("|step| temperature:        ",es12.5," K")') this%temperature
        if(temperature_issue) then
            write(errmsg,'("Temperature warning ",i10)') this%step
            call MW_errors_temperature_warning(errmsg)
        end if
      end if

      ! Electrode charges
      if (this%num_elec > 0) then
         qatoms_now = this%q_atoms_step(1)
         all_charges = 0.0_wp
         do itype = 1, this%num_elec
            electrode_charge = 0.0_wp
            offset = this%electrodes(itype)%offset
            do i = 1, this%electrodes(itype)%count
               electrode_charge = electrode_charge + this%q_atoms(offset+i, qatoms_now)
            end do
            if (do_output) then
               write(ounit,'("|step| Global charge on electrode ",i2,": ",es12.5)')  itype, electrode_charge
            end if
            all_charges = all_charges + electrode_charge
         end do
         if (do_output) then
            write(ounit,'("|step| Global charge on electrodes:   ",es12.5)')  all_charges
         end if
      end if
      call MW_timers_stop(TIMER_DIAG_IO)
   end subroutine print_step

   ! ================================================================================
   !> Cycle history
   subroutine update_history(this)
      use MW_thermostat, only: MW_thermostat_update_history => update_history
      use MW_barostat, only: MW_barostat_update_history => update_history
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: step_now, step

      ! Increment step counter
      this%step = this%step + 1
      if (this%step >= 7) then
         this%electrode_use_predictor = .true.
         this%dipoles_use_predictor = .true.
      end if

      ! current step data will replace oldest step data
      step_now = this%xyz_ions_step(XYZ_NUM_STEPS)
      do step = XYZ_NUM_STEPS, 2, -1
         this%xyz_ions_step(step) = this%xyz_ions_step(step-1)
      end do
      this%xyz_ions_step(1) = step_now

      step_now = this%velocity_ions_step(VELOCITY_NUM_STEPS)
      do step = VELOCITY_NUM_STEPS, 2, -1
         this%velocity_ions_step(step) = this%velocity_ions_step(step-1)
      end do
      this%velocity_ions_step(1) = step_now

      step_now = this%forces_ions_step(FORCES_NUM_STEPS)
      do step = FORCES_NUM_STEPS, 2, -1
         this%forces_ions_step(step) = this%forces_ions_step(step-1)
      end do
      this%forces_ions_step(1) = step_now

      step_now = this%dipoles_ions_step(DIPOLES_NUM_STEPS)
      do step = DIPOLES_NUM_STEPS, 2, -1
         this%dipoles_ions_step(step) = this%dipoles_ions_step(step-1)
      end do
      this%dipoles_ions_step(1) = step_now
      if (step_now == 1) this%dipoles_use_predictor = .true.

      step_now = this%radius_ions_step(RADIUS_NUM_STEPS)
      do step = RADIUS_NUM_STEPS, 2, -1
         this%radius_ions_step(step) = this%radius_ions_step(step-1)
      end do
      this%radius_ions_step(1) = step_now

      step_now = this%q_atoms_step(QATOMS_NUM_STEPS)
      do step = QATOMS_NUM_STEPS, 2, -1
         this%q_atoms_step(step) = this%q_atoms_step(step-1)
      end do
      this%q_atoms_step(1) = step_now
      if (this%electrode_charge_neutrality) then
         step_now = this%potential_shift_step(QATOMS_NUM_STEPS)
         do step = QATOMS_NUM_STEPS, 2, -1
            this%potential_shift_step(step) = this%potential_shift_step(step-1)
         end do
         this%potential_shift_step(1) = step_now
      end if
      if (step_now == 1) this%electrode_use_predictor = .true.
      !$acc data present(this%q_atoms_step(:))
      !$acc update device(this%q_atoms_step(:))
      !$acc end data
      call MW_thermostat_update_history(this%thermostat)
      call MW_barostat_update_history(this%barostat)

   end subroutine update_history

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

   subroutine forces_compute_coulomb(this)
      use MW_coulomb, only: &
            MW_coulomb_melt_forces => melt_forces, &
            MW_coulomb_fix_molecule_melt_forces => fix_molecule_melt_forces
      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz,jxyz
      integer :: qatoms_now, xyz_now, dipoles_now

      qatoms_now = this%q_atoms_step(1)
      xyz_now = this%xyz_ions_step(1)
      dipoles_now = this%dipoles_ions_step(1)

      !$acc data present(this%coulomb_forces_ions(:,:,:))
      ! Coulomb forces
      ! --------------
      if (this%elecrun .or. this%pimrun .or. this%aimrun .or. this%charged_species) then
         !$acc kernels
         this%coulomb_forces_ions(:,:,:) = 0.0_wp
         !$acc end kernels
         this%coulomb_stress_tensor(:,:,:) = 0.0_wp

         call MW_coulomb_melt_forces(this%num_PBC, this%localwork, this%ewald, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%type_ions, this%electrodes, &
               this%xyz_atoms(:,:), this%type_electrode_atoms, &
               this%q_atoms(:,qatoms_now), this%coulomb_forces_ions(:,:,2:4), &
               this%coulomb_stress_tensor(:,:,2:4), this%pimrun, &
               this%dipoles_ions(:,:,dipoles_now), this%tt, this%electrode_compute_force)

         if (this%num_molecule_types > 0) then
            call MW_coulomb_fix_molecule_melt_forces(this%num_PBC, this%localwork, this%molecules, &
                  this%box, this%ions, this%xyz_ions(:,:,xyz_now), this%coulomb_forces_ions(:,:,5), &
                  this%coulomb_stress_tensor(:,:,5), &                  
                  this%pimrun, this%dipoles_ions(:,:,dipoles_now), this%tt)
         end if

         !$acc parallel loop collapse(2)
         do ixyz = 1, 3
            do iion = 1, this%num_ions
               this%coulomb_forces_ions(iion,ixyz,1)       &
                     = this%coulomb_forces_ions(iion,ixyz,2) & ! coulomb forces lr
                     + this%coulomb_forces_ions(iion,ixyz,3) & ! coulomb forces k=0
                     + this%coulomb_forces_ions(iion,ixyz,4) & ! coulomb forces sr
                     + this%coulomb_forces_ions(iion,ixyz,5)   ! coulomb correction for molecules
            end do
         end do
         !$acc end parallel loop
         !$acc update host(this%coulomb_forces_ions(:,:,:))

         do ixyz = 1,3
            do jxyz = ixyz,3
              this%coulomb_stress_tensor(ixyz,jxyz,1)       &
                    = this%coulomb_stress_tensor(ixyz,jxyz,2) &   ! lr
                    + this%coulomb_stress_tensor(ixyz,jxyz,3) &   ! k=0 should be 0 since we do not know the expression
                    + this%coulomb_stress_tensor(ixyz,jxyz,4) &   ! sr
                    + this%coulomb_stress_tensor(ixyz,jxyz,5)     ! correction for molecules
            end do
         end do
      end if

      !$acc end data

   end subroutine forces_compute_coulomb

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

   subroutine forces_compute_lj(this)
      use MW_lennard_jones, only: &
            MW_lennard_jones_melt_forces => melt_forces, &
            MW_lennard_jones_melt_fix_molecule_forces => melt_fix_molecule_forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz,jxyz
      integer :: xyz_now
      logical :: soft = .False.

      xyz_now = this%xyz_ions_step(1)

      !$acc data present(this%lj_forces_ions(:,:,:))

      if (this%lj%num_species > 0) then
         !$acc kernels
         this%lj_forces_ions(:,:,:) = 0.0_wp
         !$acc end kernels
         this%lj_stress_tensor(:,:,:) = 0.0_wp
         call MW_lennard_jones_melt_forces(this%num_PBC, this%localwork, this%lj, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%type_ions, this%electrodes, this%xyz_atoms, &
               this%lj_forces_ions(:,:,2),this%lj_stress_tensor(:,:,2), this%electrode_compute_force, soft)

         if (this%num_molecule_types > 0) then
            call MW_lennard_jones_melt_fix_molecule_forces(this%num_PBC, this%localwork, this%lj, &
                  this%molecules, this%box, this%ions, this%xyz_ions(:,:,xyz_now), &
                  this%lj_forces_ions(:,:,3),this%lj_stress_tensor(:,:,3), soft)
         end if
         !$acc parallel loop collapse(2)
         do ixyz = 1, 3
            do iion = 1, this%num_ions
               this%lj_forces_ions(iion,ixyz,1)         &
                     = this%lj_forces_ions(iion,ixyz,2) &  ! lj forces
                     + this%lj_forces_ions(iion,ixyz,3)    ! lj correction for molecules

               this%intermolecular_forces_ions(iion,ixyz,1)         &
                     = this%intermolecular_forces_ions(iion,ixyz,1) &
                     + this%lj_forces_ions(iion,ixyz,1)
            end do
         end do
         !$acc end parallel loop
         !$acc update host(this%lj_forces_ions(:,:,:))

         do ixyz = 1,3
            do jxyz = ixyz,3
               this%lj_stress_tensor(ixyz,jxyz,1)       &
                     = this%lj_stress_tensor(ixyz,jxyz,2) &  ! lj stress_tensor
                     + this%lj_stress_tensor(ixyz,jxyz,3)    ! lj correction for molecules
            enddo
         enddo

      end if

      !$acc end data

   end subroutine forces_compute_lj

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

   subroutine forces_compute_lj_soft_core(this)
      use MW_lennard_jones, only: &
            MW_lennard_jones_melt_forces => melt_forces, &
            MW_lennard_jones_melt_fix_molecule_forces => melt_fix_molecule_forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz,jxyz
      integer :: xyz_now
      logical :: soft = .True.

      xyz_now = this%xyz_ions_step(1)

      !$acc data present(this%lj_forces_ions(:,:,:))

      if (this%lj%num_species > 0) then
         !$acc kernels
         this%lj_forces_ions(:,:,:) = 0.0_wp
         !$acc end kernels
         this%lj_stress_tensor(:,:,:) = 0.0_wp
         call MW_lennard_jones_melt_forces(this%num_PBC, this%localwork, this%lj, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%type_ions, this%electrodes, this%xyz_atoms, &
               this%lj_forces_ions(:,:,2),this%lj_stress_tensor(:,:,2),this%electrode_compute_force, soft)

         if (this%num_molecule_types > 0) then
            call MW_lennard_jones_melt_fix_molecule_forces(this%num_PBC, this%localwork, this%lj, &
                  this%molecules, this%box, this%ions, this%xyz_ions(:,:,xyz_now), &
                  this%lj_forces_ions(:,:,3),this%lj_stress_tensor(:,:,3), soft)
         end if
         !$acc parallel loop collapse(2)
         do ixyz = 1, 3
            do iion = 1, this%num_ions
               this%lj_forces_ions(iion,ixyz,1)         &
                     = this%lj_forces_ions(iion,ixyz,2) &  ! lj forces
                     + this%lj_forces_ions(iion,ixyz,3)    ! lj correction for molecules

               this%intermolecular_forces_ions(iion,ixyz,1)         &
                     = this%intermolecular_forces_ions(iion,ixyz,1) &
                     + this%lj_forces_ions(iion,ixyz,1)
            end do
         end do
         !$acc end parallel loop
         !$acc update host(this%lj_forces_ions(:,:,:))

         do ixyz = 1,3
            do jxyz = ixyz,3
               this%lj_stress_tensor(ixyz,jxyz,1)       &
                     = this%lj_stress_tensor(ixyz,jxyz,2) &  ! lj stress_tensor
                     + this%lj_stress_tensor(ixyz,jxyz,3)    ! lj correction for molecules
            enddo
         enddo
      end if
      !$acc end data

   end subroutine forces_compute_lj_soft_core

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

   subroutine forces_compute_ft(this)
      use MW_fumi_tosi, only: &
            MW_fumi_tosi_melt_forces => melt_forces, &
            MW_fumi_tosi_melt_fix_molecule_forces => melt_fix_molecule_forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz,jxyz
      integer :: xyz_now

      xyz_now = this%xyz_ions_step(1)

      !$acc data present(this%ft_forces_ions(:,:,:))

      if (this%ft%num_species > 0) then
         !$acc kernels
         this%ft_forces_ions(:,:,:) = 0.0_wp
         !$acc end kernels
         this%ft_stress_tensor(:,:,:) = 0.0_wp
         call MW_fumi_tosi_melt_forces(this%num_PBC, this%localwork, this%ft, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, &
               this%ft_forces_ions(:,:,2),this%ft_stress_tensor(:,:,2), this%electrode_compute_force)

         if (this%num_molecule_types > 0) then
            call MW_fumi_tosi_melt_fix_molecule_forces(this%num_PBC, this%localwork, this%ft, &
                  this%molecules, this%box, this%ions, this%xyz_ions(:,:,xyz_now), &
                  this%ft_forces_ions(:,:,3),this%ft_stress_tensor(:,:,3))
         end if
         !$acc parallel loop collapse(2)
         do ixyz = 1, 3
            do iion = 1, this%num_ions
               this%ft_forces_ions(iion,ixyz,1)        &
                     = this%ft_forces_ions(iion,ixyz,2) &      ! ft forces
                     + this%ft_forces_ions(iion,ixyz,3)        ! ft correction for molecules

               this%intermolecular_forces_ions(iion,ixyz,1)         &
                     = this%intermolecular_forces_ions(iion,ixyz,1) &
                     + this%ft_forces_ions(iion,ixyz,1)
            end do
         end do
         !$acc end parallel loop
         !$acc update host(this%ft_forces_ions(:,:,:))

         do ixyz = 1,3
            do jxyz = ixyz,3
               this%ft_stress_tensor(ixyz,jxyz,1)       &
                     = this%ft_stress_tensor(ixyz,jxyz,2) &  ! ft stress_tensor
                     + this%ft_stress_tensor(ixyz,jxyz,3)    ! ft correction for molecules
            enddo
         enddo

      end if
      !$acc end data

   end subroutine forces_compute_ft

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

   subroutine forces_compute_xft(this)
      use MW_xft, only: &
            MW_xft_melt_forces => melt_forces, &
            MW_xft_melt_fix_molecule_forces => melt_fix_molecule_forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz,jxyz
      integer :: xyz_now

      xyz_now = this%xyz_ions_step(1)

      !$acc data present(this%xft_forces_ions(:,:,:))

      if (this%xft%num_species > 0) then
         !$acc kernels
         this%xft_forces_ions(:,:,:) = 0.0_wp
         !$acc end kernels
         this%xft_stress_tensor(:,:,:) = 0.0_wp
         call MW_xft_melt_forces(this%num_PBC, this%localwork, this%xft, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, &
               this%xft_forces_ions(:,:,2),this%xft_stress_tensor(:,:,2), this%electrode_compute_force)

         if (this%num_molecule_types > 0) then
            call MW_xft_melt_fix_molecule_forces(this%num_PBC, this%localwork, this%xft, &
                  this%molecules, this%box, this%ions, this%xyz_ions(:,:,xyz_now), &
                  this%xft_forces_ions(:,:,3),this%xft_stress_tensor(:,:,3))
         end if
         !$acc parallel loop collapse(2)
         do ixyz = 1, 3
            do iion = 1, this%num_ions
               this%xft_forces_ions(iion,ixyz,1)        &
                     = this%xft_forces_ions(iion,ixyz,2) &      ! xft forces
                     + this%xft_forces_ions(iion,ixyz,3)        ! xft correction for molecules

               this%intermolecular_forces_ions(iion,ixyz,1)         &
                     = this%intermolecular_forces_ions(iion,ixyz,1) &
                     + this%xft_forces_ions(iion,ixyz,1)
            end do
         end do
         !$acc end parallel loop
         !$acc update host(this%xft_forces_ions(:,:,:))

         do ixyz = 1,3
            do jxyz = ixyz,3
               this%xft_stress_tensor(ixyz,jxyz,1)       &
                     = this%xft_stress_tensor(ixyz,jxyz,2) &  ! xft stress_tensor
                     + this%xft_stress_tensor(ixyz,jxyz,3)    ! xft correction for molecules
            enddo
         enddo

      end if
      !$acc end data

   end subroutine forces_compute_xft

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

   subroutine forces_compute_daim(this)
      use MW_daim, only: &
            MW_daim_melt_forces => melt_forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz,jxyz
      integer :: xyz_now, radius_now

      xyz_now = this%xyz_ions_step(1)
      radius_now = this%radius_ions_step(1)

      !$acc data present(this%daim_forces_ions(:,:,:))

      if (this%daim%num_species > 0) then
         !$acc kernels
         this%daim_forces_ions(:,:,:) = 0.0_wp
         !$acc end kernels
         this%daim_stress_tensor(:,:,:) = 0.0_wp
         call MW_daim_melt_forces(this%num_PBC, this%localwork, this%daim, this%box, this%ions, &
               this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, this%daim_forces_ions(:,:,2), &
               this%daim_stress_tensor(:,:,2), &
               this%deltas_ions(:,radius_now), this%epsilons_ions(:,:,radius_now), this%electrode_compute_force)

!         if (this%num_molecule_types > 0) then
!            call MW_daim_melt_fix_molecule_forces(this%num_PBC, this%localwork, this%daim, this%molecules, this%box, &
!                  this%ions, this%xyz_ions(:,:,xyz_now), &
!                  this%daim_forces_ions(:,:,3))
!         end if

         !$acc parallel loop collapse(2)
         do ixyz = 1, 3
            do iion = 1, this%num_ions
               this%daim_forces_ions(iion,ixyz,1)        &
                     = this%daim_forces_ions(iion,ixyz,2)        ! daim forces
!                    + this%daim_forces_ions(iion,ixyz,3)        ! daim correction for molecules

               this%intermolecular_forces_ions(iion,ixyz,1)         &
                     = this%intermolecular_forces_ions(iion,ixyz,1) &
                     + this%daim_forces_ions(iion,ixyz,1)
            end do
         end do
         !$acc end parallel loop
         !$acc update host(this%daim_forces_ions(:,:,:))

         do ixyz = 1,3
            do jxyz = ixyz,3
               this%daim_stress_tensor(ixyz,jxyz,1)       &
                     = this%daim_stress_tensor(ixyz,jxyz,2)    ! daim stress_tensor
!                    + this%daim_stress_tensor(ixyz,jxyz,3)    ! daim correction for molecules
            enddo
         enddo
      end if
      !$acc end data

   end subroutine forces_compute_daim

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

   subroutine forces_compute_intramolecular(this)
      use MW_harmonic_bond, only: &
            MW_harmonic_bond_forces => forces
      use MW_harmonic_angle, only: &
            MW_harmonic_angle_forces => forces
      use MW_dihedrals, only: &
            MW_dihedrals_forces => forces
      use MW_impropers, only: &
            MW_impropers_forces => forces
      use MW_born_repulsion, only: &
            MW_born_repulsion_forces => forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz,jxyz
      integer :: xyz_now

      xyz_now = this%xyz_ions_step(1)

      !$acc data present(this%intramolecular_forces_ions(:,:,:)) 

      ! Intramolecular forces
      ! ---------------------
      !$acc kernels
      this%intramolecular_forces_ions(:,:,:) = 0.0_wp
      !$acc end kernels
      this%intramolecular_stress_tensor(:,:,:) = 0.0_wp

      if (this%num_molecule_types > 0) then
         call MW_harmonic_bond_forces(this%num_PBC, this%localwork, this%box, this%molecules, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%intramolecular_forces_ions(:,:,2), &
               this%intramolecular_stress_tensor(:,:,2))
         call MW_harmonic_angle_forces(this%num_PBC, this%localwork, this%box, this%molecules, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%intramolecular_forces_ions(:,:,3),  &
               this%intramolecular_stress_tensor(:,:,3))
         call MW_dihedrals_forces(this%num_PBC, this%localwork, this%box, this%molecules, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%intramolecular_forces_ions(:,:,4), &
               this%intramolecular_stress_tensor(:,:,4))
!         call MW_born_repulsion_forces(this%num_PBC, this%localwork, this%box, this%molecules, &
!               this%ions, this%xyz_ions(:,:,xyz_now), this%intramolecular_forces_ions(:,:,4))
         call MW_impropers_forces(this%num_PBC, this%localwork, this%box, this%molecules, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%intramolecular_forces_ions(:,:,5), &
               this%intramolecular_stress_tensor(:,:,5))
      end if

      !$acc parallel loop collapse(2)
      do ixyz = 1, 3
         do iion = 1, this%num_ions
            this%intramolecular_forces_ions(iion,ixyz,1)         &
                  = this%intramolecular_forces_ions(iion,ixyz,2) & ! harmonic bonds
                  + this%intramolecular_forces_ions(iion,ixyz,3) & ! harmonic angles
                  + this%intramolecular_forces_ions(iion,ixyz,4) & ! dihedrals
                  + this%intramolecular_forces_ions(iion,ixyz,5)   ! impropers
         end do
      end do
      !$acc end parallel loop
      !$acc update host(this%intramolecular_forces_ions(:,:,:))

      do ixyz = 1,3
         do jxyz = ixyz,3
            this%intramolecular_stress_tensor(ixyz,jxyz,1)       &
                  = this%intramolecular_stress_tensor(ixyz,jxyz,2) &   ! harmonic bonds
                  + this%intramolecular_stress_tensor(ixyz,jxyz,3) &   ! harmonic angles
                  + this%intramolecular_stress_tensor(ixyz,jxyz,4) &   ! dihedrals
                  + this%intramolecular_stress_tensor(ixyz,jxyz,5)     ! impropers
         enddo
      enddo
      !$acc end data

   end subroutine forces_compute_intramolecular

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

   subroutine forces_compute_steele(this)
      use MW_steele, only: MW_steele_melt_forces => melt_forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: xyz_now, iion

      xyz_now = this%xyz_ions_step(1)

      !$acc data present(this%steele_forces_ions(:))

      ! Careful this adds directly to the intermolecular force
      if (this%steele%num_walls > 0) then
         !$acc kernels
         this%steele_forces_ions(:) = 0.0_wp
         !$acc end kernels
         this%steele_stress_tensor(:,:) = 0.0_wp
         call MW_steele_melt_forces(this%num_PBC, this%localwork, this%steele, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, &
               this%steele_forces_ions(:), this%steele_stress_tensor(:,:), &
               this%electrode_compute_force)
         !$acc update host(this%steele_forces_ions(:))
         do iion = 1, this%num_ions
            this%intermolecular_forces_ions(iion,3,1)         &
                  = this%intermolecular_forces_ions(iion,3,1) &
                  + this%steele_forces_ions(iion)
         end do

      end if
      !$acc end data

   end subroutine forces_compute_steele

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

   subroutine forces_compute_external_field(this)
      use MW_external_field, only: &
            MW_external_field_melt_forces => melt_forces

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----
      integer :: iion, ixyz
      integer :: qatoms_now

      qatoms_now = this%q_atoms_step(1)

      !$acc data present(this%external_field_forces_ions(:,:,:))

      ! External field forces
      ! ---------------------
      !$acc kernels
      this%external_field_forces_ions(:,:,:) = 0.0_wp
      !$acc end kernels

      if (this%field%field_type > 0) then
         call MW_external_field_melt_forces(this%field, this%box, this%electrodes, &
               this%ions, this%xyz_atoms, this%q_atoms(:,qatoms_now), this%polarization_field(:,:), &
               this%external_field_forces_ions(:,:,this%field%field_type))

         !$acc parallel loop collapse(2)
         do ixyz = 1, 3
            do iion = 1, this%num_ions
               this%external_field_forces_ions(iion,ixyz,1) &
               = this%external_field_forces_ions(iion,ixyz,2) & ! force due to the electric field
               + this%external_field_forces_ions(iion,ixyz,3)   ! force due to the electric displacement
            enddo
         enddo
         !$acc end parallel loop
         !$acc update host(this%external_field_forces_ions(:,:,:))
      endif
      !$acc end data

   end subroutine forces_compute_external_field

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

#ifdef MW_USE_PLUMED
   subroutine forces_compute_plumed(this, istep)
      use MW_plumed, only: MW_plumed_calc => calc

      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: istep

      ! Local
      ! ----
      integer :: xyz_now

      xyz_now = this%xyz_ions_step(1)

      ! Plumed forces
      ! -------------
      this%plumed_forces_ions(:,:,:) = 0.0_wp

      if (this%plumedlog) then
         call MW_plumed_calc(istep, this%plumed_forces_ions(:, :, 1), &
               this%xyz_ions(:, :, xyz_now), this%box, this%energy)
      end if

   end subroutine forces_compute_plumed
#endif

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

   subroutine forces_gather(this)
      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! ----

      integer :: iion, ixyz
      integer :: forces_now

      ! oldest step will be overwritten by next step
      forces_now = this%forces_ions_step(1)
      !$acc parallel loop collapse(2)
      do ixyz = 1, 3
         do iion = 1, this%num_ions
            this%forces_ions(iion,ixyz,forces_now) &
                  = this%coulomb_forces_ions(iion,ixyz,1)        & ! coulomb forces
                  + this%intramolecular_forces_ions(iion,ixyz,1) & ! intramolecular forces
                  + this%intermolecular_forces_ions(iion,ixyz,1) & ! intermolecular forces
#ifdef MW_USE_PLUMED
                  + this%plumed_forces_ions(iion,ixyz,1) &  ! plumed forces
#endif
                  + this%external_field_forces_ions(iion,ixyz,1)   ! external field forces
         end do
      end do

      !$acc end parallel loop


   end subroutine forces_gather

   !================================================================================
   !> Compute forces on melt ions
   !!
   subroutine ions_forces_compute(this, istep)
      use MW_parallel, only: MW_parallel_t
      use MW_four_site_model, only: MW_four_site_model_redistribute_force => redistribute_force
      implicit none

      ! Parameter
      ! ---------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: istep

      ! Local
      ! ----
      integer :: ixyz, ielec
      integer :: xyz_now, forces_now

      !$acc data &
      !$acc present(this, this%forces_ions(:,:,:), &
      !$acc         this%coulomb_forces_ions(:,:,:), this%intermolecular_forces_ions(:,:,:), &
      !$acc         this%intramolecular_forces_ions(:,:,:), this%lj_forces_ions(:,:,:), &
      !$acc         this%ft_forces_ions(:,:,:), this%xft_forces_ions(:,:,:), &
      !$acc         this%daim_forces_ions(:,:,:), this%steele_forces_ions(:), &
      !$acc         this%external_field_forces_ions(:,:,:))

      forces_now = this%forces_ions_step(1)
      xyz_now = this%xyz_ions_step(1)

      do ielec = 1, size(this%electrodes, 1)
         this%electrodes(ielec)%force_ions(:,:) = 0.0_wp
      end do

      ! Coulomb forces
      ! ---------------------
      call forces_compute_coulomb(this)

      ! Intermolecular forces
      ! ---------------------
      !$acc kernels
      this%intermolecular_forces_ions(:,:,:) = 0.0_wp
      !$acc end kernels
      call forces_compute_ft(this)
      call forces_compute_xft(this)
      call forces_compute_daim(this)
      call forces_compute_lj(this)
      call forces_compute_steele(this)

      ! Intramolecular forces
      ! ---------------------
      call forces_compute_intramolecular(this)

      ! External field
      ! ---------------
      call forces_compute_external_field(this)

      ! Plumed forces
      ! -------------
#ifdef MW_USE_PLUMED
      call forces_compute_plumed(this, istep)
#endif

      call forces_gather(this)

      if (this%electrode_compute_force) then
         do ielec = 1, size(this%electrodes, 1)
            do ixyz = 1, 3
               this%electrodes(ielec)%force_ions(ixyz, 1) = this%electrodes(ielec)%force_ions(ixyz, 2) +&
                     this%electrodes(ielec)%force_ions(ixyz, 3) +&
                     this%electrodes(ielec)%force_ions(ixyz, 4) +&
                     this%electrodes(ielec)%force_ions(ixyz, 5) +&
                     this%electrodes(ielec)%force_ions(ixyz, 6) +&
                     this%electrodes(ielec)%force_ions(ixyz, 7) +&
                     this%electrodes(ielec)%force_ions(ixyz, 8) +&
                     this%electrodes(ielec)%force_ions(ixyz, 9) +&
                     this%electrodes(ielec)%force_ions(ixyz, 10)
            end do
         end do
      end if

      ! Redistribute force of the dummy atom if 4-site model
      if (this%four_site_model) then
         this%four_site_stress_tensor(:,:) = 0.0_wp
         call MW_four_site_model_redistribute_force(this%num_pbc, this%localwork, this%molecules, &
               this%xyz_ions(:,:,xyz_now), this%forces_ions(:,:,forces_now), this%box, this%ions, &
               this%four_site_stress_tensor)
      end if

      !$acc update host (this%forces_ions(:,:,:), this%coulomb_forces_ions(:,:,:), this%intermolecular_forces_ions(:,:,:))
      !$acc end data

   end subroutine ions_forces_compute

   !================================================================================
   !> Updates ion positions
   subroutine ions_positions_update(this,parallel)
      use MW_parallel, only: MW_parallel_t
      use MW_box, only: &
            MW_box_update_type => update_type, &
            MW_box_fix_outliers => fix_outliers, &
            MW_box_unwrap => unwrap
      use MW_molecule, only: &
            CONSTRAINT_ALGORITHM_RATTLE, CONSTRAINT_ALGORITHM_LINEAR
      use MW_rattle, only: &
            MW_rattle_constrain_positions => constrain_positions
      use MW_linear_molecule, only: &
            MW_linear_molecule_constrain_positions => constrain_positions
      use MW_thermostat, only: &
            MW_thermostat_ions_positions_update => ions_positions_update
      use MW_barostat, only: &
            MW_barostat_ions_positions_update => ions_positions_update
      use MW_ewald, only: &
            MW_ewald_redefine_kpoints => redefine_kpoints
      use MW_external_field, only:&
            MW_external_field_melt_polarization => melt_polarization
      use MW_four_site_model, only: &
            MW_four_site_model_compute_fourth_site_position => compute_fourth_site_position
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this
      type(MW_parallel_t), intent(inout) :: parallel

      ! Local
      ! -----
      integer :: xyz_now, velocity_now
      integer :: xyz_prev, velocity_prev, forces_prev
      integer :: itype, ixyz, offset, iion, count
      integer :: num_ions
      real(wp) :: massrec, dt, halfdt
      integer :: max_iteration
      real(wp) :: tolerance
      real(wp) :: volume_prev, volume_factor

      ! Pointer to current step data
      xyz_now = this%xyz_ions_step(1)
      velocity_now = this%velocity_ions_step(1)

      ! Pointer to previous step data
      xyz_prev = this%xyz_ions_step(2)
      velocity_prev = this%velocity_ions_step(2)
      forces_prev = this%forces_ions_step(2)

      ! Update positions and velocities with forces computed at previous time step
      dt = this%dt
      halfdt = 0.5_wp*dt
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%mobile .and. (.not. this%ions(itype)%is_fourth_site)) then
            massrec = 1.0_wp / this%ions(itype)%mass
            offset = this%ions(itype)%offset
            count = this%ions(itype)%count
            do ixyz = 1, 3
               do iion = offset+1, offset+count
                  this%velocity_ions(iion,ixyz,velocity_now) = this%velocity_ions(iion,ixyz,velocity_prev) + &
                        halfdt * massrec * this%forces_ions(iion,ixyz,forces_prev)
                  this%xyz_ions(iion,ixyz,xyz_now) = this%xyz_ions(iion,ixyz,xyz_prev) + &
                        dt * this%velocity_ions(iion,ixyz,velocity_now)
               end do
            end do
         else ! fixed ions
            offset = this%ions(itype)%offset
            count = this%ions(itype)%count
            do ixyz = 1, 3
               do iion = offset+1, offset+count
                  this%xyz_ions(iion,ixyz,xyz_now) = this%xyz_ions(iion,ixyz,xyz_prev)
               end do
            end do
         end if
      end do

      ! Thermostat positions and velocities
      if (this%thermostat%chain_length > 0) then
         call MW_thermostat_ions_positions_update(this%thermostat, this%dt, &
               this%ions, &
               this%velocity_ions(:,:,velocity_prev), &
               this%xyz_ions(:,:,xyz_now), this%velocity_ions(:,:,velocity_now))
      end if

      ! Barostat positions and velocities
      if (this%barostat%ions_th_chain_length > 0) then
         volume_prev = this%box%volume
         call MW_barostat_ions_positions_update(this%barostat, this%num_dof, this%dt, &
               this%ions, this%velocity_ions(:,:,velocity_prev), &
               this%xyz_ions(:,:,xyz_now), this%velocity_ions(:,:,velocity_now),this%box%volume)
         volume_factor = this%box%volume / volume_prev
         call MW_box_update_type(this%box, volume_factor)
         num_ions = size(this%xyz_ions,1)
         call MW_ewald_redefine_kpoints(this%ewald, this%box, num_ions)
         call redistribute_work(this, parallel)
      end if


      ! Rattle positions and velocities to enforce constraints on positions
      this%constraint_stress_tensor(:,:)=0.0_wp
      do itype = 1, this%num_molecule_types
         if (this%molecules(itype)%constraint_algorithm == CONSTRAINT_ALGORITHM_RATTLE) then
            max_iteration = this%molecules(itype)%constraint_max_iteration
            tolerance = this%molecules(itype)%constraint_tolerance
            call MW_rattle_constrain_positions(this%num_PBC, this%molecules(itype), &
                  this%dt, this%box, this%ions, &
                  this%xyz_ions(:,:,xyz_prev), this%xyz_ions(:,:,xyz_now), &
                  this%velocity_ions(:,:,velocity_now),this%constraint_stress_tensor)
         else if (this%molecules(itype)%constraint_algorithm == CONSTRAINT_ALGORITHM_LINEAR) then
            call MW_linear_molecule_constrain_positions(this%num_PBC, this%molecules(itype), &
                  this%dt, this%box, this%ions, &
                  this%xyz_ions(:,:,xyz_prev), this%forces_ions(:,:,xyz_prev),&
                  this%xyz_ions(:,:,xyz_now), this%velocity_ions(:,:,velocity_now))
         end if
      end do

      ! Make sure ions are all in the box
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%mobile .and. (.not. this%ions(itype)%is_fourth_site)) then
            massrec = 1.0_wp / this%ions(itype)%mass
            offset = this%ions(itype)%offset
            count = this%ions(itype)%count
            do iion = offset+1, offset + count
               call MW_box_fix_outliers(this%box, this%num_PBC, this%xyz_ions(iion,1,xyz_now), &
                     this%xyz_ions(iion,2,xyz_now), this%xyz_ions(iion,3,xyz_now))
            end do
         end if
      end do

      ! Update the polarization of the ions
      call MW_external_field_melt_polarization(this%num_PBC, this%box, this%ions, &
           this%xyz_ions(:,:,xyz_prev), this%xyz_ions(:,:,xyz_now), this%polarization_field(:,:))

      ! Update four site atom 
      call MW_four_site_model_compute_fourth_site_position(this%num_pbc, this%molecules, &
            this%xyz_ions(:,:,xyz_now), this%box, this%ions)

      ! Unwrap ion positions (will this work for the fourth site?)
      if (this%store_xyz_ions_unwrapped) then 
         do itype = 1, this%num_ion_types
            ! Unwrap all non-virtual sites 
            if (this%ions(itype)%mobile .and. (.not. this%ions(itype)%is_fourth_site)) then
               offset = this%ions(itype)%offset
               count = this%ions(itype)%count
               do iion = offset+1, offset+count
                  call MW_box_unwrap(this%box, this%num_PBC,   &
                     this%xyz_ions_unwrapped(iion,1,xyz_now),  &
                     this%xyz_ions_unwrapped(iion,2,xyz_now),  &
                     this%xyz_ions_unwrapped(iion,3,xyz_now),  &
                     this%xyz_ions_unwrapped(iion,1,xyz_prev), &
                     this%xyz_ions_unwrapped(iion,2,xyz_prev), &
                     this%xyz_ions_unwrapped(iion,3,xyz_prev), &
                     this%xyz_ions(iion,1,xyz_now),            &
                     this%xyz_ions(iion,2,xyz_now),            &
                     this%xyz_ions(iion,3,xyz_now))
               end do
            end if
         end do
         ! Unwrap all virtual sites 
         call MW_four_site_model_compute_fourth_site_position(this%num_pbc, this%molecules, &
              this%xyz_ions_unwrapped(:,:,xyz_now), this%box, this%ions)
      end if 

      !$acc data present(this%xyz_ions(:,:,:))
      !$acc update device(this%xyz_ions(:,:,:))
      !$acc end data
   end subroutine ions_positions_update

   !================================================================================
   !> Determine the initial contribution of constraints to stress tensor
   subroutine initial_constraints_stress_tensor_compute(this)

      use MW_molecule, only: &
            CONSTRAINT_ALGORITHM_RATTLE!, CONSTRAINT_ALGORITHM_LINEAR
      use MW_rattle, only: &
            MW_rattle_constrain_positions => constrain_positions
!     use MW_linear_molecule, only: &
!           MW_linear_molecule_constrain_positions => constrain_positions
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: xyz_now, velocity_now, forces_now
      integer :: itype, ixyz, offset, iion, count
      real(wp) :: massrec, dt, halfdt
      integer :: max_iteration
      real(wp) :: tolerance
      real(wp), allocatable :: local_velocity_ions(:,:), local_xyz_ions(:,:)

      ! Pointer to current step data
      xyz_now = this%xyz_ions_step(1)
      velocity_now = this%velocity_ions_step(1)
      forces_now = this%forces_ions_step(1)

      allocate(local_velocity_ions(size(this%velocity_ions,1),size(this%velocity_ions,2)))
      allocate(local_xyz_ions(size(this%xyz_ions,1),size(this%xyz_ions,2)))

      ! Update positions and velocities with forces computed at previous time step
      dt = this%dt
      halfdt = 0.5_wp*dt
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%mobile .and. (.not. this%ions(itype)%is_fourth_site)) then
            massrec = 1.0_wp / this%ions(itype)%mass
            offset = this%ions(itype)%offset
            count = this%ions(itype)%count
            do ixyz = 1, 3
               do iion = offset+1, offset+count
                  local_velocity_ions(iion,ixyz) = this%velocity_ions(iion,ixyz,velocity_now) + &
                        halfdt * massrec * this%forces_ions(iion,ixyz,forces_now)
                  local_xyz_ions(iion,ixyz) = this%xyz_ions(iion,ixyz,xyz_now) + &
                        dt * local_velocity_ions(iion,ixyz)
               end do
            end do
         else ! fixed ions
            offset = this%ions(itype)%offset
            count = this%ions(itype)%count
            do ixyz = 1, 3
               do iion = offset+1, offset+count
                  local_xyz_ions(iion,ixyz) = this%xyz_ions(iion,ixyz,xyz_now)
               end do
            end do
         end if
      end do

      ! Rattle positions and velocities to enforce constraints on positions
      this%constraint_stress_tensor(:,:)=0.0_wp
      do itype = 1, this%num_molecule_types
         if (this%molecules(itype)%constraint_algorithm == CONSTRAINT_ALGORITHM_RATTLE) then
            max_iteration = this%molecules(itype)%constraint_max_iteration
            tolerance = this%molecules(itype)%constraint_tolerance
            call MW_rattle_constrain_positions(this%num_PBC, this%molecules(itype), &
                  this%dt, this%box, this%ions, &
                  this%xyz_ions(:,:,xyz_now), local_xyz_ions(:,:), &
                  local_velocity_ions(:,:),this%constraint_stress_tensor)
!        else if (this%molecules(itype)%constraint_algorithm == CONSTRAINT_ALGORITHM_LINEAR) then
!           call MW_linear_molecule_constrain_positions(this%num_PBC, this%molecules(itype), &
!                 this%dt, this%box, this%ions, &
!                 this%xyz_ions(:,:,xyz_prev), this%forces_ions(:,:,xyz_prev),&
!                 this%xyz_ions(:,:,xyz_now), this%velocity_ions(:,:,velocity_now))
         end if
      end do

      deallocate(local_velocity_ions)
      deallocate(local_xyz_ions)

   end subroutine initial_constraints_stress_tensor_compute


   !================================================================================
   !> Update piston positions
   subroutine piston_positions_update(this)
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: itype, offset, iion, count
      real(wp) :: massrec, dt, halfdt

      ! Update positions and velocities with forces computed at previous time step
      dt = this%dt
      halfdt = 0.5_wp*dt
      do itype = 1, this%num_elec
         if (this%electrodes(itype)%piston) then
            massrec = 1.0_wp / this%electrodes(itype)%mass
            this%electrodes(itype)%velocity = this%electrodes(itype)%velocity + &
                  halfdt * massrec * (this%electrodes(itype)%force_ions(3,1) + &
                  this%electrodes(itype)%piston_pressure * this%box%area(3))
            offset = this%electrodes(itype)%offset
            count = this%electrodes(itype)%count
            do iion = offset+1, offset+count
               this%xyz_atoms(iion,3) = this%xyz_atoms(iion,3) + &
                     dt * this%electrodes(itype)%velocity
            end do
         end if
      end do

      call comz_compute(this, .false.)

   end subroutine piston_positions_update

   !================================================================================
   !> Updates ion velocities
   subroutine ions_velocities_update(this, istep)
      use MW_molecule, only: &
            CONSTRAINT_ALGORITHM_RATTLE, CONSTRAINT_ALGORITHM_LINEAR
      use MW_rattle, only: &
            MW_rattle_constrain_velocities => constrain_velocities
      use MW_linear_molecule, only: &
            MW_linear_molecule_constrain_velocities => constrain_velocities
      use MW_thermostat, only: &
            MW_thermostat_ions_velocities_update => ions_velocities_update
      use MW_barostat, only: &
            MW_barostat_ions_velocities_update => ions_velocities_update
      use MW_velocity, only: &
            MW_velocity_rescale => rescale, &
            MW_velocity_com_velocity_compute => com_velocity_compute, &
            MW_velocity_com_velocity_reset => com_velocity_reset
      use MW_constants, only: boltzmann
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: istep

      ! Local
      ! -----
      integer :: xyz_now, velocity_now, forces_now
      real(wp) :: dt, halfdt
      integer :: itype, i, ixyz, offset, iion
      real(wp) :: massrec
      integer :: max_iteration
      real(wp) :: tolerance, threshold
      real(wp) :: com_vx, com_vy, com_vz, com_ke
      real(wp) :: configurational_pressure

      ! Pointer to current step data
      xyz_now = this%xyz_ions_step(1)
      velocity_now = this%velocity_ions_step(1)
      forces_now = this%forces_ions_step(1)


      ! Update velocities with forces computed at current time step
      dt = this%dt
      halfdt = 0.5_wp*dt
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%mobile .and. (.not. this%ions(itype)%is_fourth_site)) then
            massrec = 1.0_wp / this%ions(itype)%mass
            offset = this%ions(itype)%offset
            do i = 1, this%ions(itype)%count
               iion = offset + i
               do ixyz = 1, 3
                  this%velocity_ions(iion,ixyz,velocity_now) = this%velocity_ions(iion,ixyz,velocity_now) + &
                        halfdt * massrec * this%forces_ions(iion,ixyz,forces_now)
               end do
            end do
         end if
      end do

      ! Rattle velocities to enforce constraints on velocities
      do itype = 1, this%num_molecule_types
         if (this%molecules(itype)%constraint_algorithm == CONSTRAINT_ALGORITHM_RATTLE) then
            max_iteration = this%molecules(itype)%constraint_max_iteration
            tolerance = this%molecules(itype)%constraint_tolerance
            call MW_rattle_constrain_velocities(this%num_PBC, this%molecules(itype), &
                  this%box, this%ions, &
                  this%xyz_ions(:,:,xyz_now), this%velocity_ions(:,:,velocity_now))
         else if (this%molecules(itype)%constraint_algorithm == CONSTRAINT_ALGORITHM_LINEAR) then
            call MW_linear_molecule_constrain_velocities(this%num_PBC, this%molecules(itype), &
                  this%dt, this%box, this%ions, &
                  this%xyz_ions(:,:,xyz_now), this%forces_ions(:,:,xyz_now), &
                  this%velocity_ions(:,:,velocity_now))
         end if
      end do

      ! Update configurational pressure for the barostat
      ! Scale velocities with barostat
      if (this%barostat%ions_th_chain_length > 0) then
         call configurational_pressure_compute(this,configurational_pressure)
         call MW_barostat_ions_velocities_update(this%barostat, this%num_dof, this%dt, this%ions, &
               this%velocity_ions(:,:,velocity_now), configurational_pressure, this%box%volume)
      end if


      ! Scale velocities with thermostat
      if (this%thermostat%chain_length > 0) then
         call MW_thermostat_ions_velocities_update(this%thermostat, this%num_dof, this%dt, this%ions, &
               this%velocity_ions(:,:,velocity_now))
      end if


      ! Or scale method
      if (this%velocity_scale) then
         if ((this%velocity_scale_period > 0) .and. (mod(istep, this%velocity_scale_period) == 0)) then
            call MW_velocity_rescale(this%velocity_scale_method, this%velocity_scale_temperature, &
                  this%num_dof, this%molecules, this%ions, this%velocity_ions(:,:,velocity_now))
         end if
      end if

      ! Make sure COM does not move too much
      threshold = this%velocity_com_threshold * this%num_dof * this%temperature * boltzmann
      call MW_velocity_com_velocity_compute(this%ions, &
            this%velocity_ions(:,1,velocity_now), &
            this%velocity_ions(:,2,velocity_now), &
            this%velocity_ions(:,3,velocity_now), &
            com_vx, com_vy, com_vz, com_ke)
      if (com_ke > threshold) then
         call MW_velocity_com_velocity_reset(com_vx, com_vy, com_vz, this%ions, &
            this%velocity_ions(:,1,velocity_now), &
            this%velocity_ions(:,2,velocity_now), &
            this%velocity_ions(:,3,velocity_now))
      end if

   end subroutine ions_velocities_update

   !================================================================================
   !> Updates ion velocities
   subroutine piston_velocities_update(this)
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      real(wp) :: dt, halfdt
      integer :: itype
      real(wp) :: massrec

      ! Update velocities with forces computed at current time step
      dt = this%dt
      halfdt = 0.5_wp*dt
      do itype = 1, this%num_elec
         if (this%electrodes(itype)%piston) then
            massrec = 1.0_wp / this%electrodes(itype)%mass
            this%electrodes(itype)%velocity = this%electrodes(itype)%velocity + &
                  halfdt * massrec * (this%electrodes(itype)%force_ions(3,1) + &
                  this%electrodes(itype)%piston_pressure * this%box%area(3))
         end if
      end do

   end subroutine piston_velocities_update

   ! ================================================================================
   ! Compute kinetic energy and temperature of the system
   subroutine temperature_compute(this, temperature_issue)
      use MW_kinds, only: wp
      use MW_constants, only: boltzmann
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      logical, intent(out) :: temperature_issue

      ! Local
      ! -----
      call kinetic_energy_compute(this, temperature_issue)
      this%temperature = this%kinetic_energy * (2.0_wp / (real(this%num_dof,wp)*boltzmann))
   end subroutine temperature_compute

   !================================================================================
   subroutine kinetic_energy_compute(this, temperature_issue)
      use MW_kinds, only: wp
      use MW_constants, only: boltzmann
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      logical, intent(out) :: temperature_issue

      ! Local
      ! -----
      integer :: iion, ixyz, itype, i, offset
      integer :: velocity_now
      real(wp) :: mi, visq, tol_max, target_kin_energy
      real(wp), parameter :: tolmax_factor = 50

      temperature_issue = .False.
      target_kin_energy = this%temperature * boltzmann
      tol_max = tolmax_factor*target_kin_energy

      velocity_now = this%velocity_ions_step(1)

      this%kinetic_energy = 0.0_wp
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%mobile .and. (.not. this%ions(itype)%is_fourth_site)) then
            mi = this%ions(itype)%mass
            offset = this%ions(itype)%offset
            do i = 1, this%ions(itype)%count
               iion = offset + i
               visq = 0.0_wp
               do ixyz = 1, 3
                  visq = visq + this%velocity_ions(iion,ixyz,velocity_now)*this%velocity_ions(iion,ixyz,velocity_now)
               end do
               visq = mi*visq
               this%kinetic_energy = this%kinetic_energy + visq
            end do
         end if
      end do
      this%kinetic_energy = 0.5_wp * this%kinetic_energy

   end subroutine kinetic_energy_compute

   !================================================================================
   subroutine pressure_compute(this)
      use MW_kinds, only: wp
      use MW_lennard_jones, only: &
            MW_lennard_jones_stress_tensor_tail_correction => stress_tensor_tail_correction
      use MW_fumi_tosi, only: &
            MW_fumi_tosi_stress_tensor_tail_correction => stress_tensor_tail_correction
      use MW_xft, only: &
            MW_xft_stress_tensor_tail_correction => stress_tensor_tail_correction
      use MW_daim, only: &
            MW_daim_stress_tensor_tail_correction => stress_tensor_tail_correction
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: iion, ixyz, itype, i, offset,jxyz
      integer :: velocity_now
      real(wp) :: mi,volfactor

      velocity_now = this%velocity_ions_step(1)

      volfactor=1.0_wp/this%box%volume

      call stress_tensor_compute(this)

      this%pressure_tensor = this%stress_tensor

      do itype = 1, this%num_ion_types
         if (this%ions(itype)%mobile .and. (.not. this%ions(itype)%is_fourth_site)) then
            mi = this%ions(itype)%mass
            offset = this%ions(itype)%offset
            do i = 1, this%ions(itype)%count
               iion = offset + i
               do ixyz = 1, 3
                  do jxyz = ixyz,3
                     this%pressure_tensor(ixyz,jxyz) = this%pressure_tensor(ixyz,jxyz) + &
                        mi*this%velocity_ions(iion,ixyz,velocity_now)*this%velocity_ions(iion,jxyz,velocity_now)*volfactor
                  end do
               end do
            end do
         end if
      end do
      this%pressure_tensor(2,1)=this%pressure_tensor(1,2)
      this%pressure_tensor(3,1)=this%pressure_tensor(1,3)
      this%pressure_tensor(3,2)=this%pressure_tensor(2,3)

      if(.not.this%pressure_tail_correction_computed)then
         if(this%lj%lj_3D_tail_correction)then
         ! le cas LJ softcore est a verifier
            call MW_lennard_jones_stress_tensor_tail_correction(this%lj,this%box,this%ions,this%lj_pressure_tail_correction)
         endif

         if(this%ft%ft_3D_tail_correction)then
            call MW_fumi_tosi_stress_tensor_tail_correction(this%ft,this%box,this%ions,this%ft_pressure_tail_correction)
         endif

         if(this%xft%xft_3D_tail_correction)then
            call MW_xft_stress_tensor_tail_correction(this%xft,this%box,this%ions,this%xft_pressure_tail_correction)
         endif

         if(this%daim%daim_3D_tail_correction)then
            call MW_daim_stress_tensor_tail_correction(this%daim,this%box,this%ions,this%daim_pressure_tail_correction)
         endif

         this%pressure_tail_correction = this%lj_pressure_tail_correction &
                                       + this%ft_pressure_tail_correction &
                                       + this%xft_pressure_tail_correction &
                                       + this%daim_pressure_tail_correction

         this%pressure_tail_correction = this%pressure_tail_correction * volfactor/3.0_wp

         if(this%barostat%b_th_chain_length==0)this%pressure_tail_correction_computed=.true.

      endif

      this%pressure=(this%pressure_tensor(1,1)+this%pressure_tensor(2,2)+this%pressure_tensor(3,3))/3.0_wp &
                     +this%pressure_tail_correction

   end subroutine pressure_compute

   !================================================================================
   subroutine configurational_pressure_compute(this, configurational_pressure)
      use MW_kinds, only: wp
      use MW_lennard_jones, only: &
            MW_lennard_jones_stress_tensor_tail_correction => stress_tensor_tail_correction
      use MW_fumi_tosi, only: &
            MW_fumi_tosi_stress_tensor_tail_correction => stress_tensor_tail_correction
      use MW_xft, only: &
            MW_xft_stress_tensor_tail_correction => stress_tensor_tail_correction
      use MW_daim, only: &
            MW_daim_stress_tensor_tail_correction => stress_tensor_tail_correction
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      real(wp), intent(out) :: configurational_pressure

      ! Local
      ! -----
      real(wp) :: volfactor

      volfactor=1.0_wp/this%box%volume

      call stress_tensor_compute(this)

      if(.not.this%pressure_tail_correction_computed)then
         if(this%lj%lj_3D_tail_correction)then
            ! le cas LJ softcore est a verifier
            call MW_lennard_jones_stress_tensor_tail_correction(this%lj,this%box,this%ions,this%lj_pressure_tail_correction)
         endif

         if(this%ft%ft_3D_tail_correction)then
            call MW_fumi_tosi_stress_tensor_tail_correction(this%ft,this%box,this%ions,this%ft_pressure_tail_correction)
         endif

         if(this%xft%xft_3D_tail_correction)then
            call MW_xft_stress_tensor_tail_correction(this%xft,this%box,this%ions,this%xft_pressure_tail_correction)
         endif

         if(this%daim%daim_3D_tail_correction)then
            call MW_daim_stress_tensor_tail_correction(this%daim,this%box,this%ions,this%daim_pressure_tail_correction)
         endif

         this%pressure_tail_correction = this%lj_pressure_tail_correction &
                                       + this%ft_pressure_tail_correction &
                                       + this%xft_pressure_tail_correction &
                                       + this%daim_pressure_tail_correction

         this%pressure_tail_correction = this%pressure_tail_correction * volfactor/3.0_wp

!         if(this%barostat%b_th_chain_length==0)this%pressure_tail_correction_computed=.true.
      end if

      configurational_pressure=(this%stress_tensor(1,1)+this%stress_tensor(2,2)+this%stress_tensor(3,3))/3.0_wp &
                     +this%pressure_tail_correction

   end subroutine configurational_pressure_compute

   !================================================================================
   subroutine stress_tensor_compute(this)
      use MW_kinds, only: wp
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: ixyz,jxyz
      real(wp) :: volfactor

      volfactor=1.0_wp/this%box%volume

      do ixyz = 1,3
         do jxyz = 1,3
            this%stress_tensor(ixyz,jxyz) = this%lj_stress_tensor(ixyz,jxyz,1) &
                  + this%coulomb_stress_tensor(ixyz,jxyz,1) &
                  + this%ft_stress_tensor(ixyz,jxyz,1) &
                  + this%xft_stress_tensor(ixyz,jxyz,1) &
                  + this%daim_stress_tensor(ixyz,jxyz,1) &
                  + this%intramolecular_stress_tensor(ixyz,jxyz,1)  &
                  + this%steele_stress_tensor(ixyz,jxyz) &
                  + this%constraint_stress_tensor(ixyz,jxyz) &
                  + this%four_site_stress_tensor(ixyz,jxyz)

            this%stress_tensor(ixyz,jxyz) = this%stress_tensor(ixyz,jxyz)*volfactor
         enddo
      enddo

   end subroutine stress_tensor_compute

   !================================================================================
   subroutine polarization_energy_compute(this)
      use MW_kinds, only: wp
      use MW_ion, only: MW_ion_t
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: iion, ixyz, itype, i, offset
      integer :: dipoles_now
      real(wp) :: alphai, dipisq

      dipoles_now = this%dipoles_ions_step(1)

      this%polarization_energy = 0.0_wp
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%polarizability>0.0_wp) then
            alphai = 1.0_wp/this%ions(itype)%polarizability
            offset = this%ions(itype)%offset
            do i = 1, this%ions(itype)%count
               iion = offset + i
               dipisq = 0.0_wp
               do ixyz = 1, 3
                  dipisq = dipisq + this%dipoles_ions(iion,ixyz,dipoles_now)*this%dipoles_ions(iion,ixyz,dipoles_now)
               end do
               this%polarization_energy = this%polarization_energy +alphai* dipisq
            end do
         end if
      end do
      this%polarization_energy = 0.5_wp * this%polarization_energy

   end subroutine polarization_energy_compute

   ! ================================================================================
   ! Compute electrodes com
   subroutine comz_compute(this, initial)
      use MW_kinds, only: wp
      use MW_electrode, only: MW_electrode_set_comz => set_comz
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      logical :: initial

      ! Local
      ! -----
      integer :: num_elec, ielec, iatom, i
      real(wp) :: com

      num_elec = size(this%electrodes, 1)
      do ielec = 1, num_elec
         com = 0
         do iatom = 1, this%electrodes(ielec)%count
            i = iatom + this%electrodes(ielec)%offset
            com = com + this%xyz_atoms(i, 3)
         end do
         com = com / this%electrodes(ielec)%count
         this%electrodes(ielec)%comz = com
         if (initial) then
            call MW_electrode_set_comz(this%electrodes(ielec), com)
         end if
      end do

   end subroutine comz_compute

   !================================================================================
   ! Compute the electric field gradient. 
   subroutine coulomb_melt_efg(this, step)
      use MW_coulomb, only: MW_coulomb_melt_efg => melt_efg
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: step

      ! Local
      ! -----
      integer :: xyz_now, dipoles_now

      xyz_now = this%xyz_ions_step(1)
      dipoles_now = this%dipoles_ions_step(1)

      ! Compute only at these steps, when dumping is done
      if (mod(step, this%output%efg_dump_frequency) == 0) then
         call MW_coulomb_melt_efg(this%num_PBC, this%localwork, this%ewald, this%box, &
                                  this%ions, this%xyz_ions(:,:,xyz_now), this%pimrun, &
                                  this%dipoles_ions(:,:,dipoles_now), this%efg_ions)
      end if 
   end subroutine coulomb_melt_efg

   !================================================================================
   ! Setup cos and sin for Ewald summation
   subroutine ewald_setup_cossin_ions(this)
      use MW_ewald, only: MW_ewald_setup_cossin_ions => setup_cossin_ions
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: xyz_now

      xyz_now = this%xyz_ions_step(1)
      call MW_ewald_setup_cossin_ions(this%ewald, this%box, this%xyz_ions(:,:,xyz_now))
   end subroutine ewald_setup_cossin_ions

   !================================================================================
   ! Setup cos and sin for Ewald summation
   subroutine ewald_setup_cossin_elec(this, istep)
      use MW_ewald, only: MW_ewald_setup_cossin_elec => setup_cossin_elec
      implicit none
      ! Parameter inout
      ! ---------------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: istep

      ! Local
      ! ---------------
      integer :: num_elec

      num_elec = size(this%electrodes, 1)

      if (istep == 0 .or. this%pistonrun) then
         call MW_ewald_setup_cossin_elec(this%ewald, this%box, this%xyz_atoms(:,:))
      end if

   end subroutine ewald_setup_cossin_elec

   ! ================================================================================
   ! Setup output files
   subroutine output_setup(this, rank)
      use MW_output, only: &
            MW_output_trajectories_setup => trajectories_setup, &
            MW_output_xyz_setup => xyz_setup, &
            MW_output_pdb_setup => pdb_setup, &
            MW_output_lammps_setup => lammps_setup, &
            MW_output_charges_setup => charges_setup, &
            MW_output_total_charges_setup => total_charges_setup, &
            MW_output_forces_setup => forces_setup, &
            MW_output_elec_forces_setup => elec_forces_setup, &
            MW_output_dipoles_setup => dipoles_setup, &
            MW_output_efg_setup => efg_setup, &
            MW_output_radius_setup => radius_setup, &
            MW_output_potential_shift_setup => potential_shift_setup, &
            MW_output_energies_setup => energies_setup, &
            MW_output_restart_setup => restart_setup, &
            MW_output_temperature_setup => temperature_setup, &
            MW_output_pressure_setup => pressure_setup, &
            MW_output_box_setup => box_setup, &
            MW_output_stress_tensor_setup => stress_tensor_setup, &
            MW_output_charges_set_dump_frequency => charges_set_dump_frequency, &
            MW_output_total_charges_set_dump_frequency => total_charges_set_dump_frequency, &
            MW_output_polarization_setup => polarization_setup, &
            MW_output_elec_forces_set_dump_frequency => elec_forces_set_dump_frequency, &
            MW_output_pressure_set_dump_frequency => pressure_set_dump_frequency, &
            MW_output_box_set_dump_frequency => box_set_dump_frequency, &
            MW_output_stress_tensor_set_dump_frequency => stress_tensor_set_dump_frequency, &
            MW_output_diagnostic_setup => diagnostic_setup

      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in), optional :: rank

      ! Make sure we do not try to dump charges data if do not have electrodes!
      if (this%num_elec == 0) then
         call MW_output_charges_set_dump_frequency(this%output, 0)
         call MW_output_total_charges_set_dump_frequency(this%output, 0)
         call MW_output_elec_forces_set_dump_frequency(this%output, 0)
      end if

      ! Make sure we do not try to dump pressure data if 2D PBCs!
      if (this%num_PBC == 2) then
         call MW_output_pressure_set_dump_frequency(this%output, 0)
         call MW_output_stress_tensor_set_dump_frequency(this%output, 0)
      end if

      ! Make sure we do not try to dump box data if it is not NPT
      if (this%barostat%b_th_chain_length == 0) then
         call MW_output_box_set_dump_frequency(this%output, 0)
      end if

      ! Make sure we do not try to dump pressure data if linear molecules are present!
      if (this%linear_present) then
         call MW_output_pressure_set_dump_frequency(this%output, 0)
         call MW_output_stress_tensor_set_dump_frequency(this%output, 0)
      end if

      if (present(rank)) then
         call MW_output_trajectories_setup(this%output, rank)
         call MW_output_xyz_setup(this%output, rank)
         call MW_output_pdb_setup(this%output, rank)
         call MW_output_lammps_setup(this%output, rank)
         call MW_output_charges_setup(this%output, rank)
         call MW_output_total_charges_setup(this%output, rank)
         call MW_output_forces_setup(this%output, rank)
         call MW_output_elec_forces_setup(this%output, rank)
         call MW_output_dipoles_setup(this%output, rank)
         call MW_output_radius_setup(this%output, rank)
         call MW_output_potential_shift_setup(this%output, rank)
         call MW_output_energies_setup(this%output, this%energies_to_dump, &
               this%energy_terms_to_dump, rank)
         call MW_output_restart_setup(this%output, rank)
         call MW_output_temperature_setup(this%output, rank)
         call MW_output_pressure_setup(this%output, rank)
         call MW_output_box_setup(this%output, rank)
         call MW_output_stress_tensor_setup(this%output, rank)
         call MW_output_polarization_setup(this%output, rank)
         call MW_output_diagnostic_setup(this%output, rank)
      else
         call MW_output_trajectories_setup(this%output)
         call MW_output_xyz_setup(this%output)
         call MW_output_pdb_setup(this%output)
         call MW_output_lammps_setup(this%output)
         call MW_output_charges_setup(this%output)
         call MW_output_total_charges_setup(this%output)
         call MW_output_forces_setup(this%output)
         call MW_output_elec_forces_setup(this%output)
         call MW_output_dipoles_setup(this%output)
         call MW_output_efg_setup(this%output)
         call MW_output_radius_setup(this%output)
         call MW_output_potential_shift_setup(this%output)
         call MW_output_energies_setup(this%output, this%energies_to_dump, &
               this%energy_terms_to_dump)
         call MW_output_restart_setup(this%output)
         call MW_output_temperature_setup(this%output)
         call MW_output_pressure_setup(this%output)
         call MW_output_box_setup(this%output)
         call MW_output_stress_tensor_setup(this%output)
         call MW_output_polarization_setup(this%output)
         call MW_output_diagnostic_setup(this%output)
      end if
      if (.not. this%electrode_compute_force) then
         call MW_output_elec_forces_set_dump_frequency(this%output, 0)
      end if
   end subroutine output_setup

   ! ================================================================================
   ! Dump output
   !
   ! Dump data only if current step is a multiple of the requested dump frequency
   ! If frequency is 0 then no dump occurs
   ! If current step is 0 then dump occurs if frequency > 0
   subroutine output_dump(this, istep, do_output)
      use MW_timers, only: TIMER_DIAG_IO, MW_timers_start, MW_timers_stop, &
                           MW_timers_pause, MW_timers_resume
      use MW_output, only: &
            MW_output_trajectories_dump => trajectories_dump, &
            MW_output_xyz_dump => xyz_dump, &
            MW_output_pdb_dump => pdb_dump, &
            MW_output_lammps_dump => lammps_dump, &
            MW_output_charges_dump => charges_dump, &
            MW_output_total_charges_dump => total_charges_dump, &
            MW_output_forces_dump => forces_dump, &
            MW_output_elec_forces_dump => elec_forces_dump, &
            MW_output_dipoles_dump => dipoles_dump, &
            MW_output_efg_dump => efg_dump, &
            MW_output_radius_dump => radius_dump, &
            MW_output_potential_shift_dump => potential_shift_dump, &
            MW_output_energies_dump => energies_dump, &
            MW_output_temperature_dump => temperature_dump, &
            MW_output_pressure_dump => pressure_dump, &
            MW_output_box_dump => box_dump, &
            MW_output_stress_tensor_dump => stress_tensor_dump, &
            MW_output_polarization_dump => polarization_dump, &
            MW_output_diagnostic_dump => diagnostic_dump
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: istep
      logical, intent(in) :: do_output !< flag if the process actually output something
      ! Local
      ! -----
      integer :: frequency
      integer :: xyz_now, velocity_now, qatoms_now, forces_now, dipoles_now, radius_now, potential_shift_now
      character(64) :: fname
      logical :: temperature_issue

      call MW_timers_start(TIMER_DIAG_IO)
      xyz_now = this%xyz_ions_step(1)
      velocity_now = this%velocity_ions_step(1)
      qatoms_now = this%q_atoms_step(1)
      forces_now = this%forces_ions_step(1)
      dipoles_now = this%dipoles_ions_step(1)
      radius_now = this%radius_ions_step(1)
      potential_shift_now = this%potential_shift_step(1)

      ! Trajectories
      frequency = this%output%trajectories_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output .and. (this%trajectories_unwrapped == 1)) then
               call MW_output_trajectories_dump(this%output, istep, this%ions, &
                    this%xyz_ions_unwrapped(:, :, xyz_now), &
                    this%velocity_ions(:, :, velocity_now))
            else if (do_output) then
               call MW_output_trajectories_dump(this%output, istep, this%ions, &
                    this%xyz_ions(:, :, xyz_now), &
                    this%velocity_ions(:, :, velocity_now))
            end if
         end if
      end if

      ! xyz trajectories
      frequency = this%output%xyz_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output .and. (this%xyz_unwrapped == 1)) then
               call MW_output_xyz_dump(this%output, istep, this%ions, this%electrodes, &
                    this%xyz_ions_unwrapped(:, :, xyz_now), this%xyz_atoms(:, :), &
                    this%num_ions, this%num_atoms)
            else if (do_output) then 
               call MW_output_xyz_dump(this%output, istep, this%ions, this%electrodes, &
                    this%xyz_ions(:, :, xyz_now), this%xyz_atoms(:, :), &
                    this%num_ions, this%num_atoms)
            end if
         end if
      end if

      ! pdb trajectories
      frequency = this%output%pdb_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output .and. (this%pdb_unwrapped == 1)) then
               call MW_output_pdb_dump(this%output, this%ions, this%electrodes, &
                    this%xyz_ions_unwrapped(:, :, xyz_now), this%xyz_atoms(:, :), &
                    this%q_atoms(:, qatoms_now), this%box, this%molecules)
            else if (do_output) then
               call MW_output_pdb_dump(this%output, this%ions, this%electrodes, &
                    this%xyz_ions(:, :, xyz_now), this%xyz_atoms(:, :), &
                    this%q_atoms(:, qatoms_now), this%box, this%molecules)
            end if
         end if
      end if

      ! lammps trajectories
      frequency = this%output%lammps_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output .and. (this%lammps_unwrapped == 1)) then
               call MW_output_lammps_dump(this%output, istep, this%ions, this%electrodes, &
                    this%xyz_ions_unwrapped(:, :, xyz_now), this%xyz_atoms(:, :), this%num_ions, &
                    this%num_atoms, this%q_atoms(:, qatoms_now), this%box, this%molecules)
            else if (do_output) then 
               call MW_output_lammps_dump(this%output, istep, this%ions, this%electrodes, &
                    this%xyz_ions(:, :, xyz_now), this%xyz_atoms(:, :), this%num_ions, &
                    this%num_atoms, this%q_atoms(:, qatoms_now), this%box, this%molecules)
            end if
         end if
      end if

      ! Charges on electrode atoms
      frequency = this%output%charges_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_charges_dump(this%output, istep, this%electrodes, &
                    this%q_atoms(:, qatoms_now))
            end if
         end if
      end if

      ! Total charges on electrode atoms
      frequency = this%output%total_charges_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_total_charges_dump(this%output, istep, this%electrodes, &
                     this%q_atoms(:, qatoms_now))
            end if
         end if
      end if

      ! forces on ions
      frequency = this%output%forces_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_forces_dump(this%output, istep, this%ions, &
                    this%forces_ions(:,:,forces_now), &
                    this%coulomb_forces_ions(:,:,:), &
                    this%intermolecular_forces_ions(:,:,1), &
                    this%intramolecular_forces_ions(:,:,1))
            end if
         end if
      end if

      ! forces on electrodes
      frequency = this%output%elec_forces_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_elec_forces_dump(this%output, istep, this%electrodes, this%box%area(3))
            end if
         end if
      end if

      ! dipoles on ions
      frequency = this%output%dipoles_dump_frequency
      if (this%pimrun .and. frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_dipoles_dump(this%output, istep, this%ions, &
                    this%dipoles_ions(:,:,dipoles_now))
            end if
         end if
      end if

      ! efg on ions
      frequency = this%output%efg_dump_frequency
      if (this%efgrun .and. frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_efg_dump(this%output, istep, this%ions, this%efg_ions)
            end if
         end if
      end if

      ! Radius
      frequency = this%output%radius_dump_frequency
      if (this%aimrun .and. frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_radius_dump(this%output, istep, this%ions, &
                    this%deltas_ions(:,radius_now),this%epsilons_ions(:,:,radius_now))
            end if
         end if
      end if

      ! Potential shift
      frequency = this%output%potential_shift_dump_frequency
      if (this%electrode_charge_neutrality .and. frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_potential_shift_dump(this%output, istep, &
                     this%chi(potential_shift_now))
            end if
         end if
      end if

      ! energies
      frequency = this%output%energies_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            call MW_timers_pause(TIMER_DIAG_IO)
            call energy(this)
            call MW_timers_resume(TIMER_DIAG_IO)
            if (do_output) then
               call MW_output_energies_dump(this%output, istep, &
                    this%energy, this%energies_to_dump, this%energy_terms_to_dump)
            end if
         end if
      end if

      ! restart
      frequency = this%output%restart_dump_frequency
      fname = this%output%restart_file_name
      if (frequency > 0) then
         if ((istep > 0) .and. (mod(istep, frequency) == 0)) then
            if (do_output) then
               call write_data(this, istep, fname)
            end if
         end if
      end if

      ! temperature
      frequency = this%output%temperature_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            call temperature_compute(this, temperature_issue)
            if (do_output) then
               call MW_output_temperature_dump(this%output, istep, &
                     this%temperature)
            end if
         end if
      end if

      ! pressure
      frequency = this%output%pressure_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
!           if(.not.this%linear_present)then
            call pressure_compute(this)
            if (do_output) then
               call MW_output_pressure_dump(this%output, istep, &
                     this%pressure,this%pressure_tensor,this%pressure_tail_correction)
            end if
!           end if
         end if
      end if

      ! stress tensor
      frequency = this%output%stress_tensor_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
!           if(.not.this%linear_present)then
            call stress_tensor_compute(this)
            if (do_output) then
               call MW_output_stress_tensor_dump(this%output, istep, &
                     this%stress_tensor)
            end if
!           end if
         end if
      end if

      ! box parameters
      frequency = this%output%box_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if(.not.this%linear_present)then
               if (do_output) then
                  call MW_output_box_dump(this%output, istep, &
                        this%box%volume,this%box%length)
               end if
            end if
         end if
      end if

      ! polarization
      frequency = this%output%polarization_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_polarization_dump(this%output, istep, &
                    this%polarization_field(:,1))
            endif
         endif
      endif

      ! density
      frequency = this%output%diagnostic_dump_frequency
      if (frequency > 0) then
         if (mod(istep, frequency) == 0) then
            if (do_output) then
               call MW_output_diagnostic_dump(this%output, this%diagnostic, &
               this%box%area(3), this%species_names)
            end if
         end if
      end if

      call MW_timers_stop(TIMER_DIAG_IO)
   end subroutine output_dump

   ! ================================================================================
   ! Distribute work across parallel ranks
   subroutine distribute_work(this, parallel)
      use MW_parallel, only: MW_parallel_t
      use MW_localwork, only: &
            MW_localwork_define_type => define_type, &
            MW_localwork_setup_ewald_local_work => setup_ewald_local_work, &
            MW_localwork_setup_pair_atom2atom_local_work => setup_pair_atom2atom_local_work, &
            MW_localwork_setup_pair_ion2ion_local_work => setup_pair_ion2ion_local_work, &
            MW_localwork_setup_pair_ion2ion_local_work_ms => setup_pair_ion2ion_local_work_ms, &
            MW_localwork_setup_pair_atom2ion_local_work => setup_pair_atom2ion_local_work, &
            MW_localwork_setup_pair_ion2atom_local_work => setup_pair_ion2atom_local_work, &
            MW_localwork_setup_pair_ion2atom_local_work_ms => setup_pair_ion2atom_local_work_ms, &
            MW_localwork_setup_atoms_local_work => setup_atoms_local_work, &
            MW_localwork_setup_ions_local_work => setup_ions_local_work, &
            MW_localwork_setup_molecules_local_work => setup_molecules_local_work
      use MW_ewald, only: &
            MW_ewald_define_Skcossin => define_Skcossin, &
            MW_ewald_define_Skmucossin => define_Skmucossin
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      type(MW_parallel_t), intent(inout) :: parallel

      call MW_localwork_define_type(this%localwork)

      call MW_localwork_setup_ewald_local_work(this%localwork, this%ewald, this%num_PBC, this%box, &
            parallel%size, parallel%comm_rank)
      call MW_localwork_setup_pair_atom2atom_local_work(this%localwork, this%electrodes, &
            parallel%size, parallel%comm_rank)
      call MW_localwork_setup_pair_ion2ion_local_work(this%localwork, this%ions, &
            parallel%size, parallel%comm_rank)
      call MW_localwork_setup_pair_ion2ion_local_work_ms(this%localwork, this%ions, &
            parallel%size, parallel%comm_rank)
      call MW_localwork_setup_pair_ion2atom_local_work(this%localwork, &
            this%electrodes, this%ions, parallel%size, parallel%comm_rank)
      call MW_localwork_setup_pair_ion2atom_local_work_ms(this%localwork, &
            this%electrodes, this%ions, parallel%size, parallel%comm_rank)
      call MW_localwork_setup_pair_atom2ion_local_work(this%localwork, &
            this%ions, this%electrodes, parallel%size, parallel%comm_rank)
      call MW_localwork_setup_atoms_local_work(this%localwork, this%electrodes, &
            parallel%size, parallel%comm_rank)
      call MW_localwork_setup_ions_local_work(this%localwork, this%ions, &
            parallel%size, parallel%comm_rank)
      if (this%num_molecule_types > 0) then
         call MW_localwork_setup_molecules_local_work(this%localwork, this%molecules, &
               parallel%size, parallel%comm_rank)
      end if

      ! Allocate storage
      call MW_ewald_define_Skcossin(this%ewald, this%localwork%ewald_num_mode_local)
      call MW_ewald_define_Skmucossin(this%ewald, this%localwork%ewald_num_mode_local) ! should be fone only for PIM!
   end subroutine distribute_work

   ! ================================================================================
   ! Redistribute work across parallel ranks
   subroutine redistribute_work(this, parallel)
      use MW_parallel, only: MW_parallel_t
      use MW_localwork, only: &
            MW_localwork_setup_ewald_local_work => setup_ewald_local_work
      use MW_ewald, only: &
            MW_ewald_redefine_Skcossin => redefine_Skcossin, &
            MW_ewald_redefine_Skmucossin => redefine_Skmucossin
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      type(MW_parallel_t), intent(inout) :: parallel

      call MW_localwork_setup_ewald_local_work(this%localwork, this%ewald, this%num_PBC, this%box, &
            parallel%size, parallel%comm_rank)

      ! Allocate storage
      call MW_ewald_redefine_Skcossin(this%ewald, this%localwork%ewald_num_mode_local)
      call MW_ewald_redefine_Skmucossin(this%ewald, this%localwork%ewald_num_mode_local) ! should be fone only for PIM!
   end subroutine redistribute_work

   ! ================================================================================
   ! Setup sr interaction matrices
   subroutine setup_sr_interaction(this)
      use MW_coulomb_sr, only: MW_coulomb_sr_setup_sr_interaction => setup_sr_interaction
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: xyz_now

      xyz_now = this%xyz_ions_step(1)

      call MW_coulomb_sr_setup_sr_interaction(this%num_pbc, this%ewald, this%ions, this%electrodes, &
               this%xyz_ions(:,:,xyz_now), this%xyz_atoms(:,:), this%box)
   end subroutine setup_sr_interaction

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

   subroutine energy_coulomb(this)
      use MW_coulomb, only: &
            MW_coulomb_energy => energy, &
            MW_coulomb_fix_molecule_energy => fix_molecule_energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: xyz_now, q_atoms_now, dipoles_now
      real(wp) :: h_coulomb(5)

      xyz_now = this%xyz_ions_step(1)
      q_atoms_now = this%q_atoms_step(1)
      dipoles_now = this%dipoles_ions_step(1)

      h_coulomb(:) = 0.0_wp

      ! Coulomb potential energy
      ! ------------------------
      if (this%elecrun .or. this%pimrun .or. this%aimrun .or. this%charged_species) then
         call MW_coulomb_energy(this%num_PBC, this%localwork, this%ewald, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms(:,:),&
               this%q_atoms(:,q_atoms_now), h_coulomb(1:4), this%pimrun, &
               this%dipoles_ions(:,:,dipoles_now), this%tt)

         if(this%pimrun) call polarization_energy_compute(this)

         if (this%num_molecule_types > 0) then
            call MW_coulomb_fix_molecule_energy(this%num_PBC, this%localwork, this%molecules, &
                  this%box, this%ions, this%xyz_ions(:,:,xyz_now), this%pimrun, &
                  this%dipoles_ions(:,:,dipoles_now), this%tt, h_coulomb(5))
         end if
      end if

      call lut_set(this%energy, "Coulomb_lr", h_coulomb(1))
      call lut_set(this%energy, "Coulomb_keq0", h_coulomb(2))
      call lut_set(this%energy, "Coulomb_sr", h_coulomb(3))
      call lut_set(this%energy, "Coulomb_self", h_coulomb(4))
      call lut_set(this%energy, "Coulomb_molecule", h_coulomb(5))

      call lut_set(this%energy, "Coulomb", lut_get(this%energy, "Coulomb_lr") + &
                                           lut_get(this%energy, "Coulomb_keq0") + &
                                           lut_get(this%energy, "Coulomb_sr") + &
                                           lut_get(this%energy, "Coulomb_self") + &
                                           lut_get(this%energy, "Coulomb_molecule") + &
                                           this%polarization_energy)
   end subroutine energy_coulomb

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

   subroutine energy_lj(this)
      use MW_lennard_jones, only: &
            MW_lennard_jones_energy => energy, &
            MW_lennard_jones_fix_molecule_energy => fix_molecule_energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: xyz_now
      real(wp) :: h_lj(4)
      logical :: soft = .False.

      xyz_now = this%xyz_ions_step(1)

      h_lj(:) = 0.0_wp

      ! LJ potential energy
      if (this%lj%num_species > 0) then
         call MW_lennard_jones_energy(this%num_PBC, this%localwork, this%lj, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, &
               h_lj(1:2), soft)

         if (this%num_molecule_types > 0) then
            call MW_lennard_jones_fix_molecule_energy(this%num_PBC, this%localwork, &
                  this%lj, this%molecules, this%box, this%ions, this%xyz_ions(:,:,xyz_now), &
                  h_lj(3:4), soft)
         end if
      end if

      call lut_set(this%energy, "Lennard-Jones", h_lj(1) + h_lj(3))
      call lut_set(this%energy, "Lennard-Jones_cutoff", h_lj(2) + h_lj(4))

   end subroutine energy_lj

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

   subroutine energy_lj_soft_core(this, h_lj)
      use MW_lennard_jones, only: &
            MW_lennard_jones_energy => energy, &
            MW_lennard_jones_fix_molecule_energy => fix_molecule_energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      real(wp), intent(out) :: h_lj(4)

      ! Locals
      ! ------
      integer :: xyz_now
      logical :: soft = .True.

      xyz_now = this%xyz_ions_step(1)

      h_lj(:) = 0.0_wp

      ! LJ potential energy
      if (this%lj%num_species > 0) then
         call MW_lennard_jones_energy(this%num_PBC, this%localwork, this%lj, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, &
               h_lj(1:2), soft)

         if (this%num_molecule_types > 0) then
            call MW_lennard_jones_fix_molecule_energy(this%num_PBC, this%localwork, &
                  this%lj, this%molecules, this%box, this%ions, this%xyz_ions(:,:,xyz_now), &
                  h_lj(3:4), soft)
         end if
      end if

   end subroutine energy_lj_soft_core

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

   subroutine energy_ft(this)
      use MW_fumi_tosi, only: &
            MW_fumi_tosi_energy => energy, &
            MW_fumi_tosi_fix_molecule_energy => fix_molecule_energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: xyz_now
      real(wp) :: h_ft(2)

      xyz_now = this%xyz_ions_step(1)

      h_ft(:) = 0.0_wp

      ! FT potential energy
      if (this%ft%num_species > 0) then
         call MW_fumi_tosi_energy(this%num_PBC, this%localwork, this%ft, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, h_ft(1))

         if (this%num_molecule_types > 0) then
            call MW_fumi_tosi_fix_molecule_energy(this%num_PBC, this%localwork, this%ft, &
                  this%molecules, this%box,this%ions, this%xyz_ions(:,:,xyz_now), h_ft(2))
         end if
      end if

      call lut_set(this%energy, "Fumi-Tosi", h_ft(1))
      call lut_set(this%energy, "Fumi-Tosi_molecule", h_ft(2))

   end subroutine energy_ft

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

   subroutine energy_xft(this)
      use MW_xft, only: &
            MW_xft_energy => energy, &
            MW_xft_fix_molecule_energy => fix_molecule_energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: xyz_now
      real(wp) :: h_xft(2)

      xyz_now = this%xyz_ions_step(1)

      h_xft(:) = 0.0_wp

      ! XFT potential energy
      if (this%xft%num_species > 0) then
         call MW_xft_energy(this%num_PBC, this%localwork, this%xft, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, h_xft(1))

         if (this%num_molecule_types > 0) then
            call MW_xft_fix_molecule_energy(this%num_PBC, this%localwork, this%xft, &
                  this%molecules, this%box,this%ions, this%xyz_ions(:,:,xyz_now), h_xft(2))
         end if
      end if

      call lut_set(this%energy, "XFT", h_xft(1))
      call lut_set(this%energy, "XFT_molecule", h_xft(2))

   end subroutine energy_xft

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

   subroutine energy_daim(this)
      use MW_daim, only: &
            MW_daim_energy => energy, &
            MW_daim_selfenergy => selfenergy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: xyz_now, radius_now
      real(wp) :: h_daim(3)

      xyz_now = this%xyz_ions_step(1)
      radius_now = this%radius_ions_step(1)

      h_daim(:) = 0.0_wp

      if (this%daim%num_species > 0) then
         call MW_daim_energy(this%num_PBC, this%localwork, this%daim, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, &
               this%deltas_ions(:,radius_now), this%epsilons_ions(:,:,radius_now), h_daim(1))

         call MW_daim_selfenergy(this%num_ion_types, this%ions, this%deltas_ions(:,radius_now), &
               this%epsilons_ions(:,:,radius_now), h_daim(2))

!        if (this%num_molecule_types > 0) then
!           call MW_daim_fix_molecule_energy(this%num_PBC, this%localwork, this%daim, this%molecules, &
!                 this%box,this%ions, this%xyz_ions(:,:,xyz_now), h_daim(3))
!        end if
      end if

      call lut_set(this%energy, "DAIM", h_daim(1))
      call lut_set(this%energy, "DAIM_self", h_daim(2))

   end subroutine energy_daim

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

   subroutine energy_intramolecular(this)
      use MW_harmonic_bond, only: &
            MW_harmonic_bond_energy => energy
      use MW_harmonic_angle, only: &
            MW_harmonic_angle_energy => energy
      use MW_dihedrals, only: &
            MW_dihedrals_energy => energy
      use MW_born_repulsion, only: &
            MW_born_repulsion_energy => energy
      use MW_impropers, only: &
            MW_impropers_energy => energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      integer :: xyz_now
      real(wp) :: h_bond, h_angle, h_dihedral, h_improper, h_born

      xyz_now = this%xyz_ions_step(1)

      h_bond = 0.0_wp
      h_angle = 0.0_wp
      h_born = 0.0_wp
      h_dihedral = 0.0_wp
      h_improper = 0.0_wp

      ! Intramolecular potential energy
      ! -------------------------------
      if (this%num_molecule_types > 0) then
         call MW_harmonic_bond_energy(this%num_PBC, this%localwork, this%box, this%molecules, &
              this%ions, this%xyz_ions(:,:,xyz_now), h_bond)
      end if

      if (this%num_molecule_types > 0) then
         call MW_harmonic_angle_energy(this%num_PBC, this%localwork, this%box, this%molecules, &
              this%ions, this%xyz_ions(:,:,xyz_now), h_angle)
      end if

      if (this%num_molecule_types > 0) then
         call MW_dihedrals_energy(this%num_PBC, this%localwork, this%box, this%molecules, &
              this%ions, this%xyz_ions(:,:,xyz_now), h_dihedral)
      endif

      if (this%num_molecule_types > 0) then
         call MW_born_repulsion_energy(this%num_PBC, this%localwork, this%box, this%molecules, &
              this%ions, this%xyz_ions(:,:,xyz_now), h_born)
      end if

      if (this%num_molecule_types > 0) then
         call MW_impropers_energy(this%num_PBC, this%localwork, this%box, this%molecules, &
              this%ions, this%xyz_ions(:,:,xyz_now), h_improper)
      end if

      call lut_set(this%energy, "Harmonic_bond", h_bond)
      call lut_set(this%energy, "Harmonic_angle", h_angle)
      call lut_set(this%energy, "Dihedrals", h_dihedral+h_improper)

      call lut_set(this%energy, "Intramolecular", lut_get(this%energy, "Harmonic_bond") + &
                                                  lut_get(this%energy, "Harmonic_angle") + &
                                                  lut_get(this%energy, "Dihedrals"))
   end subroutine energy_intramolecular

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

   subroutine energy_steele(this)
      use MW_steele, only: MW_steele_energy => energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      real(wp) :: h_steele
      integer :: xyz_now

      xyz_now = this%xyz_ions_step(1)

      h_steele = 0.0_wp

      ! Steele potential energy
      if (this%steele%num_walls > 0) then
         call MW_steele_energy(this%num_PBC, this%localwork, this%steele, this%box, &
               this%ions, this%xyz_ions(:,:,xyz_now), this%electrodes, this%xyz_atoms, h_steele)
      end if

      call lut_set(this%energy, "Steele", h_steele)

   end subroutine energy_steele

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

   subroutine energy_thermostat(this)
      use MW_thermostat, only: &
            MW_thermostat_energy => energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      real(wp) :: h_thermostat
      logical :: temperature_issue

      h_thermostat = 0.0_wp

      ! Kinetic energy
      call kinetic_energy_compute(this, temperature_issue)

      ! Thermostat energy
      ! -----------------
      if (this%thermostat%chain_length > 0) then
         call MW_thermostat_energy(this%thermostat, this%num_dof, h_thermostat)
      end if

      call lut_set(this%energy, "Thermostat", h_thermostat)

   end subroutine energy_thermostat

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

   subroutine energy_barostat(this)
      use MW_barostat, only: &
            MW_barostat_energy => energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      real(wp) :: h_barostat
      logical :: temperature_issue

      h_barostat = 0.0_wp

      ! Kinetic energy
      call kinetic_energy_compute(this, temperature_issue)

      ! Barostat energy
      ! -----------------
      if (this%barostat%ions_th_chain_length > 0) then
         call MW_barostat_energy(this%barostat, this%num_dof, this%box%volume, h_barostat)
      end if

      call lut_set(this%energy, "Barostat", h_barostat)

   end subroutine energy_barostat

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

   subroutine energy_piston(this)
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      real(wp) :: h_piston
      integer :: ielec

      h_piston = 0.0_wp

      ! Piston work
      ! ------------
      do ielec = 1, size(this%electrodes, 1)
         h_piston = h_piston - this%electrodes(ielec)%piston_pressure * this%box%area(3) * &
               (this%electrodes(ielec)%comz - this%electrodes(ielec)%comz0)
      end do

      call lut_set(this%energy, "Piston_work", h_piston)

   end subroutine energy_piston

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

   subroutine energy_tf(this)
      use MW_thomas_fermi, only: &
            MW_thomas_fermi_kinetic_energy => kinetic_energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      real(wp) :: h_thomasfermi
      integer :: q_atoms_now

      h_thomasfermi = 0.0_wp

      q_atoms_now = this%q_atoms_step(1)

      !  Energy of the electron gas
      call MW_thomas_fermi_kinetic_energy(this%thomas_fermi(:), this%electrodes, &
            this%q_atoms(:,q_atoms_now), h_thomasfermi)

      call lut_set(this%energy, "Thomas_Fermi", h_thomasfermi)

   end subroutine energy_tf

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

   subroutine energy_external_field(this)
      use MW_external_field, only: &
            MW_external_field_energy => energy

      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      ! Locals
      ! ------
      real(wp) :: h_field
      integer :: q_atoms_now

      h_field = 0.0_wp

      q_atoms_now = this%q_atoms_step(1)

      ! Energy due to the external field
      call MW_external_field_energy(this%field, this%box, this%polarization_field(:,:), h_field)

      call lut_set(this%energy, "External_field", h_field)

   end subroutine energy_external_field

   ! ================================================================================
   ! Compute energy contribution
   !
   ! Energy is decomposed into several parts
   !  1. Total
   !  2. Kinetic energy
   !  3. Potential energy
   !  4. Coulomb potential energy including polarization
   !  5. Intramolecular potential energy
   !  6. Intermolecular potential energy
   !  7. Total energy
   !  8. Thermostat energy
   !  9. coulomb_lr
   ! 10. coulomb_keq0
   ! 11. coulomb_sr
   ! 12. coulomb_self
   ! 13. coulomb_molecule
   ! 14. harmonic_bonc
   ! 15. harmonic_angle
   ! 16. dihedral+improper
   ! 17. ft
   ! 18. ft_molecule
   ! 19. xft
   ! 20. xft_molecule
   ! 21. lj
   ! 22. lj_cutoff
   ! 23. daim
   ! 24. daim_self
   ! 25. piston_work

   subroutine energy(this)
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      !call lut_set_all(this%energy, 0.0_wp) zeroes also plumed

      call energy_coulomb(this)
      call energy_lj(this)
      call energy_ft(this)
      call energy_xft(this)
      call energy_daim(this)
      call energy_steele(this)
      call energy_intramolecular(this)
      call energy_thermostat(this)
      call energy_barostat(this)
      call energy_piston(this)
      call energy_tf(this)
      call energy_external_field(this)

      call lut_set(this%energy, "Kinetic", this%kinetic_energy)
      call lut_set(this%energy, "van_der_Waals", lut_get(this%energy, "Fumi-Tosi") + &
                                                  lut_get(this%energy, "Fumi-Tosi_molecule") + &
                                                  lut_get(this%energy, "XFT") + &
                                                  lut_get(this%energy, "XFT_molecule") + &
                                                  lut_get(this%energy, "Lennard-Jones") + &
                                                  lut_get(this%energy, "Lennard-Jones_cutoff") + &
                                                  lut_get(this%energy, "DAIM") + &
                                                  lut_get(this%energy, "DAIM_self") + &
                                                  lut_get(this%energy, "Steele"))
      call lut_set(this%energy, "Potential", lut_get(this%energy, "Coulomb") + &
                                             lut_get(this%energy, "Intramolecular") + &
                                             lut_get(this%energy, "van_der_Waals") + &
                                             lut_get(this%energy, "Thomas_Fermi"))
      call lut_set(this%energy, "Hamiltonian", lut_get(this%energy, "Kinetic") + &
                                               lut_get(this%energy, "Potential") + &
                                               lut_get(this%energy, "Thermostat") + &
                                               lut_get(this%energy, "Barostat") + &
                                               lut_get(this%energy, "Piston_work") + &
                                               lut_get(this%energy, "Plumed_bias") + &
                                               lut_get(this%energy, "External_field"))
      call lut_set(this%energy, "Total", lut_get(this%energy, "Kinetic") + &
                                         lut_get(this%energy, "Potential") + &
                                         lut_get(this%energy, "External_field"))

   end subroutine energy

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

   function required_steps(array, d, step)
     integer :: required_steps
     integer :: d, step
     real(wp), allocatable :: array(:,:,:)       !< ions xyz positions
     if(allocated(array)) then
       required_steps = min(size(array,d)-1, step+1)
     else
       required_steps = -1
     end if
   end function

   ! ================================================================================
   ! Dump system data
   !
   ! # header
   ! step                        000000
   ! num_atoms                   000000
   ! num_electrode_atoms         000000
   ! # box
   ! a b c
   ! # coordinates : {num_species} species - step {step}
   ! name(i) x(i,step) y(i,step) z(i,step)
   ! (...) 1 line per particle
   ! -> repeat {coordinates_num_steps} times (including title)
   ! # velocities : {num_species} species - step {step}
   ! name(i) vx(i,step) vy(i,step) vz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {velocities_num_steps} times (including title)
   ! # forces : {num_species} species - step {step}
   ! name(i) fx(i,step) fy(i,step) fz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {forces_num_steps} times (including title)
   ! # dipoles : {num_species} species - step {step}
   ! name(i) mux(i,step) muy(i,step) muz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {dipoles_num_steps} times (including title)
   ! # radius : {num_species} species - step {step}
   ! name(i) delta (i,step) epsilonx(i,step) epsilony(i,step) epsilonz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {radius_num_steps} times (including title)
   ! # charges : {num_electrode_species} species - step {step}
   ! q(i,step) name(i)
   ! (...) 1 line per particle
   ! -> repeat {electrode_charges_num_steps} times (including title)
   ! # thermostat : {thermostat_chain_length} links - step {step}
   ! zeta(i,step) v_zeta(i,step) g_zeta(i,step)
   ! (...) 1 line per link in the chain
   ! -> repeat {themostat_num_steps} times (including title)
   ! # barostat : {barostat_chain_length} links - step {step}
   ! epsilon(step) v_epsilon(step) F_epsilon(step)
   ! zeta(i,step) v_zeta(i,step) g_zeta(i,step)
   ! (...) 1 line per link in the chain
   ! -> repeat {barostat_num_steps} times (including title)
   subroutine write_data(this, istep, restartname)
      use MW_errors, only: MW_errors_open_error => open_error, &
            MW_errors_close_error => close_error
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      integer, intent(in) :: istep
      character(*), intent(in) :: restartname

      ! Local
      ! -----
      integer :: funit, ierr
      integer :: num_ions, xyz_ions_steps, velocity_ions_steps, forces_ions_steps, dipoles_ions_steps
      integer :: num_atoms, xyz_atoms_steps, q_atoms_steps,radius_ions_steps
      integer :: thermostat_length, thermostat_num_steps
      integer :: barostat_length, barostat_num_steps
      integer :: i, step, itype, iion, step_index
      integer :: num_species
      character(64) :: fname, fext

      ! Open file
      if (this%alternate_restart > 0) then
         write(fext, '(".",i1.1,".out")') this%alternate_restart
         this%alternate_restart = modulo(this%alternate_restart, 2) + 1
         fname = trim(adjustl(restartname)) // trim(adjustl(fext))
      else
         write(fext, '(".",i9.9,".out")') this%step
         fname = trim(adjustl(restartname)) // trim(adjustl(fext))
      end if

      call MW_fileunit_get_new_unit(funit)
      open(unit=funit, file=fname, access="SEQUENTIAL", action="WRITE", &
            position="REWIND", form="FORMATTED", status="REPLACE", iostat=ierr)
      if (ierr /= 0) then
         call MW_errors_open_error("write_data", "system.f90", fname, ierr)
      end if


      num_ions = this%num_ions
      num_atoms = this%num_atoms
      num_species = num_ions + num_atoms


      !xyz_ions_steps = min(size(this%xyz_ions,3)-1, this%step+1)
      xyz_ions_steps = required_steps(this%xyz_ions, 3, this%step)
      velocity_ions_steps = required_steps(this%velocity_ions,3, this%step)
      forces_ions_steps = required_steps(this%forces_ions,3, this%step)
      dipoles_ions_steps = required_steps(this%dipoles_ions, 3, this%step)
      radius_ions_steps = required_steps(this%epsilons_ions, 3, this%step)

      xyz_atoms_steps = 0
      q_atoms_steps = 0
      if (num_atoms > 0) then
         xyz_atoms_steps = 1
         q_atoms_steps = min(size(this%q_atoms,2)-1, this%step+1)
      end if

      thermostat_length = max(this%thermostat%chain_length,this%barostat%ions_th_chain_length)
      thermostat_num_steps = 0
      if (this%thermostat%chain_length > 0) then
         thermostat_num_steps = min(size(this%thermostat%zeta,2)-1,this%step+1)
      elseif (this%barostat%ions_th_chain_length > 0) then
         thermostat_num_steps = min(size(this%barostat%ions_th_zeta,2)-1,this%step+1)
      end if

      barostat_length = this%barostat%b_th_chain_length
      barostat_num_steps = 0
      if (barostat_length > 0) then
         barostat_num_steps = min(size(this%barostat%b_th_zeta,2)-1,this%step+1)
      end if

      ! Write header
      ! ------------
      write(funit, '("# header")')
      write(funit, '("step                        ",i10,1x,i10)') this%step, istep
      write(funit, '("num_atoms                   ",i8)') num_species
      write(funit, '("num_electrode_atoms         ",i8)') num_atoms

      ! Write box parameters
      ! --------------------
      write(funit, '("# box")')
      write(funit, '(3(es22.15,1x))') this%box%length(:)

      ! Write coordinates
      ! -----------------
      do step_index = 1, xyz_ions_steps
         write(funit, '("# coordinates : ",i8," atoms - step ",es22.15)') num_species, (this%step-step_index+1)*this%dt
         step = this%xyz_ions_step(step_index)
         do itype = 1, this%num_ion_types
            do iion = 1, this%ions(itype)%count
               i = iion + this%ions(itype)%offset
               if (this%store_xyz_ions_unwrapped) then 
                  write(funit, '(a8, 3(1x,es22.15))') this%ions(itype)%name, &
                        this%xyz_ions_unwrapped(i,1,step), this%xyz_ions_unwrapped(i,2,step), this%xyz_ions_unwrapped(i,3,step)
               else 
                  write(funit, '(a8, 3(1x,es22.15))') this%ions(itype)%name, &
                        this%xyz_ions(i,1,step), this%xyz_ions(i,2,step), this%xyz_ions(i,3,step)
               end if 
            end do
         end do

         do itype = 1, this%num_elec
            do iion = 1, this%electrodes(itype)%count
               i = iion + this%electrodes(itype)%offset
               write(funit, '(a8, 3(1x,es22.15))') this%electrodes(itype)%name, &
                     this%xyz_atoms(i,1), this%xyz_atoms(i,2), this%xyz_atoms(i,3)
            end do
         end do

      end do

      ! Write velocities
      ! ----------------
      do step_index = 1, velocity_ions_steps
         write(funit, '("# velocities : ",i8," atoms - step ",es22.15)') num_species, (this%step-step_index+1)*this%dt
         step = this%velocity_ions_step(step_index)
         do itype = 1, this%num_ion_types
            do iion = 1, this%ions(itype)%count
               i = iion + this%ions(itype)%offset
               write(funit, '(a8, 3(1x,es22.15))') this%ions(itype)%name, &
                     this%velocity_ions(i,1,step), this%velocity_ions(i,2,step), this%velocity_ions(i,3,step)
            end do
         end do

         do itype = 1, this%num_elec
            do iion = 1, this%electrodes(itype)%count
               write(funit, '(a8, 3(1x,es22.15))') this%electrodes(itype)%name, &
                     0.0_wp, 0.0_wp, 0.0_wp
            end do
         end do

      end do

      ! Write forces
      ! ------------
      do step_index = 1, forces_ions_steps
         write(funit, '("# forces : ",i8," atoms - step ",es22.15)') num_species, (this%step-step_index+1)*this%dt
         step = this%forces_ions_step(step_index)
         do itype = 1, this%num_ion_types
            do iion = 1, this%ions(itype)%count
               i = iion + this%ions(itype)%offset
               write(funit, '(a8, 3(1x,es22.15))') this%ions(itype)%name, &
                     this%forces_ions(i,1,step), this%forces_ions(i,2,step), this%forces_ions(i,3,step)
            end do
         end do
         do itype = 1, this%num_elec
            do iion = 1, this%electrodes(itype)%count
               write(funit, '(a8, 3(1x,es22.15))') this%electrodes(itype)%name, &
                     0.0_wp, 0.0_wp, 0.0_wp
            end do
         end do

      end do

      ! Write dipoles
      ! ------------
      do step_index = 1, dipoles_ions_steps
         write(funit, '("# dipoles : ",i8," atoms - step ",es22.15)') num_species, (this%step-step_index+1)*this%dt
         step = this%dipoles_ions_step(step_index)
         do itype = 1, this%num_ion_types
            do iion = 1, this%ions(itype)%count
               i = iion + this%ions(itype)%offset
               write(funit, '(a8, 3(1x,es22.15))') this%ions(itype)%name, &
                     this%dipoles_ions(i,1,step), this%dipoles_ions(i,2,step), this%dipoles_ions(i,3,step)
            end do
         end do
         do itype = 1, this%num_elec
            do iion = 1, this%electrodes(itype)%count
               write(funit, '(a8, 3(1x,es22.15))') this%electrodes(itype)%name, &
                     0.0_wp, 0.0_wp, 0.0_wp
            end do
         end do

      end do

      ! Write radius
      ! ------------
      do step_index = 1, radius_ions_steps
         write(funit, '("# radius : ",i8," atoms - step ",es22.15)') num_species, (this%step-step_index+1)*this%dt
         step = this%radius_ions_step(step_index)
         do itype = 1, this%num_ion_types
            do iion = 1, this%ions(itype)%count
               i = iion + this%ions(itype)%offset
               write(funit, '(a8, 4(1x,es22.15))') this%ions(itype)%name, &
                     this%deltas_ions(i,step),this%epsilons_ions(i,1,step), &
                     this%epsilons_ions(i,2,step), this%epsilons_ions(i,3,step)
            end do
         end do
         do itype = 1, this%num_elec
            do iion = 1, this%electrodes(itype)%count
               write(funit, '(a8, 4(1x,es22.15))') this%electrodes(itype)%name, &
                     0.0_wp, 0.0_wp, 0.0_wp, 0.0_wp
            end do
         end do

      end do

      ! Write charges on electrode atoms
      ! --------------------------------
      do step_index = 1, q_atoms_steps
         write(funit, '("# charges : ",i8," electrode_atoms - step ",es22.15)') num_atoms, (this%step-step_index+1)*this%dt
         step = this%q_atoms_step(step_index)
         do itype = 1, this%num_elec
            do iion = 1, this%electrodes(itype)%count
               i = iion + this%electrodes(itype)%offset
               write(funit, '(a8, 1x, es22.15)') this%electrodes(itype)%name, this%q_atoms(i,step)
            end do
         end do
      end do

      ! Write potential shift when charge neutrality is enforced
      ! --------------------------------
      if (this%electrode_charge_neutrality) then
         do step_index = 1, q_atoms_steps
            write(funit, '("# potential_shift - step ",es22.15)') (this%step-step_index+1)*this%dt
            step = this%q_atoms_step(step_index)
            write(funit, '(a8, 1x, es22.15)') "Chi", this%chi(step)
         end do
      end if

      ! Write piston velocity
      ! --------------------------------
      if (this%num_elec > 0) then
         write(funit, '("# piston_velocity - step ",es22.15)') this%step*this%dt
         do itype = 1, this%num_elec
            write(funit, '(a8, 1x, es22.15)') this%electrodes(itype)%name, &
                  this%electrodes(itype)%velocity
         end do
      end if

      ! Write thermostat parameters
      ! ---------------------------
      if (this%thermostat%chain_length > 0) then
         do step_index = 1, thermostat_num_steps
            write(funit, '("# thermostat : ",i8," links - step ",es22.15)') &
                  thermostat_length, (this%step-step_index+1)*this%dt
            step = this%thermostat%zeta_step(step_index)
            do i = 1, thermostat_length
               write(funit, '(3(es22.15,1x))') this%thermostat%zeta(i,step), &
                     this%thermostat%v_zeta(i,step), this%thermostat%G_zeta(i,step)
            end do
         enddo
      elseif (this%barostat%ions_th_chain_length > 0) then
         do step_index = 1, thermostat_num_steps
            write(funit, '("# thermostat : ",i8," links - step ",es22.15)') &
                  thermostat_length, (this%step-step_index+1)*this%dt
            step = this%barostat%zeta_step(step_index)
            do i = 1, thermostat_length
               write(funit, '(3(es22.15,1x))') this%barostat%ions_th_zeta(i,step), &
                     this%barostat%ions_th_v_zeta(i,step), this%barostat%ions_th_G_zeta(i,step)
            end do
         enddo
      end if

      ! Write barostat parameters
      ! ---------------------------
      if (this%barostat%b_th_chain_length > 0) then
         do step_index = 1, barostat_num_steps
            write(funit, '("# barostat : ",i8," links - step ",es22.15)') &
                  barostat_length, (this%step-step_index+1)*this%dt
            step = this%barostat%zeta_step(step_index)
            write(funit, '(3(es22.15,1x))') this%barostat%b_epsilon(step), &
                  this%barostat%b_v_epsilon(step), this%barostat%b_F_epsilon(step)
            do i = 1, barostat_length
               write(funit, '(3(es22.15,1x))') this%barostat%b_th_zeta(i,step), &
                     this%barostat%b_th_v_zeta(i,step), this%barostat%b_th_G_zeta(i,step)
            end do
         enddo
      end if

      ! Write the polarization of the melt ions
      ! --------------------
      write(funit, '("# polarization")')
      write(funit, '(3(es22.15,1x))') this%polarization_field(:,2)

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

   end subroutine write_data

   subroutine check_section_header(hash_char, keyword, expected_keyword)
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      character(1), intent(in) :: hash_char
      character(len=*), intent(in) :: keyword
      character(len=*), intent(in) :: expected_keyword

      if ((hash_char /= '#') .or. (keyword /= expected_keyword)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Expecting '"//expected_keyword//"' in data input file")
      end if
   end subroutine check_section_header

   subroutine build_keyword(datafile, nb_sections)
      use MW_errors, only: MW_errors_open_error => open_error, &
        MW_errors_close_error => close_error, &
        MW_errors_runtime_error => runtime_error
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit

      character(*), intent(in) :: datafile
      type(MW_restart_section_t), intent(out) :: nb_sections
      character(len=1) :: hash_char
      character(32) :: keyword
      integer :: io, ierr, funit
      nb_sections%coordinates = 0
      nb_sections%velocities = 0
      nb_sections%forces = 0
      nb_sections%dipoles = 0
      nb_sections%charges = 0
      nb_sections%potential_shift = 0
      nb_sections%piston = 0
      nb_sections%radius = 0
      nb_sections%thermostat = 0
      nb_sections%barostat = 0
      nb_sections%polarization = 0

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

      do
        read(funit, *, IOSTAT=io) hash_char, keyword
        if(io < 0) then
          exit
        else if (io .eq. 0 .and. hash_char .eq. '#') then
          ! Assign value to the corresponding variable
          select case(adjustl(keyword))
          case("header")
          case("box")
          case("coordinates")
            nb_sections%coordinates = nb_sections%coordinates + 1
          case("velocities")
            nb_sections%velocities = nb_sections%velocities + 1
          case("forces")
            nb_sections%forces = nb_sections%forces + 1
          case("dipoles")
            nb_sections%dipoles = nb_sections%dipoles + 1
          case("radius")
            nb_sections%radius = nb_sections%radius + 1
          case("charges")
            nb_sections%charges = nb_sections%charges + 1
         case("potential_shift")
            nb_sections%potential_shift = nb_sections%potential_shift + 1
          case("piston_velocity")
            nb_sections%piston = nb_sections%piston + 1
          case("thermostat")
            nb_sections%thermostat = nb_sections%thermostat + 1
          case("barostat")
            nb_sections%barostat = nb_sections%barostat + 1
          case("polarization")
            nb_sections%polarization = nb_sections%polarization + 1
          case default
            call MW_errors_runtime_error("build_keyword", "system.f90", &
               "Keyword '"//keyword//"' not expected in data input file")
          end select
        end if
      end do

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


   end subroutine build_keyword


   ! ================================================================================
   ! Read system data
   !
   ! # header
   ! step                        000000
   ! num_atoms                   000000
   ! num_electrode_atoms         000000
   ! # box
   ! a b c
   ! # coordinates : {num_species} species - step {step}
   ! name(i) x(i,step) y(i,step) z(i,step)
   ! (...) 1 line per particle
   ! -> repeat {coordinates_num_steps} times (including title)
   ! # velocities : {num_species} species - step {step}
   ! name(i) vx(i,step) vy(i,step) vz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {velocities_num_steps} times (including title)
   ! # forces : {num_species} species - step {step}
   ! name(i) fx(i,step) fy(i,step) fz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {forces_num_steps} times (including title)
   ! # dipoles : {num_species} species - step {step}
   ! name(i) mux(i,step) muy(i,step) muz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {dipoles_num_steps} times (including title)
   ! # radius : {num_species} species - step {step}
   ! name(i) sigma(i),epsilonx(i,step) epsilony(i,step) epsilonz(i,step)
   ! (...) 1 line per particle
   ! -> repeat {radius_num_steps} times (including title)
   ! # charges : {num_electrode_species} species - step {step}
   ! q(i,step) name(i)
   ! (...) 1 line per particle
   ! -> repeat {electrode_charges_num_steps} times (including title)
   ! # thermostat : {thermostat_chain_length} links - step {step}
   ! zeta(i,step) v_zeta(i,step) g_zeta(i,step)
   ! (...) 1 line per link in the chain
   ! -> repeat {thermostat_num_steps} times (including title)
   ! # barostat : {barostat_chain_length} links - step {step}
   ! epsilon(step) v_epsilon(step) F_epsilon(step)
   ! zeta(i,step) v_zeta(i,step) g_zeta(i,step)
   ! -> repeat {barostat_num_steps} times (including title)
   subroutine read_data(this, datafile)
      use MW_errors, only: MW_errors_open_error => open_error, &
            MW_errors_allocate_error => allocate_error, &
            MW_errors_close_error => close_error, &
            MW_errors_runtime_error => runtime_error, &
            MW_errors_runtime_warning => runtime_warning
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_box, only: MW_box_define_type => define_type
      use MW_ion, only: MW_ion_t
      use MW_electrode, only: MW_electrode_t
      use MW_ewald, only: MW_ewald_define_kpoints => define_kpoints
      use MW_diagnostic, only: MW_diagnostic_define_type => define_type
      implicit none

      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this
      character(*), intent(in) :: datafile

      ! Local
      ! -----
      integer :: funit, ierr, io
      integer :: num_species, num_electrode_species, num_bulk_species
      integer :: thermostat_chain_length, barostat_chain_length
      integer :: i, istep, step, itype
      integer :: num_ions, num_atoms, offset
      real(wp) :: a, b, c
      real(wp) :: dummy, dummy_x, dummy_y, dummy_z
      character(32) :: keyword
      character(1) :: dummychar

      character(1) :: hash_char
      type(MW_restart_section_t) :: nb_sections

      ! Parse the whole data  file and build up the number of sections available
      call build_keyword(datafile, nb_sections)

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

      call deallocate_data_arrays(this)
      call allocate_data_arrays(this)

      ! Read header
      ! -----------
      read(funit, *) hash_char, keyword
      call check_section_header(hash_char, keyword, "header")
      ! read  step & num_species
      read(funit, *) keyword, step
      call check_section_header('#', keyword, "step")
      read(funit, *) keyword, num_species
      call check_section_header('#', keyword, "num_atoms")
      read(funit, *) keyword, num_electrode_species
      call check_section_header('#', keyword, "num_electrode_atoms")

      ! Validate atom numbers
      num_bulk_species = num_species - num_electrode_species
      if (num_bulk_species .ne. this%num_ions) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Data and runtime number of electrolyte atoms do not agree")
      end if
      if (num_electrode_species .ne. this%num_atoms) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Data and runtime number of electrode atoms do not agree")
      end if

      ! Other checks
      if (size(this%electrodes, 1) == 0 .and. nb_sections%piston > 0) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid piston velocity section in data file for simulation without electrodes")
      end if
      if (.not.this%electrode_charge_neutrality .and. nb_sections%potential_shift > 0) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid potential shift section in data file for non electroneutral electrodes")
      end if
      if (this%mazerun .and. this%pimrun .and. nb_sections%dipoles < 2) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for xyz dipole data: maze needs at least 2")
      end if
      if (this%mazerun .and. this%elecrun .and. nb_sections%charges < 2) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for charge data: maze needs at least 2")
      end if

      ! Read box parameters
      ! -------------------
      read(funit, *) hash_char, keyword
      call check_section_header(hash_char, keyword, "box")

      ! read box lengthparameters
      read(funit, *) a, b, c

      if (2 * this%lj%rcut > min(a, b, c)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid value for lj rcut > min(a, b, c)/2")
      end if

      if (2 * this%ft%rcut > min(a, b, c)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid value for ft rcut > min(a, b, c)/2")
      end if

      if (2 * this%xft%rcut > min(a, b, c)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid value for xft rcut > min(a, b, c)/2")
      end if

      if (2 * this%daim%rcut > min(a, b, c)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid value for daim rcut > min(a, b, c)/2")
      end if

      if (2 * this%ewald%rcut > min(a, b, c)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid value for ewald rcut > min(a, b, c)/2")
      end if

      ! Read coordinates
      ! ----------------
      ! Validate array dimensions
      if (num_bulk_species > size(this%xyz_ions,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of electrolyte species for xyz data")
      end if
      if (nb_sections%coordinates > size(this%xyz_ions,3)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for xyz electrolyte atoms' data")
      end if
      if (num_electrode_species > size(this%xyz_atoms,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of electrode species for xyz data")
      end if

      do istep = 1, nb_sections%coordinates
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "coordinates")

         ! Read ions positions
         do i = 1, num_bulk_species
            read(funit, *, IOSTAT=io) keyword, this%xyz_ions(i,1,istep), this%xyz_ions(i,2,istep), &
                  this%xyz_ions(i,3,istep)
            ! Store the initial unwrapped positions 
            if (this%store_xyz_ions_unwrapped) then
               this%xyz_ions_unwrapped(i,1,istep) = this%xyz_ions(i,1,istep)
               this%xyz_ions_unwrapped(i,2,istep) = this%xyz_ions(i,2,istep)
               this%xyz_ions_unwrapped(i,3,istep) = this%xyz_ions(i,3,istep)
            end if 
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of xyz coordinates is inconsistent with num_species")
            end if
         end do

         ! Read atoms positions (1st time)
         if (istep == 1) then
            do i = 1, num_electrode_species
               read(funit, *, IOSTAT=io) keyword, this%xyz_atoms(i,1), this%xyz_atoms(i,2), this%xyz_atoms(i,3)
               if (io /= 0) then
                  call MW_errors_runtime_error("read_data", "system.f90", &
                        "Electrode atoms position must be identical for all step values")
               end if
            end do
         end if
      end do

      call comz_compute(this, .true.)

      ! Read velocities
      ! ----------------
      ! Validate array dimensions
      if (num_bulk_species > size(this%velocity_ions,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of electrolyte species for velocity data")
      end if
      if (nb_sections%velocities > size(this%velocity_ions,3)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for electrolyte atoms' velocity data")
      end if

      do istep = 1, nb_sections%velocities
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "velocities")

         ! Read ions velocities
         do i = 1, num_bulk_species
            read(funit, *, IOSTAT=io) keyword, this%velocity_ions(i,1,istep), this%velocity_ions(i,2,istep), &
                  this%velocity_ions(i,3,istep)
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of velocity coordinates is inconsistent with num_species")
            end if
         end do

         ! Check atoms velocities are always 0.0
         do i = 1, num_electrode_species
            read(funit, *, IOSTAT=io) keyword, dummy_x, dummy_y, dummy_z
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of velocity coordinates is inconsistent with num_species")
            end if
            if ((dummy_x /= 0.0_wp) .or. (dummy_y /= 0.0_wp) .or. (dummy_z /= 0.0_wp)) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Electrode atoms velocities must be 0.0 for all step values")
            end if
         end do
      end do

      ! Read forces
      ! -----------
      ! Validate array dimensions
      if (num_bulk_species > size(this%forces_ions,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of electrolyte species for forces data")
      end if
      if (nb_sections%forces > size(this%forces_ions,3)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for electrolyte atoms' forces data")
      end if

      do istep = 1, nb_sections%forces
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "forces")

         ! Read forces on ions
         do i = 1, num_bulk_species
            read(funit, *, IOSTAT=io) keyword, this%forces_ions(i,1,istep), this%forces_ions(i,2,istep), &
                  this%forces_ions(i,3,istep)
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of forces coordinates is inconsistent with num_species")
            end if
         end do

         ! Check atoms forces are always 0.0
         do i = 1, num_electrode_species
            read(funit, *, IOSTAT=io) keyword, dummy_x, dummy_y, dummy_z
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of forces coordinates is inconsistent with num_species")
            end if
            if ((dummy_x /= 0.0_wp) .or. (dummy_y /= 0.0_wp) .or. (dummy_z /= 0.0_wp)) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Electrode atoms forces must be 0.0 for all step values")
            end if
         end do
      end do

      ! Read dipoles
      ! -----------
      ! Validate array dimensions
      if (this%pimrun .and. num_bulk_species > size(this%dipoles_ions,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of electrolyte species for dipoles data")
      end if
      if (this%pimrun .and. nb_sections%dipoles > size(this%dipoles_ions,3)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for electrolyte atoms' dipoles data")
      end if

      do istep = 1, nb_sections%dipoles
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "dipoles")

         ! Read dipoles on ions
         do i = 1, num_bulk_species
            read(funit, *, IOSTAT=io) keyword, this%dipoles_ions(i,1,istep), this%dipoles_ions(i,2,istep), &
                  this%dipoles_ions(i,3,istep)
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of dipoles coordinates is inconsistent with num_species")
            end if
         end do

         ! Check atoms dipoles are always 0.0
         do i = 1, num_electrode_species
            read(funit, *, IOSTAT=io) keyword, dummy_x, dummy_y, dummy_z
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of dipoles coordinates is inconsistent with num_species")
            end if
            if ((dummy_x /= 0.0_wp) .or. (dummy_y /= 0.0_wp) .or. (dummy_z /= 0.0_wp)) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Electrode atoms dipoles must be 0.0 for all step values")
            end if
         end do
      end do

      ! Read radius
      ! -----------
      ! Validate array dimensions
      if (this%aimrun .and. num_bulk_species > size(this%deltas_ions,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of electrolyte species for radius data")
      end if
      if (this%aimrun .and. nb_sections%radius > size(this%epsilons_ions,3)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for electrolyte atoms' radius data")
      end if

      do istep = 1, nb_sections%radius
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "radius")

         ! Read radius on ions
         do i = 1, num_bulk_species
            read(funit, *, IOSTAT=io) keyword, this%deltas_ions(i,istep),this%epsilons_ions(i,1,istep),&
                  this%epsilons_ions(i,2,istep), this%epsilons_ions(i,3,istep)
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of radius coordinates is inconsistent with num_species")
            end if
         end do

         ! Check atoms radii are always 0.0
         do i = 1, num_electrode_species
            read(funit, *, IOSTAT=io) keyword, dummy, dummy_x, dummy_y, dummy_z
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of radius coordinates is inconsistent with num_species")
            end if
            if ((dummy /= 0.0_wp) .or. (dummy_x /= 0.0_wp) .or. (dummy_y /= 0.0_wp) .or. (dummy_z /= 0.0_wp)) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Electrode atoms radii must be 0.0 for all step values")
            end if
         end do
      end do

      ! Read electrode atom charges
      ! ---------------------------
      if (num_electrode_species > size(this%q_atoms,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of species for electrode atoms charges data")
      end if
      if (nb_sections%charges > size(this%q_atoms,2)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for electrode atoms charges data")
      end if

      do itype = 1, size(this%electrodes, 1)
         offset = this%electrodes(itype)%offset
         do i = 1, this%electrodes(itype)%count
            this%q_atoms(i + offset, :) = this%electrodes(itype)%charge
         end do
      end do

      do istep = 1, nb_sections%charges
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "charges")

         do i = 1, num_electrode_species
            read(funit, *, IOSTAT=io) keyword, this%q_atoms(i,istep)
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Number of charges is inconsistent with num_electrode_species")
            end if
         end do
      end do

      ! Read potential shift
      ! ---------------------------
      if (this%electrode_charge_neutrality .and. nb_sections%potential_shift > size(this%chi,1)) then
         call MW_errors_runtime_error("read_data", "system.f90", &
               "Invalid number of steps for potential shift data (should be zero for charge_neutrality run)")
      end if

      do istep = 1, nb_sections%potential_shift
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "potential_shift")

         read(funit, *) keyword, this%chi(istep)
      end do

      ! Read piston velocity
      ! ---------------------------
      do istep = 1, nb_sections%piston
         read(funit, *) hash_char, keyword
         call check_section_header(hash_char, keyword, "piston_velocity")

         do i = 1, size(this%electrodes, 1)
            read(funit, *) keyword, this%electrodes(i)%velocity
         end do
      end do

      ! Read thermostat parameters
      ! --------------------------
      if (nb_sections%thermostat > 0) then
!         if ( (thermostat_chain_length /= this%thermostat%chain_length ).and. &
!              (thermostat_chain_length /= this%barostat%ions_th_chain_length)) then
!            call MW_errors_runtime_error("read_data", "system.f90", &
!                  "Invalid chain length for thermostat")
!         end if
         if ( (nb_sections%thermostat > size(this%thermostat%zeta_step,1)).and. &
              (nb_sections%thermostat > size(this%barostat%zeta_step,1))   ) then
            call MW_errors_runtime_error("read_data", "system.f90", &
                  "Invalid number of step for thermostat data")
         end if
      end if

      do istep = 1, nb_sections%thermostat
         read(funit, *) hash_char, keyword, dummychar, thermostat_chain_length
         call check_section_header(hash_char, keyword, "thermostat")

         if (this%thermostat%chain_length > 0 .and. this%thermostat%chain_length .eq. thermostat_chain_length)then
            do i = 1, this%thermostat%chain_length
               read(funit, *, IOSTAT=io) this%thermostat%zeta(i,istep), this%thermostat%v_zeta(i,istep), &
                     this%thermostat%G_zeta(i,istep)
               if (io /= 0) then
                  call MW_errors_runtime_error("read_data", "system.f90", &
                        "Thermostat chain length is inconsistent with thermostat_chain_length defined in the runtime")
               end if
            end do
         else if (this%barostat%ions_th_chain_length > 0 .and. this%barostat%ions_th_chain_length .eq. thermostat_chain_length)then
            do i = 1, this%barostat%ions_th_chain_length
               read(funit, *, IOSTAT=io) this%barostat%ions_th_zeta(i,istep), &
                              this%barostat%ions_th_v_zeta(i,istep), &
                              this%barostat%ions_th_G_zeta(i,istep)
               if (io /= 0) then
                  call MW_errors_runtime_error("read_data", "system.f90", &
                        "Thermostat chain length is inconsistent with thermostat_chain_length defined in the runtime")
               end if
            end do
         else
            do i = 1, thermostat_chain_length
               call MW_errors_runtime_warning("read_data", "system.f90", &
                     "Thermostat information skipped in the data")
               read(funit, *, IOSTAT=io) dummy, dummy_x, dummy_y 
               if (io /= 0) then
                  call MW_errors_runtime_error("read_data", "system.f90", &
                        "Thermostat chain length is inconsistent with thermostat_chain_length defined in the data")
               end if
            end do
         endif
      end do

      ! Read barostat parameters
      ! --------------------------
      if (nb_sections%barostat > 0) then
!        if ( barostat_chain_length /= this%barostat%b_th_chain_length) then
!           call MW_errors_runtime_error("read_data", "system.f90", &
!                 "Invalid chain length for barostat")
!        end if
         if ( nb_sections%barostat > size(this%barostat%zeta_step,1)) then
            call MW_errors_runtime_error("read_data", "system.f90", &
                  "Invalid number of step for barostat data")
         end if
      end if

      do istep = 1, nb_sections%barostat
         read(funit, *) hash_char, keyword, dummychar, barostat_chain_length
         call check_section_header(hash_char, keyword, "barostat")

         if (this%barostat%b_th_chain_length > 0 .and. this%barostat%b_th_chain_length .eq. barostat_chain_length) then
            read(funit, *, IOSTAT=io) this%barostat%b_epsilon(istep), &
                           this%barostat%b_v_epsilon(istep), &
                           this%barostat%b_F_epsilon(istep)
            if (io /= 0) then
               call MW_errors_runtime_error("read_data", "system.f90", &
                     "Barostat parameters are invalid")
            end if

            do i = 1, this%barostat%b_th_chain_length
               read(funit, *, IOSTAT=io) this%barostat%b_th_zeta(i,istep), &
                              this%barostat%b_th_v_zeta(i,istep), &
                              this%barostat%b_th_G_zeta(i,istep)
               if (io /= 0) then
                  call MW_errors_runtime_error("read_data", "system.f90", &
                        "Barostat chain length is inconsistent with barostat_chain_length defined in the runtime")
               end if
            end do
         else
            do i = 1, barostat_chain_length + 1
               call MW_errors_runtime_warning("read_data", "system.f90", &
                     "Barostat information skipped in the data")
               read(funit, *, IOSTAT=io) dummy, dummy_x, dummy_y 
               if (io /= 0) then
                  call MW_errors_runtime_error("read_data", "system.f90", &
                        "Thermostat chain length is inconsistent with thermostat_chain_length defined in the data")
               end if
            end do
         endif
      end do

      ! Read polarization of the melts ions
      ! --------------------------
      if (nb_sections%polarization > 0) then
         do istep = 1, nb_sections%polarization
            read(funit, *) hash_char, keyword
            call check_section_header(hash_char, keyword, "polarization")

            read(funit,*) this%polarization_field(1,2), this%polarization_field(2,2), &
                    this%polarization_field(3,2)
            this%polarization_field(:,1) = this%polarization_field(:,2)
         end do
      endif

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

      ! Finalize system setup
      ! ---------------------
      this%step = step
      call MW_box_define_type(this%box, a, b, c)
      num_ions = size(this%xyz_ions,1)
      call assign_ion_types(this)
      num_atoms = size(this%xyz_atoms,1)
      call assign_electrode_atom_types(this)
      call MW_ewald_define_kpoints(this%ewald, this%num_pbc, this%box, num_ions, num_atoms)
      ! Setup history pointers
      call setup_history(this)

      if (nb_sections%velocities > 0) this%velocity_initialized = .true.
      if (nb_sections%dipoles > 0) this%dipoles_initialized = .true.
      if (nb_sections%charges >= 7) this%dipoles_use_predictor = .true.
      if (nb_sections%radius > 0) this%radius_initialized = .true.
      if (nb_sections%charges > 0) this%electrode_charges_initialized = .true.
      if (nb_sections%charges >= 7) this%electrode_use_predictor = .true.
      if (nb_sections%forces > 0) then
         if ((this%electrode_charges_initialized) .or. (num_electrode_species == 0)) then
            this%forces_initialized = .true.
         end if
      if (nb_sections%polarization > 0) this%polarization_initialized = .true.
      end if

      ! finalize diagnostics
      call MW_diagnostic_define_type(this%diagnostic, num_species, this%box%length(3))

   end subroutine read_data

   ! ================================================================================
   subroutine assign_ion_types(this)
      use MW_ion, only: MW_ion_t
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      integer :: itype,j
      integer :: num_types

      num_types = size(this%ions,1)

      do itype = 1, num_types
         do j = 1, this%ions(itype)%count
            this%type_ions(this%ions(itype)%offset+j)=itype
         enddo
      enddo

   end subroutine assign_ion_types  

   ! ================================================================================
   subroutine assign_electrode_atom_types(this)
      use MW_electrode, only: MW_electrode_t
      implicit none
      ! Parameters
      ! ----------
      type(MW_system_t), intent(inout) :: this

      integer :: itype,j
      integer :: num_types

      num_types = size(this%electrodes,1)

      do itype = 1, num_types
         do j = 1, this%electrodes(itype)%count
            this%type_electrode_atoms(this%electrodes(itype)%offset+j)=itype
         enddo
      enddo

   end subroutine assign_electrode_atom_types  

   ! ================================================================================
   ! Compute density histogram
   subroutine compute_diagnostics(this)
      use MW_diagnostic, only : MW_diagnostic_compute_density => compute_density
      use MW_timers, only: TIMER_DIAG_COMPUTE, MW_timers_start, MW_timers_stop
      implicit none
      type(MW_system_t), intent(inout) :: this
      integer :: xyz_now, q_atoms_now

      call MW_timers_start(TIMER_DIAG_COMPUTE)
      xyz_now = this%xyz_ions_step(1)
      q_atoms_now = this%q_atoms_step(1)
      call MW_diagnostic_compute_density(this%diagnostic, this%ions, this%electrodes, &
            this%xyz_ions(:,3,xyz_now), this%xyz_atoms(:,3), this%q_atoms(:,q_atoms_now))
      call MW_timers_stop(TIMER_DIAG_COMPUTE)
   end subroutine compute_diagnostics

   ! ================================================================================
   ! Check whether we are using the electrodes
   subroutine check_electrodes(constant_charge, this)
      implicit none
      logical,           intent(in)    :: constant_charge
      type(MW_system_t), intent(inout) :: this

      this%electrode_constant_charge = constant_charge
      this%elecrun=.false.
      if(this%num_atoms>0) then
         this%elecrun=.true.
      end if
   end subroutine check_electrodes
   ! ================================================================================

   ! Check whether we are using the polarizable ion model
   subroutine check_polarizability(this)
      use MW_ion, only: MW_ion_t
      implicit none
      type(MW_system_t), intent(inout) :: this

      integer :: itype

      this%pimrun=.false.
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%polarizability > 0.0_wp) then
            this%pimrun=.true.
         end if
      end do
   end subroutine check_polarizability

   ! ================================================================================
   ! Check whether we are using the aspherical ion model (DAIM)
   subroutine check_deformability(this)
      use MW_ion, only: MW_ion_t
      implicit none
      type(MW_system_t), intent(inout) :: this

      integer :: itype

      this%aimrun=.false.
      do itype = 1, this%num_ion_types
         if (this%ions(itype)%selfdaimD > 0.0_wp) then
           this%aimrun=.true.
         end if
      enddo

   end subroutine check_deformability

   ! ================================================================================
   ! logical to text function
   character(3) function yes_or_no(test)
      logical, intent(in) :: test
      if (test) then
         yes_or_no = "yes"
      else
         yes_or_no = " no"
      end if
   end function yes_or_no

end module MW_system
