! Module to control output from simulations
!
! Different output files contain
!  - trajectories: ions position and velocities
!  - electrode charges: integral charge on each electrode atoms
!  - forces: forces on ions
!  - dipoles: dipoles on ions
!  - efg: efg on ions
!  - radius: radius of ions (delta,epsilon)
!  - energies: energy breakdown (total, kinetic, potential) + temperature
!  - pressure: internal pressure + pressure tensor of the system
!  - stress tensor: stress tensor of the system
!  - box parameters: volume + lengths of the box (NPT runs)
!  - polarization: total volumic polarization of the cell
module MW_output
   use MW_kinds, only: wp, PARTICLE_NAME_LEN
   use MW_ion, only: MW_ion_t
   use MW_electrode, only: MW_electrode_t
   use MW_external_field, only: MW_external_field_t
   use MW_diagnostic, only: MW_diagnostic_t
   implicit none
   private

   ! Macro for PGI compiler support
#if defined(__PGI)
#define SIGN_SUPPRESS
#define SIGN_PLUS
#else
#define SIGN_SUPPRESS , sign="suppress"
#define SIGN_PLUS , sign="PLUS"
#endif

   ! Public subroutines
   ! ------------------
   public :: close_all
   public :: trajectories_set_dump_frequency
   public :: trajectories_setup
   public :: trajectories_dump
   public :: xyz_set_dump_frequency
   public :: xyz_setup
   public :: xyz_dump
   public :: pdb_set_dump_frequency
   public :: pdb_setup
   public :: pdb_dump
   public :: lammps_set_dump_frequency
   public :: lammps_setup
   public :: lammps_dump
   public :: charges_set_dump_frequency
   public :: charges_setup
   public :: charges_dump
   public :: total_charges_set_dump_frequency
   public :: total_charges_setup
   public :: total_charges_dump
   public :: forces_set_dump_frequency
   public :: forces_setup
   public :: forces_dump
   public :: elec_forces_set_dump_frequency
   public :: elec_forces_setup
   public :: elec_forces_dump
   public :: dipoles_set_dump_frequency
   public :: dipoles_setup
   public :: dipoles_dump
   public :: efg_set_dump_frequency
   public :: efg_setup
   public :: efg_dump
   public :: radius_set_dump_frequency
   public :: radius_setup
   public :: radius_dump
   public :: potential_shift_set_dump_frequency
   public :: potential_shift_setup
   public :: potential_shift_dump
   public :: energies_set_dump_frequency
   public :: energies_setup
   public :: energies_dump
   public :: restart_setup
   public :: restart_set_dump_frequency
   public :: temperature_set_dump_frequency
   public :: temperature_setup
   public :: temperature_dump
   public :: pressure_set_dump_frequency
   public :: pressure_setup
   public :: pressure_dump
   public :: stress_tensor_set_dump_frequency
   public :: stress_tensor_setup
   public :: stress_tensor_dump
   public :: box_set_dump_frequency
   public :: box_setup
   public :: box_dump
   public :: polarization_set_dump_frequency
   public :: polarization_setup
   public :: polarization_dump
   public :: step_set_dump_frequency
   public :: diagnostic_set_dump_frequency
   public :: diagnostic_setup
   public :: diagnostic_dump

   ! Public type
   ! -----------
   public :: MW_output_t

   type MW_output_t
      ! Trajectories (positions and velocities)
      character(64) :: trajectories_file_name = ""
      integer :: trajectories_file_unit = 0
      integer :: trajectories_dump_frequency = 0

      ! Trajectories with xyz format (positions)
      character(64) :: xyz_file_name = ""
      integer :: xyz_file_unit = 0
      integer :: xyz_dump_frequency = 0

      ! Trajectories with pdb format (positions and charges)
      character(64) :: pdb_file_name = ""
      integer :: pdb_file_unit = 0
      integer :: pdb_dump_frequency = 0

      ! Trajectories with lammps format (positions and charges)
      character(64) :: lammps_file_name = ""
      integer :: lammps_file_unit = 0
      integer :: lammps_dump_frequency = 0

      ! Charges on electrode atoms
      character(64) :: charges_file_name = ""
      integer :: charges_file_unit = 0
      integer :: charges_dump_frequency = 0

      ! Total Charges on electrodes
      character(64) :: total_charges_file_name = ""
      integer :: total_charges_file_unit = 0
      integer :: total_charges_dump_frequency = 0

      ! Forces on ions
      character(64) :: forces_file_name = ""
      integer :: forces_file_unit = 0
      integer :: forces_dump_frequency = 0

      ! Forces on electrodes
      character(64) :: elec_forces_file_name = ""
      integer :: elec_forces_file_unit = 0
      integer :: elec_forces_dump_frequency = 0

      ! Dipoles on ions
      character(64) :: dipoles_file_name = ""
      integer :: dipoles_file_unit = 0
      integer :: dipoles_dump_frequency = 0

      ! Electric field gradient on ions
      character(64) :: efg_file_name = ""
      integer :: efg_file_unit = 0
      integer :: efg_dump_frequency = 0

      ! Radius of ions
      character(64) :: radius_file_name = ""
      integer :: radius_file_unit = 0
      integer :: radius_dump_frequency = 0

      ! Energies of the system
      character(64) :: energies_file_name = ""
      integer :: energies_file_unit = 0
      character(64) :: energies_detailed_file_name = ""
      integer :: energies_detailed_file_unit = 0
      integer :: energies_dump_frequency = 0

      ! Temperature
      character(64) :: temperature_file_name = ""
      integer :: temperature_file_unit = 0
      integer :: temperature_dump_frequency = 0

      ! Pressure
      character(64) :: pressure_file_name = ""
      integer :: pressure_file_unit = 0
      integer :: pressure_dump_frequency = 0

      ! Stress tensor
      character(64) :: stress_tensor_file_name = ""
      integer :: stress_tensor_file_unit = 0
      integer :: stress_tensor_dump_frequency = 0

      ! Box parameters
      character(64) :: box_file_name = ""
      integer :: box_file_unit = 0
      integer :: box_dump_frequency = 0

      ! Restart
      character(64) :: restart_file_name = ""
      integer :: restart_dump_frequency = 0

      ! Step (in run.out)
      integer :: step_dump_frequency = 0

      ! Polarization of the cell
      character(64) :: polarization_file_name = ""
      integer :: polarization_file_unit = 0
      integer :: polarization_dump_frequency = 0

      ! diagnostics
      character(64) :: number_density_file_name = ""
      character(64) :: charge_density_file_name = ""
      integer :: diagnostic_dump_frequency = 0

      !Dump and read back matrix for maze with electrode charges
      character(64) :: matrix_file_name = ""
      integer :: matrix_file_unit

      ! Potential shift for simulations at enforced neutrality
      character(64) :: potential_shift_file_name = ""
      integer :: potential_shift_file_unit = 0
      integer :: potential_shift_dump_frequency = 0

   end type MW_output_t

contains

   ! ================================================================================
   ! Close all output files
   subroutine close_all(this)
      use MW_errors, only: MW_errors_close_error => close_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this

      ! Local
      ! -----
      integer :: funit
      logical :: lopen
      integer :: ierr

      funit = this%trajectories_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%xyz_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%pdb_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%lammps_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%charges_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%total_charges_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%forces_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%elec_forces_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%dipoles_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%efg_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%radius_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%energies_detailed_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%energies_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%polarization_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%potential_shift_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%temperature_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%pressure_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%stress_tensor_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%box_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

      funit = this%matrix_file_unit
      if (funit > 0) then
         inquire(unit=funit, opened=lopen)
         if (lopen) then
            close(funit, iostat=ierr)
            if (ierr /= 0) then
               call MW_errors_close_error("MW_output_close", "output.f90", &
                     funit, ierr)
            end if
         end if
      end if

   end subroutine close_all


   ! ================================================================================
   ! Set dump frequency for trajectories
   subroutine trajectories_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%trajectories_dump_frequency = dump_frequency
   end subroutine trajectories_set_dump_frequency

   ! ================================================================================
   ! Open trajectories output file
   subroutine trajectories_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%trajectories_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("trajectories_",i6.6,".out")') rank
         else
            fname = "trajectories.out"
         end if

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

         this%trajectories_file_name = fname
         this%trajectories_file_unit = funit

         write(funit, '("# Trajectories of mobile particles in atomic unit")')
         write(funit, '("# -----------------------------------------------")')
         write(funit, '("# Length:   1 au = 0.052917721067 nm")')
         write(funit, '("# Velocity: 1 au = 2.1876912633e6 m.s^-1")')
         write(funit, '("# ",a21,5(1x,a23))') &
               "         x          ", &
               "           y           ", &
               "           z           ", &
               "           vx          ", &
               "           vy          ", &
               "           vz          "
      end if

   end subroutine trajectories_setup

   ! ================================================================================
   ! Dump trajectories to output file
   subroutine trajectories_dump(this, istep, ions, xyz_ions, velocity_ions)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      type(MW_ion_t), intent(in) :: ions(:)
      real(wp), intent(in) :: xyz_ions(:,:)
      real(wp), intent(in) :: velocity_ions(:,:)

      ! Local
      ! -----
      integer :: funit
      integer :: iion, itype, offset, i

      funit = this%trajectories_file_unit

      write(funit, '("# step ",i8)' SIGN_SUPPRESS) istep

      do itype = 1, size(ions,1)
         offset = ions(itype)%offset
         if (ions(itype)%dump_trajectories .and. ions(itype)%mobile) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '(6(es23.15E3,:,1x))') &
                     xyz_ions(iion,1), xyz_ions(iion,2), xyz_ions(iion,3), &
                     velocity_ions(iion, 1), velocity_ions(iion, 2), velocity_ions(iion, 3)
            end do
         end if
      end do

   end subroutine trajectories_dump

   ! ================================================================================
   ! Set dump frequency for xyz trajectories
   subroutine xyz_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%xyz_dump_frequency = dump_frequency
   end subroutine xyz_set_dump_frequency

   ! ================================================================================
   ! Open xyz trajectory output file
   subroutine xyz_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%xyz_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("trajectories_",i6.6,".xyz")') rank
         else
            fname = "trajectories.xyz"
         end if

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

         this%xyz_file_name = fname
         this%xyz_file_unit = funit
      end if

   end subroutine xyz_setup

   ! ================================================================================
   ! Dump xyz trajectories to output file
   subroutine xyz_dump(this, istep, ions, electrodes, xyz_ions, xyz_atoms, num_ions, num_atoms)
      use MW_constants, only: bohr2angstrom
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep, num_ions, num_atoms
      type(MW_ion_t), intent(in) :: ions(:)
      type(MW_electrode_t), intent(in) :: electrodes(:)
      real(wp), intent(in) :: xyz_ions(:,:), xyz_atoms(:,:)

      ! Local
      ! -----
      integer :: funit
      integer :: iion, iatom, itype, offset, i
      integer :: total_ions_atoms

      funit = this%xyz_file_unit

      total_ions_atoms = 0
      do itype = 1, size(ions,1)
         if (ions(itype)%dump_xyz) then
            total_ions_atoms = total_ions_atoms + ions(itype)%count
         end if
      end do
      do itype = 1, size(electrodes,1)
         if (electrodes(itype)%dump_xyz) then
            total_ions_atoms = total_ions_atoms + electrodes(itype)%count
         end if
      end do

      write(funit, '(i8)' SIGN_SUPPRESS) total_ions_atoms
      write(funit, '("# step ",i8)' SIGN_SUPPRESS) istep

      do itype = 1, size(ions,1)
         offset = ions(itype)%offset
         if (ions(itype)%dump_xyz) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '(A8,6(es23.15E3,:,1x))') ions(itype)%name, xyz_ions(iion, :)*bohr2angstrom
            end do
         end if
      end do

      do itype = 1, size(electrodes,1)
         offset = electrodes(itype)%offset
         if (electrodes(itype)%dump_xyz) then
            do i = 1, electrodes(itype)%count
               iatom = offset + i
               write(funit, '(A8,6(es23.15E3,:,1x))') electrodes(itype)%name, xyz_atoms(iatom, :)*bohr2angstrom
            end do
         end if
      end do

   end subroutine xyz_dump

   ! ================================================================================
   ! Set dump frequency for pdb trajectories
   subroutine pdb_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%pdb_dump_frequency = dump_frequency
   end subroutine pdb_set_dump_frequency

   ! ================================================================================
   ! Open pdb trajectory output file
   subroutine pdb_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%pdb_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("trajectories_",i6.6,".pdb")') rank
         else
            fname = "trajectories.pdb"
         end if

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

         this%pdb_file_name = fname
         this%pdb_file_unit = funit
      end if

   end subroutine pdb_setup

   ! ================================================================================
   ! Dump pdb trajectories to output file
   subroutine pdb_dump(this, ions, electrodes, xyz_ions, xyz_atoms, q_elec, box, molecules)
      use MW_constants, only: bohr2angstrom
      use MW_box, only: MW_box_t
      use MW_molecule, only: MW_molecule_t
      use MW_errors, only: MW_errors_allocate_error => allocate_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      type(MW_ion_t), intent(in) :: ions(:)
      type(MW_electrode_t), intent(in) :: electrodes(:)
      real(wp), intent(in) :: xyz_ions(:,:), xyz_atoms(:,:)
      type(MW_box_t), intent(in) :: box
      real(wp), intent(in) :: q_elec(:)
      type(MW_molecule_t), intent(in) :: molecules(:)

      ! Local
      ! -----
      integer :: funit
      integer :: ierr
      integer :: iion, iatom, itype, imol, isite, offset, i, c_atoms, c_mol, c_mol_type
      logical, allocatable :: in_molecule(:)

      funit = this%pdb_file_unit

      write(funit, '("CRYST1",3f9.3,"  90.00  90.00  90.00 P 1           1")' SIGN_SUPPRESS) &
                box%length(1)*bohr2angstrom, box%length(2)*bohr2angstrom, &
                box%length(3)*bohr2angstrom

      allocate(in_molecule(size(ions,1)), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("pdb_dump", "output.f90",ierr)
      end if

      in_molecule(:) = .false.
      c_atoms = 1
      c_mol_type = 1

      ! Loop on ion types in molecules
      do imol = 1, size(molecules)
         do isite = 1, molecules(imol)%num_sites
            itype = molecules(imol)%sites(isite)
            in_molecule(itype) = .true.
            c_mol = c_mol_type

            offset = ions(itype)%offset
            if (ions(itype)%dump_pdb) then
               do i = 1, ions(itype)%count
                  iion = offset + i
                  write(funit, '("ATOM  ",i5,1X,A4,6X,i4,4X,3f8.3,6X,f6.2)' SIGN_SUPPRESS) &
                       c_atoms, ions(itype)%name, c_mol, xyz_ions(iion,:)*bohr2angstrom, &
                       ions(itype)%charge
                  c_atoms = c_atoms + 1
                  c_mol = c_mol + 1
               end do
            end if
         end do
         c_mol_type = c_mol
      end do

      ! Loop on ion types not in molecules
      do itype = 1, size(ions,1)
         if (in_molecule(itype)) CYCLE ! skip if in molecule
         offset = ions(itype)%offset
         if (ions(itype)%dump_pdb) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '("ATOM  ",i5,1X,A4,6X,i4,4X,3f8.3,6X,f6.2)' SIGN_SUPPRESS) &
                    c_atoms, ions(itype)%name, 0, xyz_ions(iion, :)*bohr2angstrom, &
                    ions(itype)%charge
               c_atoms = c_atoms + 1
            end do
         end if
      end do

      ! Loop on electrodes
      do itype = 1, size(electrodes,1)
         offset = electrodes(itype)%offset
         if (electrodes(itype)%dump_pdb) then
            do i = 1, electrodes(itype)%count
               iatom = offset + i
               write(funit, '("ATOM  ",i5,1X,A4,6X,i4,4X,3f8.3,6X,es6.0E1)' SIGN_SUPPRESS) &
                    c_atoms, electrodes(itype)%name, c_mol_type, xyz_atoms(iatom, :)*bohr2angstrom, &
                    q_elec(iatom)
               c_atoms = c_atoms + 1
            end do
         end if
         c_mol_type = c_mol_type + 1
      end do

      write(funit, '("END")')

   end subroutine pdb_dump

   ! ================================================================================
   ! Set dump frequency for lammps trajectories
   subroutine lammps_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%lammps_dump_frequency = dump_frequency
   end subroutine lammps_set_dump_frequency

   ! ================================================================================
   ! Open lammps trajectory output file
   subroutine lammps_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%lammps_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("trajectories_",i6.6,".lammpstrj")') rank
         else
            fname = "trajectories.lammpstrj"
         end if

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

         this%lammps_file_name = fname
         this%lammps_file_unit = funit
      end if

   end subroutine lammps_setup

   ! ================================================================================
   ! Dump lammps trajectories to output file
   subroutine lammps_dump(this, istep, ions, electrodes, xyz_ions, xyz_atoms, num_ions, num_atoms, &
             q_elec, box, molecules)
      use MW_constants, only: bohr2angstrom
      use MW_box, only: MW_box_t
      use MW_molecule, only: MW_molecule_t
      use MW_errors, only: MW_errors_allocate_error => allocate_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep, num_ions, num_atoms
      type(MW_ion_t), intent(in) :: ions(:)
      type(MW_electrode_t), intent(in) :: electrodes(:)
      real(wp), intent(in) :: xyz_ions(:,:), xyz_atoms(:,:)
      type(MW_box_t), intent(in) :: box
      real(wp), intent(in) :: q_elec(:)
      type(MW_molecule_t), intent(in) :: molecules(:)

      ! Local
      ! -----
      integer :: funit
      integer :: ierr
      integer :: iion, iatom, itype, imol, isite, offset, i, c_atoms, c_mol, c_mol_type
      integer :: total_ions_atoms
      logical, allocatable :: in_molecule(:)

      funit = this%lammps_file_unit

      total_ions_atoms = 0
      do itype = 1, size(ions,1)
         if (ions(itype)%dump_lammps) then
            total_ions_atoms = total_ions_atoms + ions(itype)%count
         end if
      end do
      do itype = 1, size(electrodes,1)
         if (electrodes(itype)%dump_lammps) then
            total_ions_atoms = total_ions_atoms + electrodes(itype)%count
         end if
      end do

      write(funit, '("ITEM: TIMESTEP")')
      write(funit, '(i8)' SIGN_SUPPRESS) istep
      write(funit, '("ITEM: NUMBER OF ATOMS")')
      write(funit, '(i8)' SIGN_SUPPRESS) total_ions_atoms
      write(funit, '("ITEM: BOX BOUNDS")')
      write(funit, '(2f9.3)' SIGN_SUPPRESS) 0.0, box%length(1)*bohr2angstrom
      write(funit, '(2f9.3)' SIGN_SUPPRESS) 0.0, box%length(2)*bohr2angstrom
      write(funit, '(2f9.3)' SIGN_SUPPRESS) 0.0, box%length(3)*bohr2angstrom
      write(funit, '("ITEM: ATOMS element id molid x y z q vx vy vz")')

      allocate(in_molecule(size(ions,1)), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("lammps_dump", "output.f90",ierr)
      end if

      in_molecule(:) = .false.
      c_atoms = 1
      c_mol_type = 1

      ! Loop on ion types in molecules
      do imol = 1, size(molecules)
         do isite = 1, molecules(imol)%num_sites
            itype = molecules(imol)%sites(isite)
            in_molecule(itype) = .true.
            c_mol = c_mol_type

            offset = ions(itype)%offset
            if (ions(itype)%dump_lammps) then
               do i = 1, ions(itype)%count
                  iion = offset + i
                  write(funit, '(A8,6X,i8,6X,i8,6X,3f9.3,6X,4f9.3)' SIGN_SUPPRESS) &
                       ions(itype)%name, c_atoms, c_mol, xyz_ions(iion,:)*bohr2angstrom, &
                       ions(itype)%charge, 0.0, 0.0, 0.0
                  c_atoms = c_atoms + 1
                  c_mol = c_mol + 1
               end do
            end if
         end do
         c_mol_type = c_mol
      end do

      ! Loop on ion types not in molecules
      do itype = 1, size(ions,1)
         if (in_molecule(itype)) CYCLE ! skip if in molecule
         offset = ions(itype)%offset
         if (ions(itype)%dump_lammps) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '(A8,6X,i8,6X,i8,6X,3f9.3,6X,4f9.3)' SIGN_SUPPRESS) &
                    ions(itype)%name, c_atoms, 0, xyz_ions(iion, :)*bohr2angstrom, &
                    ions(itype)%charge, 0.0, 0.0, 0.0
               c_atoms = c_atoms + 1
            end do
         end if
      end do

      ! Loop on electrodes
      do itype = 1, size(electrodes,1)
         offset = electrodes(itype)%offset
         if (electrodes(itype)%dump_lammps) then
            do i = 1, electrodes(itype)%count
               iatom = offset + i
               write(funit, '(A8,6X,i8,6X,i8,6X,3f9.3,6X,f9.3,es15.3E3,2f9.3)' SIGN_SUPPRESS) &
                    electrodes(itype)%name, c_atoms, c_mol_type, xyz_atoms(iatom, :)*bohr2angstrom, &
                    0.0, q_elec(iatom), 0.0, 0.0
               c_atoms = c_atoms + 1
            end do
         end if
         c_mol_type = c_mol_type + 1
      end do

   end subroutine lammps_dump

   ! ================================================================================
   ! Set dump frequency for charges
   subroutine charges_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%charges_dump_frequency = dump_frequency
   end subroutine charges_set_dump_frequency

   ! ================================================================================
   ! Open charges output file
   subroutine charges_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%charges_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("charges_",i6.6,".out")') rank
         else
            fname = "charges.out"
         end if
         call MW_fileunit_get_new_unit(funit)
         open(unit=funit, file=fname, access="SEQUENTIAL", action="WRITE", &
               position="REWIND", form="FORMATTED" SIGN_PLUS, status="REPLACE", &
               iostat=ierr)
         if (ierr /= 0) then
            call MW_errors_open_error("MW_output_frequencies_setup", "output.f90", fname, ierr)
         end if

         this%charges_file_name = fname
         this%charges_file_unit = funit

         write(funit, '("# Electrode atom charges in atomic unit")')
         write(funit, '("# -------------------------------------")')
         write(funit, '("# Charge:   1 au = 1.602176565e-19 C = 1 e")')
      end if

   end subroutine charges_setup

   ! ================================================================================
   ! Dump charges to output file
   subroutine charges_dump(this, istep, electrodes, q_elec)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      type(MW_electrode_t), intent(in) :: electrodes(:)
      real(wp), intent(in) :: q_elec(:)

      ! Local
      ! -----
      integer :: funit
      integer :: iatom, itype, offset, i

      funit = this%charges_file_unit
      write(funit, '("# step ",i8)' SIGN_SUPPRESS) istep

      do itype = 1, size(electrodes,1)
         offset = electrodes(itype)%offset
         do i = 1, electrodes(itype)%count
            iatom = offset+i
            write(funit, '(1(es23.15E3,:,1x))') q_elec(iatom)
         end do
      end do

   end subroutine charges_dump

   ! ================================================================================
   ! Set dump frequency for charges
   subroutine total_charges_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%total_charges_dump_frequency = dump_frequency
   end subroutine total_charges_set_dump_frequency

   ! ================================================================================
   ! Open charges output file
   subroutine total_charges_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%total_charges_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("total_charges_",i6.6,".out")') rank
         else
            fname = "total_charges.out"
         end if

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

         this%total_charges_file_name = fname
         this%total_charges_file_unit = funit

         write(funit, '("# Total charges on electrodes in atomic unit")')
         write(funit, '("# ------------------------------------------")')
         write(funit, '("# Charge:   1 au = 1.602176565e-19 C = 1 e")')
      end if

   end subroutine total_charges_setup

   ! ================================================================================
   ! Dump charges to output file
   subroutine total_charges_dump(this, istep, electrodes, q_elec)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      type(MW_electrode_t), intent(in) :: electrodes(:)
      real(wp), intent(in) :: q_elec(:)

      ! Local
      ! -----
      integer :: funit
      integer :: iatom, itype, offset, i
      real(wp) :: total_charges(100) ! max 100 electrodes!

      funit = this%total_charges_file_unit
      if (size(electrodes,1) > 0) then
         total_charges(:) = 0.0_wp
         do itype = 1, size(electrodes,1)
            offset = electrodes(itype)%offset
            do i = 1, electrodes(itype)%count
               iatom = offset+i
               total_charges(itype) = total_charges(itype) + q_elec(iatom)
            end do
         end do
         write(funit, '(i8, 100(1x, es23.15E3))') istep, &
               (total_charges(itype), itype = 1, size(electrodes,1))
      end if

   end subroutine total_charges_dump

   ! ================================================================================
   ! Set dump frequency for forces
   subroutine forces_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%forces_dump_frequency = dump_frequency
   end subroutine forces_set_dump_frequency

   ! ================================================================================
   ! Open forces output file
   subroutine forces_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%forces_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("forces_",i6.6,".out")') rank
         else
            fname = "forces.out"
         end if

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

         this%forces_file_name = fname
         this%forces_file_unit = funit

         write(funit, '("# Forces on mobile particles in atomic unit")')
         write(funit, '("# -----------------------------------------")')
         write(funit, '("# Force:   1 au = 8.2387225e-8 N = 51.421 eV/angstrom")')
         write(funit, '("#",a22,23(1x,a23))') &
               "       fx_total        ", "       fy_total        ", "       fz_total        ",&
               "      fx_coulomb       ", "      fy_coulomb       ", "      fz_coulomb       ",&
               "     fx_coulomb_lr     ", "     fy_coulomb_lr     ", "     fz_coulomb_lr     ",&
               "     fx_coulomb_k0     ", "     fy_coulomb_k0     ", "     fz_coulomb_k0     ",&
               "     fx_coulomb_sr     ", "     fy_coulomb_sr     ", "     fz_coulomb_sr     ",&
               "     fx_coulomb_mol    ", "     fy_coulomb_mol    ", "     fz_coulomb_mol    ",&
               "    fx_van_der_Waals   ", "    fy_van_der_Waals   ", "    fz_van_der_Waals   ",&
               "   fx_intramolecular   ", "   fy_intramolecular   ", "   fz_intramolecular   "
      end if

   end subroutine forces_setup

   ! ================================================================================
   ! Dump forces to output file
   subroutine forces_dump(this, step, ions, forces_ions, coulomb_forces_ions, &
         intermolecular_forces_ions, intramolecular_forces_ions)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: step
      type(MW_ion_t), intent(in) :: ions(:) !< ion types parameters
      real(wp), intent(in) :: forces_ions(:,:) !< forces on ions
      real(wp), intent(in) :: coulomb_forces_ions(:,:,:) !< Coulomb forces on ions
      real(wp), intent(in) :: intermolecular_forces_ions(:,:) !< Intermolecular forces on ions
      real(wp), intent(in) :: intramolecular_forces_ions(:,:) !< Intramolecular forces on ions

      ! Local
      ! -----
      integer :: funit
      integer :: iion, itype, offset, i

      funit = this%forces_file_unit

      write(funit, '("# step ",i8)' SIGN_SUPPRESS) step

      do itype = 1, size(ions,1)
         offset = ions(itype)%offset
         if (ions(itype)%mobile) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '(24(es23.15E3,:,1x))') &
                     forces_ions(iion, 1), forces_ions(iion, 2), forces_ions(iion, 3), &
                     coulomb_forces_ions(iion, 1, 1), coulomb_forces_ions(iion, 2, 1), coulomb_forces_ions(iion, 3, 1), &
                     coulomb_forces_ions(iion, 1, 2), coulomb_forces_ions(iion, 2, 2), coulomb_forces_ions(iion, 3, 2), &
                     coulomb_forces_ions(iion, 1, 3), coulomb_forces_ions(iion, 2, 3), coulomb_forces_ions(iion, 3, 3), &
                     coulomb_forces_ions(iion, 1, 4), coulomb_forces_ions(iion, 2, 4), coulomb_forces_ions(iion, 3, 4), &
                     coulomb_forces_ions(iion, 1, 5), coulomb_forces_ions(iion, 2, 5), coulomb_forces_ions(iion, 3, 5), &
                     intermolecular_forces_ions(iion, 1), intermolecular_forces_ions(iion, 2), &
                     intermolecular_forces_ions(iion, 3), &
                     intramolecular_forces_ions(iion, 1), intramolecular_forces_ions(iion, 2), &
                     intramolecular_forces_ions(iion, 3)
            end do
         end if
      end do
   end subroutine forces_dump

   ! ================================================================================
   ! Set dump frequency for elec_forces
   subroutine elec_forces_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%elec_forces_dump_frequency = dump_frequency
   end subroutine elec_forces_set_dump_frequency

   ! ================================================================================
   ! Open forces output file
   subroutine elec_forces_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%elec_forces_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("electrodes_forces_",i6.6,".out")') rank
         else
            fname = "electrode_forces.out"
         end if

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

         this%elec_forces_file_name = fname
         this%elec_forces_file_unit = funit

         write(funit, '("# Total force on electrodes and pressure in atomic unit")')
         write(funit, '("# -----------------------------------------")')
         write(funit, '("# Force:   1 au = 8.2387225e-8 N = 51.421 eV/angstrom")')
         write(funit, '("# Pressure:   1 bar = 3.3988273773641905e-09 ua")')
         write(funit, '("#",a22,23(1x,a23))') &
               "      fx_total        ", &
               "       fy_total        ", &
               "       fz_total        ", &
               "     fx_coulomb_sr     ", &
               "     fy_coulomb_sr     ", &
               "     fz_coulomb_sr     ", &
               "     fx_coulomb_k0     ", &
               "     fy_coulomb_k0     ", &
               "     fz_coulomb_k0     ", &
               "     fx_coulomb_lr     ", &
               "     fy_coulomb_lr     ", &
               "     fz_coulomb_lr     ", &
               "   fx_lennard_jones    ", &
               "   fy_lennard_jones    ", &
               "   fz_lennard_jones    ", &
               "     fx_fumi_tosi      ", &
               "     fy_fumi_tosi      ", &
               "     fz_fumi_tosi      ", &
               "        fx_xft         ", &
               "        fy_xft         ", &
               "        fz_xft         ", &
               "        pressure       ", &
               "       velocity_z      ", &
               "         com_z         "
      end if

   end subroutine elec_forces_setup

   ! ================================================================================
   ! Dump forces to output file
   subroutine elec_forces_dump(this, step, electrodes, area)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: step
      type(MW_electrode_t), intent(in) :: electrodes(:) !< ion types parameters
      real(wp), intent(in) :: area

      ! Local
      ! -----
      integer :: funit
      integer :: itype

      funit = this%elec_forces_file_unit

      write(funit, '("# step ",i8)' SIGN_SUPPRESS) step

      do itype = 1, size(electrodes,1)
         write(funit, '(21(es23.15E3,:,1x))') electrodes(itype)%force_ions(1, 1), &
               electrodes(itype)%force_ions(2, 1), &
               electrodes(itype)%force_ions(3, 1), &
               electrodes(itype)%force_ions(1, 2) + electrodes(itype)%force_ions(1, 3), &
               electrodes(itype)%force_ions(2, 2) + electrodes(itype)%force_ions(2, 3), &
               electrodes(itype)%force_ions(3, 2) + electrodes(itype)%force_ions(3, 3), &
               electrodes(itype)%force_ions(1, 4) + electrodes(itype)%force_ions(1, 5), &
               electrodes(itype)%force_ions(2, 4) + electrodes(itype)%force_ions(2, 5), &
               electrodes(itype)%force_ions(3, 4) + electrodes(itype)%force_ions(3, 5), &
               electrodes(itype)%force_ions(1, 6), &
               electrodes(itype)%force_ions(2, 6), &
               electrodes(itype)%force_ions(3, 6), &
               electrodes(itype)%force_ions(1, 7), &
               electrodes(itype)%force_ions(2, 7), &
               electrodes(itype)%force_ions(3, 7), &
               electrodes(itype)%force_ions(1, 8), &
               electrodes(itype)%force_ions(2, 8), &
               electrodes(itype)%force_ions(3, 8), &
               electrodes(itype)%force_ions(3, 1) / area, &
               electrodes(itype)%velocity, electrodes(itype)%comz
      end do
   end subroutine elec_forces_dump

   ! ================================================================================
   ! Set dump frequency for dipoles
   subroutine dipoles_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%dipoles_dump_frequency = dump_frequency
   end subroutine dipoles_set_dump_frequency

   ! ================================================================================
   ! Open dipoles output file
   subroutine dipoles_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%dipoles_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("dipoles_",i6.6,".out")') rank
         else
            fname = "dipoles.out"
         end if

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

         this%dipoles_file_name = fname
         this%dipoles_file_unit = funit

         write(funit, '("# Dipoles on mobile particles in atomic unit")')
         write(funit, '("# -----------------------------------------")')
         write(funit, '("# Dipole:   1 au = 2.541746 D")')
         write(funit, '("#",a22,2(1x,a23))') &
               "      mux         ", "       muy         ", "       muz         "
      end if

   end subroutine dipoles_setup

   ! ================================================================================
   ! Dump dipoles to output file
   subroutine dipoles_dump(this, step, ions, dipoles_ions)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: step
      type(MW_ion_t), intent(in) :: ions(:) !< ion types parameters
      real(wp), intent(in) :: dipoles_ions(:,:) !< dipoles on ions

      ! Local
      ! -----
      integer :: funit
      integer :: iion, itype, offset, i

      funit = this%dipoles_file_unit
      write(funit, '("# step ",i8)' SIGN_SUPPRESS) step

      do itype = 1, size(ions,1)
         offset = ions(itype)%offset
         if (ions(itype)%mobile) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '(3(es23.15E3,:,1x))') &
                     dipoles_ions(iion, 1), dipoles_ions(iion, 2), dipoles_ions(iion, 3)
            end do
         end if
      end do
   end subroutine dipoles_dump

   ! ================================================================================
   ! Set dump frequency for the electric field gradient
   subroutine efg_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%efg_dump_frequency = dump_frequency
   end subroutine efg_set_dump_frequency

   ! ================================================================================
   ! Open the electric field gradient output file
   subroutine efg_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%efg_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("efg_",i6.6,".out")') rank
         else
            fname = "efg.out"
         end if

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

         this%efg_file_name = fname
         this%efg_file_unit = funit

         write(funit, '("# Electric field gradient on specified ion types in atomic units")')
         write(funit, '("# ------------------------------------------------------")')
         write(funit, '("# Electric field gradient:   1 au = 9.717362E21 SI")')
         write(funit, '("#",a22,5(1x,a23))') &
               "      xx         ", "       xy         ", "       xz         ", &
               "       yy         ", "       yz         ", "       zz         "
      end if

   end subroutine efg_setup

   ! ================================================================================
   ! Dump dipoles to output file
   subroutine efg_dump(this, step, ions, efg_ions)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: step
      type(MW_ion_t), intent(in) :: ions(:) !< ion types parameters
      real(wp), intent(in) :: efg_ions(:,:) !< dipoles on ions

      ! Local
      ! -----
      integer :: funit
      integer :: iion, itype, offset, i

      funit = this%efg_file_unit
      write(funit, '("# step ",i8)' SIGN_SUPPRESS) step

      do itype = 1, size(ions,1)
         offset = ions(itype)%offset
         if (ions(itype)%dump_efg) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '(6(es23.15E3,:,1x))') &
                     efg_ions(iion, 1), efg_ions(iion, 2), efg_ions(iion, 3), &
                     efg_ions(iion, 4), efg_ions(iion, 5), efg_ions(iion, 6)
            end do
         end if
      end do
   end subroutine efg_dump

   ! ================================================================================
   ! Set dump frequency for radius
   subroutine radius_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%radius_dump_frequency = dump_frequency
   end subroutine radius_set_dump_frequency

   ! ================================================================================
   ! Open radius output file
   subroutine radius_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%radius_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("radius_",i6.6,".out")') rank
         else
            fname = "radius.out"
         end if

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

         this%radius_file_name = fname
         this%radius_file_unit = funit

         write(funit, '("# Radius of mobile particles in atomic unit")')
         write(funit, '("# -----------------------------------------")')
         write(funit, '("# Radius:   1 au = 0.529177 angstrom")')
         write(funit, '("#",a22,4(1x,a23))') &
               "    delta         ", "  epsilonx         ", "  epsilony         ", "  epsilonz         "
      end if

   end subroutine radius_setup

   ! ================================================================================
   ! Dump radius to output file
   subroutine radius_dump(this, step, ions, deltas_ions,epsilons_ions)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: step
      type(MW_ion_t), intent(in) :: ions(:) !< ion types parameters
      real(wp), intent(in) :: deltas_ions(:) !< deltas of ions
      real(wp), intent(in) :: epsilons_ions(:,:) !< epsilons of ions

      ! Local
      ! -----
      integer :: funit
      integer :: iion, itype, offset, i

      funit = this%radius_file_unit
      write(funit, '("# step ",i8)' SIGN_SUPPRESS) step

      do itype = 1, size(ions,1)
         offset = ions(itype)%offset
         if (ions(itype)%mobile) then
            do i = 1, ions(itype)%count
               iion = offset + i
               write(funit, '(4(es23.15E3,:,1x))') &
                     deltas_ions(iion),epsilons_ions(iion, 1), epsilons_ions(iion, 2), epsilons_ions(iion, 3)
            end do
         end if
      end do
   end subroutine radius_dump

   ! ================================================================================
   ! Set dump frequency for potential shift
   subroutine potential_shift_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%potential_shift_dump_frequency = dump_frequency
   end subroutine potential_shift_set_dump_frequency

   ! ================================================================================
   ! Open potential shift output file
   subroutine potential_shift_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%potential_shift_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("potential_shift_",i6.6,".out")') rank
         else
            fname = "potential_shift.out"
         end if

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

         this%potential_shift_file_name = fname
         this%potential_shift_file_unit = funit

         write(funit, '("# Potential shift of the electrodes in atomic unit")')
         write(funit, '("# -----------------------------------------")')
         write(funit, '("# Potential:   1 au = 27.2113834 V")')
      end if

   end subroutine potential_shift_setup

   ! ================================================================================
   ! Dump potential shift to output file
   subroutine potential_shift_dump(this, istep, ki)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      real(wp), intent(in) :: ki

      ! Local
      ! -----
      integer :: funit

      funit = this%potential_shift_file_unit
      write(funit, '(i9,1x,es23.15E3)') istep, ki
   end subroutine potential_shift_dump

   ! ================================================================================
   ! Set dump frequency for energies
   subroutine energies_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%energies_dump_frequency = dump_frequency
   end subroutine energies_set_dump_frequency

   ! ================================================================================
   ! Open energies output file
   subroutine energies_setup(this, energies_to_dump, energy_terms_to_dump, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      use MW_lookup_table, only: MW_lut_t, lut_get, CHARLENGTH
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      character(CHARLENGTH), intent(in) :: energies_to_dump(:)     !< energy of the system
      integer, intent(in) :: energy_terms_to_dump
      integer, intent(in), optional :: rank    !< mpi rank energy_num_terms

      ! Local
      ! -----
      integer :: funit, detailed_funit
      character(64) :: fname, detailed_fname
      integer :: ierr, term
      character(256) :: errmsg
      character(len=:), allocatable :: energy_label
      character(9) :: step_str       = "# step   "
      character(25) :: hamilt_str    = "      Hamiltonian        "
      character(25) :: tot_str       = "      Total energy       "
      character(25) :: kin_str       = "     Kinetic energy      "
      character(25) :: pot_str       = "    Potential energy     "
      character(25) :: coul_str      = "     Coulomb energy      "
      character(25) :: intra_mol_str = " Intramolecular energy   "
      character(25) :: inter_mol_str = "  van_der_Waals energy   "
      character(25) :: thermo_str    = "   Thermostat energy     "
      character(25) :: baro_str      = "     Barostat energy     "
      character(25) :: coul_lr_str   = "   Coulomb LR energy     "
      character(25) :: coul_k0_str   = "  Coulomb keq0 energy    "
      character(25) :: coul_sr_str   = "   Coulomb SR energy     "
      character(25) :: coul_self_str = "  Coulomb self energy    "
      character(25) :: coul_mol_str  = " Coulomb molecule energy "
      character(25) :: harm_bond_str = "  Harmonic bond energy   "
      character(25) :: harm_ang_str  = " Harmonic angle energy   "
      character(25) :: dihedrals_str = "    Dihedrals energy     "
      character(25) :: ft_str        = "    Fumi-Tosi energy     "
      character(25) :: ft_mol_str    = "Fumi-Tosi molecule energy"
      character(25) :: xft_str       = "         XFT energy      "
      character(25) :: xft_mol_str   = "   XFT molecule energy   "
      character(25) :: lj_str        = "  Lennard-Jones energy   "
      character(25) :: lj_cut_str    = "    L-J cutoff energy    "
      character(25) :: daim_str      = "       DAIM energy       "
      character(25) :: daim_self_str = "     DAIM self energy    "
      character(25) :: piston_str    = "       Piston work       "
      character(25) :: plumed_str    = "       Plumed bias       "
      character(25) :: tf_str        = "   Thomas-Fermi energy   "
      character(25) :: ext_field_str = "  External field energy  "
      character(25) :: steele_str    = "      Steele energy      "

      if (this%energies_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("energies_",i6.6,".out")') rank
            write(detailed_fname, '("energies_breakdown_",i6.6,".out")') rank
         else
            fname = "energies.out"
            detailed_fname = "energies_breakdown.out"
         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("MW_output_energies", "output.f90", fname, ierr)
         end if

         this%energies_file_name = fname
         this%energies_file_unit = funit

         call MW_fileunit_get_new_unit(detailed_funit)
         open(unit=detailed_funit, file=detailed_fname, access="SEQUENTIAL", action="WRITE", &
               position="REWIND", form="FORMATTED", status="REPLACE", &
               iostat=ierr, iomsg=errmsg)
         if (ierr /= 0) then
            print *, errmsg
            call MW_errors_open_error("MW_output_energies_setup", "output.f90", detailed_fname, ierr)
         end if

         this%energies_detailed_file_name = detailed_fname
         this%energies_detailed_file_unit = detailed_funit

         ! Header
         ! ------
         write(funit, '("# Energy in atomic unit (1 Eh = 2625.499640 kJ/mol)")')
         write(funit, '("# -------------------------------------------------")')
         write(funit, '("#",a7,6(1x,a23))') " step  ", &
               "     Hamiltonian       ", &
               "     Total energy      ", &
               "    Kinetic energy     ", &
               "   Potential energy    ", &
               "Thermostat/barostat en.", &
               " External field energy "

         write(detailed_funit, '("# Energy in atomic unit (1 Eh = 2625.499640 kJ/mol)")')
         write(detailed_funit, '("# -------------------------------------------------")')

         energy_label = step_str
         do term = 1, energy_terms_to_dump
            if(energies_to_dump(term) == "Hamiltonian") energy_label = energy_label // hamilt_str
            if(energies_to_dump(term) == "Kinetic") energy_label = energy_label // kin_str
            if(energies_to_dump(term) == "Potential") energy_label = energy_label // pot_str
            if(energies_to_dump(term) == "Intramolecular") energy_label = energy_label // intra_mol_str
            if(energies_to_dump(term) == "van_der_Waals") energy_label = energy_label // inter_mol_str
            if(energies_to_dump(term) == "Total") energy_label = energy_label // tot_str
            if(energies_to_dump(term) == "Thermostat") energy_label = energy_label // thermo_str
            if(energies_to_dump(term) == "Barostat") energy_label = energy_label // baro_str
            if(energies_to_dump(term) == "Coulomb") energy_label = energy_label // coul_str
            if(energies_to_dump(term) == "Coulomb_lr") energy_label = energy_label // coul_lr_str
            if(energies_to_dump(term) == "Coulomb_keq0") energy_label = energy_label // coul_k0_str
            if(energies_to_dump(term) == "Coulomb_sr") energy_label = energy_label // coul_sr_str
            if(energies_to_dump(term) == "Coulomb_self") energy_label = energy_label // coul_self_str
            if(energies_to_dump(term) == "Coulomb_molecule") energy_label = energy_label // coul_mol_str
            if(energies_to_dump(term) == "Harmonic_bond") energy_label = energy_label // harm_bond_str
            if(energies_to_dump(term) == "Harmonic_angle") energy_label = energy_label // harm_ang_str
            if(energies_to_dump(term) == "Dihedrals") energy_label = energy_label // dihedrals_str
            if(energies_to_dump(term) == "Fumi-Tosi") energy_label = energy_label // ft_str
            if(energies_to_dump(term) == "Fumi-Tosi_molecule") energy_label = energy_label // ft_mol_str
            if(energies_to_dump(term) == "XFT") energy_label = energy_label // xft_str
            if(energies_to_dump(term) == "XFT_molecule") energy_label = energy_label // xft_mol_str
            if(energies_to_dump(term) == "Lennard-Jones") energy_label = energy_label // lj_str
            if(energies_to_dump(term) == "Lennard-Jones_cutoff") energy_label = energy_label // lj_cut_str
            if(energies_to_dump(term) == "DAIM") energy_label = energy_label // daim_str
            if(energies_to_dump(term) == "DAIM_self") energy_label = energy_label //daim_self_str
            if(energies_to_dump(term) == "Piston_work") energy_label = energy_label // piston_str
            if(energies_to_dump(term) == "Plumed_bias") energy_label = energy_label // plumed_str
            if(energies_to_dump(term) == "Thomas_Fermi") energy_label = energy_label // tf_str
            if(energies_to_dump(term) == "External_field") energy_label = energy_label // ext_field_str
            if(energies_to_dump(term) == "Steele") energy_label = energy_label // steele_str
         end do

         write(detailed_funit, "(a)") trim(energy_label)
      end if
   end subroutine energies_setup

   subroutine append_value(energy, key, str_to_append)
      use MW_lookup_table, only: MW_lut_t, lut_get
      implicit none
      ! Parameters
      ! ----------
      type(MW_lut_t), intent(in) :: energy     !< energy of the system
      character(len=*), intent(in) :: key
      character(len=:), intent(inout), allocatable :: str_to_append

      character(25) :: value_str

      write(value_str,'(1x,es24.15e3)') lut_get(energy, key)
      str_to_append = str_to_append//value_str
   end subroutine append_value

   ! ================================================================================
   ! Dump energy to output file
   subroutine energies_dump(this, step, energy, energies_to_dump, energy_terms_to_dump)
      use MW_lookup_table, only: MW_lut_t, lut_get, CHARLENGTH
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: step
      type(MW_lut_t), intent(in) :: energy     !< energy of the system
      character(CHARLENGTH), intent(in) :: energies_to_dump(:)     !< energy of the system
      integer, intent(in) :: energy_terms_to_dump

      ! Local
      ! -----
      integer :: funit, detailed_funit
      character(len=:), allocatable :: energy_values
      character(7) :: step_str
      integer :: term

      funit = this%energies_file_unit
      detailed_funit = this%energies_detailed_file_unit
      if(lut_get(energy, "Barostat").ne.0.0_wp)then
         write(funit,'(i8,6(1x,es23.15e3))') step, lut_get(energy, "Hamiltonian"), &
                                                   lut_get(energy, "Total"), &
                                                   lut_get(energy, "Kinetic"), &
                                                   lut_get(energy, "Potential"), &
                                                   lut_get(energy, "Barostat"), &
                                                   lut_get(energy, "External_field")
         write(step_str,'(i7)') step
      else
         write(funit,'(i8,6(1x,es23.15e3))') step, lut_get(energy, "Hamiltonian"), &
                                                   lut_get(energy, "Total"), &
                                                   lut_get(energy, "Kinetic"), &
                                                   lut_get(energy, "Potential"), &
                                                   lut_get(energy, "Thermostat"), &
                                                   lut_get(energy, "External_field")
         write(step_str,'(i7)') step
      endif

      energy_values = step_str
      do term = 1, energy_terms_to_dump
         call append_value(energy, energies_to_dump(term), energy_values)
      end do

      write(detailed_funit, "(a)") energy_values
   end subroutine energies_dump

   ! ================================================================================
   ! setup restart output file name
   subroutine restart_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      character(64) :: fname

      if (present(rank)) then
         write(fname, '("restart_",i6.6)') rank
      else
         fname = "restart"
      end if

      this%restart_file_name = fname

   end subroutine restart_setup

   ! ================================================================================
   ! Set dump frequency for restart
   subroutine restart_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%restart_dump_frequency = dump_frequency
   end subroutine restart_set_dump_frequency

   ! ================================================================================
   ! Set dump frequency for temperature
   subroutine temperature_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%temperature_dump_frequency = dump_frequency
   end subroutine temperature_set_dump_frequency

   ! ================================================================================
   ! Open temperature output file
   subroutine temperature_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%temperature_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("temperature_",i6.6,".out")') rank
         else
            fname = "temperature.out"
         end if

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

         this%temperature_file_name = fname
         this%temperature_file_unit = funit

         write(funit, '("# Temperature in K")')
         write(funit, '("# ----------------")')
         write(funit, '("# step temperature (K)")')
      end if

   end subroutine temperature_setup

   ! ================================================================================
   ! Dump temperature to output file
   subroutine temperature_dump(this, istep, temperature)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      real(wp), intent(in) :: temperature

      ! Local
      ! -----
      integer :: funit
      funit = this%temperature_file_unit
      write(funit, '(i9,1x,es23.15E3)') istep, temperature
   end subroutine temperature_dump

      ! ================================================================================
   ! Set dump frequency for pressure
   subroutine pressure_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%pressure_dump_frequency = dump_frequency
   end subroutine pressure_set_dump_frequency

   ! ================================================================================
   ! Open pressure output file
   subroutine pressure_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%pressure_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("pressure_",i6.6,".out")') rank
         else
            fname = "pressure.out"
         end if

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

         this%pressure_file_name = fname
         this%pressure_file_unit = funit

         write(funit, '("# Pressure of the system")')
         write(funit, '("# ----------------")')
         write(funit, '("# Pressure:   1 bar = 3.3988273773641905e-09 ua")')
         write(funit, '("#",a7,8(1x,a23))') " step  ", &
               "    Total pressure     ", &
               "     xx component      ", &
               "     yy component      ", &
               "     zz component      ", &
               "     xy component      ", &
               "     xz component      ", &
               "     yz component      ", &
               "     LR correction     "

      end if

   end subroutine pressure_setup

   ! ================================================================================
   ! Dump pressure to output file
   subroutine pressure_dump(this, istep, pressure,pressure_tensor,pressure_tail_correction)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      real(wp), intent(in) :: pressure,pressure_tail_correction
      real(wp), intent(in) :: pressure_tensor(3,3)

      ! Local
      ! -----
      integer :: funit
      funit = this%pressure_file_unit

      write(funit,'(i8,8(1x,es23.15e3))') istep, pressure, &
                                               pressure_tensor(1,1), &
                                               pressure_tensor(2,2), &
                                               pressure_tensor(3,3), &
                                               pressure_tensor(1,2), &
                                               pressure_tensor(1,3), &
                                               pressure_tensor(2,3), &
                                               pressure_tail_correction


   end subroutine pressure_dump

   ! ================================================================================
   ! Set dump frequency for stress_tensor
   subroutine stress_tensor_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%stress_tensor_dump_frequency = dump_frequency
   end subroutine stress_tensor_set_dump_frequency

   ! ================================================================================
   ! Open stress_tensor output file
   subroutine stress_tensor_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%stress_tensor_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("stress_tensor_",i6.6,".out")') rank
         else
            fname = "stress_tensor.out"
         end if

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

         this%stress_tensor_file_name = fname
         this%stress_tensor_file_unit = funit

         write(funit, '("# Stress tensor of the system")')
         write(funit, '("# ----------------")')
         write(funit, '("# Pressure:   1 bar = 3.3988273773641905e-09 ua")')
         write(funit, '("#",a7,6(1x,a23))') " step  ", &
               "     xx component      ", &
               "     yy component      ", &
               "     zz component      ", &
               "     xy component      ", &
               "     xz component      ", &
               "     yz component      "

      end if

   end subroutine stress_tensor_setup

   ! ================================================================================
   ! Dump stress_tensor to output file
   subroutine stress_tensor_dump(this, istep, stress_tensor)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      real(wp), intent(in) :: stress_tensor(3,3)

      ! Local
      ! -----
      integer :: funit
      funit = this%stress_tensor_file_unit

      write(funit,'(i8,6(1x,es23.15e3))') istep, stress_tensor(1,1), &
                                               stress_tensor(2,2), &
                                               stress_tensor(3,3), &
                                               stress_tensor(1,2), &
                                               stress_tensor(1,3), &
                                               stress_tensor(2,3)


   end subroutine stress_tensor_dump
   ! ================================================================================


     ! ================================================================================
   ! Set dump frequency for box parameters
   subroutine box_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%box_dump_frequency = dump_frequency
   end subroutine box_set_dump_frequency

   ! ================================================================================
   ! Open pressure output file
   subroutine box_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%box_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("box_parameters_",i6.6,".out")') rank
         else
            fname = "box_parameters.out"
         end if

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

         this%box_file_name = fname
         this%box_file_unit = funit

         write(funit, '("# Box parameters of the system")')
         write(funit, '("# ----------------------------")')
         write(funit, '("# Length:   1 au = 0.052917721067 nm")')
         write(funit, '("# Volume:   1 ua = 1.48184711276e-4 nm**3")')
         write(funit, '("#",a7,4(1x,a23))') " step  ", &
               "       length(1)       ", &
               "       length(2)       ", &
               "       length(3)       ", &
               "        volume         "

      end if

   end subroutine box_setup

   ! ================================================================================
   ! Dump pressure to output file
   subroutine box_dump(this, istep, volume, length)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      real(wp), intent(in) :: volume
      real(wp), intent(in) :: length(3)

      ! Local
      ! -----
      integer :: funit
      funit = this%box_file_unit

      write(funit,'(i8,4(1x,es23.15e3))') istep, length(1), length(2), length(3), volume


   end subroutine box_dump

   ! Set dump frequency for step
   subroutine step_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%step_dump_frequency = dump_frequency
   end subroutine step_set_dump_frequency

   ! ================================================================================
   ! Set dump frequency for polarization
   subroutine polarization_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%polarization_dump_frequency = dump_frequency
   end subroutine polarization_set_dump_frequency

   ! ================================================================================
   ! Open polarization output file
   subroutine polarization_setup(this, rank)
      use MW_fileunit, only: MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank
      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr

      if (this%polarization_dump_frequency > 0) then
         if (present(rank)) then
            write(fname, '("polarization_",i6.6,".out")') rank
         else
            fname = "polarization.out"
         end if

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

         this%polarization_file_name = fname
         this%polarization_file_unit = funit

         write(funit, '("# Polarization of the simulation cell in atomic unit")')
         write(funit, '("# -----------------------------------------")')
         write(funit, '("# Polarization:   1 au =  57.214 765 75 C/m2")')
      endif

   end subroutine polarization_setup

   ! ================================================================================
   ! Dump polarization to output file
   subroutine polarization_dump(this, istep, polarization_field)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this !< output structure
      integer, intent(in) :: istep
      real(wp), intent(in) :: polarization_field(:)
      ! Local
      ! -----
      integer :: funit

      funit = this%polarization_file_unit
      write(funit,'(i8,3(1x,es23.15e3))') istep, polarization_field(:)
   end subroutine polarization_dump

   ! ================================================================================
   ! setup diagnostic output file name
   subroutine diagnostic_setup(this, rank)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in), optional :: rank    !< mpi rank

      ! Local
      ! -----
      character(64) :: fname

      if (present(rank)) then
         write(fname, '("density_",i6.6,".out")') rank
      else
         fname = "density.out"
      end if

      this%number_density_file_name = fname

      if (present(rank)) then
         write(fname, '("charge_density_",i6.6,".out")') rank
      else
         fname = "charge_density.out"
      end if

      this%charge_density_file_name = fname

   end subroutine diagnostic_setup

   ! ================================================================================
   ! Set dump frequency for diagnostic
   subroutine diagnostic_set_dump_frequency(this, dump_frequency)
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(inout) :: this !< output structure
      integer, intent(in) :: dump_frequency    !< dump frequency
      this%diagnostic_dump_frequency = dump_frequency
   end subroutine diagnostic_set_dump_frequency

   ! --------------------------------------------------------------------------------
   ! Average density histograms
   subroutine diagnostic_dump(this, diagnostic, vol_factor, species_names)
      use MW_fileunit, only:  MW_fileunit_get_new_unit => get_new_unit
      use MW_errors, only: MW_errors_open_error => open_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_output_t), intent(in) :: this
      type(MW_diagnostic_t), intent(in) :: diagnostic
      real(wp), intent(in) :: vol_factor
      character(PARTICLE_NAME_LEN), intent(in) :: species_names(:)

      ! Local
      ! -----
      integer :: funit
      character(64) :: fname
      integer :: ierr, ibin, ispecies
      real(wp) :: norm_factor, zbin
      integer :: num_species

      num_species = size(species_names)
      if (diagnostic%num_density_samples > 0) then
         norm_factor = 1.0_wp / (vol_factor * real(diagnostic%num_density_samples) * diagnostic%bin_width)
      else
         norm_factor = 0.0_wp
      end if


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

      write(funit, '("# Number density")')
      write(funit, '("# ------------------")')
      write(funit, '("# Average over ",i10," steps.")') diagnostic%num_density_samples

      write(funit, '(100a13)') "# z (au)     ", "total        ", (species_names(ispecies),ispecies=1,num_species)

      ! write out densities
      do ibin = 1, diagnostic%num_bins
         zbin = diagnostic%zmin + (real(ibin,wp) - 0.5_wp) * diagnostic%bin_width
         write(funit, '(100f13.9)') zbin, &
               (diagnostic%number_density(ispecies,ibin)*norm_factor, ispecies=0, num_species)
      end do

      close(funit)

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

      write(funit, '("# Charge density")')
      write(funit, '("# ------------------")')
      write(funit, '("# Average over ",i10," steps.")') diagnostic%num_density_samples

      write(funit, '(100a13)') "# z (au)     ", "total        ", &
            (species_names(ispecies),ispecies=1,num_species)

      ! write out densities
      do ibin = 1, diagnostic%num_bins
         zbin = (real(ibin,wp) - 0.5_wp) * diagnostic%bin_width
         write(funit, '(100f13.9)') zbin, &
               (diagnostic%charge_density(ispecies,ibin)*norm_factor, ispecies=0,num_species)
      end do

      close(funit)
   end subroutine diagnostic_dump

end module MW_output
