58 type(distributed_t) :: dist
61 integer,
parameter :: &
62 ION_COMPONENT_REAL = 1, &
70 type(ion_interaction_t),
intent(out) :: this
71 type(namespace_t),
intent(in) :: namespace
72 class(space_t),
intent(in) :: space
73 integer,
intent(in) :: natoms
87 call parse_variable(namespace,
'EwaldAlpha', 0.21_real64, this%alpha)
91 if (space%periodic_dim == 1)
then
99 type(ion_interaction_t),
intent(inout) :: this
100 integer,
intent(in) :: natoms
101 type(multicomm_t),
intent(in) :: mc
117 type(ion_interaction_t),
intent(inout) :: this
133 energy_components, force_components)
134 type(ion_interaction_t),
intent(inout) :: this
135 class(space_t),
intent(in) :: space
136 type(lattice_vectors_t),
intent(in) :: latt
137 type(atom_t),
contiguous,
intent(in) :: atom(:)
138 integer,
intent(in) :: natoms
139 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
140 real(real64),
contiguous,
intent(in) :: lsize(:)
141 real(real64),
intent(out) :: energy
142 real(real64),
contiguous,
intent(out) :: force(:, :)
143 real(real64),
contiguous,
optional,
intent(out) :: energy_components(:)
144 real(real64),
contiguous,
optional,
intent(out) :: force_components(:, :, :)
150 if (
present(energy_components))
then
155 if (
present(force_components))
then
164 if (space%is_periodic())
then
169 call ion_interaction_periodic(this, space, latt, atom, natoms, pos, energy, force, energy_components, force_components)
187 class(
space_t),
intent(in) :: space
188 type(
atom_t),
contiguous,
intent(in) :: atom(:)
189 real(real64),
contiguous,
intent(in) :: lsize(:)
190 real(real64) :: energy
195 assert(
size(atom) == 1)
197 assert(space%periodic_dim == 2)
199 select type(spec => atom(1)%species)
201 area = lsize(1) * lsize(2) *
m_four
202 energy =
m_pi * spec%get_density(lsize) **2 * area * spec%thickness()**3 /
m_three
224 type(
atom_t),
contiguous,
intent(in) :: atom(:)
225 real(real64),
contiguous,
intent(in) :: lsize(:)
226 real(real64) :: energy
230 logical :: lattice_is_orthogonal
236 lattice_is_orthogonal = .not. latt%nonorthogonal
238 do iatom = dist%start, dist%end
239 spec => atom(iatom)%species
250 assert(lattice_is_orthogonal)
251 energy = energy +
m_pi * zi**2 / (
m_four * lsize(1)*lsize(2)) * spec%thickness() /
m_three
266 class(
space_t),
intent(in) :: space
267 type(
atom_t),
contiguous,
intent(in) :: atom(:)
268 real(real64),
contiguous,
intent(in) :: pos(:,:)
269 real(real64),
contiguous,
intent(in) :: lsize(:)
270 real(real64),
intent(out) :: energy
271 real(real64),
contiguous,
intent(out) :: force(:, :)
273 class(
species_t),
pointer :: species_i, species_j
274 real(real64) :: r(space%dim),
f(space%dim)
275 real(real64) :: r_mag
277 real(real64) :: zi, zj
278 integer :: iatom, jatom, natoms
284 force(1:space%dim, 1:natoms) =
m_zero
286 do iatom = dist%start, dist%end
287 species_i => atom(iatom)%species
288 zi = species_i%get_zval()
290 do jatom = iatom + 1, natoms
291 species_j => atom(jatom)%species
292 zj = species_j%get_zval()
294 r = pos(:, iatom) - pos(:, jatom)
296 u_e = zi * zj / r_mag
298 energy = energy + u_e
299 f(1:space%dim) = (u_e / r_mag**2) * r(1:space%dim)
300 force(1:space%dim, iatom) = force(1:space%dim, iatom) +
f(1:space%dim)
301 force(1:space%dim, jatom) = force(1:space%dim, jatom) -
f(1:space%dim)
308 nullify(species_i, species_j)
316 energy_components, force_components)
318 class(
space_t),
intent(in) :: space
320 type(
atom_t),
contiguous,
intent(in) :: atom(:)
321 integer,
intent(in) :: natoms
322 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
323 real(real64),
intent(out) :: energy
324 real(real64),
contiguous,
intent(out) :: force(:, :)
325 real(real64),
contiguous,
optional,
intent(out) :: energy_components(:)
326 real(real64),
contiguous,
optional,
intent(out) :: force_components(:, :, :)
328 real(real64) :: ereal, efourier, epseudo, eself
329 real(real64) :: charge
330 real(real64),
allocatable :: force_real(:,:)
335 force(1:space%dim, 1:natoms) =
m_zero
337 call ewald_short(this%dist, space, latt, atom, pos, this%alpha, ereal, force)
338 if (
present(force_components))
then
339 force_components(1:space%dim, 1:natoms, ion_component_real) = force(1:space%dim, 1:natoms)
345 select case (space%periodic_dim)
352 force(1:space%dim, 1:natoms) =
m_zero
353 safe_allocate(force_real(1:space%dim, 1:natoms))
354 call ewald_1d(this, space, latt, atom, natoms, pos, ereal, eself, efourier, force, force_real)
355 if (
present(force_components))
then
356 force_components(1:space%dim, 1:natoms, ion_component_real) = force_real(1:space%dim, 1:natoms)
358 safe_deallocate_a(force_real)
364 call ewald_long_2d(this, space, latt, atom, natoms, pos, efourier, force)
366 call ewald_long_3d(this, space, latt, atom, natoms, pos, efourier, force, charge)
372 if (
present(energy_components))
then
373 energy_components(ion_component_real) = ereal
378 if (
present(force_components))
then
381 force(1:space%dim, 1:natoms) - force_components(1:space%dim, 1:natoms, ion_component_real)
384 energy = ereal + efourier + eself + epseudo
411 subroutine ewald_short(dist, space, latt, atom, pos, alpha, ereal, force)
413 class(
space_t),
intent(in) :: space
415 type(
atom_t),
contiguous,
intent(in) :: atom(:)
416 real(real64),
contiguous,
intent(in) :: pos(:, :)
418 real(real64),
intent(in) :: alpha
419 real(real64),
intent(out) :: ereal
420 real(real64),
contiguous,
intent(inout) :: force(:, :)
422 integer :: iatom, jatom, icopy, natoms
423 real(real64) :: rnorm, xi(space%dim)
424 real(real64) :: force_real(space%dim)
425 real(real64) :: zi, zj
426 real(real64) :: erfc_rnorm
429 real(real64) :: charge, coeff
435 rcut = 6.0_real64 / alpha
440 do iatom = dist%start, dist%end
441 if (.not. atom(iatom)%species%represents_real_atom()) cycle
442 zi = atom(iatom)%species%get_zval()
443 charge = charge + zi**2
448 do icopy = 1, latt_iter%n_cells
449 rnorm = norm2(latt_iter%get(icopy))
451 if (rnorm > rcut) cycle
452 ereal = ereal +
m_half * charge * erfc(alpha * rnorm) /rnorm
460 do iatom = dist%start, dist%end
461 if (.not. atom(iatom)%species%represents_real_atom()) cycle
462 zi = atom(iatom)%species%get_zval()
465 do jatom = iatom + 1, natoms
466 zj = atom(jatom)%species%get_zval()
471 do icopy = 1, latt_iter%n_cells
472 xi = pos(:, iatom) + latt_iter%get(icopy)
473 rnorm = norm2(xi - pos(:, jatom))
474 if (rnorm > rcut) cycle
476 erfc_rnorm = erfc(alpha * rnorm) / rnorm
479 ereal = ereal + charge * erfc_rnorm
481 force_real(:) = charge * (xi - pos(:, jatom)) * &
482 (erfc_rnorm + coeff *
exp(-(alpha*rnorm)**2)) / rnorm**2
485 force(1:space%dim, jatom) = force(1:space%dim, jatom) - force_real
488 force(1:space%dim, iatom) = force(1:space%dim, iatom) + force_real
508 type(
atom_t),
contiguous,
intent(in) :: atom(:)
509 real(real64),
intent(in) :: alpha
510 real(real64),
intent(out) :: eself
511 real(real64),
intent(out) :: charge
521 do iatom = dist%start, dist%end
522 zi = atom(iatom)%species%get_zval()
524 eself = eself - alpha /
sqrt(
m_pi) * zi**2
534 subroutine ewald_long_3d(this, space, latt, atom, natoms, pos, efourier, force, charge)
536 class(
space_t),
intent(in) :: space
538 type(
atom_t),
contiguous,
intent(in) :: atom(:)
539 integer,
intent(in) :: natoms
540 real(real64),
contiguous,
intent(in) :: pos(:,:)
541 real(real64),
intent(inout) :: efourier
542 real(real64),
contiguous,
intent(inout) :: force(:, :)
543 real(real64),
intent(in) :: charge
545 real(real64) :: rcut, gmax_squared
547 integer :: ix, iy, iz, isph
548 real(real64) :: gvec(3), gred(3), gg2, gx
549 real(real64) :: factor
550 complex(real64) :: sumatoms, tmp(3), aa
552 complex(real64),
allocatable :: phase(:)
556 assert(space%dim == 3)
557 assert(space%periodic_dim == 3)
560 safe_allocate(phase(1:natoms))
563 rcut =
sqrt(minval(sum(latt%klattice**2, dim=1)))
566 isph = ceiling(9.5_real64*this%alpha/rcut)
569 efourier = -
m_pi*charge**2/(
m_two*this%alpha**2*latt%rcell_volume)
572 gmax_squared = isph**2 * minval(sum(latt%klattice**2, dim=1))
582 if (ix == 0 .and. iy < 0) cycle
583 if (ix == 0 .and. iy == 0 .and. iz <= 0) cycle
587 gg2 = dot_product(gvec, gvec)
589 if (gg2 > gmax_squared*1.001_real64) cycle
591 gx = -0.25_real64*gg2/this%alpha**2
593 if (gx < -36.0_real64) cycle
598 if (factor < epsilon(factor)) cycle
603 gx = sum(gvec*pos(:,iatom))
604 aa = atom(iatom)%species%get_zval()*cmplx(
cos(gx),
sin(gx), real64)
606 sumatoms = sumatoms + aa
609 efourier = efourier + factor * real(sumatoms*conjg(sumatoms), real64)
612 tmp =
m_zi*gvec*phase(iatom)
613 force(1:space%dim, iatom) = force(1:space%dim, iatom) - factor*real(conjg(tmp)*sumatoms + tmp*conjg(sumatoms), real64)
621 safe_deallocate_a(phase)
630 subroutine ewald_long_2d(this, space, latt, atom, natoms, pos, efourier, force)
632 class(
space_t),
intent(in) :: space
634 type(
atom_t),
contiguous,
intent(in) :: atom(:)
635 integer,
intent(in) :: natoms
636 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
637 real(real64),
intent(inout) :: efourier
638 real(real64),
contiguous,
intent(inout) :: force(:, :)
640 real(real64) :: rcut, gmax_squared
641 integer :: iatom, jatom
642 integer :: ix, iy, ix_max, iy_max
643 real(real64) :: gvec(space%dim), gg2, gx, gg_abs
644 real(real64) :: factor,factor1,factor2, coeff
645 real(real64) :: dz_max, dz_ij, erfc1, erfc2, tmp_erf
646 real(real64),
allocatable :: force_tmp(:,:)
647 real(real64),
parameter :: tol = 1e-10_real64
651 assert(space%periodic_dim == 2)
652 assert(space%dim == 2 .or. space%dim == 3)
657 if (space%dim == 3)
then
660 do jatom = iatom + 1, natoms
661 dz_max = max(dz_max, abs(pos(3, iatom) - pos(3, jatom)))
670 rcut =
m_two*this%alpha*4.6_real64 +
m_two*this%alpha**2*dz_max
671 if (dz_max > tol)
then
674 erfc1 = erfc(this%alpha*dz_max +
m_half*rcut/this%alpha)
675 if (erfc1 *
exp(rcut*dz_max) < 1.e-10_real64)
exit
676 rcut = rcut * 1.414_real64
680 ix_max = ceiling(rcut/norm2(latt%klattice(:, 1)))
681 iy_max = ceiling(rcut/norm2(latt%klattice(:, 2)))
683 safe_allocate(force_tmp(1:space%dim, 1:natoms))
688 factor =
m_pi/latt%rcell_volume
691 do iatom = this%dist%start, this%dist%end
694 if (space%dim == 3)
then
695 dz_ij = pos(3, iatom) - pos(3, jatom)
700 tmp_erf = erf(this%alpha*dz_ij)
701 factor1 = dz_ij*tmp_erf
702 factor2 =
exp(-(this%alpha*dz_ij)**2)/(this%alpha*
sqrt(
m_pi))
704 efourier = efourier - factor &
705 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() * (factor1 + factor2)
708 if (iatom == jatom)cycle
711 if (space%dim == 3)
then
712 force_tmp(3, iatom) = force_tmp(3, iatom) - (-
m_two*factor) &
713 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() * tmp_erf
720 gmax_squared = ix_max**2 * sum(latt%klattice(:, 1)**2)
721 gmax_squared = min(gmax_squared, iy_max**2 * sum(latt%klattice(:, 2)**2))
725 do ix = -ix_max, ix_max
726 do iy = -iy_max, iy_max
728 gvec = ix*latt%klattice(:, 1) + iy*latt%klattice(:, 2)
732 if (gg2 <
m_epsilon .or. gg2 > gmax_squared*1.001_real64) cycle
734 factor =
m_half*
m_pi/(latt%rcell_volume*gg_abs)
736 do iatom = this%dist%start, this%dist%end
737 do jatom = iatom, natoms
739 gx = sum(gvec(1:2) * (pos(1:2, iatom) - pos(1:2, jatom)))
740 gx = gvec(1)*(pos(1, iatom) - pos(1, jatom)) + gvec(2)*(pos(2, iatom) - pos(2, jatom))
741 if (space%dim == 3)
then
742 dz_ij = pos(3, iatom) - pos(3, jatom)
747 erfc1 = erfc(this%alpha*dz_ij +
m_half*gg_abs/this%alpha)
749 factor1 =
exp(gg_abs*dz_ij)*erfc1
753 erfc2 = erfc(-this%alpha*dz_ij +
m_half*gg_abs/this%alpha)
755 factor2 =
exp(-gg_abs*dz_ij)*erfc2
760 if (iatom == jatom)
then
766 efourier = efourier &
768 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() &
769 *
cos(gx)* ( factor1 + factor2)
772 if (iatom == jatom) cycle
774 force_tmp(1:2, iatom) = force_tmp(1:2, iatom) &
775 +
m_two * factor * gvec(1:2) &
776 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() &
777 *
sin(gx)*(factor1 + factor2)
779 force_tmp(1:2, jatom) = force_tmp(1:2, jatom) &
780 -
m_two * factor * gvec(1:2) &
781 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() &
782 *
sin(gx)*(factor1 + factor2)
784 factor1 = gg_abs*erfc1 &
787 factor1 = factor1*
exp(gg_abs*dz_ij)
792 factor2 = gg_abs*erfc2 &
795 factor2 = factor2*
exp(-gg_abs*dz_ij)
800 if (space%dim == 3)
then
801 force_tmp(3, iatom) = force_tmp(3, iatom) &
803 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() &
804 *
cos(gx)* ( factor1 - factor2)
805 force_tmp(3, jatom) = force_tmp(3, jatom) &
807 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() &
808 *
cos(gx)* ( factor1 - factor2)
821 force = force + force_tmp
823 safe_deallocate_a(force_tmp)
881 subroutine ewald_1d(this, space, latt, atom, natoms, pos, ereal, eself, efourier, force, force_real)
883 class(
space_t),
intent(in) :: space
885 type(
atom_t),
intent(in) :: atom(:)
886 integer,
intent(in) :: natoms
887 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
888 real(real64),
intent(out) :: ereal
889 real(real64),
intent(out) :: eself
890 real(real64),
intent(out) :: efourier
891 real(real64),
intent(inout) :: force(:, :)
892 real(real64),
intent(out) :: force_real(:, :)
894 real(real64),
parameter :: tol = 1e-10_real64
899 real(real64),
parameter :: sigma =
m_one
900 integer :: iatom, jatom, n, n_max, icopy
901 real(real64) :: length, gk, rcut
902 real(real64) :: uaxis(space%dim), rij(space%dim), rperp(space%dim), xi(space%dim), gvec(space%dim)
903 real(real64) :: ri(space%dim), rj(space%dim)
904 real(real64) :: xpar, rho, rbar, zi, zj, zz, rnorm, rt2s, phase, gmag
905 real(real64) :: dcoeff, k0v, k1v, ctrans, calong
906 real(real64) :: fvec(space%dim)
907 real(real64),
allocatable :: force_recip(:,:)
912 assert(space%periodic_dim == 1)
913 assert(space%dim >= 1 .and. space%dim <= 3)
915 length = norm2(latt%rlattice(1:space%dim, 1))
916 uaxis(1:space%dim) = latt%rlattice(1:space%dim, 1) / length
924 do iatom = this%dist%start, this%dist%end
925 if (atom(iatom)%species%is_user_defined()) cycle
926 eself = eself -
m_half * atom(iatom)%species%get_zval()**2 / sigma
932 rcut =
sqrt(sigma**2 / (
m_two * length * tol))
936 force_real(1:space%dim, 1:natoms) =
m_zero
940 do iatom = this%dist%start, this%dist%end
941 if (atom(iatom)%species%is_user_defined()) cycle
942 zi = atom(iatom)%species%get_zval()
946 do icopy = 1, latt_iter%n_cells
947 rnorm = norm2(latt_iter%get(icopy))
949 if (rnorm > rcut) cycle
953 do jatom = iatom + 1, natoms
954 if (atom(jatom)%species%is_user_defined()) cycle
955 zj = atom(jatom)%species%get_zval()
958 do icopy = 1, latt_iter%n_cells
959 xi = ri + latt_iter%get(icopy)
960 rnorm = norm2(xi - rj)
962 if (rnorm > rcut) cycle
963 rt2s =
sqrt(rnorm**2 + sigma**2)
964 ereal = ereal + zz * (
m_one/rnorm -
m_one/rt2s)
966 fvec(1:space%dim) = zz * (xi(1:space%dim) - rj(1:space%dim)) * dcoeff
967 force_real(1:space%dim, iatom) = force_real(1:space%dim, iatom) + fvec(1:space%dim)
968 force_real(1:space%dim, jatom) = force_real(1:space%dim, jatom) - fvec(1:space%dim)
978 n_max = max(1, ceiling(-
log(tol)/(sigma*gk)) + 1)
980 safe_allocate(force_recip(1:space%dim, 1:natoms))
981 force_recip(1:space%dim, 1:natoms) =
m_zero
987 do iatom = this%dist%start, this%dist%end
988 if (atom(iatom)%species%is_user_defined()) cycle
989 zi = atom(iatom)%species%get_zval()
990 do jatom = iatom, natoms
991 if (atom(jatom)%species%is_user_defined()) cycle
992 zj = atom(jatom)%species%get_zval()
994 rij(1:space%dim) = pos(1:space%dim, iatom) - pos(1:space%dim, jatom)
995 xpar = dot_product(rij(1:space%dim), uaxis(1:space%dim))
996 rperp(1:space%dim) = rij(1:space%dim) - xpar*uaxis(1:space%dim)
997 rho = norm2(rperp(1:space%dim))
998 rbar =
sqrt(rho**2 + sigma**2)
1000 if (iatom == jatom)
then
1003 efourier = efourier - (
m_one/length) * zz *
log(rbar)
1011 efourier = efourier - (
m_two/length) * zz *
log(rbar)
1012 fvec(1:space%dim) = (
m_two/length) * zz * rperp(1:space%dim) / rbar**2
1013 force_recip(1:space%dim, iatom) = force_recip(1:space%dim, iatom) + fvec(1:space%dim)
1014 force_recip(1:space%dim, jatom) = force_recip(1:space%dim, jatom) - fvec(1:space%dim)
1018 gvec(1:space%dim) = gmag*uaxis(1:space%dim)
1022 efourier = efourier + (
m_four/length) * zz * k0v *
cos(phase)
1024 ctrans = (
m_four/length) * zz * gmag * k1v *
cos(phase) / rbar
1025 calong = (
m_four/length) * zz * k0v *
sin(phase)
1026 fvec(1:space%dim) = ctrans * rperp(1:space%dim) + calong * gvec(1:space%dim)
1027 force_recip(1:space%dim, iatom) = force_recip(1:space%dim, iatom) + fvec(1:space%dim)
1028 force_recip(1:space%dim, jatom) = force_recip(1:space%dim, jatom) - fvec(1:space%dim)
1037 force(1:space%dim, 1:natoms) = force(1:space%dim, 1:natoms) &
1038 + force_real(1:space%dim, 1:natoms) + force_recip(1:space%dim, 1:natoms)
1040 safe_deallocate_a(force_recip)
1055 type(
atom_t),
contiguous,
intent(in) :: atom(:)
1056 real(real64),
intent(in) :: charge
1057 real(real64),
intent(out) :: epseudo
1065 do iatom = dist%start, dist%end
1066 select type(spec => atom(iatom)%species)
1068 zi = spec%get_zval()
1069 epseudo = epseudo +
m_pi *zi * &
1070 (spec%ps%sigma_erf *
sqrt(
m_two))**2 / latt%rcell_volume * charge
1082 class(
space_t),
intent(in) :: space
1084 type(
atom_t),
contiguous,
intent(in) :: atom(:)
1085 integer,
intent(in) :: natoms
1086 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
1087 real(real64),
intent(out) :: stress_ii(space%dim, space%dim)
1089 real(real64) :: stress_short(1:space%dim, 1:space%dim), stress_Ewald(1:space%dim, 1:space%dim)
1096 assert(space%is_periodic())
1102 select case(space%periodic_dim)
1104 call ewald_3d_stress(this, space, latt, atom, natoms, pos, stress_ewald)
1106 call ewald_2d_stress(this, space, latt, atom, natoms, pos, stress_ewald)
1111 stress_ii = stress_short + stress_ewald
1137 class(
space_t),
intent(in) :: space
1139 type(
atom_t),
contiguous,
intent(in) :: atom(:)
1140 integer,
intent(in) :: natoms
1141 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
1142 real(real64),
intent(out) :: stress_short(1:space%dim, 1:space%dim)
1144 real(real64) :: xi(space%dim)
1145 real(real64) :: r_ij, zi, zj, Hp, factor
1146 integer :: iatom, jatom, icopy, idir, jdir
1147 real(real64) :: alpha, rcut
1154 assert(space%is_periodic())
1159 rcut = 6.0_real64/alpha
1165 do iatom = this%dist%start, this%dist%end
1166 select type(spec => atom(iatom)%species)
1170 zi = atom(iatom)%species%get_zval()
1172 do icopy = 1, latt_iter%n_cells
1173 xi = pos(:, iatom) + latt_iter%get(icopy)
1175 do jatom = 1, natoms
1176 zj = atom(jatom)%species%get_zval()
1177 r_ij = norm2(xi - pos(:, jatom))
1181 hp = -
m_two/
sqrt(
m_pi)*
exp(-(alpha*r_ij)**2) - erfc(alpha*r_ij)/(alpha*r_ij)
1182 factor =
m_half*zj*zi*alpha*hp
1183 do idir = 1, space%periodic_dim
1184 do jdir = 1, space%periodic_dim
1185 stress_short(idir, jdir) = stress_short(idir, jdir) &
1186 - factor*(xi(idir) - pos(idir, jatom))*(xi(jdir) - pos(jdir, jatom))/(r_ij**2)
1194 if (this%dist%parallel)
then
1198 stress_short = stress_short/latt%rcell_volume
1222 subroutine ewald_3d_stress(this, space, latt, atom, natoms, pos, stress_Ewald)
1224 class(
space_t),
intent(in) :: space
1226 type(
atom_t),
contiguous,
intent(in) :: atom(:)
1227 integer,
intent(in) :: natoms
1228 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
1229 real(real64),
intent(out) :: stress_ewald(3, 3)
1231 real(real64) :: zi, rcut, gmax_squared
1233 integer :: ix, iy, iz, isph, idim, idir, jdir
1234 real(real64) :: gred(3), gvec(3), gg2, gx
1235 real(real64) :: factor, charge, charge_sq, off_diagonal_weight
1236 complex(real64) :: sumatoms, aa
1242 assert(space%dim == 3)
1243 assert(space%periodic_dim == 3)
1252 do iatom = 1, natoms
1253 zi = atom(iatom)%species%get_zval()
1254 charge = charge + zi
1255 charge_sq = charge_sq + zi**2
1260 do idim = 1, space%periodic_dim
1261 rcut = min(rcut, sum(latt%klattice(1:space%periodic_dim, idim)**2))
1266 isph = ceiling(9.5_real64*this%alpha/rcut)
1269 gmax_squared = isph**2 * minval(sum(latt%klattice**2, dim=1))
1279 if (ix == 0 .and. iy < 0) cycle
1280 if (ix == 0 .and. iy == 0 .and. iz <= 0) cycle
1287 if (gg2 > gmax_squared*1.001_real64) cycle
1289 gx = -0.25_real64*gg2/this%alpha**2
1291 if (gx < -36.0_real64) cycle
1296 if (factor < epsilon(factor)) cycle
1300 do iatom = 1, natoms
1301 gx = sum(gvec*pos(:, iatom))
1302 aa = atom(iatom)%species%get_zval()*cmplx(
cos(gx),
sin(gx), real64)
1303 sumatoms = sumatoms + aa
1306 factor = factor*abs(sumatoms)**2
1307 off_diagonal_weight = -
m_two*factor/gg2*(0.25_real64*gg2/this%alpha**2+
m_one)
1311 stress_ewald(idir, jdir) = stress_ewald(idir, jdir) &
1312 + gvec(idir) * gvec(jdir) * off_diagonal_weight
1314 stress_ewald(idir, idir) = stress_ewald(idir, idir) + factor
1323 factor =
m_half*
m_pi*charge**2/(latt%rcell_volume*this%alpha**2)
1325 stress_ewald(idir,idir) = stress_ewald(idir,idir) - factor
1328 stress_ewald = stress_ewald / latt%rcell_volume
1352 subroutine ewald_2d_stress(this, space, latt, atom, natoms, pos, stress_Ewald)
1354 type(
space_t),
intent(in) :: space
1356 type(
atom_t),
contiguous,
intent(in) :: atom(:)
1357 integer,
intent(in) :: natoms
1358 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
1359 real(real64),
intent(out) :: stress_Ewald(3, 3)
1361 real(real64) :: rcut, efourier
1362 integer :: iatom, jatom, idir, jdir
1363 integer :: ix, iy, ix_max, iy_max
1364 real(real64) :: gvec(3), gred(3), gg2, cos_gx, gg_abs, gmax_squared
1365 real(real64) :: factor,factor1,factor2, coeff, e_ewald
1366 real(real64) :: dz_max, z_ij, erfc1, erfc2, diff(3)
1367 real(real64),
parameter :: tol = 1e-10_real64
1371 assert(space%periodic_dim == 2)
1372 assert(space%dim == 3)
1378 do iatom = 1, natoms
1379 do jatom = iatom + 1, natoms
1380 dz_max = max(dz_max, abs(pos(3, iatom) - pos(3, jatom)))
1386 rcut =
m_two*this%alpha*4.6_real64 +
m_two*this%alpha**2*dz_max
1387 if (dz_max > tol)
then
1390 erfc1 = erfc(this%alpha*dz_max +
m_half*rcut/this%alpha)
1391 if (erfc1 *
exp(rcut*dz_max) < tol)
exit
1392 rcut = rcut * 1.414_real64
1398 factor =
m_pi/latt%rcell_volume
1400 do iatom = 1, natoms
1401 do jatom = 1, natoms
1402 z_ij = pos(3, iatom) - pos(3, jatom)
1404 factor1 = z_ij * erf(this%alpha*z_ij)
1405 factor2 =
exp(-(this%alpha*z_ij)**2)/(this%alpha*
sqrt(
m_pi))
1407 efourier = efourier - factor &
1408 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() * (factor1 + factor2)
1414 stress_ewald(idir, idir) = efourier
1418 ix_max = ceiling(rcut/norm2(latt%klattice(:, 1)))
1419 iy_max = ceiling(rcut/norm2(latt%klattice(:, 2)))
1420 gmax_squared = ix_max**2 * sum(latt%klattice(:, 1)**2)
1421 gmax_squared = min(gmax_squared, iy_max**2 * sum(latt%klattice(:, 2)**2))
1426 do ix = -ix_max, ix_max
1427 do iy = -iy_max, iy_max
1431 gg2 = dot_product(gvec,gvec)
1434 if (gg2 <
m_epsilon .or. gg2 > gmax_squared*1.001_real64) cycle
1437 factor =
m_fourth*
m_pi/(latt%rcell_volume*this%alpha*gg2)
1439 do iatom = 1, natoms
1440 do jatom = iatom, natoms
1441 diff = pos(:, iatom) - pos(:, jatom)
1442 cos_gx =
cos(sum(gvec(1:2) * diff(1:2)))
1448 if (iatom == jatom)
then
1456 stress_ewald(idir, jdir) = stress_ewald(idir, jdir) &
1457 - factor*gvec(idir)*gvec(jdir) * cos_gx * (factor1 + factor2) * coeff&
1458 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval()
1463 factor1 =
exp(-gg_abs*z_ij)*erfc1
1468 factor2 =
exp(gg_abs*z_ij)*erfc2
1473 e_ewald =
m_half *
m_pi/latt%rcell_volume * coeff &
1474 * atom(iatom)%species%get_zval() * atom(jatom)%species%get_zval() &
1475 * cos_gx / gg_abs * (factor1 + factor2)
1478 stress_ewald(idir, idir) = stress_ewald(idir, idir) + e_ewald
1488 stress_ewald = stress_ewald / latt%rcell_volume
1495 real(real64) function screening_function_2d(alpha, z_ij, gg_abs, erfc_arg) result(factor)
1496 real(real64),
intent(in) :: alpha
1497 real(real64),
intent(in) :: z_ij
1498 real(real64),
intent(in) :: gg_abs
1499 real(real64),
intent(out) :: erfc_arg
1503 arg = -alpha*z_ij +
m_half*gg_abs/alpha
1504 erfc_arg = erfc(arg)
1506 factor = factor*
exp(-gg_abs*z_ij)
1514 class(space_t),
intent(in) :: space
1515 type(lattice_vectors_t),
intent(in) :: latt
1516 type(atom_t),
contiguous,
intent(in) :: atom(:)
1517 integer,
intent(in) :: natoms
1518 real(real64),
intent(in) :: pos(1:space%dim,1:natoms)
1519 real(real64),
contiguous,
intent(in) :: lsize(:)
1520 type(namespace_t),
intent(in) :: namespace
1521 type(multicomm_t),
intent(in) :: mc
1524 real(real64) :: energy
1525 real(real64),
allocatable :: force(:, :), force_components(:, :, :)
1526 real(real64) :: energy_components(1:ION_NUM_COMPONENTS)
1527 integer :: iatom, idir
1534 safe_allocate(force(1:space%dim, 1:natoms))
1535 safe_allocate(force_components(1:space%dim, 1:natoms, 1:ion_num_components))
1538 energy_components = energy_components, force_components = force_components)
1540 call messages_write(
'Ionic energy =')
1541 call messages_write(energy, fmt =
'(f20.10)')
1542 call messages_info(namespace=namespace)
1544 call messages_write(
'Real space energy =')
1546 call messages_info(namespace=namespace)
1548 call messages_write(
'Self energy =')
1550 call messages_info(namespace=namespace)
1552 call messages_write(
'Fourier energy =')
1554 call messages_info(namespace=namespace)
1556 call messages_info(namespace=namespace)
1558 do iatom = 1, natoms
1559 call messages_write(
'Ionic force atom')
1560 call messages_write(iatom)
1561 call messages_write(
' =')
1562 do idir = 1, space%dim
1563 call messages_write(force(idir, iatom), fmt =
'(f20.10)')
1565 call messages_info(namespace=namespace)
1567 call messages_write(
'Real space force atom')
1568 call messages_write(iatom)
1569 call messages_write(
' =')
1570 do idir = 1, space%dim
1571 call messages_write(force_components(idir, iatom,
ion_component_real), fmt =
'(f20.10)')
1573 call messages_info(namespace=namespace)
1575 call messages_write(
'Fourier space force atom')
1576 call messages_write(iatom)
1577 call messages_write(
' =')
1578 do idir = 1, space%dim
1581 call messages_info(namespace=namespace)
1583 call messages_info(namespace=namespace)
1586 safe_deallocate_a(force)
1587 safe_deallocate_a(force_components)
double log(double __x) __attribute__((__nothrow__
double exp(double __x) __attribute__((__nothrow__
double sin(double __x) __attribute__((__nothrow__
double sqrt(double __x) __attribute__((__nothrow__
double cos(double __x) __attribute__((__nothrow__
pure logical function, public all_species_are_jellium_slab(atom)
Check if all species are jellium slab.
pure logical function, public any_species_is_jellium_sphere(atom)
Check if any species is a jellium sphere.
type(debug_t), save, public debug
subroutine, public distributed_end(this)
subroutine, public distributed_init(this, total, comm, tag, scalapack_compat)
Distribute N instances across M processes of communicator comm
subroutine, public distributed_init_serial(this, total)
Serial initialization of a distributed instance. The calling process is assigned all total instances,...
real(real64), parameter, public m_two
real(real64), parameter, public m_max_exp_arg
real(real64), parameter, public m_zero
real(real64), parameter, public m_four
real(real64), parameter, public m_pi
some mathematical constants
real(real64), parameter, public m_fourth
complex(real64), parameter, public m_z0
complex(real64), parameter, public m_zi
real(real64), parameter, public r_min_atom_dist
Minimal distance between two distinguishable atoms.
real(real64), parameter, public m_epsilon
real(real64), parameter, public m_half
real(real64), parameter, public m_one
real(real64), parameter, public m_three
real(real64), parameter, public m_five
subroutine, public ion_interaction_stress(this, space, latt, atom, natoms, pos, stress_ii)
Computes the contribution to the stress tensor the ion-ion energy.
subroutine, public ion_interaction_init_parallelization(this, natoms, mc)
subroutine ewald_1d(this, space, latt, atom, natoms, pos, ereal, eself, efourier, force, force_real)
Total ion-ion Ewald energy and forces for systems periodic in one dimension.
integer, parameter ion_component_self
real(real64) function jellium_slab_energy_periodic(space, atom, lsize)
Electrostatic energy of a periodic jellium slab.
subroutine, public ion_interaction_test(space, latt, atom, natoms, pos, lsize, namespace, mc)
subroutine ewald_long_2d(this, space, latt, atom, natoms, pos, efourier, force)
Computes the long-range part of the 2D Ewald summation.
real(real64) function screening_function_2d(alpha, z_ij, gg_abs, erfc_arg)
Auxiliary function for the Ewald 2D stress.
subroutine ion_interaction_stress_short(this, space, latt, atom, natoms, pos, stress_short)
Computes the short-range contribution to the stress tensor the ion-ion energy.
subroutine ion_interaction_periodic(this, space, latt, atom, natoms, pos, energy, force, energy_components, force_components)
Total Ewald electrostatic energy and forces, for 1D, 2D and 3D systems.
real(real64) function jellium_self_energy_finite(dist, latt, atom, lsize)
Electrostatic self-interaction for jellium instances, with orthogonal cells.
subroutine, public ion_interaction_init(this, namespace, space, natoms)
subroutine ewald_short(dist, space, latt, atom, pos, alpha, ereal, force)
Short range component of the Ewald electrostatic energy and force.
subroutine pseudopotential_correction_3d(dist, latt, atom, charge, epseudo)
G=0 component of Ewald energy arising from the pseudopotentials, for 3D systems.
subroutine ewald_long_3d(this, space, latt, atom, natoms, pos, efourier, force, charge)
Computes the long-range part of the 3D Ewald summation.
integer, parameter ion_component_real
integer, parameter ion_num_components
subroutine ewald_3d_stress(this, space, latt, atom, natoms, pos, stress_Ewald)
Computes the contribution to the stress tensor from the 3D Ewald sum.
integer, parameter ion_component_fourier
subroutine ion_interaction_finite(dist, space, atom, pos, lsize, energy, force)
Electrostatic Ewald energy and forces for finite systems.
subroutine, public ion_interaction_end(this)
subroutine, public ion_interaction_calculate(this, space, latt, atom, natoms, pos, lsize, energy, force, energy_components, force_components)
Top level routine for computing electrostatic energies and forces between ions.
subroutine ewald_2d_stress(this, space, latt, atom, natoms, pos, stress_Ewald)
Computes the contribution to the stress tensor from the 2D Ewald sum.
subroutine ewald_self_interaction(dist, atom, alpha, eself, charge)
@ brief Ewald self-interaction energy
subroutine, public kpoints_to_absolute(latt, kin, kout)
subroutine, public messages_not_implemented(feature, namespace)
subroutine, public messages_experimental(name, namespace)
This module handles the communicators for the various parallelization strategies.
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
static double f(double w, void *p)
Distribution of N instances over mpi_grpsize processes, for the local rank mpi_grprank....
The following class implements a lattice iterator. It allows one to loop over all cells that are with...
An abstract class for species. Derived classes include jellium, all electron, and pseudopotential spe...