!================================================================================
! Compute the Coulomb potential felt by each electrode atoms due to all electrode atoms
!DEC$ ATTRIBUTES NOINLINE :: elec2elec_potential_2DPBC
subroutine gradQelec_potential_@PBC@(localwork, ewald, box, &
      electrodes, xyz_atoms, gradQ_V)
   implicit none

   ! Parameters in
   ! -------------
   type(MW_localwork_t), intent(in)    :: localwork  !< Localwork work distribution
   type(MW_ewald_t),     intent(in)    :: ewald     !< Ewald summation parameters
   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
   ! --------------
   real(wp),             intent(inout) :: gradQ_V(:,:)       !< Coulomb potential

   ! Local
   ! -----
   integer :: num_elec_types
   integer :: i, j
   real(wp) :: a, b, c
   real(wp) :: pot_ij
   real(wp) :: drnorm, drnorm2
   real(wp) :: etai, etaj, etaij, etaijsq, erfdiff
   real(wp) :: alpha, alphasq

   integer :: iblock, iblock_offset, istart, iend, jstart, jend, itype, jtype
   integer :: num_block_diag, num_block_full
   integer :: count_n, offset_n, count_m, offset_m

   num_elec_types = size(electrodes,1)

   ! Precompute some factors
   ! -----------------------
   a = box%length(1)
   b = box%length(2)
   c = box%length(3)

   alpha = ewald%alpha
   alphasq = alpha*alpha

   !$acc data present(xyz_atoms(:,:), gradQ_V(:,:))   

   ! Short range contribution
   ! ------------------------
   do itype = 1, num_elec_types
      count_n = electrodes(itype)%count
      offset_n = electrodes(itype)%offset
      do jtype = 1, itype-1
         count_m = electrodes(jtype)%count
         offset_m = electrodes(jtype)%offset
         if (ewald%sr_interaction_atom2atom(itype,jtype)) then
            etai = electrodes(itype)%eta
            etaj = electrodes(jtype)%eta
            etaij = (etai*etaj)/sqrt(etai*etai + etaj*etaj)
            etaijsq = etaij*etaij

            ! Number of blocks with full interactions (below the diagonal
            num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,jtype)
            iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,jtype)
            do iblock = 1, num_block_full
               call update_other_block_boundaries(iblock_offset+iblock, &
                     offset_n, count_n, offset_m, count_m, &
                     istart, iend, jstart, jend)
               !$acc parallel loop &
               !$acc private(drnorm2, drnorm, erfdiff, pot_ij)                     
               do i = istart, iend
                  do j = jstart, jend
                     call minimum_image_distance_2DPBC(a, b, c, &
                           xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                           xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                           drnorm2)
                     if (drnorm2 < ewald%rcutsq)  then
                        drnorm = sqrt(drnorm2)
                        ! Change erfc to 1-erf because of underflows
                        ! erfdiff = exp(-alphasq*drnorm2) * &
                        !       (erfc_scaled(alpha*drnorm) - exp((alphasq - etaijsq)*drnorm2) * erfc_scaled(etaij*drnorm))
                        ! erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                        erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                        pot_ij =  erfdiff/drnorm
                       !$acc atomic update                        
                        gradQ_V(j,i) = gradQ_V(j,i) + pot_ij
                     end if
                  end do
               end do
               !$acc end parallel loop 
            end do
         end if
      end do

      if (ewald%sr_interaction_atom2atom(itype,itype)) then

         etai = electrodes(itype)%eta
         etaij = etai/sqrt(2.0_wp)
         etaijsq = etaij*etaij

         ! Blocks on the diagonal compute only half of the pair interactions
         num_block_diag = localwork%pair_atom2atom_num_block_diag_local(itype)
         iblock_offset = localwork%pair_atom2atom_diag_iblock_offset(itype)
         do iblock = 1, num_block_diag
            call update_diag_block_boundaries(iblock_offset+iblock, offset_n, count_n, istart, iend)
            !$acc parallel loop &
            !$acc private(drnorm2, drnorm, erfdiff, pot_ij)                     
            do i = istart, iend
               do j = istart, iend
                  call minimum_image_distance_2DPBC(a, b, c, &
                        xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                        xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                        drnorm2)
                  if ((drnorm2 > 0.0_wp) .and. (drnorm2 < ewald%rcutsq)) then
                     drnorm = sqrt(drnorm2)
                     erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                     pot_ij =  erfdiff/drnorm
                     !$acc atomic update                        
                     gradQ_V(j,i) = gradQ_V(j,i) + pot_ij
                  end if
               end do
            end do
            !$acc end parallel loop 
         end do

         ! Number of blocks with full interactions (below the diagonal
         num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,itype)
         iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,itype)
         do iblock = 1, num_block_full
            call update_tri_block_boundaries(iblock_offset+iblock, offset_n, count_n, &
                  istart, iend, jstart, jend)
            !$acc parallel loop &
            !$acc private(drnorm2, drnorm, erfdiff, pot_ij)                     
            do i = istart, iend
               do j = jstart, jend
                  call minimum_image_distance_2DPBC(a, b, c, &
                        xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                        xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                        drnorm2)
                  if (drnorm2 < ewald%rcutsq)  then
                     drnorm = sqrt(drnorm2)
                     erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                     pot_ij =  erfdiff/drnorm
                     !$acc atomic update                        
                     gradQ_V(j,i) = gradQ_V(j,i) + pot_ij
                  end if
               end do
            end do
            !$acc end parallel loop 
         end do
      end if
   end do
   !$acc end data
end subroutine gradQelec_potential_@PBC@

!================================================================================
! Compute the Coulomb potential felt by each electrode atoms due to all melt ions
!DEC$ ATTRIBUTES NOINLINE :: melt2elec_potential_@PBC@
subroutine qmelt2Qelec_potential_@PBC@(localwork, ewald, box, &
      ions, xyz_ions, electrodes, xyz_atoms, V)
   implicit none

   ! Parameters in
   ! -------------
   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

   ! Parameters out
   ! --------------
   real(wp),             intent(inout) :: V(:)       !< Coulomb potential

   ! Local
   ! -----
   integer :: num_ion_types, num_elec_types
   integer :: i, j, itype, jtype
   real(wp) :: a, b, c
   real(wp) :: qj !< ion charge
   real(wp) :: pot_ij, vi
   real(wp) :: drnorm, drnorm2, drnormrec
   real(wp) :: etai, etaisq, erfdiff
   real(wp) :: alpha, alphasq
   integer :: num_block_full
   integer :: iblock, iblock_offset, istart, iend, jstart, jend
   integer :: count_n, offset_n, count_m, offset_m

   num_ion_types = size(ions,1)
   num_elec_types = size(electrodes,1)

   ! Precompute some factors
   ! -----------------------
   a = box%length(1)
   b = box%length(2)
   c = box%length(3)

   alpha = ewald%alpha
   alphasq = alpha*alpha

   !$acc data present(xyz_ions(:,:), xyz_atoms(:,:), V(:))

   ! Short range contribution
   ! ------------------------
   do itype = 1, num_elec_types
      count_n = electrodes(itype)%count
      offset_n = electrodes(itype)%offset

      etai = electrodes(itype)%eta
      etaisq = etai*etai
      do jtype = 1, num_ion_types
         count_m = ions(jtype)%count
         offset_m = ions(jtype)%offset

         qj = ions(jtype)%charge
         if ((qj /= 0.0_wp) .and. ewald%sr_interaction_ion2atom(itype,jtype)) then
            ! Number of blocks with full interactions (below the diagonal
            num_block_full = localwork%pair_ion2atom_num_block_full_local(itype,jtype)
            iblock_offset = localwork%pair_ion2atom_full_iblock_offset(itype,jtype)
            do iblock = 1, num_block_full
               call update_other_block_boundaries(iblock_offset+iblock, &
                     offset_n, count_n, offset_m, count_m, &
                     istart, iend, jstart, jend)
               !$acc parallel loop &
               !$acc private(vi, drnorm2, drnorm, drnormrec, erfdiff, pot_ij)
               do i = istart, iend
                  vi = 0.0_wp
                  !$acc loop reduction(+:vi)
                  do j = jstart, jend
                     call minimum_image_distance_@PBC@(a, b, c, &
                           xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                           xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                           drnorm2)
                     if (drnorm2 < ewald%rcutsq) then
                        drnorm = sqrt(drnorm2)
                        drnormrec = 1.0_wp/drnorm
                        erfdiff = (erfc(alpha*drnorm) - erfc(etai*drnorm))
                        pot_ij =  erfdiff/drnorm
                        Vi = Vi + qj*pot_ij
                     end if
                  end do
                  !$acc end loop
                  V(i) = V(i) + vi
               end do
               !$acc end parallel loop
            end do
         end if
      end do
   end do
   !$acc end data
end subroutine qmelt2Qelec_potential_@PBC@

!================================================================================
! Compute the Coulomb potential felt by each electrode atoms due to all electrode atoms
!DEC$ ATTRIBUTES NOINLINE :: elec2elec_potential_@PBC@
subroutine Qelec2Qelec_potential_@PBC@(localwork, ewald, box, &
      electrodes, xyz_atoms, q_elec, V)
   implicit none

   ! Parameters in
   ! -------------
   type(MW_localwork_t), intent(in)    :: localwork  !< Localwork work distribution
   type(MW_ewald_t),     intent(in)    :: ewald     !< Ewald summation parameters
   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
   ! --------------
   real(wp),             intent(inout) :: V(:)       !< Coulomb potential

   ! Local
   ! -----
   integer :: num_elec_types
   integer :: i, j
   real(wp) :: a, b, c
   real(wp) :: pot_ij, vi, qi
   real(wp) :: drnorm, drnorm2
   real(wp) :: etai, etaj, etaij, etaijsq, erfdiff
   real(wp) :: alpha, alphasq
   integer :: iblock, iblock_offset, istart, iend, jstart, jend, itype, jtype
   integer :: num_block_diag, num_block_full
   integer :: count_n, offset_n, count_m, offset_m

   num_elec_types = size(electrodes,1)

   ! Precompute some factors
   ! -----------------------
   a = box%length(1)
   b = box%length(2)
   c = box%length(3)

   alpha = ewald%alpha
   alphasq = alpha*alpha

   !$acc data present(xyz_atoms(:,:), q_elec(:), V(:))

   ! Short range contribution
   ! ------------------------
   do itype = 1, num_elec_types
      count_n = electrodes(itype)%count
      offset_n = electrodes(itype)%offset
      do jtype = 1, itype-1
         count_m = electrodes(jtype)%count
         offset_m = electrodes(jtype)%offset
         if (ewald%sr_interaction_atom2atom(itype,jtype)) then
            etai = electrodes(itype)%eta
            etaj = electrodes(jtype)%eta
            etaij = (etai*etaj)/sqrt(etai*etai + etaj*etaj)
            etaijsq = etaij*etaij

            ! Number of blocks with full interactions (below the diagonal
            num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,jtype)
            iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,jtype)
            do iblock = 1, num_block_full
               call update_other_block_boundaries(iblock_offset+iblock, &
                     offset_n, count_n, offset_m, count_m, &
                     istart, iend, jstart, jend)
               !$acc parallel loop &
               !$acc private(vi, drnorm2, drnorm, erfdiff, pot_ij)
               do i = istart, iend
                  vi = 0.0_wp
                  qi = q_elec(i)
                  !$acc loop reduction(+:vi)
                  do j = jstart, jend
                     call minimum_image_distance_@PBC@(a, b, c, &
                           xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                           xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                           drnorm2)
                     if (drnorm2 < ewald%rcutsq)  then
                        drnorm = sqrt(drnorm2)
                        ! Change erfc to 1-erf because of underflows
                        ! erfdiff = exp(-alphasq*drnorm2) * &
                        !       (erfc_scaled(alpha*drnorm) - exp((alphasq - etaijsq)*drnorm2) * erfc_scaled(etaij*drnorm))
                        ! erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                        erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                        pot_ij =  erfdiff/drnorm

                        !$acc atomic update
                        V(j) = V(j) + qi*pot_ij

                        vi = vi + q_elec(j)*pot_ij
                     end if
                  end do
                  !$acc end loop
                  V(i) = V(i) + vi
               end do
               !$acc end parallel loop
            end do
         end if
      end do

      if (ewald%sr_interaction_atom2atom(itype,itype)) then

         etai = electrodes(itype)%eta
         etaij = etai/sqrt(2.0_wp)
         etaijsq = etaij*etaij

         ! Blocks on the diagonal compute only half of the pair interactions
         num_block_diag = localwork%pair_atom2atom_num_block_diag_local(itype)
         iblock_offset = localwork%pair_atom2atom_diag_iblock_offset(itype)
         do iblock = 1, num_block_diag
            call update_diag_block_boundaries(iblock_offset+iblock, offset_n, count_n, istart, iend)
            !$acc parallel loop &
            !$acc private(vi, drnorm2, drnorm, erfdiff, pot_ij)
            do i = istart, iend
               vi = 0.0_wp
              !$acc loop reduction(+:vi)
               do j = istart, iend
                  call minimum_image_distance_@PBC@(a, b, c, &
                        xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                        xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                        drnorm2)
                  if ((drnorm2 > 0.0_wp) .and. (drnorm2 < ewald%rcutsq)) then
                     drnorm = sqrt(drnorm2)
                     erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                     pot_ij =  erfdiff/drnorm
                     vi = vi + q_elec(j)*pot_ij
                  end if
               end do
               !$acc end loop 
               V(i) = V(i) + vi
            end do
            !$acc end parallel loop 
         end do

         ! Number of blocks with full interactions (below the diagonal
         num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,itype)
         iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,itype)
         do iblock = 1, num_block_full
            call update_tri_block_boundaries(iblock_offset+iblock, offset_n, count_n, &
                  istart, iend, jstart, jend)
            !$acc parallel loop &
            !$acc private(vi, drnorm2, drnorm, erfdiff, pot_ij)
            do i = istart, iend
               vi = 0.0_wp
               qi = q_elec(i)
               !$acc loop reduction(+:vi)
               do j = jstart, jend
                  call minimum_image_distance_@PBC@(a, b, c, &
                        xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                        xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                        drnorm2)
                  if (drnorm2 < ewald%rcutsq)  then
                     drnorm = sqrt(drnorm2)
                     erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                     pot_ij =  erfdiff/drnorm
                     !$acc atomic update
                     V(j) = V(j) + qi*pot_ij

                     Vi = vi + q_elec(j)*pot_ij
                  end if
               end do
               !$acc end loop 
               V(i) = V(i) + vi
            end do
            !$acc end parallel loop
         end do
      end if
   end do
   !$acc end data
end subroutine Qelec2Qelec_potential_@PBC@

!================================================================================
! Compute the Coulomb forces felt by each melt ions due to other melt ions
!DEC$ ATTRIBUTES NOINLINE :: melt_forces_@PBC@
subroutine melt_forces_@PBC@(localwork, ewald, box, ions, xyz_ions, type_ions, &
      electrodes, xyz_atoms, type_electrode_atoms, q_elec, force, stress_tensor, compute_force)
   implicit none

   ! Parameters in
   ! -------------
   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
   integer,              intent(in)    :: type_ions(:)   !< ions 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
   logical,              intent(in)    :: compute_force

   ! Parameters out
   ! --------------
   real(wp),             intent(inout) :: force(:,:) !< Coulomb force on ions
   real(wp),             intent(inout) :: stress_tensor(:,:) !< Coulomb force contrib. to stress 

   ! Local
   ! -----
   integer :: num_ion_types, num_elec_types
   integer :: num_ions, num_elec_atoms
   integer :: i, j, itype, jtype
   real(wp) :: a, b, c
   real(wp) :: qi, qj !< ion charge
   real(wp) :: Q_j    !< electrode charge
   real(wp) :: erfc_alpha, erfc_eta, exp_alpha, exp_eta
   real(wp) :: fij, fix, fiy, fiz
   real(wp) :: drnorm, drnorm2, drnorm2rec, drnormrec, dx, dy, dz
   real(wp) :: alpha, alphasq, alphapi
   real(wp) :: eta, etasq, etapi,etai,etaj,etaij,etaijsq,etaijpi

   integer :: num_block_diag, num_block_full
   integer :: iblock, iblock_offset, istart, iend, jstart, jend
   integer :: count_n, offset_n, count_m, offset_m
   real(wp) :: fx_ele2ele, fy_ele2ele, fz_ele2ele
   real(wp) :: loc_stress_tensor_xx, loc_stress_tensor_yy, loc_stress_tensor_zz
   real(wp) :: loc_stress_tensor_xy, loc_stress_tensor_xz, loc_stress_tensor_yz   

   num_ion_types = size(ions,1)
   num_elec_types = size(electrodes,1)
   num_ions = 0
   if (num_ion_types > 0) then
      num_ions = ions(num_ion_types)%offset+ions(num_ion_types)%count
   end if
   num_elec_atoms = 0
   if (num_elec_types > 0) then
      num_elec_atoms = electrodes(num_elec_types)%offset+electrodes(num_elec_types)%count
   end if

   ! Precompute some factors
   ! -----------------------
   a = box%length(1)
   b = box%length(2)
   c = box%length(3)

   alpha = ewald%alpha
   alphasq = alpha*alpha
   alphapi = 2.0_wp*alpha/sqrtpi

   loc_stress_tensor_xx = 0.0_wp
   loc_stress_tensor_yy = 0.0_wp
   loc_stress_tensor_zz = 0.0_wp
   loc_stress_tensor_xy = 0.0_wp
   loc_stress_tensor_xz = 0.0_wp
   loc_stress_tensor_yz = 0.0_wp   
   stress_tensor = 0.0_wp

   !$acc data present(xyz_ions(:,:), type_ions(:), xyz_atoms(:,:), type_electrode_atoms(:), &
   !$acc              q_elec(:), force(:,:), electrodes)  &
   !$acc      copy(loc_stress_tensor_xx,loc_stress_tensor_yy,loc_stress_tensor_zz, &
   !$acc           loc_stress_tensor_xy,loc_stress_tensor_xz,loc_stress_tensor_yz, &
   !$acc           fx_ele2ele, fy_ele2ele, fz_ele2ele)

   ! Short range contribution
   ! ------------------------
   ! Blocks on the diagonal compute only half of the pair interactions
   num_block_diag = localwork%pair_ion2ion_num_block_diag_local_ms
   iblock_offset = localwork%pair_ion2ion_diag_iblock_offset_ms
   count_n = num_ions
   offset_n = 0

   do iblock = 1, num_block_diag
      call update_diag_block_boundaries(iblock_offset+iblock, &
            offset_n, count_n, istart, iend)
      !$acc parallel loop &
      !$acc private(fix, fiy, fiz, dx, dy, dz, drnorm2, drnorm, drnormrec, drnorm2rec, &
      !$acc         erfc_alpha, exp_alpha, fij, itype, jtype, qi, qj)  &
      !$acc reduction(+:loc_stress_tensor_xx, loc_stress_tensor_yy,loc_stress_tensor_zz, &
      !$acc             loc_stress_tensor_xy,loc_stress_tensor_xz, loc_stress_tensor_yz)          
      do i = istart, iend
         fix = 0.0_wp
         fiy = 0.0_wp
         fiz = 0.0_wp
         !$acc loop reduction(+:fix, fiy, fiz)
         do j = i+1, iend ! GPU optimization: run over half-blocks; for CPUs it used to be j = istart, iend
            call minimum_image_displacement_@PBC@(a, b, c, &
                  xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                  xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                  dx, dy, dz, drnorm2)
            if ((drnorm2 > 0.0_wp) .and. (drnorm2 < ewald%rcutsq)) then

               itype = type_ions(i)
               jtype = type_ions(j)
               qi = ions(itype)%charge
               qj = ions(jtype)%charge
               
               drnorm = sqrt(drnorm2)
               drnormrec = 1.0_wp/drnorm
               drnorm2rec = drnormrec*drnormrec
               erfc_alpha = erfc(alpha*drnorm)*drnormrec
               exp_alpha = exp(-alphasq*drnorm2)*alphapi

               fij = qi*qj*drnorm2rec*(erfc_alpha + exp_alpha)                        

               fix = fix + fij*dx
               fiy = fiy + fij*dy
               fiz = fiz + fij*dz

               loc_stress_tensor_xx = loc_stress_tensor_xx + dx * dx * fij
               loc_stress_tensor_yy = loc_stress_tensor_yy + dy * dy * fij
               loc_stress_tensor_zz = loc_stress_tensor_zz + dz * dz * fij
               loc_stress_tensor_xy = loc_stress_tensor_xy + dx * dy * fij
               loc_stress_tensor_xz = loc_stress_tensor_xz + dx * dz * fij
               loc_stress_tensor_yz = loc_stress_tensor_yz + dy * dz * fij                           

               !$acc atomic update
               force(j,1) = force(j,1) + fij*dx
               !$acc atomic update
               force(j,2) = force(j,2) + fij*dy
               !$acc atomic update
               force(j,3) = force(j,3) + fij*dz

            end if
         end do
         !$acc end loop
         force(i,1) = force(i,1) - fix
         force(i,2) = force(i,2) - fiy
         force(i,3) = force(i,3) - fiz
      end do
      !$acc end parallel loop
   end do

   ! Number of blocks with full ion2ion interactions
   num_block_full = localwork%pair_ion2ion_num_block_full_local_ms
   iblock_offset = localwork%pair_ion2ion_full_iblock_offset_ms
   count_n = num_ions
   offset_n = 0

   do iblock = 1, num_block_full
      call update_tri_block_boundaries(iblock_offset+iblock, offset_n, count_n, &
            istart, iend, jstart, jend)
      !$acc parallel loop &
      !$acc private(fix, fiy, fiz, dx, dy, dz, drnorm2, drnorm, drnormrec, drnorm2rec, &
      !$acc         erfc_alpha, exp_alpha, fij, itype, jtype, qi, qj)  &
      !$acc reduction(+:loc_stress_tensor_xx, loc_stress_tensor_yy,loc_stress_tensor_zz, &
      !$acc             loc_stress_tensor_xy,loc_stress_tensor_xz, loc_stress_tensor_yz)          
      do i = istart, iend
         fix = 0.0_wp
         fiy = 0.0_wp
         fiz = 0.0_wp
         !$acc loop reduction(+:fix, fiy, fiz)
         do j = jstart, jend
            call minimum_image_displacement_@PBC@(a, b, c, &
                  xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                  xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                  dx, dy, dz, drnorm2)
            if (drnorm2 < ewald%rcutsq) then
               itype = type_ions(i)
               jtype = type_ions(j)
               qi = ions(itype)%charge
               qj = ions(jtype)%charge

               drnorm = sqrt(drnorm2)
               drnormrec = 1.0_wp/drnorm
               drnorm2rec = drnormrec*drnormrec
               erfc_alpha = erfc(alpha*drnorm)*drnormrec
               exp_alpha = exp(-alphasq*drnorm2)*alphapi

               fij = qi*qj*drnorm2rec*(erfc_alpha + exp_alpha)                        
               fix = fix + fij*dx
               fiy = fiy + fij*dy
               fiz = fiz + fij*dz

               loc_stress_tensor_xx = loc_stress_tensor_xx + dx * dx * fij
               loc_stress_tensor_yy = loc_stress_tensor_yy + dy * dy * fij
               loc_stress_tensor_zz = loc_stress_tensor_zz + dz * dz * fij
               loc_stress_tensor_xy = loc_stress_tensor_xy + dx * dy * fij
               loc_stress_tensor_xz = loc_stress_tensor_xz + dx * dz * fij
               loc_stress_tensor_yz = loc_stress_tensor_yz + dy * dz * fij                           

               !$acc atomic update
               force(j,1) = force(j,1) + fij*dx
               !$acc atomic update
               force(j,2) = force(j,2) + fij*dy
               !$acc atomic update
               force(j,3) = force(j,3) + fij*dz
            end if
         end do
         !$acc end loop
         force(i,1) = force(i,1) - fix
         force(i,2) = force(i,2) - fiy
         force(i,3) = force(i,3) - fiz
      end do
      !$acc end parallel loop
   end do

   do itype = 1, num_ion_types
      count_n = ions(itype)%count
      offset_n = ions(itype)%offset
      qi = ions(itype)%charge
      if (qi /= 0.0_wp) then
         do jtype = 1, num_elec_types      
            count_m = electrodes(jtype)%count
            offset_m = electrodes(jtype)%offset
            ! Number of blocks with full ion2ion interactions
            num_block_full = localwork%pair_atom2ion_num_block_full_local(itype,jtype)
            iblock_offset = localwork%pair_atom2ion_full_iblock_offset(itype,jtype)         
            eta = electrodes(jtype)%eta
            etasq = eta*eta
            etapi = 2.0*eta / sqrtpi
            do iblock = 1, num_block_full
               call update_other_block_boundaries(iblock_offset+iblock, &
                          offset_n, count_n, offset_m, count_m, &
                          istart, iend, jstart, jend)
               !$acc parallel loop &
               !$acc private(fix, fiy, fiz, dx, dy, dz, drnorm2, drnorm, drnormrec, drnorm2rec, &
               !$acc         erfc_alpha, exp_alpha, erfc_eta, exp_eta, fij, Q_j)  &
               !$acc reduction(+:loc_stress_tensor_xx, loc_stress_tensor_yy,loc_stress_tensor_zz, &
               !$acc             loc_stress_tensor_xy,loc_stress_tensor_xz, loc_stress_tensor_yz)
               do i = istart, iend
                  fix = 0.0_wp
                  fiy = 0.0_wp
                  fiz = 0.0_wp
                  !$acc loop reduction(+:fix, fiy, fiz) 
                  do j = jstart, jend
                     call minimum_image_displacement_@PBC@(a, b, c, &
                             xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                             xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                             dx, dy, dz, drnorm2)
                     if (drnorm2 < ewald%rcutsq) then

                        Q_j = q_elec(j)
                        drnorm = sqrt(drnorm2)
                        drnormrec = 1.0_wp/drnorm
                        drnorm2rec = drnormrec*drnormrec

                        ! Change to 1-erf because of underflows
                        erfc_alpha = erfc(alpha*drnorm)*drnormrec
                        exp_alpha = exp(-alphasq*drnorm2)*alphapi
                        erfc_eta = erfc(eta*drnorm)*drnormrec
                        exp_eta = exp(-etasq*drnorm2)*etapi

                        fij = qi*Q_j*drnorm2rec*((erfc_alpha + exp_alpha) - (erfc_eta + exp_eta))                           
                        fix = fix + fij*dx
                        fiy = fiy + fij*dy
                        fiz = fiz + fij*dz

                        loc_stress_tensor_xx = loc_stress_tensor_xx + dx * dx * fij
                        loc_stress_tensor_yy = loc_stress_tensor_yy + dy * dy * fij
                        loc_stress_tensor_zz = loc_stress_tensor_zz + dz * dz * fij
                        loc_stress_tensor_xy = loc_stress_tensor_xy + dx * dy * fij
                        loc_stress_tensor_xz = loc_stress_tensor_xz + dx * dz * fij
                        loc_stress_tensor_yz = loc_stress_tensor_yz + dy * dz * fij                           
                     end if
                  end do
                  !$acc end loop

                  force(i,1) = force(i,1) - fix
                  force(i,2) = force(i,2) - fiy
                  force(i,3) = force(i,3) - fiz

                  electrodes(jtype)%force_ions(1,3) = electrodes(jtype)%force_ions(1,3) + fix
                  electrodes(jtype)%force_ions(2,3) = electrodes(jtype)%force_ions(2,3) + fiy
                  electrodes(jtype)%force_ions(3,3) = electrodes(jtype)%force_ions(3,3) + fiz
               end do
               !$acc end parallel loop
            end do
         end do
      end if
   end do

   ! Elec -> Elec interactions
   if (compute_force) then
      do itype = 1, num_elec_types
         count_n = electrodes(itype)%count
         offset_n = electrodes(itype)%offset
         do jtype = 1, itype-1
            count_m =  electrodes(jtype)%count
            offset_m = electrodes(jtype)%offset
            if (ewald%sr_interaction_atom2atom(itype,jtype)) then
               etai = electrodes(itype)%eta
               etaj = electrodes(jtype)%eta
               etaij = (etai*etaj) / sqrt(etai*etai + etaj*etaj)
               etaijsq = etaij * etaij
               etaijpi = 2.0*etaij / sqrtpi

               ! Number of blocks with full interactions (below the diagonal
               num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,jtype)
               iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,jtype)

               do iblock = 1, num_block_full
                  call update_other_block_boundaries(iblock_offset+iblock, &
                        offset_n, count_n, offset_m, count_m, istart, iend, jstart, jend)
                  !$acc parallel loop &
                  !$acc private(fx_ele2ele, fy_ele2ele, fz_ele2ele, &
                  !$acc                dx, dy, dz, drnorm2, drnorm, drnormrec, drnorm2rec, &
                  !$acc         erfc_alpha, exp_alpha, erfc_eta, exp_eta, fij)          
                  do i = istart, iend
                     fx_ele2ele = 0.0_wp
                     fy_ele2ele = 0.0_wp
                     fz_ele2ele = 0.0_wp
                     !$acc loop reduction(+:fx_ele2ele, fy_ele2ele, fz_ele2ele)
                     do j = jstart, jend
                        call minimum_image_displacement_@PBC@(a, b, c, &
                              xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                              xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                              dx, dy, dz, drnorm2)
                        if (drnorm2 < ewald%rcutsq) then
                           drnorm = sqrt(drnorm2)
                           drnormrec = 1.0_wp / drnorm
                           drnorm2rec = drnormrec * drnormrec
                           ! Change erfc to 1-erf because of underflows
                           erfc_alpha = erfc(alpha*drnorm)*drnormrec
                           exp_alpha = exp(-alphasq*drnorm2)*alphapi
                           erfc_eta = erfc(etaij*drnorm)*drnormrec
                           exp_eta = exp(-etaijsq*drnorm2)*etaijpi
                           fij = q_elec(i) * q_elec(j) * drnorm2rec * ((erfc_alpha+exp_alpha) - (erfc_eta+exp_eta))

                           !$acc atomic update
                           electrodes(jtype)%force_ions(1,2) = electrodes(jtype)%force_ions(1,2) + fij * dx
                           !$acc atomic update
                           electrodes(jtype)%force_ions(2,2) = electrodes(jtype)%force_ions(2,2) + fij * dy
                           !$acc atomic update
                           electrodes(jtype)%force_ions(3,2) = electrodes(jtype)%force_ions(3,2) + fij * dz

                           fx_ele2ele = fx_ele2ele + fij * dx
                           fy_ele2ele = fy_ele2ele + fij * dy
                           fz_ele2ele = fz_ele2ele + fij * dz
                        end if
                     end do
                     !$acc end loop
                     electrodes(itype)%force_ions(1,2) = electrodes(itype)%force_ions(1,2) - fx_ele2ele
                     electrodes(itype)%force_ions(2,2) = electrodes(itype)%force_ions(2,2) - fy_ele2ele
                     electrodes(itype)%force_ions(3,2) = electrodes(itype)%force_ions(3,2) - fz_ele2ele
                  end do
                  !$acc end parallel loop
               end do
            end if
         end do

         if (ewald%sr_interaction_atom2atom(itype,itype)) then

            etai = electrodes(itype)%eta
            etaij = etai / sqrt(2.0_wp)
            etaijsq = etaij * etaij
            etaijpi = 2.0*etaij / sqrtpi

            ! Blocks on the diagonal compute only half of the pair interactions
            num_block_diag = localwork%pair_atom2atom_num_block_diag_local(itype)
            iblock_offset = localwork%pair_atom2atom_diag_iblock_offset(itype)

            do iblock = 1, num_block_diag
               call update_diag_block_boundaries(iblock+iblock_offset, &
                     offset_n, count_n, istart, iend)
               !$acc parallel loop &
               !$acc private(fx_ele2ele, fy_ele2ele, fz_ele2ele, &
               !$acc         dx, dy, dz, drnorm2, drnorm, drnormrec, drnorm2rec, &
               !$acc          erfc_alpha, exp_alpha, erfc_eta, exp_eta, fij)          
               do i = istart, iend
                  fx_ele2ele = 0.0_wp
                  fy_ele2ele = 0.0_wp
                  fz_ele2ele = 0.0_wp
                  !$acc loop reduction(+:fx_ele2ele, fy_ele2ele, fz_ele2ele)
                  do j = istart, iend
                     call minimum_image_displacement_@PBC@(a, b, c, &
                           xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                           xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                           dx, dy, dz, drnorm2)
                     if ((drnorm2 > 0.0_wp) .and. (drnorm2 < ewald%rcutsq))  then
                        drnorm = sqrt(drnorm2)
                        drnormrec = 1.0_wp / drnorm
                        drnorm2rec = drnormrec * drnormrec
                        ! Change erfc to 1-erf because of underflows
                        erfc_alpha = erfc(alpha*drnorm)*drnormrec
                        exp_alpha = exp(-alphasq*drnorm2)*alphapi
                        erfc_eta = erfc(etaij*drnorm)*drnormrec
                        exp_eta = exp(-etaijsq*drnorm2)*etaijpi
                        fij = q_elec(i) * q_elec(j) * drnorm2rec * ((erfc_alpha+exp_alpha) - (erfc_eta+exp_eta))
                        fx_ele2ele = fx_ele2ele + fij * dx
                        fy_ele2ele = fy_ele2ele + fij * dy
                        fz_ele2ele = fz_ele2ele + fij * dz
                     end if
                  end do
                  !$acc end loop
                  electrodes(itype)%force_ions(1,2) = electrodes(itype)%force_ions(1,2) - fx_ele2ele
                  electrodes(itype)%force_ions(2,2) = electrodes(itype)%force_ions(2,2) - fy_ele2ele
                  electrodes(itype)%force_ions(3,2) = electrodes(itype)%force_ions(3,2) - fz_ele2ele
               end do
               !$acc end parallel loop
            end do

            ! Number of blocks with full interactions (below the diagonal
            num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,itype)
            iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,itype)

            do iblock = 1, num_block_full
               call update_tri_block_boundaries(iblock_offset+iblock, offset_n, count_n, &
                     istart, iend, jstart, jend)
               !$acc parallel loop &
               !$acc private(fx_ele2ele, fy_ele2ele, fz_ele2ele, &
               !$acc         dx, dy, dz, drnorm2, drnorm, drnormrec, drnorm2rec, &
               !$acc         erfc_alpha, exp_alpha, erfc_eta, exp_eta, fij)          
               do i = istart, iend
                  fx_ele2ele = 0.0_wp
                  fy_ele2ele = 0.0_wp
                  fz_ele2ele = 0.0_wp
                  !$acc loop reduction(+:fx_ele2ele, fy_ele2ele, fz_ele2ele)
                  do j = jstart, jend
                     call minimum_image_displacement_@PBC@(a, b, c, &
                           xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                           xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                           dx, dy, dz, drnorm2)
                     if (drnorm2 < ewald%rcutsq) then
                        drnorm = sqrt(drnorm2)
                        drnormrec = 1.0_wp / drnorm
                        drnorm2rec = drnormrec * drnormrec

                        ! Change erfc to 1-erf because of underflows
                        erfc_alpha = erfc(alpha*drnorm)*drnormrec
                        exp_alpha = exp(-alphasq*drnorm2)*alphapi
                        erfc_eta = erfc(etaij*drnorm)*drnormrec
                        exp_eta = exp(-etaijsq*drnorm2)*etaijpi

                        fij = q_elec(i) * q_elec(j) * drnorm2rec * ((erfc_alpha+exp_alpha) - (erfc_eta+exp_eta))

                        electrodes(itype)%force_ions(1,2) = electrodes(itype)%force_ions(1,2) + fij * dx
                        electrodes(itype)%force_ions(2,2) = electrodes(itype)%force_ions(2,2) + fij * dy
                        electrodes(itype)%force_ions(3,2) = electrodes(itype)%force_ions(3,2) + fij * dz

                        fx_ele2ele = fx_ele2ele + fij * dx
                        fy_ele2ele = fy_ele2ele + fij * dy
                        fz_ele2ele = fz_ele2ele + fij * dz
                     end if
                  end do
                  !$acc end loop 
                  electrodes(itype)%force_ions(1,2) = electrodes(itype)%force_ions(1,2) - fx_ele2ele
                  electrodes(itype)%force_ions(2,2) = electrodes(itype)%force_ions(2,2) - fy_ele2ele
                  electrodes(itype)%force_ions(3,2) = electrodes(itype)%force_ions(3,2) - fz_ele2ele
               end do
              !$acc end parallel loop 
            end do
         end if
      end do
   end if

   !$acc end data
   stress_tensor(1,1) = loc_stress_tensor_xx
   stress_tensor(2,2) = loc_stress_tensor_yy
   stress_tensor(3,3) = loc_stress_tensor_zz
   stress_tensor(1,2) = loc_stress_tensor_xy
   stress_tensor(1,3) = loc_stress_tensor_xz
   stress_tensor(2,3) = loc_stress_tensor_yz   

end subroutine melt_forces_@PBC@

!================================================================================
! Compute the Energy due to Coulomb interactions
!DEC$ ATTRIBUTES NOINLINE :: energy_@PBC@
subroutine energy_@PBC@(localwork, ewald, box, ions, xyz_ions, &
      electrodes, xyz_atoms, q_elec, h)
   use MW_constants, only: sqrt2
   implicit none

   ! Parameters in
   ! -------------
   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

   ! Parameters out
   ! --------------
   real(wp),             intent(inout) :: h !< Coulomb energy

   ! Local
   ! -----
   integer :: num_ion_types, num_elec_types
   integer :: i, j, itype, jtype
   real(wp) :: a, b, c
   real(wp) :: qi, qj !< ion charge
   real(wp) :: Q_i, Q_j !< electrode charges
   real(wp) :: erfc_ij, erfdiff
   real(wp) :: vi, pot_ij
   real(wp) :: drnorm, drnorm2, drnormrec, rcutsq
   real(wp) :: alpha, alphasq, alphapi
   real(wp) :: etai, etaj, etaij, etaijsq

   integer :: num_block_diag, num_block_full
   integer :: iblock, iblock_offset, istart, iend, jstart, jend
   integer :: count_n, offset_n, count_m, offset_m

   num_ion_types = size(ions,1)
   num_elec_types = size(electrodes,1)

   ! Precompute some factors
   ! -----------------------
   a = box%length(1)
   b = box%length(2)
   c = box%length(3)

   alpha = ewald%alpha
   alphasq = alpha*alpha
   alphapi = 2.0_wp*alpha/sqrtpi

   rcutsq = ewald%rcutsq

   ! Zero energy
   ! ----------------
   h = 0.0_wp
   vi = 0.0_wp

   !$acc data present(xyz_ions(:,:), xyz_atoms(:,:), q_elec(:)) copy(vi)

   ! Short range contribution from Melt->Melt and Elec->Melt interaction
   ! -------------------------------------------------------------------
   do itype = 1, num_ion_types
      count_n = ions(itype)%count
      offset_n = ions(itype)%offset
      qi = ions(itype)%charge
      if (qi /= 0.0_wp) then
         do jtype = 1, itype - 1
            count_m = ions(jtype)%count
            offset_m = ions(jtype)%offset
            qj = ions(jtype)%charge
            if ((qj /= 0.0_wp) .and. ewald%sr_interaction_ion2ion(itype,jtype)) then
               ! Number of blocks with full ion2ion interactions
               num_block_full = localwork%pair_ion2ion_num_block_full_local(itype,jtype)
               iblock_offset = localwork%pair_ion2ion_full_iblock_offset(itype,jtype)

               do iblock = 1, num_block_full
                  call update_other_block_boundaries(iblock+iblock_offset, &
                        offset_n, count_n, offset_m, count_m, &
                        istart, iend, jstart, jend)
                  !$acc parallel loop collapse(2) reduction(+:vi) private(drnorm2, drnorm, drnormrec, erfc_ij)          
                  do i = istart, iend
                     do j = jstart, jend
                        call minimum_image_distance_@PBC@(a, b, c, &
                              xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                              xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                              drnorm2)
                        if (drnorm2 < rcutsq) then
                           drnorm = sqrt(drnorm2)
                           drnormrec = 1.0_wp/drnorm
                           erfc_ij = erfc(alpha*drnorm)
                           vi = vi + qi*qj*erfc_ij*drnormrec
                        end if
                     end do
                  end do
                  !$acc end parallel loop
               end do
            end if
         end do

         if (ewald%sr_interaction_ion2ion(itype,itype)) then
            ! Blocks on the diagonal compute only half of the pair interactions
            qj = ions(itype)%charge
            num_block_diag = localwork%pair_ion2ion_num_block_diag_local(itype)
            iblock_offset = localwork%pair_ion2ion_diag_iblock_offset(itype)
            do iblock = 1, num_block_diag
               call update_diag_block_boundaries(iblock_offset+iblock, offset_n, count_n, istart, iend)
               !$acc parallel loop collapse(2) reduction(+:vi) private(drnorm2, drnorm, drnormrec, erfc_ij)          
               do i = istart, iend
                  do j = istart, iend
                     call minimum_image_distance_@PBC@(a, b, c, &
                           xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                           xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                           drnorm2)
                     if ((drnorm2 > 0.0_wp) .and. (drnorm2 < rcutsq)) then
                        drnorm = sqrt(drnorm2)
                        drnormrec = 1.0_wp/drnorm
                        erfc_ij = erfc(alpha*drnorm)
                        vi = vi + 0.5_wp*qi*qj*erfc_ij*drnormrec
                     end if
                  end do
               end do
               !$acc end parallel loop
            end do

            qi = ions(itype)%charge
            qj = ions(itype)%charge
            ! Number of blocks with full ion2ion interactions
            num_block_full = localwork%pair_ion2ion_num_block_full_local(itype,itype)
            iblock_offset = localwork%pair_ion2ion_full_iblock_offset(itype,itype)

            do iblock = 1, num_block_full
               call update_tri_block_boundaries(iblock_offset+iblock, offset_n, count_n, &
                     istart, iend, jstart, jend)
               !$acc parallel loop collapse(2) reduction(+:vi) private(drnorm2, drnorm, drnormrec, erfc_ij)          
               do i = istart, iend
                  do j = jstart, jend
                     call minimum_image_distance_@PBC@(a, b, c, &
                           xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                           xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                           drnorm2)
                     if (drnorm2 < rcutsq) then
                        drnorm = sqrt(drnorm2)
                        drnormrec = 1.0_wp/drnorm
                        erfc_ij = erfc(alpha*drnorm)
                        vi = vi + qi*qj*erfc_ij*drnormrec
                     end if
                  end do
               end do
               !$acc end parallel loop
            end do
         end if

         qi = ions(itype)%charge
         do jtype = 1, num_elec_types
            count_m = electrodes(jtype)%count
            offset_m = electrodes(jtype)%offset
            etaj = electrodes(jtype)%eta
            if (ewald%sr_interaction_atom2ion(itype,jtype)) then
               ! Number of blocks with full ion2ion interactions
               num_block_full = localwork%pair_atom2ion_num_block_full_local(itype,jtype)
               iblock_offset = localwork%pair_atom2ion_full_iblock_offset(itype,jtype)

               do iblock = 1, num_block_full
                  call update_other_block_boundaries(iblock+iblock_offset, &
                        offset_n, count_n, offset_m, count_m, &
                        istart, iend, jstart, jend)
                  !$acc parallel loop collapse(2) reduction(+:vi) private(drnorm2, drnorm, drnormrec, erfdiff)          
                  do i = istart, iend
                     do j = jstart, jend
                        call minimum_image_distance_@PBC@(a, b, c, &
                              xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                              xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                              drnorm2)
                        if (drnorm2 < rcutsq) then
                           drnorm = sqrt(drnorm2)
                           drnormrec = 1.0_wp/drnorm
                           ! Change erfc to 1-erf because of underflows
                           erfdiff = (erfc(alpha*drnorm) - erfc(etaj*drnorm))
                           vi = vi + qi*q_elec(j)*erfdiff*drnormrec
                        end if
                     end do
                  end do
                  !$acc end parallel loop
               end do
            end if
         end do
      end if
   end do

   ! Elec -> Elec interactions
   do itype = 1, num_elec_types
      count_n = electrodes(itype)%count
      offset_n = electrodes(itype)%offset
      do jtype = 1, itype-1
         count_m =  electrodes(jtype)%count
         offset_m = electrodes(jtype)%offset
         if (ewald%sr_interaction_atom2atom(itype,jtype)) then
            etai = electrodes(itype)%eta
            etaj = electrodes(jtype)%eta
            etaij = (etai*etaj)/sqrt(etai*etai + etaj*etaj)
            etaijsq = etaij*etaij

            ! Number of blocks with full interactions (below the diagonal
            num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,jtype)
            iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,jtype)

            do iblock = 1, num_block_full
               call update_other_block_boundaries(iblock_offset+iblock, &
                     offset_n, count_n, offset_m, count_m, istart, iend, jstart, jend)
               !$acc parallel loop collapse(2) reduction(+:vi) private(drnorm2, drnorm, erfdiff, pot_ij)          
               do i = istart, iend
                  do j = jstart, jend
                     Q_i = q_elec(i)
                     call minimum_image_distance_@PBC@(a, b, c, &
                           xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                           xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                           drnorm2)
                     if (drnorm2 < rcutsq)  then
                        Q_j = q_elec(j)
                        drnorm = sqrt(drnorm2)
                        ! Change erfc to 1-erf because of underflows
                        ! erfdiff = exp(-alphasq*drnorm2) * &
                        !       (erfc_scaled(alpha*drnorm) - exp((alphasq - etaijsq)*drnorm2) * erfc_scaled(etaij*drnorm))
                        ! erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                        erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                        pot_ij =  erfdiff/drnorm
                        Vi = Vi + Q_i*Q_j*pot_ij
                     end if
                  end do
               end do
               !$acc end parallel loop
            end do
         end if
      end do

      if (ewald%sr_interaction_atom2atom(itype,itype)) then

         etai = electrodes(itype)%eta
         etaij = etai/sqrt2
         etaijsq = etaij*etaij

         ! Blocks on the diagonal compute only half of the pair interactions
         num_block_diag = localwork%pair_atom2atom_num_block_diag_local(itype)
         iblock_offset = localwork%pair_atom2atom_diag_iblock_offset(itype)

         do iblock = 1, num_block_diag
            call update_diag_block_boundaries(iblock+iblock_offset, &
                  offset_n, count_n, istart, iend)
            !$acc parallel loop collapse(2) reduction(+:vi) private(drnorm2, drnorm, erfdiff, pot_ij)          
            do i = istart, iend
               do j = istart, iend
                  Q_i = q_elec(i)
                  call minimum_image_distance_@PBC@(a, b, c, &
                        xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                        xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                        drnorm2)
                  if ((drnorm2 > 0.0_wp) .and. (drnorm2 < rcutsq))  then
                     Q_j = q_elec(j)
                     drnorm = sqrt(drnorm2)
                     erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                     pot_ij =  erfdiff/drnorm
                     vi = vi + 0.5_wp*Q_i*Q_j*pot_ij
                  end if
               end do
            end do
            !$acc end parallel loop
         end do

         ! Number of blocks with full interactions (below the diagonal
         num_block_full = localwork%pair_atom2atom_num_block_full_local(itype,itype)
         iblock_offset = localwork%pair_atom2atom_full_iblock_offset(itype,itype)

         do iblock = 1, num_block_full
            call update_tri_block_boundaries(iblock_offset+iblock, offset_n, count_n, &
                  istart, iend, jstart, jend)
            !$acc parallel loop collapse(2) reduction(+:vi) private(drnorm2, drnorm, erfdiff, pot_ij)          
            do i = istart, iend
               do j = jstart, jend
                  Q_i = q_elec(i)
                  call minimum_image_distance_@PBC@(a, b, c, &
                        xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                        xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), &
                        drnorm2)
                  if (drnorm2 < rcutsq)  then
                     Q_j = q_elec(j)
                     drnorm = sqrt(drnorm2)
                     erfdiff = (erfc(alpha*drnorm) - erfc(etaij*drnorm))
                     pot_ij =  erfdiff/drnorm
                     vi = vi + Q_i*Q_j*pot_ij
                  end if
               end do
            end do
            !$acc end parallel loop
         end do
      end if
   end do
   !$acc end data
   h = vi
end subroutine energy_@PBC@

!=========================================================================================
! Compute the electric field gradient felt by each melt ion due to other melt ion charges
subroutine melt_efg_@PBC@(localwork, ewald, box, ions, xyz_ions, efg)
   implicit none

   ! Parameters in
   ! -------------
   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

   ! Parameters out
   ! --------------
   real(wp),             intent(inout) :: efg(:,:)    !< Electric field gradient on ions


   ! Local
   ! -----
   integer :: num_ion_types
   integer :: i, j, itype, jtype
   logical :: itype_needs_efg, jtype_needs_efg
   real(wp) :: a, b, c
   real(wp) :: qi, qj !< ion charge
   real(wp) :: drnorm2, dx, dy, dz
   real(wp) :: alpha, alphasq, alphapi

   real(wp), dimension(3, 3) :: tij_ab ! local multipole tensor of rank 2

   integer :: num_block_diag, num_block_full
   integer :: iblock, iblock_offset, istart, iend, jstart, jend
   integer :: count_n, offset_n, count_m, offset_m

   num_ion_types = size(ions,1)

   ! Precompute some factors
   ! -----------------------
   a = box%length(1)
   b = box%length(2)
   c = box%length(3)

   alpha = ewald%alpha
   alphasq = alpha*alpha
   alphapi = 2.0_wp*alpha/sqrtpi

   ! Short range contribution
   ! ------------------------
   do itype = 1, num_ion_types
      qi = ions(itype)%charge
      count_n = ions(itype)%count
      offset_n = ions(itype)%offset
      itype_needs_efg = ions(itype)%dump_efg

      do jtype = 1, itype - 1
         count_m = ions(jtype)%count
         offset_m = ions(jtype)%offset
         qj = ions(jtype)%charge 
         jtype_needs_efg = ions(jtype)%dump_efg

         ! Run the computation if one of the types requires the electric field gradient.
         if ((ewald%sr_interaction_ion2ion(itype,jtype)) .and. &
             ((itype_needs_efg) .or. (jtype_needs_efg))) then

            ! Number of blocks with full ion2ion interactions
            num_block_full = localwork%pair_ion2ion_num_block_full_local(itype,jtype)
            iblock_offset = localwork%pair_ion2ion_full_iblock_offset(itype,jtype)

            do iblock = 1, num_block_full
               call update_other_block_boundaries(iblock_offset+iblock, &
                                                  offset_n, count_n, offset_m, count_m, &
                                                  istart, iend, jstart, jend)
               do i = istart, iend
                  do j = jstart, jend
                     call minimum_image_displacement_@PBC@(a, b, c, &
                                    xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                                    xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                                    dx, dy, dz, drnorm2)
                     if (drnorm2 < ewald%rcutsq) then
                        call MW_multipole_tensors_tij_ab(drnorm2, dx, dy, dz, &
                                                                 alpha, alphasq, alphapi, tij_ab)
                        ! Contribution on i (of type itype) due to j (of jtype)
                        if (itype_needs_efg) then
                           efg(i,1) = efg(i,1) - qj*tij_ab(1,1) ! txx
                           efg(i,2) = efg(i,2) - qj*tij_ab(1,2) ! txy
                           efg(i,3) = efg(i,3) - qj*tij_ab(1,3) ! txz
                           efg(i,4) = efg(i,4) - qj*tij_ab(2,2) ! tyy
                           efg(i,5) = efg(i,5) - qj*tij_ab(2,3) ! tyz
                           efg(i,6) = efg(i,6) - qj*tij_ab(3,3) ! tzz
                        end if 
                        ! Tij of rank 2 is symmetric for i<->j
                        ! Contribution on j (of type jtype) due to i (of itype)
                        if (jtype_needs_efg) then
                           efg(j,1) = efg(j,1) - qi*tij_ab(1,1) ! txx
                           efg(j,2) = efg(j,2) - qi*tij_ab(1,2) ! txy
                           efg(j,3) = efg(j,3) - qi*tij_ab(1,3) ! txz
                           efg(j,4) = efg(j,4) - qi*tij_ab(2,2) ! tyy
                           efg(j,5) = efg(j,5) - qi*tij_ab(2,3) ! tyz
                           efg(j,6) = efg(j,6) - qi*tij_ab(3,3) ! tzz
                        end if 
                     end if ! Ewald cutoff
                  end do
               end do ! loop over all pairs in a given block
            end do ! loop over all blocks
         end if ! itype-jtype interaction flag
      end do ! loop over jtype < itype
      
      ! Run this computation only if itype requires the electric field gradient
      if (ewald%sr_interaction_ion2ion(itype,itype) .and. (itype_needs_efg)) then

         ! First, go over all diagonal blocks
         ! Blocks on the diagonal compute only half of the pair interactions
         qj = ions(itype)%charge
         num_block_diag = localwork%pair_ion2ion_num_block_diag_local(itype)
         iblock_offset = localwork%pair_ion2ion_diag_iblock_offset(itype)

         do iblock = 1, num_block_diag
            call update_diag_block_boundaries(iblock_offset+iblock, &
                                              offset_n, count_n, istart, iend)
            do i = istart, iend
               do j = istart, iend
                  call minimum_image_displacement_@PBC@(a, b, c, &
                                    xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                                    xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                                    dx, dy, dz, drnorm2)
                  if ((drnorm2 > 0.0_wp) .and. (drnorm2 < ewald%rcutsq)) then
                     call MW_multipole_tensors_tij_ab(drnorm2, dx, dy, dz, &
                                                      alpha, alphasq, alphapi, tij_ab)
                     ! Update only i but not j because we are on the diagonal, qj = qi
                     efg(i,1) = efg(i,1) - qj*tij_ab(1,1) ! txx
                     efg(i,2) = efg(i,2) - qj*tij_ab(1,2) ! txy
                     efg(i,3) = efg(i,3) - qj*tij_ab(1,3) ! txz
                     efg(i,4) = efg(i,4) - qj*tij_ab(2,2) ! tyy
                     efg(i,5) = efg(i,5) - qj*tij_ab(2,3) ! tyz
                     efg(i,6) = efg(i,6) - qj*tij_ab(3,3) ! tzz
                  end if ! Ewald cutoff
               end do
            end do ! loop over all pairs in a given block
         end do ! loop over all _diagonal_ blocks

         ! Second, go over all off-diagonal blocks 
         ! Number of blocks with full ion2ion interactions
         num_block_full = localwork%pair_ion2ion_num_block_full_local(itype,itype)
         iblock_offset = localwork%pair_ion2ion_full_iblock_offset(itype,itype)

         do iblock = 1, num_block_full
            call update_tri_block_boundaries(iblock_offset+iblock, offset_n, count_n, &
                                             istart, iend, jstart, jend)
            do i = istart, iend
               do j = jstart, jend
                  call minimum_image_displacement_@PBC@(a, b, c, &
                           xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                           xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                           dx, dy, dz, drnorm2)
                  if (drnorm2 < ewald%rcutsq) then
                     call MW_multipole_tensors_tij_ab(drnorm2, dx, dy, dz, &
                                                      alpha, alphasq, alphapi, tij_ab)
                     ! Update only i and j because we are off the diagonal, qj = qi
                     efg(i,1) = efg(i,1) - qj*tij_ab(1,1) ! txx
                     efg(i,2) = efg(i,2) - qj*tij_ab(1,2) ! txy
                     efg(i,3) = efg(i,3) - qj*tij_ab(1,3) ! txz
                     efg(i,4) = efg(i,4) - qj*tij_ab(2,2) ! tyy
                     efg(i,5) = efg(i,5) - qj*tij_ab(2,3) ! tyz
                     efg(i,6) = efg(i,6) - qj*tij_ab(3,3) ! tzz

                     efg(j,1) = efg(j,1) - qi*tij_ab(1,1) ! txx
                     efg(j,2) = efg(j,2) - qi*tij_ab(1,2) ! txy
                     efg(j,3) = efg(j,3) - qi*tij_ab(1,3) ! txz
                     efg(j,4) = efg(j,4) - qi*tij_ab(2,2) ! tyy
                     efg(j,5) = efg(j,5) - qi*tij_ab(2,3) ! tyz
                     efg(j,6) = efg(j,6) - qi*tij_ab(3,3) ! tzz
                  end if ! Ewald cutoff
               end do
            end do ! loop over all pairs in a given block
         end do ! loop over all _off diagonal_ blocks
      end if ! itype-itype interaction flag
   end do ! loop over all itype (melt ion types)

end subroutine melt_efg_@PBC@

! ==========================================================================================
! Determine if short-range coulomb interaction should be taken into account between 2 particle types
!
! By default short-range coulomb interaction is taken into account
! For fixed particles, short-range coulomb interaction is not taken
! into account between 2 particle types if the minimum distance
! between 2 particles of the distinct sets is greater than the
! specified cut-off (rcut)
subroutine setup_sr_interaction_@PBC@(ewald, ions, electrodes, &
      xyz_ions, xyz_atoms, box)
   implicit none
   ! Parameters
   ! ----------
   type(MW_ewald_t), intent(inout) :: ewald           !< Coulomb ewald structure
   type(MW_ion_t), intent(in) :: ions(:)             !< ions parameters
   type(MW_electrode_t), intent(in) :: electrodes(:) !< electrode parameters
   real(wp), intent(in) :: xyz_ions(:,:)             !< ions xyz coordinates
   real(wp), intent(in) :: xyz_atoms(:,:)            !< atoms xyz coordinates
   type(MW_box_t), intent(in) :: box                 !< box parameters

   ! Local
   ! -----
   integer :: num_ion_types, num_atom_types
   integer :: ios, itype, jtype, i, j
   real(wp) :: a, b, c
   real(wp) :: drnorm2

   a = box%length(1)
   b = box%length(2)
   c = box%length(3)

   num_ion_types = size(ions,1)
   num_atom_types = size(electrodes,1)
   allocate(ewald%sr_interaction_ion2ion(num_ion_types, num_ion_types), &
         ewald%sr_interaction_atom2atom(num_atom_types, num_atom_types), &
         ewald%sr_interaction_ion2atom(num_atom_types, num_ion_types), &
         ewald%sr_interaction_atom2ion(num_ion_types, num_atom_types), stat=ios)
   if (ios /= 0) then
      call MW_errors_allocate_error("setup_sr_interaction", "ewald.f90", ios)
   end if
   ewald%sr_interaction_ion2ion(:,:) = .true.
   ewald%sr_interaction_atom2atom(:,:) = .true.
   ewald%sr_interaction_ion2atom(:,:) = .true.
   ewald%sr_interaction_atom2ion(:,:) = .true.

   ! Check fixed ion minimum distance
   do itype = 1, num_ion_types
      if (.not. ions(itype)%mobile) then
         do jtype = 1, itype-1
            if (.not. ions(jtype)%mobile) then
               ! Assume no interaction and check if there is interaction
               ewald%sr_interaction_ion2ion(itype,jtype) = .false.
               ewald%sr_interaction_ion2ion(jtype,itype) = .false.
               ! Look for 1st interacting pair and reset interaction to .true. if we find one
               Ion2IonMinimumDistanceLoop: do i = ions(itype)%offset + 1, ions(itype)%offset + ions(itype)%count
                  do j = ions(jtype)%offset + 1, ions(jtype)%offset + ions(jtype)%count
                     call minimum_image_distance_@PBC@(a, b, c, &
                           xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                           xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), &
                           drnorm2)
                     if (drnorm2 < ewald%rcutsq) then
                        ewald%sr_interaction_ion2ion(itype,jtype) = .true.
                        ewald%sr_interaction_ion2ion(jtype,itype) = .true.
                        exit Ion2IonMinimumDistanceLoop
                     end if
                  end do
               end do Ion2IonMinimumDistanceLoop
            end if
         end do

         ! Assume no self-interaction and check if there is interaction
         ewald%sr_interaction_ion2ion(itype,itype) = .false.
         ! Look for 1st interacting pair and reset interaction to .true. if we find one
         SelfIonMinimumDistanceLoop: do i = ions(itype)%offset+1, ions(itype)%offset + ions(itype)%count
            do j = ions(itype)%offset+1, i-1
               call minimum_image_distance_@PBC@(a, b, c, &
                     xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                     xyz_ions(j,1), xyz_ions(j,2), xyz_ions(j,3), drnorm2)
               if (drnorm2 < ewald%rcutsq) then
                  ewald%sr_interaction_ion2ion(itype,itype) = .true.
                  exit SelfIonMinimumDistanceLoop
               end if
            end do
         end do SelfIonMinimumDistanceLoop

         do jtype = 1, num_atom_types
            ! Assume no interaction and check if there is interaction
            ewald%sr_interaction_atom2ion(itype,jtype) = .false.
            ewald%sr_interaction_ion2atom(jtype,itype) = .false.
            ! Look for 1st interacting pair and reset interaction to .true. if we find one
            Ion2AtomMinimumDistanceLoop: do i = ions(itype)%offset+1, ions(itype)%offset + ions(itype)%count
               do j = electrodes(jtype)%offset+1, electrodes(jtype)%offset+ electrodes(jtype)%count
                  call minimum_image_distance_@PBC@(a, b, c, &
                        xyz_ions(i,1), xyz_ions(i,2), xyz_ions(i,3), &
                        xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), drnorm2)
                  if (drnorm2 < ewald%rcutsq) then
                     ewald%sr_interaction_atom2ion(itype,jtype) = .true.
                     ewald%sr_interaction_ion2atom(jtype,itype) = .true.
                     exit Ion2AtomMinimumDistanceLoop
                  end if
               end do
            end do Ion2AtomMinimumDistanceLoop
         end do
      end if
   end do

   do itype = 1, num_atom_types
      do jtype = 1, itype-1
         ! Assume no interaction and check if there is interaction
         ewald%sr_interaction_atom2atom(itype,jtype) = .false.
         ewald%sr_interaction_atom2atom(jtype,itype) = .false.
         ! Look for 1st interacting pair and reset interaction to .true. if we find one
         Atom2AtomMinimumDistanceLoop: do i = electrodes(itype)%offset+1, electrodes(itype)%offset + electrodes(itype)%count
            do j = electrodes(jtype)%offset+1, electrodes(jtype)%offset + electrodes(jtype)%count
               call minimum_image_distance_@PBC@(a, b, c, &
                     xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                     xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), drnorm2)
               if (drnorm2 < ewald%rcutsq) then
                  ewald%sr_interaction_atom2atom(itype,jtype) = .true.
                  ewald%sr_interaction_atom2atom(jtype,itype) = .true.
                  exit Atom2AtomMinimumDistanceLoop
               end if
            end do
         end do Atom2AtomMinimumDistanceLoop
      end do

      ! Check for self-interaction
      ! Assume no interaction and check if there is interaction
      ewald%sr_interaction_atom2atom(itype,jtype) = .false.
      ! Look for 1st interacting pair and reset interaction to .true. if we find one
      SelfAtomMinimumDistanceLoop: do i = electrodes(itype)%offset+1, electrodes(itype)%offset+electrodes(itype)%count
         do j = electrodes(itype)%offset+1, i-1
            call minimum_image_distance_@PBC@(a, b, c, &
                  xyz_atoms(i,1), xyz_atoms(i,2), xyz_atoms(i,3), &
                  xyz_atoms(j,1), xyz_atoms(j,2), xyz_atoms(j,3), drnorm2)
            if (drnorm2 < ewald%rcutsq) then
               ewald%sr_interaction_atom2atom(itype,jtype) = .true.
               exit SelfAtomMinimumDistanceLoop
            end if
         end do
      end do SelfAtomMinimumDistanceLoop
   end do
end subroutine setup_sr_interaction_@PBC@
