Compute the dispersion repulsion energy for the whole system using a double loop algorithm
All neighbors done!
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
type(ommp_nonbonded_type), | intent(in), | target | :: | vdw |
Nonbonded data structure |
|
real(kind=rp), | intent(inout) | :: | V |
Potential, result will be added |
subroutine vdw_potential(vdw, V)
!! Compute the dispersion repulsion energy for the whole system
!! using a double loop algorithm
use mod_io, only : fatal_error
use mod_profiling, only: time_push, time_pull
use mod_constants, only: eps_rp
use mod_memory, only: mallocate, mfree
use mod_neighbor_list, only: get_ith_nl
implicit none
type(ommp_nonbonded_type), intent(in), target :: vdw
!! Nonbonded data structure
real(rp), intent(inout) :: V
!! Potential, result will be added
integer(ip) :: i, j, jc, l, ipair, ineigh, nthreads, ithread, nn
real(rp) :: eij, rij0, rij, ci(3), cj(3), s, vtmp
type(ommp_topology_type), pointer :: top
procedure(vdw_term), pointer :: vdw_func
integer(ip), allocatable :: nl_neigh(:,:)
real(rp), allocatable :: nl_r(:,:)
integer :: omp_get_num_threads, omp_get_thread_num
call time_push()
!$omp parallel
nthreads = omp_get_num_threads()
!$omp end parallel
top => vdw%top
select case(vdw%vdwtype)
case(OMMP_VDWTYPE_LJ)
vdw_func => vdw_lennard_jones
case(OMMP_VDWTYPE_BUF714)
vdw_func => vdw_buffered_7_14
case default
vdw_func => vdw_buffered_7_14
call fatal_error("Unexpected error in vdw_potential")
end select
if(vdw%use_nl) then
call mallocate('vdw_potential [rneigh]', top%mm_atoms, nthreads, nl_r)
call mallocate('vdw_potential [nl_neigh]', top%mm_atoms, nthreads, nl_neigh)
end if
!$omp parallel do default(shared) reduction(+:v) schedule(dynamic) &
!$omp private(i,j,jc,ineigh,ithread,nn,s,ci,cj,ipair,l,Eij,Rij0,Rij,vtmp)
do i=1, top%mm_atoms
ithread = omp_get_thread_num() + 1
if(abs(vdw%vdw_f(i) - 1.0_rp) < eps_rp) then
ci = top%cmm(:,i)
else
! Scale factors are used only for monovalent atoms, in that
! case the vdw center is displaced along the axis connecting
! the atom to its neighbour
if(top%conn(1)%ri(i+1) - top%conn(1)%ri(i) /= 1) then
call fatal_error("Scale factors are only expected for &
&monovalent atoms")
end if
ineigh = top%conn(1)%ci(top%conn(1)%ri(i))
ci = top%cmm(:,ineigh) + (top%cmm(:,i) - top%cmm(:,ineigh)) &
* vdw%vdw_f(i)
endif
! If neighbor list are enabled get the one for the current
if(vdw%use_nl) call get_ith_nl(vdw%nl, i, top%cmm, &
nl_neigh(:,ithread), &
nl_r(:,ithread), nn)
do jc=1, top%mm_atoms
! If the two atoms aren't neighbors, just skip the loop
if(vdw%use_nl) then
if(jc > nn) exit !! All neighbors done!
j = nl_neigh(jc,ithread)
if(j <= i) cycle
else
! Skip all iteration with j <= i
if(jc > i) then
j = jc
else
cycle
end if
end if
! Compute the screening factor for this pair
s = 1.0_rp
do ineigh=1,4
! Look if j is at distance ineigh from i
if(any(top%conn(ineigh)%ci(top%conn(ineigh)%ri(i): &
top%conn(ineigh)%ri(i+1)-1) == j)) then
s = vdw%vdw_screening(ineigh)
! Exit the loop
exit
end if
end do
if(s > eps_rp) then
ipair = -1
do l=1, vdw%npair
if((vdw%vdw_pair_mask_a(i,l) .and. vdw%vdw_pair_mask_b(j,l)) .or. &
(vdw%vdw_pair_mask_a(j,l) .and. vdw%vdw_pair_mask_b(i,l))) then
ipair = l
exit
end if
end do
if(ipair > 0) then
Rij0 = vdw%vdw_pair_r(ipair)
eij = vdw%vdw_pair_e(ipair)
else
Rij0 = get_Rij0(vdw, i, j)
eij = get_eij(vdw, i, j)
end if
if(abs(vdw%vdw_f(j) - 1.0_rp) < eps_rp) then
cj = top%cmm(:,j)
else
! Scale factors are used only for monovalent atoms, in that
! case the vdw center is displaced along the axis connecting
! the atom to its neighbour
if(top%conn(1)%ri(j+1) - top%conn(1)%ri(j) /= 1) then
call fatal_error("Scale factors are only expected &
& for monovalent atoms")
end if
ineigh = top%conn(1)%ci(top%conn(1)%ri(j))
cj = top%cmm(:,ineigh) + &
(top%cmm(:,j) - top%cmm(:,ineigh)) * vdw%vdw_f(j)
endif
Rij = norm2(ci-cj)
if(Rij < eps_rp) then
call fatal_error("Requesting non-bonded potential for two atoms &
&placed in the same point, this could be &
&an internal bug or a problem in your input &
&file, please check.")
end if
vtmp = 0.0_rp
call vdw_func(Rij, Rij0, Eij, vtmp)
v = v + vtmp*s
end if
end do
end do
if(vdw%use_nl) then
call mfree('vdw_potential [rneigh]', nl_r)
call mfree('vdw_potential [nl_neigh]', nl_neigh)
end if
call time_pull('VdW potential calculation')
end subroutine vdw_potential