Octopus
ion_interaction.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2006 M. Marques, A. Castro, A. Rubio, G. Bertsch
2!! Copyright (C) 2021 N. Tancogne-Dejean
3!!
4!! This program is free software; you can redistribute it and/or modify
5!! it under the terms of the GNU General Public License as published by
6!! the Free Software Foundation; either version 2, or (at your option)
7!! any later version.
8!!
9!! This program is distributed in the hope that it will be useful,
10!! but WITHOUT ANY WARRANTY; without even the implied warranty of
11!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12!! GNU General Public License for more details.
13!!
14!! You should have received a copy of the GNU General Public License
15!! along with this program; if not, write to the Free Software
16!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17!! 02110-1301, USA.
18!!
19
20#include "global.h"
21
23 use atom_oct_m
24 use comm_oct_m
25 use debug_oct_m
26 use global_oct_m
33 use mpi_oct_m
36 use parser_oct_m
38 use ps_oct_m
40 use space_oct_m
43
44 implicit none
45
46 private
47 public :: &
55
57 real(real64) :: alpha
58 type(distributed_t) :: dist
59 end type ion_interaction_t
60
61 integer, parameter :: &
62 ION_COMPONENT_REAL = 1, &
66
67contains
68
69 subroutine ion_interaction_init(this, namespace, space, natoms)
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
74
75 push_sub(ion_interaction_init)
76
77 !%Variable EwaldAlpha
78 !%Type float
79 !%Default 0.21
80 !%Section Hamiltonian
81 !%Description
82 !% The value 'Alpha' that controls the splitting of the Coulomb
83 !% interaction in the Ewald sum used to calculation the ion-ion
84 !% interaction for periodic systems. This value affects the speed
85 !% of the calculation, normally users do not need to modify it.
86 !%End
87 call parse_variable(namespace, 'EwaldAlpha', 0.21_real64, this%alpha)
88
89 call distributed_init_serial(this%dist, natoms)
90
91 if (space%periodic_dim == 1) then
92 call messages_experimental('Ewald summation for 1D-periodic systems', namespace=namespace)
93 end if
94
96 end subroutine ion_interaction_init
97
98 subroutine ion_interaction_init_parallelization(this, natoms, mc)
99 type(ion_interaction_t), intent(inout) :: this
100 integer, intent(in) :: natoms
101 type(multicomm_t), intent(in) :: mc
102
104
105 !As the code below is not parallelized with any of k-point, states nor domain
106 !we can safely parallelize it over atoms
107 if (debug%info) then
108 call distributed_init(this%dist, natoms, mc%master_comm, "Ions")
109 else
110 call distributed_init(this%dist, natoms, mc%master_comm)
111 end if
112
115
116 subroutine ion_interaction_end(this)
117 type(ion_interaction_t), intent(inout) :: this
118
119 push_sub(ion_interaction_end)
120
121 this%alpha = -m_one
122
123 call distributed_end(this%dist)
124
125 pop_sub(ion_interaction_end)
126 end subroutine ion_interaction_end
127
132 subroutine ion_interaction_calculate(this, space, latt, atom, natoms, pos, lsize, energy, force, &
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(:, :, :)
145
146
148 call profiling_in("ION_ION_INTERACTION")
149
150 if (present(energy_components)) then
151 assert(ubound(energy_components, dim = 1) == ion_num_components)
152 energy_components = m_zero
153 end if
154
155 if (present(force_components)) then
156 assert(all(ubound(force_components) == (/space%dim, natoms, ion_num_components/)))
157 force_components = m_zero
158 end if
159
160 if (space%is_periodic() .and. any_species_is_jellium_sphere(atom)) then
161 call messages_not_implemented('No periodic implementation of ion-ion energy for the jellium sphere')
162 end if
163
164 if (space%is_periodic()) then
165 if (all_species_are_jellium_slab(atom)) then
166 energy = jellium_slab_energy_periodic(space, atom, lsize)
167 force = 0._real64
168 else
169 call ion_interaction_periodic(this, space, latt, atom, natoms, pos, energy, force, energy_components, force_components)
170 end if
171 else
172 call ion_interaction_finite(this%dist, space, atom, pos, lsize, energy, force)
173 energy = energy + jellium_self_energy_finite(this%dist, latt, atom, lsize)
174 end if
175
176 call profiling_out("ION_ION_INTERACTION")
178
179 end subroutine ion_interaction_calculate
180
186 function jellium_slab_energy_periodic(space, atom, lsize) result(energy)
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
191
192 real(real64) :: area
194 ! Implementation assumes a single atom
195 assert(size(atom) == 1)
196 ! This is only allowed if periodic dim = 2. In that case the lattice volume is in fact an area.
197 assert(space%periodic_dim == 2)
198
199 select type(spec => atom(1)%species)
200 type is (jellium_slab_t)
201 area = lsize(1) * lsize(2) * m_four
202 energy = m_pi * spec%get_density(lsize) **2 * area * spec%thickness()**3 / m_three
203 class default
204 assert(.false.)
205 end select
206
208
221 function jellium_self_energy_finite(dist, latt, atom, lsize) result(energy)
222 type(distributed_t), intent(in) :: dist
223 type(lattice_vectors_t), intent(in) :: latt
224 type(atom_t), contiguous, intent(in) :: atom(:)
225 real(real64), contiguous, intent(in) :: lsize(:)
226 real(real64) :: energy
228 real(real64) :: zi
229 integer :: iatom
230 logical :: lattice_is_orthogonal
231 class(species_t), pointer :: spec
232
234
235 energy = 0._real64
236 lattice_is_orthogonal = .not. latt%nonorthogonal
237
238 do iatom = dist%start, dist%end
239 spec => atom(iatom)%species
240 zi = spec%get_zval()
241
242 select type(spec)
243 type is (jellium_sphere_t)
244 energy = energy + (m_three / m_five) * zi**2 / spec%radius()
245 ! The part depending on the simulation sphere is neglected
246
247 type is (jellium_slab_t)
248 ! Jellium slab energy only implemented for orthogonal cells.
249 ! One would need to replace (lsize(1) * lsize(2)) * spec%thickness()) with the triple product
250 assert(lattice_is_orthogonal)
251 energy = energy + m_pi * zi**2 / (m_four * lsize(1)*lsize(2)) * spec%thickness() / m_three
252 ! The part depending on the simulation box transverse dimension is neglected
253 end select
254 nullify(spec)
255 enddo
256
257 call comm_allreduce(dist%mpi_grp, energy)
258
260
261 end function jellium_self_energy_finite
262
264 subroutine ion_interaction_finite(dist, space, atom, pos, lsize, energy, force)
265 type(distributed_t), intent(in) :: dist
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(:, :)
272
273 class(species_t), pointer :: species_i, species_j
274 real(real64) :: r(space%dim), f(space%dim)
275 real(real64) :: r_mag
276 real(real64) :: u_e
277 real(real64) :: zi, zj
278 integer :: iatom, jatom, natoms
279
280 push_sub(ion_interaction_finite)
282 natoms = size(atom)
283 energy = m_zero
284 force(1:space%dim, 1:natoms) = m_zero
285
286 do iatom = dist%start, dist%end
287 species_i => atom(iatom)%species
288 zi = species_i%get_zval()
289
290 do jatom = iatom + 1, natoms
291 species_j => atom(jatom)%species
292 zj = species_j%get_zval()
293
294 r = pos(:, iatom) - pos(:, jatom)
295 r_mag = norm2(r)
296 u_e = zi * zj / r_mag
297
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)
302 end do
303 end do
304
305 call comm_allreduce(dist%mpi_grp, energy)
306 call comm_allreduce(dist%mpi_grp, force)
307
308 nullify(species_i, species_j)
309
311
312 end subroutine ion_interaction_finite
313
315 subroutine ion_interaction_periodic(this, space, latt, atom, natoms, pos, energy, force, &
316 energy_components, force_components)
317 type(ion_interaction_t), intent(in) :: this
318 class(space_t), intent(in) :: space
319 type(lattice_vectors_t), intent(in) :: latt
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(:, :, :)
327
328 real(real64) :: ereal, efourier, epseudo, eself
329 real(real64) :: charge
330 real(real64), allocatable :: force_real(:,:)
331
333
334 energy = m_zero
335 force(1:space%dim, 1:natoms) = m_zero
336
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)
340 end if
341
342 call ewald_self_interaction(this%dist, atom, this%alpha, eself, charge)
343
344 call profiling_in("EWALD_LONG")
345 select case (space%periodic_dim)
346 case (1)
347 ! The 1D-periodic Ewald sum uses a soft-Coulomb range separation (see ewald_1D),
348 ! different from the shared erfc Ewald_short/Ewald_self_interaction computed above.
349 ! Recompute all components and discard the erfc real-space contribution.
350 ! The long range part of the pseudopotential is not accounted for here (same situation as the 2D case). See Issue #950.
351 epseudo = m_zero
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)
357 end if
358 safe_deallocate_a(force_real)
359 case (2)
360 ! The energy contribution of the long range part of the pseudo is
361 ! not correctly accounted for in systems periodic in 1D or 2D, however
362 ! this term should not appear here anyway. See Issue #950.
363 epseudo = m_zero
364 call ewald_long_2d(this, space, latt, atom, natoms, pos, efourier, force)
365 case (3)
366 call ewald_long_3d(this, space, latt, atom, natoms, pos, efourier, force, charge)
367 !TODO(Alex/Nicolas) Issue #950. Refactor: Move G=0 correction from ion-ion energy to pseudopotential energy
368 call pseudopotential_correction_3d(this%dist, latt, atom, charge, epseudo)
369 end select
370 call profiling_out("EWALD_LONG")
371
372 if (present(energy_components)) then
373 energy_components(ion_component_real) = ereal
374 energy_components(ion_component_self) = eself
375 energy_components(ion_component_fourier) = efourier
376 end if
377
378 if (present(force_components)) then
379 ! This is dependent on the order in which the force terms are computed
380 force_components(1:space%dim, 1:natoms, ion_component_fourier) = &
381 force(1:space%dim, 1:natoms) - force_components(1:space%dim, 1:natoms, ion_component_real)
382 end if
383
384 energy = ereal + efourier + eself + epseudo
385
387 end subroutine ion_interaction_periodic
388
411 subroutine ewald_short(dist, space, latt, atom, pos, alpha, ereal, force)
412 type(distributed_t), intent(in) :: dist
413 class(space_t), intent(in) :: space
414 type(lattice_vectors_t), intent(in) :: latt
415 type(atom_t), contiguous, intent(in) :: atom(:)
416 real(real64), contiguous, intent(in) :: pos(:, :)
417
418 real(real64), intent(in) :: alpha
419 real(real64), intent(out) :: ereal
420 real(real64), contiguous, intent(inout) :: force(:, :)
421 ! Intent(inout) allows force contributions to be summed
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
427 real(real64) :: rcut
428 type(lattice_iterator_t) :: latt_iter
429 real(real64) :: charge, coeff
430
431 push_sub_with_profile(ewald_short)
432
433 ereal = m_zero
434 ! Origin of this value is not documented
435 rcut = 6.0_real64 / alpha
436 latt_iter = lattice_iterator_t(latt, rcut)
437 natoms = size(atom)
438
439 charge = m_zero
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
444 end do
445
446 ! Diagonal terms iatom == jatom for all cells, except T=(0,0,0)
447 ! Note: Only half of the copies are needed, by symmetries
448 do icopy = 1, latt_iter%n_cells
449 rnorm = norm2(latt_iter%get(icopy))
450 if (rnorm < r_min_atom_dist) cycle
451 if (rnorm > rcut) cycle
452 ereal = ereal + m_half * charge * erfc(alpha * rnorm) /rnorm
453 end do
454
455 coeff = m_two * alpha / sqrt(m_pi)
456
457 !$omp parallel default(shared) &
458 !$omp& private(iatom, jatom, zi, zj, icopy, xi, rnorm, erfc_rnorm, force_real, charge) &
459 !$omp& reduction(+: ereal, force)
460 do iatom = dist%start, dist%end
461 if (.not. atom(iatom)%species%represents_real_atom()) cycle
462 zi = atom(iatom)%species%get_zval()
463
464 ! Upper triangle, for all replica cells
465 do jatom = iatom + 1, natoms
466 zj = atom(jatom)%species%get_zval()
467
468 charge = zi*zj
469
470 !$omp do
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
475
476 erfc_rnorm = erfc(alpha * rnorm) / rnorm
477
478 ! Factor 1/2 omitted as one is only summing over upper triangle
479 ereal = ereal + charge * erfc_rnorm
480
481 force_real(:) = charge * (xi - pos(:, jatom)) * &
482 (erfc_rnorm + coeff *exp(-(alpha*rnorm)**2)) / rnorm**2
483
484 ! Upper trianglar contribution
485 force(1:space%dim, jatom) = force(1:space%dim, jatom) - force_real
486
487 ! Lower triangular contribution
488 force(1:space%dim, iatom) = force(1:space%dim, iatom) + force_real
489 end do
490 !$omp end do
491
492 end do
493 end do
494 !$omp end parallel
495
496 call comm_allreduce(dist%mpi_grp, ereal)
497 call comm_allreduce(dist%mpi_grp, force)
498
499 pop_sub_with_profile(ewald_short)
500 end subroutine ewald_short
501
506 subroutine ewald_self_interaction(dist, atom, alpha, eself, charge)
507 type(distributed_t), intent(in) :: dist
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
512
513 integer :: iatom
514 real(real64) :: zi
515
516 push_sub(ewald_self_interaction)
517
518 eself = m_zero
519 charge = m_zero
520
521 do iatom = dist%start, dist%end
522 zi = atom(iatom)%species%get_zval()
523 charge = charge + zi
524 eself = eself - alpha / sqrt(m_pi) * zi**2
525 end do
526
527 call comm_allreduce(dist%mpi_grp, eself)
528 call comm_allreduce(dist%mpi_grp, charge)
529
531 end subroutine ewald_self_interaction
532
534 subroutine ewald_long_3d(this, space, latt, atom, natoms, pos, efourier, force, charge)
535 type(ion_interaction_t), intent(in) :: this
536 class(space_t), intent(in) :: space
537 type(lattice_vectors_t), intent(in) :: latt
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
544
545 real(real64) :: rcut, gmax_squared
546 integer :: iatom
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
551
552 complex(real64), allocatable :: phase(:)
553
554 push_sub(ewald_long_3d)
555
556 assert(space%dim == 3)
557 assert(space%periodic_dim == 3)
558
559 ! And the long-range part, using an Ewald sum
560 safe_allocate(phase(1:natoms))
561
562 ! get a converged value for the cutoff in g
563 rcut = sqrt(minval(sum(latt%klattice**2, dim=1)))
564
565 ! 9.5 is a constant that controls the range separation
566 isph = ceiling(9.5_real64*this%alpha/rcut)
567
568 ! First the G = 0 term (charge was calculated previously)
569 efourier = -m_pi*charge**2/(m_two*this%alpha**2*latt%rcell_volume)
570
571 ! Getting the G-shell cutoff
572 gmax_squared = isph**2 * minval(sum(latt%klattice**2, dim=1))
573
574 do ix = -isph, isph
575 do iy = -isph, isph
576 do iz = -isph, isph
577
578 ! Exploit k <-> -k symmetry
579 ! Only process one half of reciprocal space.
580 ! g=0 must also be removed from the sum
581 if (ix < 0) cycle
582 if (ix == 0 .and. iy < 0) cycle
583 if (ix == 0 .and. iy == 0 .and. iz <= 0) cycle
584
585 gred = [ix, iy, iz]
586 call kpoints_to_absolute(latt, gred, gvec)
587 gg2 = dot_product(gvec, gvec)
588
589 if (gg2 > gmax_squared*1.001_real64) cycle
590
591 gx = -0.25_real64*gg2/this%alpha**2
592
593 if (gx < -36.0_real64) cycle
594
595 ! We have used the k-> -k symmetry, hence the factor 4
596 factor = m_four*m_pi/latt%rcell_volume*exp(gx)/gg2
597
598 if (factor < epsilon(factor)) cycle
599
600 sumatoms = m_z0
601 !$omp parallel do private(iatom, gx, aa) reduction(+:sumatoms)
602 do iatom = 1, natoms
603 gx = sum(gvec*pos(:,iatom))
604 aa = atom(iatom)%species%get_zval()*cmplx(cos(gx), sin(gx), real64)
605 phase(iatom) = aa
606 sumatoms = sumatoms + aa
607 end do
608
609 efourier = efourier + factor * real(sumatoms*conjg(sumatoms), real64)
610
611 do iatom = 1, natoms
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)
614
615 end do
616
617 end do
618 end do
619 end do
620
621 safe_deallocate_a(phase)
622
623 pop_sub(ewald_long_3d)
624
625 end subroutine ewald_long_3d
626
630 subroutine ewald_long_2d(this, space, latt, atom, natoms, pos, efourier, force)
631 type(ion_interaction_t), intent(in) :: this
632 class(space_t), intent(in) :: space
633 type(lattice_vectors_t), intent(in) :: latt
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(:, :)
639
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
648
649 push_sub(ewald_long_2d)
650
651 assert(space%periodic_dim == 2)
652 assert(space%dim == 2 .or. space%dim == 3)
653
654 ! And the long-range part, using an Ewald sum
655
656 ! Searching maximum distance
657 if (space%dim == 3) then
658 dz_max = m_zero
659 do iatom = 1, natoms
660 do jatom = iatom + 1, natoms
661 dz_max = max(dz_max, abs(pos(3, iatom) - pos(3, jatom)))
662 end do
663 end do
664 else
665 ! For a 2D system, all atoms are on the plane, so the distance is zero
666 dz_max = m_zero
667 end if
668
669 !get a converged value for the cutoff in g
670 rcut = m_two*this%alpha*4.6_real64 + m_two*this%alpha**2*dz_max
671 if (dz_max > tol) then
672 do
673 if (rcut * dz_max >= m_max_exp_arg) exit !Maximum double precision number
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
677 end do
678 end if
679
680 ix_max = ceiling(rcut/norm2(latt%klattice(:, 1)))
681 iy_max = ceiling(rcut/norm2(latt%klattice(:, 2)))
682
683 safe_allocate(force_tmp(1:space%dim, 1:natoms))
684 force_tmp = m_zero
685
686 ! First the G = 0 term
687 efourier = m_zero
688 factor = m_pi/latt%rcell_volume
689 !$omp parallel do private(jatom, dz_ij, tmp_erf, factor1, factor2) reduction(+:efourier,force_tmp) &
690 !$omp& collapse(2)
691 do iatom = this%dist%start, this%dist%end
692 do jatom = 1, natoms
693 ! efourier
694 if (space%dim == 3) then
695 dz_ij = pos(3, iatom) - pos(3, jatom)
696 else
697 dz_ij = m_zero
698 end if
699
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))
703
704 efourier = efourier - factor &
705 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() * (factor1 + factor2)
706
707 ! force
708 if (iatom == jatom)cycle
709 if (abs(tmp_erf) < m_epsilon) cycle
710
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
714 end if
715
716 end do
717 end do
718
719 ! Getting the G-shell cutoff
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))
722
723 !$omp parallel do private(iy, gvec, gg2, gg_abs, factor, iatom, jatom, gx, dz_ij, erfc1, factor1, erfc2, factor2, coeff) &
724 !$omp& collapse(2) reduction(+:efourier, force_tmp)
725 do ix = -ix_max, ix_max
726 do iy = -iy_max, iy_max
727
728 gvec = ix*latt%klattice(:, 1) + iy*latt%klattice(:, 2)
729 gg2 = sum(gvec**2)
730
731 ! g=0 must be removed from the sum
732 if (gg2 < m_epsilon .or. gg2 > gmax_squared*1.001_real64) cycle
733 gg_abs = sqrt(gg2)
734 factor = m_half*m_pi/(latt%rcell_volume*gg_abs)
735
736 do iatom = this%dist%start, this%dist%end
737 do jatom = iatom, natoms
738 ! efourier
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)
743 else
744 dz_ij = m_zero
745 end if
746
747 erfc1 = erfc(this%alpha*dz_ij + m_half*gg_abs/this%alpha)
748 if (abs(erfc1) > m_epsilon) then
749 factor1 = exp(gg_abs*dz_ij)*erfc1
750 else
751 factor1 = m_zero
752 end if
753 erfc2 = erfc(-this%alpha*dz_ij + m_half*gg_abs/this%alpha)
754 if (abs(erfc2) > m_epsilon) then
755 factor2 = exp(-gg_abs*dz_ij)*erfc2
756 else
757 factor2 = m_zero
758 end if
759
760 if (iatom == jatom) then
761 coeff = m_one
762 else
763 coeff = m_two
764 end if
765
766 efourier = efourier &
767 + factor * coeff &
768 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() &
769 * cos(gx)* ( factor1 + factor2)
770
771 ! force
772 if (iatom == jatom) cycle
773
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)
778
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)
783
784 factor1 = gg_abs*erfc1 &
785 - m_two*this%alpha/sqrt(m_pi)*exp(-(this%alpha*dz_ij + m_half*gg_abs/this%alpha)**2)
786 if (abs(factor1) > m_epsilon) then
787 factor1 = factor1*exp(gg_abs*dz_ij)
788 else
789 factor1 = m_zero
790 end if
791
792 factor2 = gg_abs*erfc2 &
793 - m_two*this%alpha/sqrt(m_pi)*exp(-(-this%alpha*dz_ij + m_half*gg_abs/this%alpha)**2)
794 if (abs(factor2) > m_epsilon) then
795 factor2 = factor2*exp(-gg_abs*dz_ij)
796 else
797 factor2 = m_zero
798 end if
799
800 if (space%dim == 3) then
801 force_tmp(3, iatom) = force_tmp(3, iatom) &
802 - m_two*factor &
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) &
806 + m_two*factor &
807 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() &
808 * cos(gx)* ( factor1 - factor2)
809 end if
810
811 end do
812 end do
813
814
815 end do
816 end do
817
818 call comm_allreduce(this%dist%mpi_grp, efourier)
819 call comm_allreduce(this%dist%mpi_grp, force_tmp)
820
821 force = force + force_tmp
822
823 safe_deallocate_a(force_tmp)
824
825 pop_sub(ewald_long_2d)
826 end subroutine ewald_long_2d
827
881 subroutine ewald_1d(this, space, latt, atom, natoms, pos, ereal, eself, efourier, force, force_real)
882 type(ion_interaction_t), intent(in) :: this
883 class(space_t), intent(in) :: space
884 type(lattice_vectors_t), intent(in) :: latt
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(:, :)
893
894 real(real64), parameter :: tol = 1e-10_real64
895 ! Internal soft-Coulomb splitting length. The total energy and forces are
896 ! independent of its value; it only controls the real/reciprocal convergence rate.
897 ! A value of 1 ensures most of the work is performed by the short-range sum (while L is small),
898 ! which is cheaper to evaluate than the Bessel functions of the long-range sum.
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(:,:)
908 type(lattice_iterator_t) :: latt_iter
909
910 push_sub(ewald_1d)
911
912 assert(space%periodic_dim == 1)
913 assert(space%dim >= 1 .and. space%dim <= 3)
914
915 length = norm2(latt%rlattice(1:space%dim, 1))
916 uaxis(1:space%dim) = latt%rlattice(1:space%dim, 1) / length
917 ! Reciprocal vectors are taken along the periodic axis, g_n = (2 pi n / L) uaxis.
918 ! This does not rely on klattice(:,1), which is parallel to uaxis only if the
919 ! non-periodic lattice vectors are orthogonal to the periodic one.
920 gk = m_two * m_pi / length
921
922 ! Self-energy correction (remove soft-Coulomb self)
923 eself = m_zero
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
927 end do
928 call comm_allreduce(this%dist%mpi_grp, eself)
929
930 ! Short-range real-space sum
931 ! The summand decays as sigma^2/(2 r^3); choose the cutoff so the 1/t^3 tail < tol.
932 rcut = sqrt(sigma**2 / (m_two * length * tol))
933 latt_iter = lattice_iterator_t(latt, rcut)
934
935 ereal = m_zero
936 force_real(1:space%dim, 1:natoms) = m_zero
937 !$omp parallel do default(shared) &
938 !$omp& private(iatom, zi, ri, jatom, zj, zz, rj, icopy, xi, rnorm, rt2s, dcoeff, fvec) &
939 !$omp& reduction(+:ereal, force_real)
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()
943 ri = pos(:, iatom)
944 ! Diagonal self-images (i==i, t/=0). The net force on an atom from its own,
945 ! symmetric images vanishes, so no force contribution here.
946 do icopy = 1, latt_iter%n_cells
947 rnorm = norm2(latt_iter%get(icopy))
948 if (rnorm < r_min_atom_dist) cycle
949 if (rnorm > rcut) cycle
950 ereal = ereal + m_half * zi*zi * (m_one/rnorm - m_one/sqrt(rnorm**2 + sigma**2))
951 end do
952 ! Upper-triangle pairs (i<j), summed over all images (both orderings folded in).
953 do jatom = iatom + 1, natoms
954 if (atom(jatom)%species%is_user_defined()) cycle
955 zj = atom(jatom)%species%get_zval()
956 zz = zi*zj
957 rj = pos(:, jatom)
958 do icopy = 1, latt_iter%n_cells
959 xi = ri + latt_iter%get(icopy)
960 rnorm = norm2(xi - rj)
961 if (rnorm < r_min_atom_dist) cycle
962 if (rnorm > rcut) cycle
963 rt2s = sqrt(rnorm**2 + sigma**2)
964 ereal = ereal + zz * (m_one/rnorm - m_one/rt2s)
965 dcoeff = m_one/rnorm**3 - m_one/rt2s**3
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)
969 end do
970 end do
971 end do
972 !$omp end parallel do
973 call comm_allreduce(this%dist%mpi_grp, ereal)
974 call comm_allreduce(this%dist%mpi_grp, force_real)
975
976 ! Long-range reciprocal sum (full K_0) + k_z=0 term
977 ! K_0(rbar*g_n) ~ exp(-rbar*g_n) with rbar >= sigma, so bound the number of shells.
978 n_max = max(1, ceiling(-log(tol)/(sigma*gk)) + 1)
979
980 safe_allocate(force_recip(1:space%dim, 1:natoms))
981 force_recip(1:space%dim, 1:natoms) = m_zero
982 efourier = m_zero
983 !$omp parallel do default(shared) &
984 !$omp& private(iatom, zi, jatom, zj, zz, rij, xpar, rperp, rho, rbar, &
985 !$omp& n, gmag, gvec, phase, k0v, k1v, ctrans, calong, fvec) &
986 !$omp& reduction(+:efourier, force_recip)
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()
993 zz = zi*zj
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) ! always > 0, so K_0 is never singular
999
1000 if (iatom == jatom) then
1001 ! Self term (rho=0, z_ij=0): k_z=0 log term; reciprocal self; no force.
1002 ! Here rbar = sigma, so the log term vanishes for sigma = 1; kept for generality.
1003 efourier = efourier - (m_one/length) * zz * log(rbar)
1004 do n = 1, n_max
1005 gmag = n*gk
1006 efourier = efourier + (m_two/length) * zz * loct_bessel_k0(rbar*gmag)
1007 end do
1008 else
1009 ! Ordered pair {i,j} counted once here with a factor of 2 (both orderings).
1010 ! k_z=0 log term and its transverse force.
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)
1015 ! k_z /= 0 terms.
1016 do n = 1, n_max
1017 gmag = n*gk
1018 gvec(1:space%dim) = gmag*uaxis(1:space%dim)
1019 phase = gmag*xpar
1020 k0v = loct_bessel_k0(rbar*gmag)
1021 k1v = loct_bessel_k1(rbar*gmag)
1022 efourier = efourier + (m_four/length) * zz * k0v * cos(phase)
1023 ! transverse force (through rbar) and along-axis force (through the 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)
1029 end do
1030 end if
1031 end do
1032 end do
1033 !$omp end parallel do
1034 call comm_allreduce(this%dist%mpi_grp, efourier)
1035 call comm_allreduce(this%dist%mpi_grp, force_recip)
1036
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)
1039
1040 safe_deallocate_a(force_recip)
1041
1042 pop_sub(ewald_1d)
1043 end subroutine ewald_1d
1044
1045 !TODO(Alex/Nicolas) Issue #950. Refactor: Move G=0 correction from ion-ion energy to pseudopotential energy
1052 subroutine pseudopotential_correction_3d(dist, latt, atom, charge, epseudo)
1053 type(distributed_t), intent(in) :: dist
1054 type(lattice_vectors_t), intent(in) :: latt
1055 type(atom_t), contiguous, intent(in) :: atom(:)
1056 real(real64), intent(in) :: charge
1057 real(real64), intent(out) :: epseudo
1058
1059 real(real64) :: zi
1060 integer :: iatom
1061
1063
1064 epseudo = m_zero
1065 do iatom = dist%start, dist%end
1066 select type(spec => atom(iatom)%species)
1067 class is(pseudopotential_t)
1068 zi = spec%get_zval()
1069 epseudo = epseudo + m_pi *zi * &
1070 (spec%ps%sigma_erf * sqrt(m_two))**2 / latt%rcell_volume * charge
1071 end select
1072 end do
1073 call comm_allreduce(dist%mpi_grp, epseudo)
1074
1076
1077 end subroutine pseudopotential_correction_3d
1078
1080 subroutine ion_interaction_stress(this, space, latt, atom, natoms, pos, stress_ii)
1081 type(ion_interaction_t), intent(inout) :: this
1082 class(space_t), intent(in) :: space
1083 type(lattice_vectors_t), intent(in) :: latt
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)
1088
1089 real(real64) :: stress_short(1:space%dim, 1:space%dim), stress_Ewald(1:space%dim, 1:space%dim)
1090
1091 push_sub(ion_interaction_stress)
1092
1093 stress_ii = m_zero
1094
1095 ! Only implemented in the periodic case
1096 assert(space%is_periodic())
1097
1098 ! Short range part in real space
1099 call ion_interaction_stress_short(this, space, latt, atom, natoms, pos, stress_short)
1100
1101 ! Long range part in Fourier space
1102 select case(space%periodic_dim)
1103 case(3)
1104 call ewald_3d_stress(this, space, latt, atom, natoms, pos, stress_ewald)
1105 case(2)
1106 call ewald_2d_stress(this, space, latt, atom, natoms, pos, stress_ewald)
1107 case default
1108 assert(.false.)
1109 end select
1110
1111 stress_ii = stress_short + stress_ewald
1112
1113 pop_sub(ion_interaction_stress)
1114 end subroutine ion_interaction_stress
1115
1116 ! ---------------------------------------------------------
1134
1135 subroutine ion_interaction_stress_short(this, space, latt, atom, natoms, pos, stress_short)
1136 type(ion_interaction_t), intent(inout) :: this
1137 class(space_t), intent(in) :: space
1138 type(lattice_vectors_t), intent(in) :: latt
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)
1143
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
1148 type(lattice_iterator_t) :: latt_iter
1149
1151 call profiling_in("ION_ION_STRESS_SHORT")
1152
1153 ! Only implemented in the periodic case
1154 assert(space%is_periodic())
1155
1156 alpha = this%alpha
1157
1158 ! See the code for the energy above to understand this parameter
1159 rcut = 6.0_real64/alpha
1160
1161 ! the short-range part is calculated directly
1162 stress_short = m_zero
1163 latt_iter = lattice_iterator_t(latt, rcut)
1164
1165 do iatom = this%dist%start, this%dist%end
1166 select type(spec => atom(iatom)%species)
1167 class is(jellium_t)
1168 cycle
1169 end select
1170 zi = atom(iatom)%species%get_zval()
1171
1172 do icopy = 1, latt_iter%n_cells
1173 xi = pos(:, iatom) + latt_iter%get(icopy)
1174
1175 do jatom = 1, natoms
1176 zj = atom(jatom)%species%get_zval()
1177 r_ij = norm2(xi - pos(:, jatom))
1178
1179 if (r_ij < r_min_atom_dist) cycle
1180
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)
1187 end do
1188 end do
1189
1190 end do
1191 end do
1192 end do
1193
1194 if (this%dist%parallel) then
1195 call comm_allreduce(this%dist%mpi_grp, stress_short)
1196 end if
1197
1198 stress_short = stress_short/latt%rcell_volume
1199
1200 call profiling_out("ION_ION_STRESS_SHORT")
1201
1203 end subroutine ion_interaction_stress_short
1204
1205
1206
1207 ! ---------------------------------------------------------
1222 subroutine ewald_3d_stress(this, space, latt, atom, natoms, pos, stress_Ewald)
1223 type(ion_interaction_t), intent(inout) :: this
1224 class(space_t), intent(in) :: space
1225 type(lattice_vectors_t), intent(in) :: latt
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
1232 integer :: iatom
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
1237
1238 call profiling_in("STRESS_3D_EWALD")
1239 push_sub(ewald_3d_stress)
1240
1241 ! Currently this is only implemented for 3D
1242 assert(space%dim == 3)
1243 assert(space%periodic_dim == 3) ! Not working for mixed periodicity
1244 ! (klattice along the non-periodic directions is wrong)
1245 ! Anyway gg/gg2 is not correct for mixed periodicity
1246
1247 stress_ewald = m_zero
1248
1249 ! And the long-range part, using an Ewald sum
1250 charge = m_zero
1251 charge_sq = m_zero
1252 do iatom = 1, natoms
1253 zi = atom(iatom)%species%get_zval()
1254 charge = charge + zi
1255 charge_sq = charge_sq + zi**2
1256 end do
1257
1258 ! get a converged value for the cutoff in g
1259 rcut = huge(rcut)
1260 do idim = 1, space%periodic_dim
1261 rcut = min(rcut, sum(latt%klattice(1:space%periodic_dim, idim)**2))
1262 end do
1263
1264 rcut = sqrt(rcut)
1265
1266 isph = ceiling(9.5_real64*this%alpha/rcut)
1267
1268 ! Getting the G-shell cutoff
1269 gmax_squared = isph**2 * minval(sum(latt%klattice**2, dim=1))
1270
1271 do ix = -isph, isph
1272 do iy = -isph, isph
1273 do iz = -isph, isph
1274
1275 ! Exploit k <-> -k symmetry
1276 ! Only process one half of reciprocal space.
1277 ! g=0 must also be removed from the sum
1278 if (ix < 0) cycle
1279 if (ix == 0 .and. iy < 0) cycle
1280 if (ix == 0 .and. iy == 0 .and. iz <= 0) cycle
1281
1282 gred = [ix, iy, iz]
1283 call kpoints_to_absolute(latt, gred, gvec)
1284 gg2 = sum(gvec**2)
1285
1286 ! g=0 must be removed from the sum
1287 if (gg2 > gmax_squared*1.001_real64) cycle
1288
1289 gx = -0.25_real64*gg2/this%alpha**2
1290
1291 if (gx < -36.0_real64) cycle
1292
1293 ! We have used the k-> -k symmetry, hence the factor 4
1294 factor = m_four*m_pi*exp(gx)/(latt%rcell_volume*gg2)
1295
1296 if (factor < epsilon(factor)) cycle
1297
1298 sumatoms = m_z0
1299
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
1304 end do
1305
1306 factor = factor*abs(sumatoms)**2
1307 off_diagonal_weight = - m_two*factor/gg2*(0.25_real64*gg2/this%alpha**2+m_one)
1308
1309 do idir = 1, 3
1310 do jdir = 1, 3
1311 stress_ewald(idir, jdir) = stress_ewald(idir, jdir) &
1312 + gvec(idir) * gvec(jdir) * off_diagonal_weight
1313 end do
1314 stress_ewald(idir, idir) = stress_ewald(idir, idir) + factor
1315 end do
1316
1317 end do
1318 end do
1319 end do
1320
1321
1322 ! The G = 0 term of the Ewald summation
1323 factor = m_half*m_pi*charge**2/(latt%rcell_volume*this%alpha**2)
1324 do idir = 1,3
1325 stress_ewald(idir,idir) = stress_ewald(idir,idir) - factor
1326 end do
1327
1328 stress_ewald = stress_ewald / latt%rcell_volume
1329
1330
1331 call profiling_out("STRESS_3D_EWALD")
1332 pop_sub(ewald_3d_stress)
1333
1334 end subroutine ewald_3d_stress
1335
1336 ! ---------------------------------------------------------
1352 subroutine ewald_2d_stress(this, space, latt, atom, natoms, pos, stress_Ewald)
1353 type(ion_interaction_t), intent(inout) :: this
1354 type(space_t), intent(in) :: space
1355 type(lattice_vectors_t), intent(in) :: latt
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)
1360
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
1368
1369 push_sub(ewald_2d_stress)
1370
1371 assert(space%periodic_dim == 2)
1372 assert(space%dim == 3)
1373
1374 stress_ewald = m_zero
1375
1376 ! Searching maximum distance
1377 dz_max = m_zero
1378 do iatom = 1, natoms
1379 do jatom = iatom + 1, natoms
1380 dz_max = max(dz_max, abs(pos(3, iatom) - pos(3, jatom)))
1381 end do
1382 end do
1383
1384 !get a converged value for the cutoff in g
1385 ! Note: to understand these numbers, one needs to look into the energy routine for Ewald 2D
1386 rcut = m_two*this%alpha*4.6_real64 + m_two*this%alpha**2*dz_max
1387 if (dz_max > tol) then ! Else the code here does not work properly
1388 do
1389 if (rcut * dz_max >= m_max_exp_arg) exit !Maximum double precision number
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
1393 end do
1394 end if
1395
1396 ! First the G = 0 term
1397 efourier = m_zero
1398 factor = m_pi/latt%rcell_volume
1399 !$omp parallel do private(jatom, z_ij, factor1, factor2) reduction(+:efourier) collapse(2)
1400 do iatom = 1, natoms
1401 do jatom = 1, natoms
1402 z_ij = pos(3, iatom) - pos(3, jatom)
1403
1404 factor1 = z_ij * erf(this%alpha*z_ij)
1405 factor2 = exp(-(this%alpha*z_ij)**2)/(this%alpha*sqrt(m_pi))
1406
1407 efourier = efourier - factor &
1408 * atom(iatom)%species%get_zval()*atom(jatom)%species%get_zval() * (factor1 + factor2)
1409 end do
1410 end do
1411
1412 ! Adding the G=0 term
1413 do idir = 1, 2
1414 stress_ewald(idir, idir) = efourier
1415 end do
1416
1417 ! Getting the G-shell cutoff
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))
1422
1423 !$omp parallel do private(iy, gvec, gg2, gg_abs, factor, iatom, jatom, diff, cos_gx, z_ij, idir, jdir, erfc1, factor1) &
1424 !$omp& private(erfc2, factor2, coeff, e_ewald, gred) &
1425 !$omp& collapse(2) reduction(+:stress_Ewald)
1426 do ix = -ix_max, ix_max
1427 do iy = -iy_max, iy_max
1428
1429 gred = [ix, iy, 0]
1430 call kpoints_to_absolute(latt, gred, gvec)
1431 gg2 = dot_product(gvec,gvec)
1432
1433 ! g=0 must be removed from the sum
1434 if (gg2 < m_epsilon .or. gg2 > gmax_squared*1.001_real64) cycle
1435
1436 gg_abs = sqrt(gg2)
1437 factor = m_fourth*m_pi/(latt%rcell_volume*this%alpha*gg2)
1438
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)))
1443 z_ij = diff(3)
1444
1445 factor1 = screening_function_2d(this%alpha, z_ij, gg_abs, erfc1)
1446 factor2 = screening_function_2d(this%alpha,-z_ij, gg_abs, erfc2)
1448 if (iatom == jatom) then
1449 coeff = m_one
1450 else
1451 coeff = m_two
1452 end if
1453
1454 do idir = 1, 2
1455 do jdir = 1, 2
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()
1459 end do
1460 end do
1461
1462 if (abs(erfc1) > m_epsilon) then
1463 factor1 = exp(-gg_abs*z_ij)*erfc1
1464 else
1465 factor1 = m_zero
1466 end if
1467 if (abs(erfc2) > m_epsilon) then
1468 factor2 = exp(gg_abs*z_ij)*erfc2
1469 else
1470 factor2 = m_zero
1471 end if
1472
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)
1476
1477 do idir = 1, 2
1478 stress_ewald(idir, idir) = stress_ewald(idir, idir) + e_ewald
1479 end do
1480
1481 end do !jatom
1482 end do !iatom
1483 end do !iy
1484 end do !ix
1485
1486 !call comm_allreduce(this%dist%mpi_grp, stress_Ewald)
1487
1488 stress_ewald = stress_ewald / latt%rcell_volume
1489
1490 pop_sub(ewald_2d_stress)
1491 end subroutine ewald_2d_stress
1492
1493 ! ---------------------------------------------------------
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
1500
1501 real(real64) :: arg
1502
1503 arg = -alpha*z_ij + m_half*gg_abs/alpha
1504 erfc_arg = erfc(arg)
1505 factor = m_two*alpha*(m_one/gg_abs + z_ij)*erfc_arg - m_two/sqrt(m_pi)*exp(-arg**2)
1506 factor = factor*exp(-gg_abs*z_ij)
1507
1508 end function screening_function_2d
1509
1510 ! ---------------------------------------------------------
1511
1512 subroutine ion_interaction_test(space, latt, atom, natoms, pos, lsize, &
1513 namespace, mc)
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
1522
1523 type(ion_interaction_t) :: ion_interaction
1524 real(real64) :: energy
1525 real(real64), allocatable :: force(:, :), force_components(:, :, :)
1526 real(real64) :: energy_components(1:ION_NUM_COMPONENTS)
1527 integer :: iatom, idir
1528
1529 push_sub(ion_interaction_test)
1530
1531 call ion_interaction_init(ion_interaction, namespace, space, natoms)
1532 call ion_interaction_init_parallelization(ion_interaction, natoms, mc)
1533
1534 safe_allocate(force(1:space%dim, 1:natoms))
1535 safe_allocate(force_components(1:space%dim, 1:natoms, 1:ion_num_components))
1536
1537 call ion_interaction_calculate(ion_interaction, space, latt, atom, natoms, pos, lsize, energy, force, &
1538 energy_components = energy_components, force_components = force_components)
1539
1540 call messages_write('Ionic energy =')
1541 call messages_write(energy, fmt = '(f20.10)')
1542 call messages_info(namespace=namespace)
1543
1544 call messages_write('Real space energy =')
1545 call messages_write(energy_components(ion_component_real), fmt = '(f20.10)')
1546 call messages_info(namespace=namespace)
1547
1548 call messages_write('Self energy =')
1549 call messages_write(energy_components(ion_component_self), fmt = '(f20.10)')
1550 call messages_info(namespace=namespace)
1551
1552 call messages_write('Fourier energy =')
1553 call messages_write(energy_components(ion_component_fourier), fmt = '(f20.10)')
1554 call messages_info(namespace=namespace)
1555
1556 call messages_info(namespace=namespace)
1557
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)')
1564 end do
1565 call messages_info(namespace=namespace)
1566
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)')
1572 end do
1573 call messages_info(namespace=namespace)
1574
1575 call messages_write('Fourier space force atom')
1576 call messages_write(iatom)
1577 call messages_write(' =')
1578 do idir = 1, space%dim
1579 call messages_write(force_components(idir, iatom, ion_component_fourier), fmt = '(f20.10)')
1580 end do
1581 call messages_info(namespace=namespace)
1582
1583 call messages_info(namespace=namespace)
1584 end do
1585
1586 safe_deallocate_a(force)
1587 safe_deallocate_a(force_components)
1588
1589 call ion_interaction_end(ion_interaction)
1591 pop_sub(ion_interaction_test)
1592 end subroutine ion_interaction_test
1593
1594end module ion_interaction_oct_m
1595
1596!! Local Variables:
1597!! mode: f90
1598!! coding: utf-8
1599!! End:
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.
Definition: atom.F90:294
pure logical function, public any_species_is_jellium_sphere(atom)
Check if any species is a jellium sphere.
Definition: atom.F90:311
type(debug_t), save, public debug
Definition: debug.F90:156
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
Definition: global.F90:202
real(real64), parameter, public m_max_exp_arg
Definition: global.F90:220
real(real64), parameter, public m_zero
Definition: global.F90:200
real(real64), parameter, public m_four
Definition: global.F90:204
real(real64), parameter, public m_pi
some mathematical constants
Definition: global.F90:198
real(real64), parameter, public m_fourth
Definition: global.F90:209
complex(real64), parameter, public m_z0
Definition: global.F90:210
complex(real64), parameter, public m_zi
Definition: global.F90:214
real(real64), parameter, public r_min_atom_dist
Minimal distance between two distinguishable atoms.
Definition: global.F90:195
real(real64), parameter, public m_epsilon
Definition: global.F90:216
real(real64), parameter, public m_half
Definition: global.F90:206
real(real64), parameter, public m_one
Definition: global.F90:201
real(real64), parameter, public m_three
Definition: global.F90:203
real(real64), parameter, public m_five
Definition: global.F90:205
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)
Definition: kpoints.F90:1147
subroutine, public messages_not_implemented(feature, namespace)
Definition: messages.F90:1068
subroutine, public messages_experimental(name, namespace)
Definition: messages.F90:1040
This module handles the communicators for the various parallelization strategies.
Definition: multicomm.F90:147
subroutine, public profiling_out(label)
Increment out counter and sum up difference between entry and exit time.
Definition: profiling.F90:668
subroutine, public profiling_in(label, exclude)
Increment in counter and save entry time.
Definition: profiling.F90:600
Definition: ps.F90:116
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...
Definition: species.F90:147