! Module to compute electrostatic Coulomb potential
module MW_coulomb
#ifndef MW_SERIAL
   use MPI
#endif
   use MW_timers
   use MW_kinds, only: wp
   use MW_localwork, only: MW_localwork_t
   use MW_parallel, only: MW_COMM_WORLD
   use MW_ewald, only: MW_ewald_t
   use MW_box, only: MW_box_t
   use MW_damping, only: MW_damping_t
   use MW_electrode, only: MW_electrode_t
   use MW_ion, only: MW_ion_t
   use MW_molecule, only: MW_molecule_t
   use MW_coulomb_lr, only: &
        MW_coulomb_lr_diag_gradQelec_potential => diag_gradQelec_potential, &
        MW_coulomb_lr_gradQelec_potential => gradQelec_potential, &
        MW_coulomb_lr_qmelt2Qelec_potential => qmelt2Qelec_potential, &
        MW_coulomb_lr_Qelec2Qelec_potential => Qelec2Qelec_potential, &
        MW_coulomb_lr_melt_efg => melt_efg, &
        MW_coulomb_lr_melt_forces => melt_forces, &
        MW_coulomb_lr_energy => energy
   use MW_coulomb_lr_pim, only: &
        MW_coulomb_lr_pim_gradQelec_electricfield => gradQelec_electricfield, &
        MW_coulomb_lr_pim_diag_gradmumelt_electricfield => diag_gradmumelt_electricfield, &
        MW_coulomb_lr_pim_gradmumelt_electricfield => gradmumelt_electricfield, &
        MW_coulomb_lr_pim_mumelt2Qelec_potential => mumelt2Qelec_potential, &
        MW_coulomb_lr_pim_qmelt2mumelt_electricfield => qmelt2mumelt_electricfield, &
        MW_coulomb_lr_pim_mumelt2mumelt_electricfield => mumelt2mumelt_electricfield, &
        MW_coulomb_lr_pim_Qelec2mumelt_electricfield => Qelec2mumelt_electricfield, &
        MW_coulomb_lr_pim_melt_efg => melt_efg, &
        MW_coulomb_lr_pim_melt_forces => melt_forces, &
        MW_coulomb_lr_pim_energy => energy
   use MW_coulomb_keq0, only: &
        MW_coulomb_keq0_diag_gradQelec_potential => diag_gradQelec_potential, &
        MW_coulomb_keq0_gradQelec_potential => gradQelec_potential, &
        MW_coulomb_keq0_qmelt2Qelec_potential => qmelt2Qelec_potential, &
        MW_coulomb_keq0_Qelec2Qelec_potential => Qelec2Qelec_potential, &
        MW_coulomb_keq0_melt_forces => melt_forces, &
        MW_coulomb_keq0_energy => energy
   use MW_coulomb_keq0_pim, only: &
        MW_coulomb_keq0_pim_gradQelec_electricfield => gradQelec_electricfield, &
        MW_coulomb_keq0_pim_diag_gradmumelt_electricfield => diag_gradmumelt_electricfield, &
        MW_coulomb_keq0_pim_gradmumelt_electricfield => gradmumelt_electricfield, &
        MW_coulomb_keq0_pim_mumelt2Qelec_potential => mumelt2Qelec_potential, &
        MW_coulomb_keq0_pim_qmelt2mumelt_electricfield => qmelt2mumelt_electricfield, &
        MW_coulomb_keq0_pim_mumelt2mumelt_electricfield => mumelt2mumelt_electricfield, &
        MW_coulomb_keq0_pim_Qelec2mumelt_electricfield => Qelec2mumelt_electricfield, &
        MW_coulomb_keq0_pim_melt_forces => melt_forces, &
        MW_coulomb_keq0_pim_energy => energy
   use MW_coulomb_sr, only: &
        MW_coulomb_sr_gradQelec_potential => gradQelec_potential, &
        MW_coulomb_sr_qmelt2Qelec_potential => qmelt2Qelec_potential, &
        MW_coulomb_sr_Qelec2Qelec_potential => Qelec2Qelec_potential, &
        MW_coulomb_sr_melt_efg => melt_efg, &
        MW_coulomb_sr_melt_forces => melt_forces, &
        MW_coulomb_sr_energy => energy
   use MW_coulomb_sr_pim, only: &
        MW_coulomb_sr_pim_gradQelec_electricfield => gradQelec_electricfield, &
        MW_coulomb_sr_pim_gradmumelt_electricfield => gradmumelt_electricfield, &
        MW_coulomb_sr_pim_mumelt2Qelec_potential => mumelt2Qelec_potential, &
        MW_coulomb_sr_pim_qmelt2mumelt_electricfield => qmelt2mumelt_electricfield, &
        MW_coulomb_sr_pim_mumelt2mumelt_electricfield => mumelt2mumelt_electricfield, &
        MW_coulomb_sr_pim_Qelec2mumelt_electricfield => Qelec2mumelt_electricfield, &
        MW_coulomb_sr_pim_melt_efg => melt_efg, &
        MW_coulomb_sr_pim_melt_forces => melt_forces, &
        MW_coulomb_sr_pim_energy => energy
   use MW_coulomb_self, only: &
        MW_coulomb_self_diag_gradQelec_potential => diag_gradQelec_potential, &
        MW_coulomb_self_gradQelec_potential => gradQelec_potential, &
        MW_coulomb_self_Qelec2Qelec_potential => Qelec2Qelec_potential, &
        MW_coulomb_self_melt_efg => melt_efg, &
        MW_coulomb_self_energy => energy, &
        MW_coulomb_self_diag_gradmumelt_electricfield => diag_gradmumelt_electricfield, &
        MW_coulomb_self_gradmumelt_electricfield => gradmumelt_electricfield, &
        MW_coulomb_self_mumelt2mumelt_electricfield => mumelt2mumelt_electricfield, &
        MW_coulomb_self_pim_energy => pim_energy

   implicit none
   private

   ! Public subroutines
   ! ------------------
   public :: diag_gradQelec_potential !diag[grad_Q [grad_Q [U_QQ]]]
   public :: gradQelec_potential !grad_Q [grad_Q [U_QQ]]
   public :: gradQelec_electricfield !grad_Q [grad_mu [U_muQ]]
   public :: diag_gradmumelt_electricfield !diag[grad_mu [grad_mu [U_mumu]]]
   public :: gradmumelt_electricfield !grad_mu [grad_mu [U_mumu]]
   public :: fix_molecule_gradmumelt_electricfield
   public :: qmelt2Qelec_potential !grad_Q [U_qQ]
   public :: mumelt2Qelec_potential !grad_Q [U_muQ]
   public :: Qelec2Qelec_potential !grad_Q [U_QQ]
   public :: qmelt2mumelt_electricfield !-grad_mu [U_qmu]
   public :: mumelt2mumelt_electricfield !-grad_mu [U_mumu]
   public :: Qelec2mumelt_electricfield !-grad_mu [U_muQ]
   public :: fix_molecule_qmelt2mumelt_electricfield
   public :: fix_molecule_mumelt2mumelt_electricfield
   public :: melt_forces !if (pimrun) then -grad_r [(U_qq + U_qmu + U_mumu) + (U_QQ) + (U_qQ + U_muQ)] else -grad_r [(U_qq) + (U_QQ) + (U_qQ)]
   public :: fix_molecule_melt_forces
   public :: energy ! if(pimrun) then (U_qq + U_qmu + U_mumu) + (U_QQ) + (U_qQ + U_muQ) else (U_qq) + (U_QQ) + (U_qQ)
   public :: fix_molecule_energy
   public :: melt_efg

contains

   !================================================================================
   ! Compute the Coulomb potential felt by each electrode atoms due to all electrode atoms
   subroutine diag_gradQelec_potential(num_PBC, localwork, ewald, box, &
        electrodes, diag_gradQ_V)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork  !< Localwork work distribution
      type(MW_box_t),       intent(in)    :: box       !< Simulation box parameters
      type(MW_electrode_t), intent(in)    :: electrodes(:)  !< electrode parameters

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald     !< Ewald summation parameters
      real(wp),             intent(inout) :: diag_gradQ_V(:)       !< Charge gradient of Coulomb potential

      ! Local
      ! -----
      integer :: ierr, count

      ! Initiliaze potential to 0
      ! -------------------------
      !$acc data present(diag_gradQ_V(:))
      !$acc kernels
      diag_gradQ_V(:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)
      case(2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         ! call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_LR)
         call MW_coulomb_lr_diag_gradQelec_potential(localwork, ewald, box, &
              electrodes, diag_gradQ_V)
         ! call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         ! call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_KEQ0)
         call MW_coulomb_keq0_diag_gradQelec_potential(localwork, ewald, box, &
              electrodes, diag_gradQ_V)
         ! call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_KEQ0)

         ! Self interaction correction
         ! ---------------------------
         ! call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_SELF)
         call MW_coulomb_self_diag_gradQelec_potential(localwork, ewald, &
              electrodes, diag_gradQ_V)
         ! call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_SELF)

      case(3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         ! call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_LR)
         call MW_coulomb_lr_diag_gradQelec_potential(localwork, ewald, box, &
              electrodes, diag_gradQ_V)
         ! call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_LR)

         ! Self interaction correction
         ! ---------------------------
         ! call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_SELF)
         call MW_coulomb_self_diag_gradQelec_potential(localwork, ewald, &
              electrodes, diag_gradQ_V)
         ! call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_SELF)
      end select
#ifndef MW_SERIAL
      count = size(diag_gradQ_V,1)
      call MPI_Allreduce(MPI_IN_PLACE, diag_gradQ_V, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine diag_gradQelec_potential

   !================================================================================
   ! Compute the Coulomb potential felt by each electrode atoms due to all electrode atoms
   subroutine gradQelec_potential(num_PBC, localwork, ewald, box, &
          electrodes, xyz_atoms, gradQ_V)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork  !< Localwork work distribution
      type(MW_box_t),       intent(in)    :: box       !< Simulation box parameters
      type(MW_electrode_t), intent(in)    :: electrodes(:)  !< electrode parameters
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< Electrode atoms positions

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald     !< Ewald summation parameters
      real(wp),             intent(inout) :: gradQ_V(:,:)       !< Charge gradient of Coulomb potential

      ! Local
      ! -----
      integer :: ierr, count, num_elecs

      num_elecs = size(xyz_atoms,1)
      ! Initiliaze potential to 0
      ! -------------------------
      !$acc data present(gradQ_V(:,:))
      !$acc kernels
      gradQ_V(:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)
      case(2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_LR)
         call MW_coulomb_lr_gradQelec_potential(localwork, ewald, box, &
              electrodes, gradQ_V)
         call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_KEQ0)
         call MW_coulomb_keq0_gradQelec_potential(localwork, ewald, box, &
              electrodes, xyz_atoms, gradQ_V)
         call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_KEQ0)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_SR)
         call MW_coulomb_sr_gradQelec_potential(num_PBC, localwork, ewald, box, &
              electrodes, xyz_atoms, gradQ_V)
         call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_SR)

         ! Self interaction correction
         ! ---------------------------
         call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_SELF)
         call MW_coulomb_self_gradQelec_potential(localwork, ewald, &
              electrodes, gradQ_V)
         call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_SELF)

      case(3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_LR)
         call MW_coulomb_lr_gradQelec_potential(localwork, ewald, box, &
              electrodes, gradQ_V)
         call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_SR)
         call MW_coulomb_sr_gradQelec_potential(num_PBC, localwork, ewald, box, &
              electrodes, xyz_atoms, gradQ_V)
         call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_SR)

         ! Self interaction correction
         ! ---------------------------
         call MW_timers_start(TIMER_COULOMB_GRADQ_ELECELEC_SELF)
         call MW_coulomb_self_gradQelec_potential(localwork, ewald, &
              electrodes, gradQ_V)
         call MW_timers_stop(TIMER_COULOMB_GRADQ_ELECELEC_SELF)
      end select
#ifndef MW_SERIAL
      count = size(gradQ_V,1) * size(gradQ_V,2)
      call MPI_Allreduce(MPI_IN_PLACE, gradQ_V(:,:), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine gradQelec_potential

   subroutine gradQelec_electricfield(num_PBC, localwork, ewald, box, &
        ions, xyz_ions, electrodes, xyz_atoms, gradQ_efield)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary condition
      type(MW_localwork_t), intent(in)    :: localwork !< Local work assignment
      type(MW_box_t),       intent(in)    :: box       !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      type(MW_electrode_t), intent(in)    :: electrodes(:)   !< electrode parameters
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< electrode atoms xyz positions

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald     !< Ewald summation parameters
      real(wp),             intent(inout) :: gradQ_efield(:,:)   !< Coulomb potential

      ! Local
      ! -----
      integer :: ierr, count

      ! Initiliaze gradient to 0
      ! -------------------------
      !$acc data present(gradQ_efield(:,:))
      !$acc kernels
      gradQ_efield(:,:) = 0.0_wp
      !$acc end kernels

      select case(num_PBC)
      case(2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         !call MW_timers_start(TIMER_COULOMB_MELTELEC_LR)
         call MW_coulomb_lr_pim_gradQelec_electricfield(localwork, ewald, box,&
              electrodes, gradQ_efield)

         !call MW_timers_stop(TIMER_COULOMB_MELTELEC_LR)

         ! Long range contribution from k=0 mode (only with 2D PBC)
         ! -------------------------------------
         !call MW_timers_start(TIMER_COULOMB_MELTELEC_KEQ0)
         call MW_coulomb_keq0_pim_gradQelec_electricfield(localwork, ewald, box,&
              ions, xyz_ions, electrodes, xyz_atoms, gradQ_efield)
         !call MW_timers_stop(TIMER_COULOMB_MELTELEC_KEQ0)

         ! Short range contribution
         ! ------------------------
         !call MW_timers_start(TIMER_COULOMB_MELTELEC_SR)
         call MW_coulomb_sr_pim_gradQelec_electricfield(num_PBC, localwork, ewald, box,&
              ions, xyz_ions, electrodes, xyz_atoms, gradQ_efield)
         !call MW_timers_stop(TIMER_COULOMB_MELTELEC_SR)

      case(3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         !call MW_timers_start(TIMER_COULOMB_MELTELEC_LR)
         call MW_coulomb_lr_pim_gradQelec_electricfield(localwork, ewald, box,&
              electrodes, gradQ_efield)
         !call MW_timers_stop(TIMER_COULOMB_MELTELEC_LR)

         ! Short range contribution
         ! ------------------------
         !call MW_timers_start(TIMER_COULOMB_MELTELEC_SR)
         call MW_coulomb_sr_pim_gradQelec_electricfield(num_PBC, localwork, ewald, box,&
              ions, xyz_ions, electrodes, xyz_atoms, gradQ_efield)
         !call MW_timers_stop(TIMER_COULOMB_MELTELEC_SR)
      end select

#ifndef MW_SERIAL
      count = size(gradQ_efield,1)*size(gradQ_efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, gradQ_efield(:,:), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data

   end subroutine gradQelec_electricfield

   !================================================================================
   ! Compute the gradient of the electric field for each ion in the melt with respect
   ! to variations of other dipoles in the melt
   subroutine diag_gradmumelt_electricfield(num_PBC, localwork, ewald, box, ions, &
         diag_gradmu_efield)
      implicit none

      ! Parameters Input
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork       !< Local work assignment
      type(MW_box_t),       intent(in)    :: box            !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters

      ! Parameters Output
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald          !< Ewald summation parameters
      real(wp),             intent(inout) :: diag_gradmu_efield(:) !< matrix containing the
      !< gradient of the field

      ! Local
      ! -----
      integer :: ierr, count

      ! Zero gradient
      ! ---------------------------------
      !$acc data present(diag_gradmu_efield(:))
      !$acc kernels
      diag_gradmu_efield(:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)

      case (2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         ! call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)
         call MW_coulomb_lr_pim_diag_gradmumelt_electricfield(localwork,ewald,box,ions,&
              diag_gradmu_efield)
         ! call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         ! call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_KEQ0)
         call MW_coulomb_keq0_pim_diag_gradmumelt_electricfield(localwork,ewald,box,ions,&
              diag_gradmu_efield)
         ! call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_KEQ0)

         ! Self contribution
         ! ------------------------
         ! call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)
         call MW_coulomb_self_diag_gradmumelt_electricfield(localwork,ewald,ions,diag_gradmu_efield)
         ! call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)

      case (3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         ! call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)
         call MW_coulomb_lr_pim_diag_gradmumelt_electricfield(localwork,ewald,box,ions,&
              diag_gradmu_efield)
         ! call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)

         ! Self contribution
         ! ------------------------
         ! call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)
         call MW_coulomb_self_diag_gradmumelt_electricfield(localwork,ewald,ions,diag_gradmu_efield)
         ! call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)
      end select
#ifndef MW_SERIAL
      count = size(diag_gradmu_efield,1)
      call MPI_Allreduce(MPI_IN_PLACE, diag_gradmu_efield, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine diag_gradmumelt_electricfield

   !================================================================================
   ! Compute the gradient of the electric field for each ion in the melt with respect
   ! to variations of other dipoles in the melt
   subroutine gradmumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
         gradmu_efield)
      implicit none

      ! Parameters Input
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork       !< Local work assignment
      type(MW_box_t),       intent(in)    :: box            !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions

      ! Parameters Output
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald          !< Ewald summation parameters
      real(wp),             intent(inout) :: gradmu_efield(:,:) !< matrix containing the
      !< gradient of the field

      ! Local
      ! -----
      integer :: ierr, count

      ! Zero forces on ions
      ! ---------------------------------
      !$acc data present(gradmu_efield(:,:))
      !$acc kernels
      gradmu_efield(:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)

      case (2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)
         call MW_coulomb_lr_pim_gradmumelt_electricfield(localwork,ewald,box,ions,&
              gradmu_efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_KEQ0)
         call MW_coulomb_keq0_pim_gradmumelt_electricfield(localwork,ewald,box,ions,xyz_ions,&
              gradmu_efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_KEQ0)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_SR)
         call MW_coulomb_sr_pim_gradmumelt_electricfield(num_PBC,localwork,ewald,box,ions,xyz_ions,&
              gradmu_efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_SR)

         ! Self contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)
         call MW_coulomb_self_gradmumelt_electricfield(localwork,ewald,ions,gradmu_efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)

      case (3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)
         call MW_coulomb_lr_pim_gradmumelt_electricfield(localwork,ewald,box,ions,&
              gradmu_efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_SR)
         call MW_coulomb_sr_pim_gradmumelt_electricfield(num_PBC,localwork,ewald,box,ions,xyz_ions,&
              gradmu_efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_SR)

         ! Self contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)
         call MW_coulomb_self_gradmumelt_electricfield(localwork,ewald,ions,gradmu_efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_GRADMU_ELEC_SELF)
      end select
#ifndef MW_SERIAL
      count = size(gradmu_efield,1) * size(gradmu_efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, gradmu_efield, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine gradmumelt_electricfield

   !================================================================================
   ! Compute of the electric field gradient for certain ions in the melt
   subroutine melt_efg(num_PBC, localwork, ewald, box, ions, xyz_ions, pimrun, dipoles, efg_ions)
      use MW_errors, only: MW_errors_runtime_error => runtime_error
      implicit none

      ! Parameters Input
      ! -------------
      integer,              intent(in)    :: num_PBC        !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork      !< Local work assignment
      type(MW_box_t),       intent(in)    :: box            !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      real(wp),             intent(in)    :: dipoles(:,:)   !< ions dipoles
      logical,              intent(in)    :: pimrun         !< run with the polarizable ion model

      ! Parameters Output
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald          !< Ewald summation parameters
      real(wp),             intent(inout) :: efg_ions(:,:)  !< matrix containing the efg

      ! Local
      ! -----
      integer :: ierr, count

      ! Zero efg on ions
      ! ---------------------------------
      !$acc data present(efg_ions(:,:))
      !$acc kernels
      efg_ions(:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)

      case (2)
         ! 2D PBC are not working yet
         call MW_errors_runtime_error("melt_efg", "coulomb.F90", &
           "EFG computation is not yet supported for 2D PBC.")
      case (3)
        if (pimrun) then
          ! Long range contribution is computed in reciprocal space
          ! --------------------------------------------------------
          call MW_timers_start(TIMER_COULOMB_EFG_MELT_LR)
          call MW_coulomb_lr_pim_melt_efg(localwork, ewald, box, ions, dipoles, efg_ions)
          call MW_timers_stop(TIMER_COULOMB_EFG_MELT_LR)

          ! Short range contribution
          ! ------------------------
          call MW_timers_start(TIMER_COULOMB_EFG_MELT_SR)
          call MW_coulomb_sr_pim_melt_efg(num_PBC, localwork, ewald, box, ions, xyz_ions, dipoles, efg_ions)
          call MW_timers_stop(TIMER_COULOMB_EFG_MELT_SR)
        else
          ! Long range contribution is computed in reciprocal space
          ! --------------------------------------------------------
          call MW_timers_start(TIMER_COULOMB_EFG_MELT_LR)
          call MW_coulomb_lr_melt_efg(localwork, ewald, box, ions, efg_ions)
          call MW_timers_stop(TIMER_COULOMB_EFG_MELT_LR)

          ! Short range contribution
          ! ------------------------
          call MW_timers_start(TIMER_COULOMB_EFG_MELT_SR)
          call MW_coulomb_sr_melt_efg(num_PBC, localwork, ewald, box, ions, xyz_ions, efg_ions)
          call MW_timers_stop(TIMER_COULOMB_EFG_MELT_SR)
        end if

        ! Self term contribution
        ! ----------------------
        call MW_timers_start(TIMER_COULOMB_EFG_MELT_SELF)
        call MW_coulomb_self_melt_efg(localwork, ewald, ions, efg_ions)
        call MW_timers_stop(TIMER_COULOMB_EFG_MELT_SELF)
      end select

#ifndef MW_SERIAL
      count = size(efg_ions,1) * size(efg_ions,2)
      call MPI_Allreduce(MPI_IN_PLACE, efg_ions(:,:), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine melt_efg

   ! =====================================================================================================
   ! Remove contribution from same molecule particles to the gradient of the electric field due to dipoles
   subroutine fix_molecule_gradmumelt_electricfield(num_PBC, localwork, molecules, box, &
         ions, xyz_ions, gradmu_efield)
      implicit none
      ! Parameters in
      ! ----------
      integer,              intent(in)    :: num_PBC !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork
      type(MW_molecule_t),  intent(in)    :: molecules(:) !< molecules parameters
      type(MW_box_t),       intent(in)    :: box !< box parameters
      type(MW_ion_t),       intent(in)    :: ions(:) !< ion types parameters
      real(wp),             intent(in)    :: xyz_ions(:,:) !< melt particles coordinates

      ! Parameters out
      ! ----------
      real(wp),             intent(inout) :: gradmu_efield(:,:) !< Coulomb force on ions

      ! Local
      ! -----
      integer :: count, ierr

      select case(num_PBC)
      case(2)
         call fix_molecule_gradmumelt_electricfield_2DPBC(localwork, molecules, box, &
              ions, xyz_ions, gradmu_efield)
      case(3)
         call fix_molecule_gradmumelt_electricfield_3DPBC(localwork, molecules, box, &
              ions, xyz_ions, gradmu_efield)
      end select
#ifndef MW_SERIAL
      count = size(gradmu_efield,1) * size(gradmu_efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, gradmu_efield(:,:), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
   end subroutine fix_molecule_gradmumelt_electricfield

   !================================================================================
   ! Compute the Coulomb potential felt by each electrode atoms due to all melt ions
   subroutine qmelt2Qelec_potential(num_PBC, localwork, ewald, box, &
         ions, xyz_ions, type_ions, electrodes, xyz_atoms, V)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary condition
      type(MW_localwork_t), intent(in)    :: localwork !< Local work assignment
      type(MW_box_t),       intent(in)    :: box       !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      type(MW_electrode_t), intent(in)    :: electrodes(:)   !< electrode parameters
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< electrode atoms xyz positions
      integer,              intent(in)    :: type_ions(:)  !< ion types

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald     !< Ewald summation parameters
      real(wp),             intent(inout) :: V(:,:)   !< Coulomb potential

      ! Local
      ! -----
      integer :: ierr, count

      ! Initialize potential to 0
      ! -------------------------
      !$acc data present(V(:,:))
      !$acc kernels
      V(:,:) = 0.0_wp
      !$acc end kernels

      select case(num_PBC)
      case(2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_LR)
         call MW_coulomb_lr_qmelt2Qelec_potential(localwork, ewald, box,&
                 ions, electrodes, V(:,1),type_ions)
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_LR)

         ! Long range contribution from k=0 mode (only with 2D PBC)
         ! -------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_KEQ0)
         call MW_coulomb_keq0_qmelt2Qelec_potential(localwork, ewald, box,&
         ions, xyz_ions, electrodes, xyz_atoms, V(:,2))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_KEQ0)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_SR)
         call MW_coulomb_sr_qmelt2Qelec_potential(num_PBC, localwork, ewald, box,&
         ions, xyz_ions, electrodes, xyz_atoms, V(:,3))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_SR)
      case(3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_LR)
         call MW_coulomb_lr_qmelt2Qelec_potential(localwork, ewald, box,&
                 ions, electrodes, V(:,1),type_ions)
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_SR)
         call MW_coulomb_sr_qmelt2Qelec_potential(num_PBC, localwork, ewald, box,&
         ions, xyz_ions, electrodes, xyz_atoms, V(:,3))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_SR)
      end select

#ifndef MW_SERIAL
      count = size(V,1)*3
      call MPI_Allreduce(MPI_IN_PLACE, V(:,1:3), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine qmelt2Qelec_potential

   !================================================================================
   ! Compute the Coulomb potential felt by each electrode atoms due to all melt ions
   subroutine mumelt2Qelec_potential(num_PBC, localwork, ewald, box, &
         ions, xyz_ions, electrodes, xyz_atoms, dipoles, type_electrode_atoms, V) !TANG-TONNIES COULD BE NECESSARY
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary condition
      type(MW_localwork_t), intent(in)    :: localwork !< Local work assignment
      type(MW_box_t),       intent(in)    :: box       !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      type(MW_electrode_t), intent(in)    :: electrodes(:)   !< electrode parameters
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< electrode atoms xyz positions
      real(wp),             intent(in)    :: dipoles(:,:)  !< ions dipoles
      integer,              intent(in)    :: type_electrode_atoms(:)

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald     !< Ewald summation parameters
      real(wp),             intent(inout) :: V(:,:)   !< Coulomb potential

      ! Local
      ! -----
      integer :: ierr, count

      ! Initialize potential to 0
      ! -------------------------
      !$acc data present(V(:,:))
      !$acc kernels
      V(:,:) = 0.0_wp
      !$acc end kernels

      select case(num_PBC)
      case(2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_LR)
         call MW_coulomb_lr_pim_mumelt2Qelec_potential(localwork, ewald, box,&
         ions, electrodes, dipoles, V(:,1))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_LR)

         ! Long range contribution from k=0 mode (only with 2D PBC)
         ! -------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_KEQ0)
         call MW_coulomb_keq0_pim_mumelt2Qelec_potential(localwork, ewald, box,&
         ions, xyz_ions, electrodes, xyz_atoms, dipoles(:,3), V(:,2))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_KEQ0)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_SR)
         call MW_coulomb_sr_pim_mumelt2Qelec_potential(num_PBC, localwork, ewald, box,&
         ions, xyz_ions, electrodes, xyz_atoms, dipoles, type_electrode_atoms, V(:,3))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_SR)
      case(3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_LR)
         call MW_coulomb_lr_pim_mumelt2Qelec_potential(localwork, ewald, box,&
         ions, electrodes, dipoles, V(:,1))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELTELEC_SR)
         call MW_coulomb_sr_pim_mumelt2Qelec_potential(num_PBC, localwork, ewald, box,&
         ions, xyz_ions, electrodes, xyz_atoms, dipoles, type_electrode_atoms, V(:,3))
         call MW_timers_stop(TIMER_COULOMB_MELTELEC_SR)
      end select

#ifndef MW_SERIAL
      count = size(V,1)*3
      call MPI_Allreduce(MPI_IN_PLACE, V(:,1:3), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine mumelt2Qelec_potential

   !================================================================================
   ! Compute the Coulomb potential felt by each electrode atoms due to all electrode atoms
   subroutine Qelec2Qelec_potential(num_PBC, localwork, ewald, box, &
         electrodes, xyz_atoms, q_elec, V)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork  !< Localwork work distribution
      type(MW_box_t),       intent(in)    :: box       !< Simulation box parameters
      type(MW_electrode_t), intent(in)    :: electrodes(:)  !< electrode parameters
      real(wp),             intent(in)    :: q_elec(:)      !< Electrode atoms charge
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< Electrode atoms positions

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald     !< Ewald summation parameters
      real(wp),             intent(inout) :: V(:,:)       !< Coulomb potential

      ! Local
      ! -----
      integer :: ierr, count

      ! Initialize potential to 0
      ! -------------------------
      !$acc data present(V(:,:))
      !$acc kernels
      V(:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)
      case(2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_ELECELEC_LR)
         call MW_coulomb_lr_Qelec2Qelec_potential(localwork, ewald, box, &
              electrodes, q_elec, V(:,1))
         call MW_timers_stop(TIMER_COULOMB_ELECELEC_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         call MW_timers_start(TIMER_COULOMB_ELECELEC_KEQ0)
         call MW_coulomb_keq0_Qelec2Qelec_potential(localwork, ewald, box, &
              electrodes, xyz_atoms, q_elec, V(:,2))
         call MW_timers_stop(TIMER_COULOMB_ELECELEC_KEQ0)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_ELECELEC_SR)
         call MW_coulomb_sr_Qelec2Qelec_potential(num_PBC, localwork, ewald, box, &
              electrodes, xyz_atoms, q_elec, V(:,3))
         call MW_timers_stop(TIMER_COULOMB_ELECELEC_SR)

         ! Self interaction correction
         ! ---------------------------
         call MW_timers_start(TIMER_COULOMB_ELECELEC_SELF)
         call MW_coulomb_self_Qelec2Qelec_potential(localwork, ewald, &
              electrodes, q_elec, V(:,4))
         call MW_timers_stop(TIMER_COULOMB_ELECELEC_SELF)

      case(3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_ELECELEC_LR)
         call MW_coulomb_lr_Qelec2Qelec_potential(localwork, ewald, box, &
              electrodes, q_elec, V(:,1))
         call MW_timers_stop(TIMER_COULOMB_ELECELEC_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_ELECELEC_SR)
         call MW_coulomb_sr_Qelec2Qelec_potential(num_PBC, localwork, ewald, box, &
              electrodes, xyz_atoms, q_elec, V(:,3))
         call MW_timers_stop(TIMER_COULOMB_ELECELEC_SR)

         ! Self interaction correction
         ! ---------------------------
         call MW_timers_start(TIMER_COULOMB_ELECELEC_SELF)
         call MW_coulomb_self_Qelec2Qelec_potential(localwork, ewald, &
              electrodes, q_elec, V(:,4))
         call MW_timers_stop(TIMER_COULOMB_ELECELEC_SELF)
      end select
#ifndef MW_SERIAL
      count = size(V,1) * 4
      call MPI_Allreduce(MPI_IN_PLACE, V(:,1:4), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine Qelec2Qelec_potential

   !================================================================================
   ! Compute the Electric fields felt by each melt ions due to other melt charges
   subroutine qmelt2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
         type_ions, efield, tt)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork       !< Local work assignment
      type(MW_box_t),       intent(in)    :: box            !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      integer,              intent(in)    :: type_ions(:)
      type(MW_damping_t),   intent(in)    :: tt            !< damping parameters

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald          !< Ewald summation parameters
      real(wp),             intent(inout) :: efield(:,:) !< Coulomb force on ions

      ! Local
      ! -----
      integer :: ierr, count

      ! Zero electric fields on ions
      ! ---------------------------------
      !$acc data present(efield(:,:))
      !$acc kernels
      efield(:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)

      case (2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_LR)
         call MW_coulomb_lr_pim_qmelt2mumelt_electricfield(localwork, ewald, box, ions, &
              efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_KEQ0)
         call MW_coulomb_keq0_pim_qmelt2mumelt_electricfield(localwork, ewald, box, ions, xyz_ions, &
              efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_KEQ0)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_SR)
         call MW_coulomb_sr_pim_qmelt2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
                 type_ions, efield, tt)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_SR)

      case (3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_LR)
         call MW_coulomb_lr_pim_qmelt2mumelt_electricfield(localwork, ewald, box, ions, &
              efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_SR)
         call MW_coulomb_sr_pim_qmelt2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
                 type_ions, efield, tt)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_SR)
      end select
#ifndef MW_SERIAL
      count = size(efield,1) * size(efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, efield, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine qmelt2mumelt_electricfield

   !================================================================================
   ! Compute the Electric fields felt by each melt ions due to other melt dipoles
   subroutine mumelt2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
         efield, dipoles)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork       !< Local work assignment
      type(MW_box_t),       intent(in)    :: box            !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      real(wp),             intent(in)    :: dipoles(:,:)  !< ions dipoles

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald          !< Ewald summation parameters
      real(wp),             intent(inout) :: efield(:,:) !< Electric field on ions

      ! Local
      ! -----
      integer :: ierr, count

      ! Zero electric fields on ions
      ! ---------------------------------
      !$acc data present(efield(:,:))
      !$acc kernels
      efield(:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)

      case (2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_LR)
         call MW_coulomb_lr_pim_mumelt2mumelt_electricfield(localwork, ewald, box, ions, &
              efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_KEQ0)
         call MW_coulomb_keq0_pim_mumelt2mumelt_electricfield(localwork, ewald, box, ions, xyz_ions, &
              efield, dipoles(:,3))
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_KEQ0)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_SR)
         call MW_coulomb_sr_pim_mumelt2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
              efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_SR)

         ! Self contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_SELF)
         call MW_coulomb_self_mumelt2mumelt_electricfield(localwork, ewald, ions, efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_SELF)
      case (3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_LR)
         call MW_coulomb_lr_pim_mumelt2mumelt_electricfield(localwork, ewald, box, ions, &
              efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_SR)
         call MW_coulomb_sr_pim_mumelt2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
              efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_SR)

         ! Self contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_SELF)
         call MW_coulomb_self_mumelt2mumelt_electricfield(localwork, ewald, ions, efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_SELF)
      end select
#ifndef MW_SERIAL
      count = size(efield,1) * size(efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, efield, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine mumelt2mumelt_electricfield

   !================================================================================
   ! Compute the Electric fields felt by each melt ions due to other melt charges
   subroutine Qelec2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
         electrodes, xyz_atoms, q_elec, type_electrode_atoms, efield)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork       !< Local work assignment
      type(MW_box_t),       intent(in)    :: box            !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      type(MW_electrode_t), intent(in)    :: electrodes(:)   !< electrode parameters
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< electrode atoms xyz positions
      real(wp),             intent(in)    :: q_elec(:)      !< Electrode atoms charge
      integer,              intent(in)    :: type_electrode_atoms(:)

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald          !< Ewald summation parameters
      real(wp),             intent(inout) :: efield(:,:) !< Coulomb force on ions

      ! Local
      ! -----
      integer :: ierr, count

      ! Zero electric fields on ions
      ! ---------------------------------
      !$acc data present(efield(:,:))
      !$acc kernels
      efield(:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)

      case (2)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_LR)
         call MW_coulomb_lr_pim_Qelec2mumelt_electricfield(localwork, ewald, box, ions, &
              electrodes, q_elec, efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_LR)

         ! Long range contribution from k=0 mode
         ! -------------------------------------
         !DEBUG
         !Deactivating this convergence to the "full" problem can be reached, at least for one iteration
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_KEQ0)
         call MW_coulomb_keq0_pim_Qelec2mumelt_electricfield(localwork, ewald, box, ions, xyz_ions, &
              electrodes, xyz_atoms, q_elec, efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_KEQ0)
         !END DEBUG

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_SR)
         call MW_coulomb_sr_pim_Qelec2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
              electrodes, xyz_atoms, q_elec, type_electrode_atoms, efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_SR)
      case (3)
         ! Long range contribution is computed in reciprocal space
         ! -------------------------------------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_LR)
         call MW_coulomb_lr_pim_Qelec2mumelt_electricfield(localwork, ewald, box, ions, &
              electrodes, q_elec, efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_LR)

         ! Short range contribution
         ! ------------------------
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_SR)
         call MW_coulomb_sr_pim_Qelec2mumelt_electricfield(num_PBC, localwork, ewald, box, ions, xyz_ions, &
              electrodes, xyz_atoms, q_elec, type_electrode_atoms, efield)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_SR)
      end select
#ifndef MW_SERIAL
      count = size(efield,1) * size(efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, efield, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
      !$acc end data
   end subroutine Qelec2mumelt_electricfield

   ! =====================================================================================
   ! Remove contribution from same molecule particles to the electric field due to charges
   subroutine fix_molecule_qmelt2mumelt_electricfield(num_PBC, localwork, molecules, box, &
         ions, xyz_ions, efield, tt)
      implicit none
      ! Parameters in
      ! ----------
      integer,              intent(in)    :: num_PBC !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork
      type(MW_molecule_t),  intent(in)    :: molecules(:) !< molecules parameters
      type(MW_box_t),       intent(in)    :: box !< box parameters
      type(MW_ion_t),       intent(in)    :: ions(:) !< ion types parameters
      real(wp),             intent(in)    :: xyz_ions(:,:) !< melt particles coordinates
      type(MW_damping_t),   intent(in)    :: tt   ! Damping parameters

      ! Parameters out
      ! ----------
      real(wp),             intent(inout) :: efield(:,:) !< Coulomb force on ions

      ! Local
      ! -----
      integer :: count, ierr

      select case(num_PBC)
      case(2)
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_MOL)
         call fix_molecule_qmelt2mumelt_electricfield_2DPBC(localwork, molecules, box, &
              ions, xyz_ions, efield, tt)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_MOL)
      case(3)
         call MW_timers_start(TIMER_COULOMB_MELT_ELECQ_MOL)
         call fix_molecule_qmelt2mumelt_electricfield_3DPBC(localwork, molecules, box, &
              ions, xyz_ions, efield, tt)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECQ_MOL)
      end select
#ifndef MW_SERIAL
      count = size(efield,1) * size(efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, efield(:,:), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
   end subroutine fix_molecule_qmelt2mumelt_electricfield

   ! =====================================================================================
   ! Remove contribution from same molecule particles to the electric field due to dipoles
   subroutine fix_molecule_mumelt2mumelt_electricfield(num_PBC, localwork, molecules, box, &
         ions, xyz_ions, efield, dipoles)
      implicit none
      ! Parameters in
      ! ----------
      integer,              intent(in)    :: num_PBC !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork
      type(MW_molecule_t),  intent(in)    :: molecules(:) !< molecules parameters
      type(MW_box_t),       intent(in)    :: box !< box parameters
      type(MW_ion_t),       intent(in)    :: ions(:) !< ion types parameters
      real(wp),             intent(in)    :: xyz_ions(:,:) !< melt particles coordinates
      real(wp),             intent(in)    :: dipoles(:,:)   !< ions dipoles

      ! Parameters out
      ! ----------
      real(wp),             intent(inout) :: efield(:,:) !< Coulomb force on ions

      ! Local
      ! -----
      integer :: count, ierr

      select case(num_PBC)
      case(2)
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_MOL)
         call fix_molecule_mumelt2mumelt_electricfield_2DPBC(localwork, molecules, box, &
              ions, xyz_ions, efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_MOL)
      case(3)
         call MW_timers_start(TIMER_COULOMB_MELT_ELECMU_MOL)
         call fix_molecule_mumelt2mumelt_electricfield_3DPBC(localwork, molecules, box, &
              ions, xyz_ions, efield, dipoles)
         call MW_timers_stop(TIMER_COULOMB_MELT_ELECMU_MOL)
      end select
#ifndef MW_SERIAL
      count = size(efield,1) * size(efield,2)
      call MPI_Allreduce(MPI_IN_PLACE, efield(:,:), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
   end subroutine fix_molecule_mumelt2mumelt_electricfield

   !================================================================================
   ! Compute the Coulomb forces felt by each melt ions due to other melt ions
   subroutine melt_forces(num_PBC, localwork, ewald, box, ions, xyz_ions, type_ions, &
         electrodes, xyz_atoms, type_electrode_atoms, q_elec, force, stress_tensor, pimrun, dipoles, tt, compute_force)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC         !< Number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork       !< Local work assignment
      type(MW_box_t),       intent(in)    :: box             !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)         !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)   !< ions xyz positions
      integer,              intent(in)    :: type_ions(:)    !< Ion types
      type(MW_electrode_t), intent(inout) :: electrodes(:)   !< electrode parameters
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< electrode atoms xyz positions
      integer,              intent(in)    :: type_electrode_atoms(:) !< Electrode atoms types
      real(wp),             intent(in)    :: q_elec(:)       !< Electrode atoms charge
      real(wp),             intent(in)    :: dipoles(:,:)    !< ions dipoles
      logical,              intent(in)    :: pimrun !< run with the polarizable ion model
      type(MW_damping_t),   intent(in)    :: tt   ! Damping parameters
      logical,              intent(in)    :: compute_force !< compute force on electrodes

      ! Parameters out
      ! --------------
      type(MW_ewald_t),     intent(inout) :: ewald          !< Ewald summation parameters
      real(wp),             intent(inout) :: force(:,:,:) !< Coulomb force on ions
      real(wp),             intent(inout) :: stress_tensor(:,:,:) !< Coulomb force on ions

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

      ! Zero forces on ions
      ! ---------------------------------
      !$acc data present(force(:,:,:))
      !$acc kernels
      force(:,:,:) = 0.0_wp
      !$acc end kernels

      select case (num_PBC)

      case (2)

         if(pimrun)then
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_LR)
            call MW_coulomb_lr_pim_melt_forces(localwork, ewald, box, ions, &
                 electrodes, q_elec, force(:,:,1),stress_tensor(:,:,1),dipoles)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_LR)

            ! Long range contribution from k=0 mode
            ! -------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_KEQ0)
            call MW_coulomb_keq0_pim_melt_forces(localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, force(:,:,2),dipoles(:,3))
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_KEQ0)

            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_SR)
            call MW_coulomb_sr_pim_melt_forces(num_PBC, localwork, ewald, box, ions, xyz_ions, type_ions, &
                 electrodes, xyz_atoms, type_electrode_atoms, q_elec, force(:,:,3), stress_tensor(:,:,3), &
                 dipoles,tt)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_SR)
         else
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_LR)
            call MW_coulomb_lr_melt_forces(localwork, ewald, box, ions, &
                 electrodes, q_elec, force(:,:,1),stress_tensor(:,:,1), compute_force)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_LR)

            ! Long range contribution from k=0 mode
            ! Not doing the stress tensor here
            ! -------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_KEQ0)
            call MW_coulomb_keq0_melt_forces(localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, force(:,:,2), compute_force)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_KEQ0)

            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_SR)
            call MW_coulomb_sr_melt_forces(num_PBC, localwork, ewald, box, ions, xyz_ions, type_ions, &
                    electrodes, xyz_atoms, type_electrode_atoms, q_elec, force(:,:,3), &
                 stress_tensor(:,:,3), compute_force)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_SR)
         endif

      case (3)

         if(pimrun)then
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_LR)
            call MW_coulomb_lr_pim_melt_forces(localwork, ewald, box, ions, &
                 electrodes, q_elec, force(:,:,1),stress_tensor(:,:,1),dipoles)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_LR)
            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_SR)
            call MW_coulomb_sr_pim_melt_forces(num_PBC, localwork, ewald, box, ions, xyz_ions, type_ions, &
                 electrodes, xyz_atoms, type_electrode_atoms, q_elec, force(:,:,3),stress_tensor(:,:,3),dipoles,tt)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_SR)
         else
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_LR)
            call MW_coulomb_lr_melt_forces(localwork, ewald, box, ions, &
                 electrodes, q_elec, force(:,:,1), stress_tensor(:,:,1), compute_force)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_LR)
            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_SR)
            call MW_coulomb_sr_melt_forces(num_PBC, localwork, ewald, box, ions, xyz_ions, type_ions, &
                    electrodes, xyz_atoms, type_electrode_atoms, q_elec, force(:,:,3), stress_tensor(:,:,3), compute_force)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_SR)
         endif

      end select
#ifndef MW_SERIAL
      count = size(force,1) * size(force,2) * 3
      call MPI_Allreduce(MPI_IN_PLACE, force(:,:,1:3), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)

      count = size(stress_tensor,1)*size(stress_tensor,2)*3      ! this should always be 27
      call MPI_Allreduce(MPI_IN_PLACE, stress_tensor, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)

      if (compute_force) then
         num_elec_types = size(electrodes,1)
         do i = 1, num_elec_types
            call MPI_Allreduce(MPI_IN_PLACE, electrodes(i)%force_ions(:,2:6), 15, MPI_DOUBLE_PRECISION, MPI_SUM, &
                  MW_COMM_WORLD, ierr)
        end do
      end if
#endif
      !$acc end data
   end subroutine melt_forces

   ! =======================================================================================
   ! Remove contribution from same molecule particles to the forces on particles in the melt
   subroutine fix_molecule_melt_forces(num_PBC, localwork, molecules, box, &
         ions, xyz_ions, force,stress_tensor, pimrun, dipoles, tt)
      implicit none
      ! Parameters in
      ! ----------
      integer,              intent(in)    :: num_PBC !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork
      type(MW_molecule_t),  intent(in)    :: molecules(:) !< molecules parameters
      type(MW_box_t),       intent(in)    :: box !< box parameters
      type(MW_ion_t),       intent(in)    :: ions(:) !< ion types parameters
      real(wp),             intent(in)    :: xyz_ions(:,:) !< melt particles coordinates
      real(wp),             intent(in)    :: dipoles(:,:)  !< ions dipoles
      logical,              intent(in)    :: pimrun !< run with the polarizable ion model
      type(MW_damping_t),   intent(in)    :: tt   ! Damping parameters

      ! Parameters out
      ! ----------
      real(wp),             intent(inout) :: force(:,:) !< force on melt particles
      real(wp),             intent(inout) :: stress_tensor(:,:) !< force on melt particles

      ! Local
      ! -----
      integer :: count, ierr

      select case(num_PBC)
      case(2)
         if(.not.pimrun)then
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_MOL)
            call fix_molecule_melt_forces_2DPBC(localwork, molecules, box, &
                 ions, xyz_ions, force,stress_tensor)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_MOL)
         else
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_MOL)
            call fix_molecule_pim_melt_forces_2DPBC(localwork, molecules, box, &
                 ions, xyz_ions, force,stress_tensor,dipoles,tt)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_MOL)
         endif
      case(3)
         if(.not.pimrun)then
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_MOL)
            call fix_molecule_melt_forces_3DPBC(localwork, molecules, box, &
                 ions, xyz_ions, force,stress_tensor)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_MOL)
         else
            call MW_timers_start(TIMER_COULOMB_MELT_FORCES_MOL)
            call fix_molecule_pim_melt_forces_3DPBC(localwork, molecules, box, &
                 ions, xyz_ions, force,stress_tensor,dipoles,tt)
            call MW_timers_stop(TIMER_COULOMB_MELT_FORCES_MOL)
         endif
      end select
#ifndef MW_SERIAL
      count = size(force,1) * size(force,2)
      call MPI_Allreduce(MPI_IN_PLACE, force(:,:), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)

      count = size(stress_tensor,1)*size(stress_tensor,2)        ! this should always be 9
      call MPI_Allreduce(MPI_IN_PLACE, stress_tensor, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
   end subroutine fix_molecule_melt_forces

   !================================================================================
   ! Compute the Energy due to Coulomb interactions
   subroutine energy(num_PBC, localwork, ewald, box, ions, xyz_ions, &
         electrodes, xyz_atoms, q_elec, h, pimrun, dipoles, tt)
      implicit none

      ! Parameters in
      ! -------------
      integer,              intent(in)    :: num_PBC !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork       !< Local work assignment
      type(MW_ewald_t),     intent(in)    :: ewald          !< Ewald summation parameters
      type(MW_box_t),       intent(in)    :: box            !< Simulation box parameters
      type(MW_ion_t),       intent(in)    :: ions(:)        !< ions parameters
      real(wp),             intent(in)    :: xyz_ions(:,:)  !< ions xyz positions
      type(MW_electrode_t), intent(in)    :: electrodes(:)   !< electrode parameters
      real(wp),             intent(in)    :: xyz_atoms(:,:)  !< electrode atoms xyz positions
      real(wp),             intent(in)    :: q_elec(:)      !< Electrode atoms charge
      real(wp),             intent(in)    :: dipoles(:,:)  !< ions dipoles
      logical,              intent(in)    :: pimrun !< run with the polarizable ion model
      type(MW_damping_t),   intent(in)    :: tt   ! Damping parameters

      ! Parameters out
      ! --------------
      real(wp),             intent(inout) :: h(:) !< Coulomb force on ions

      ! Local
      ! -----
      integer :: ierr, count

      ! Zero energy
      ! ----------------
      h(:) = 0.0_wp

      select case (num_PBC)
      case (2)
         if(pimrun)then
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_LR)
            call MW_coulomb_lr_pim_energy(localwork, ewald, box, ions, &
                 electrodes, q_elec, h(1), dipoles)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_LR)

            ! Long range contribution from k=0 mode
            ! -------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_KEQ0)
            call MW_coulomb_keq0_pim_energy(localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, h(2), dipoles(:,3))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_KEQ0)

            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SR)
            call MW_coulomb_sr_pim_energy(num_PBC, localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, h(3), dipoles, tt)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SR)

            ! Self contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SELF)
            call MW_coulomb_self_pim_energy(localwork, ewald, ions,  electrodes, q_elec, dipoles, h(4))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SELF)

         else
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_LR)
            call MW_coulomb_lr_energy(localwork, ewald, box, ions, &
                 electrodes, q_elec, h(1))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_LR)

            ! Long range contribution from k=0 mode
            ! -------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_KEQ0)
            call MW_coulomb_keq0_energy(localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, h(2))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_KEQ0)

            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SR)
            call MW_coulomb_sr_energy(num_PBC, localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, h(3))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SR)

            ! Self contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SELF)
            call MW_coulomb_self_energy(localwork, ewald, ions, electrodes, q_elec, h(4))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SELF)
         endif

      case (3)
         if(pimrun)then
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_LR)
            call MW_coulomb_lr_pim_energy(localwork, ewald, box, ions, &
                 electrodes, q_elec, h(1),dipoles)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_LR)
            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SR)
            call MW_coulomb_sr_pim_energy(num_PBC, localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, h(3),dipoles,tt)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SR)
            ! Self contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SELF)
            call MW_coulomb_self_pim_energy(localwork, ewald, ions,  electrodes, q_elec,dipoles, h(4))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SELF)
         else
            ! Long range contribution is computed in reciprocal space
            ! -------------------------------------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_LR)
            call MW_coulomb_lr_energy(localwork, ewald, box, ions, &
                 electrodes, q_elec, h(1))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_LR)
            ! Short range contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SR)
            call MW_coulomb_sr_energy(num_PBC, localwork, ewald, box, ions, xyz_ions, &
                 electrodes, xyz_atoms, q_elec, h(3))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SR)
            ! Self contribution
            ! ------------------------
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_SELF)
            call MW_coulomb_self_energy(localwork, ewald, ions, electrodes, q_elec, h(4))
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_SELF)
         endif

      end select
#ifndef MW_SERIAL
      count = 4
      call MPI_Allreduce(MPI_IN_PLACE, h(1:4), count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
   end subroutine energy

   ! ================================================================================
   ! Remove contribution from same molecule particles
   !
   subroutine fix_molecule_energy(num_PBC, localwork, molecules, box, &
         ions, xyz_ions,pimrun, dipoles, tt, h)
      implicit none
      ! Parameters in
      ! ----------
      integer,              intent(in)    :: num_PBC !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in)    :: localwork
      type(MW_molecule_t),  intent(in)    :: molecules(:) !< molecules parameters
      type(MW_box_t),       intent(in)    :: box !< box parameters
      type(MW_ion_t),       intent(in)    :: ions(:) !< ion types parameters
      real(wp),             intent(in)    :: xyz_ions(:,:) !< melt particles coordinates
      real(wp),             intent(in)    :: dipoles(:,:)  !< ions dipoles
      logical,              intent(in)    :: pimrun !< run with the polarizable ion model
      type(MW_damping_t),   intent(in)    :: tt   ! Damping parameters

      ! Parameters out
      ! ----------
      real(wp),             intent(inout) :: h !< energy on melt particles

      ! Local
      ! -----
      integer :: ierr

      select case(num_PBC)
      case(2)
         if(.not.pimrun)then
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_MOL)
            call fix_molecule_energy_2DPBC(localwork, molecules, box, &
                 ions, xyz_ions, h)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_MOL)
         else
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_MOL)
            call fix_molecule_pim_energy_2DPBC(localwork, molecules, box, &
                 ions, xyz_ions, dipoles, tt, h)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_MOL)
         endif
      case(3)
         if(.not.pimrun)then
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_MOL)
            call fix_molecule_energy_3DPBC(localwork, molecules, box, &
                 ions, xyz_ions, h)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_MOL)
         else
            call MW_timers_start(TIMER_COULOMB_MELT_POTENTIAL_MOL)
            call fix_molecule_pim_energy_3DPBC(localwork, molecules, box, &
                 ions, xyz_ions, dipoles, tt, h)
            call MW_timers_stop(TIMER_COULOMB_MELT_POTENTIAL_MOL)
         endif
      end select
#ifndef MW_SERIAL
      call MPI_Allreduce(MPI_IN_PLACE, h, 1, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)
#endif
   end subroutine fix_molecule_energy

   ! ================================================================================
   include 'coulomb_fix_molecule_2DPBC.inc'
   include 'coulomb_fix_molecule_3DPBC.inc'
   ! ================================================================================
   include 'minimum_image_displacement_2DPBC.inc'
   include 'minimum_image_displacement_3DPBC.inc'
   ! ================================================================================
   include 'minimum_image_distance_2DPBC.inc'
   include 'minimum_image_distance_3DPBC.inc'

end module MW_coulomb
