ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-3.0/src/UseTheForce/doForces.F90
Revision: 2266
Committed: Thu Jul 28 22:12:45 2005 UTC (18 years, 11 months ago) by chuckv
File size: 42028 byte(s)
Log Message:
Changed cutoffs... Segfaults nicely now...

File Contents

# Content
1 !!
2 !! Copyright (c) 2005 The University of Notre Dame. All Rights Reserved.
3 !!
4 !! The University of Notre Dame grants you ("Licensee") a
5 !! non-exclusive, royalty free, license to use, modify and
6 !! redistribute this software in source and binary code form, provided
7 !! that the following conditions are met:
8 !!
9 !! 1. Acknowledgement of the program authors must be made in any
10 !! publication of scientific results based in part on use of the
11 !! program. An acceptable form of acknowledgement is citation of
12 !! the article in which the program was described (Matthew
13 !! A. Meineke, Charles F. Vardeman II, Teng Lin, Christopher
14 !! J. Fennell and J. Daniel Gezelter, "OOPSE: An Object-Oriented
15 !! Parallel Simulation Engine for Molecular Dynamics,"
16 !! J. Comput. Chem. 26, pp. 252-271 (2005))
17 !!
18 !! 2. Redistributions of source code must retain the above copyright
19 !! notice, this list of conditions and the following disclaimer.
20 !!
21 !! 3. Redistributions in binary form must reproduce the above copyright
22 !! notice, this list of conditions and the following disclaimer in the
23 !! documentation and/or other materials provided with the
24 !! distribution.
25 !!
26 !! This software is provided "AS IS," without a warranty of any
27 !! kind. All express or implied conditions, representations and
28 !! warranties, including any implied warranty of merchantability,
29 !! fitness for a particular purpose or non-infringement, are hereby
30 !! excluded. The University of Notre Dame and its licensors shall not
31 !! be liable for any damages suffered by licensee as a result of
32 !! using, modifying or distributing the software or its
33 !! derivatives. In no event will the University of Notre Dame or its
34 !! licensors be liable for any lost revenue, profit or data, or for
35 !! direct, indirect, special, consequential, incidental or punitive
36 !! damages, however caused and regardless of the theory of liability,
37 !! arising out of the use of or inability to use software, even if the
38 !! University of Notre Dame has been advised of the possibility of
39 !! such damages.
40 !!
41
42 !! doForces.F90
43 !! module doForces
44 !! Calculates Long Range forces.
45
46 !! @author Charles F. Vardeman II
47 !! @author Matthew Meineke
48 !! @version $Id: doForces.F90,v 1.24 2005-07-28 22:12:45 chuckv Exp $, $Date: 2005-07-28 22:12:45 $, $Name: not supported by cvs2svn $, $Revision: 1.24 $
49
50
51 module doForces
52 use force_globals
53 use simulation
54 use definitions
55 use atype_module
56 use switcheroo
57 use neighborLists
58 use lj
59 use sticky
60 use electrostatic_module
61 use reaction_field
62 use gb_pair
63 use shapes
64 use vector_class
65 use eam
66 use status
67 #ifdef IS_MPI
68 use mpiSimulation
69 #endif
70
71 implicit none
72 PRIVATE
73
74 #define __FORTRAN90
75 #include "UseTheForce/fSwitchingFunction.h"
76 #include "UseTheForce/DarkSide/fInteractionMap.h"
77
78 INTEGER, PARAMETER:: PREPAIR_LOOP = 1
79 INTEGER, PARAMETER:: PAIR_LOOP = 2
80
81 logical, save :: haveRlist = .false.
82 logical, save :: haveNeighborList = .false.
83 logical, save :: haveSIMvariables = .false.
84 logical, save :: haveSaneForceField = .false.
85 logical, save :: haveInteractionMap = .false.
86
87 logical, save :: FF_uses_DirectionalAtoms
88 logical, save :: FF_uses_LennardJones
89 logical, save :: FF_uses_Electrostatics
90 logical, save :: FF_uses_Charges
91 logical, save :: FF_uses_Dipoles
92 logical, save :: FF_uses_Quadrupoles
93 logical, save :: FF_uses_Sticky
94 logical, save :: FF_uses_StickyPower
95 logical, save :: FF_uses_GayBerne
96 logical, save :: FF_uses_EAM
97 logical, save :: FF_uses_Shapes
98 logical, save :: FF_uses_FLARB
99 logical, save :: FF_uses_RF
100
101 logical, save :: SIM_uses_DirectionalAtoms
102 logical, save :: SIM_uses_LennardJones
103 logical, save :: SIM_uses_Electrostatics
104 logical, save :: SIM_uses_Charges
105 logical, save :: SIM_uses_Dipoles
106 logical, save :: SIM_uses_Quadrupoles
107 logical, save :: SIM_uses_Sticky
108 logical, save :: SIM_uses_StickyPower
109 logical, save :: SIM_uses_GayBerne
110 logical, save :: SIM_uses_EAM
111 logical, save :: SIM_uses_Shapes
112 logical, save :: SIM_uses_FLARB
113 logical, save :: SIM_uses_RF
114 logical, save :: SIM_requires_postpair_calc
115 logical, save :: SIM_requires_prepair_calc
116 logical, save :: SIM_uses_PBC
117 logical, save :: SIM_uses_molecular_cutoffs
118
119 !!!GO AWAY---------
120 !!!!!real(kind=dp), save :: rlist, rlistsq
121
122 public :: init_FF
123 public :: do_force_loop
124 ! public :: setRlistDF
125 !public :: addInteraction
126 !public :: setInteractionHash
127 !public :: getInteractionHash
128 public :: createInteractionMap
129 public :: createRcuts
130
131 #ifdef PROFILE
132 public :: getforcetime
133 real, save :: forceTime = 0
134 real :: forceTimeInitial, forceTimeFinal
135 integer :: nLoops
136 #endif
137
138 type, public :: Interaction
139 integer :: InteractionHash
140 real(kind=dp) :: rList = 0.0_dp
141 real(kind=dp) :: rListSq = 0.0_dp
142 end type Interaction
143
144 type(Interaction), dimension(:,:),allocatable :: InteractionMap
145
146
147
148 contains
149
150
151 subroutine createInteractionMap(status)
152 integer :: nAtypes
153 integer, intent(out) :: status
154 integer :: i
155 integer :: j
156 integer :: ihash
157 real(kind=dp) :: myRcut
158 ! Test Types
159 logical :: i_is_LJ
160 logical :: i_is_Elect
161 logical :: i_is_Sticky
162 logical :: i_is_StickyP
163 logical :: i_is_GB
164 logical :: i_is_EAM
165 logical :: i_is_Shape
166 logical :: j_is_LJ
167 logical :: j_is_Elect
168 logical :: j_is_Sticky
169 logical :: j_is_StickyP
170 logical :: j_is_GB
171 logical :: j_is_EAM
172 logical :: j_is_Shape
173
174 status = 0
175
176 if (.not. associated(atypes)) then
177 call handleError("atype", "atypes was not present before call of createDefaultInteractionMap!")
178 status = -1
179 return
180 endif
181
182 nAtypes = getSize(atypes)
183
184 if (nAtypes == 0) then
185 status = -1
186 return
187 end if
188
189 if (.not. allocated(InteractionMap)) then
190 allocate(InteractionMap(nAtypes,nAtypes))
191 endif
192
193 do i = 1, nAtypes
194 call getElementProperty(atypes, i, "is_LennardJones", i_is_LJ)
195 call getElementProperty(atypes, i, "is_Electrostatic", i_is_Elect)
196 call getElementProperty(atypes, i, "is_Sticky", i_is_Sticky)
197 call getElementProperty(atypes, i, "is_StickyPower", i_is_StickyP)
198 call getElementProperty(atypes, i, "is_GayBerne", i_is_GB)
199 call getElementProperty(atypes, i, "is_EAM", i_is_EAM)
200 call getElementProperty(atypes, i, "is_Shape", i_is_Shape)
201
202 do j = i, nAtypes
203
204 iHash = 0
205 myRcut = 0.0_dp
206
207 call getElementProperty(atypes, j, "is_LennardJones", j_is_LJ)
208 call getElementProperty(atypes, j, "is_Electrostatic", j_is_Elect)
209 call getElementProperty(atypes, j, "is_Sticky", j_is_Sticky)
210 call getElementProperty(atypes, j, "is_StickyPower", j_is_StickyP)
211 call getElementProperty(atypes, j, "is_GayBerne", j_is_GB)
212 call getElementProperty(atypes, j, "is_EAM", j_is_EAM)
213 call getElementProperty(atypes, j, "is_Shape", j_is_Shape)
214
215 if (i_is_LJ .and. j_is_LJ) then
216 iHash = ior(iHash, LJ_PAIR)
217 endif
218
219 if (i_is_Elect .and. j_is_Elect) then
220 iHash = ior(iHash, ELECTROSTATIC_PAIR)
221 endif
222
223 if (i_is_Sticky .and. j_is_Sticky) then
224 iHash = ior(iHash, STICKY_PAIR)
225 endif
226
227 if (i_is_StickyP .and. j_is_StickyP) then
228 iHash = ior(iHash, STICKYPOWER_PAIR)
229 endif
230
231 if (i_is_EAM .and. j_is_EAM) then
232 iHash = ior(iHash, EAM_PAIR)
233 endif
234
235 if (i_is_GB .and. j_is_GB) iHash = ior(iHash, GAYBERNE_PAIR)
236 if (i_is_GB .and. j_is_LJ) iHash = ior(iHash, GAYBERNE_LJ)
237 if (i_is_LJ .and. j_is_GB) iHash = ior(iHash, GAYBERNE_LJ)
238
239 if (i_is_Shape .and. j_is_Shape) iHash = ior(iHash, SHAPE_PAIR)
240 if (i_is_Shape .and. j_is_LJ) iHash = ior(iHash, SHAPE_LJ)
241 if (i_is_LJ .and. j_is_Shape) iHash = ior(iHash, SHAPE_LJ)
242
243
244 InteractionMap(i,j)%InteractionHash = iHash
245 InteractionMap(j,i)%InteractionHash = iHash
246
247 end do
248
249 end do
250 end subroutine createInteractionMap
251
252 ! Query each potential and return the cutoff for that potential. We build the neighbor list based on the largest cutoff value for that atype. Each potential can decide whether to calculate the force for that atype based upon it's own cutoff.
253 subroutine createRcuts(defaultRList,stat)
254 real(kind=dp), intent(in), optional :: defaultRList
255 integer :: iMap
256 integer :: map_i,map_j
257 real(kind=dp) :: thisRCut = 0.0_dp
258 real(kind=dp) :: actualCutoff = 0.0_dp
259 integer, intent(out) :: stat
260 integer :: nAtypes
261 integer :: myStatus
262
263 stat = 0
264 if (.not. haveInteractionMap) then
265
266 call createInteractionMap(myStatus)
267
268 if (myStatus .ne. 0) then
269 write(default_error, *) 'createInteractionMap failed in doForces!'
270 stat = -1
271 return
272 endif
273 endif
274
275
276 nAtypes = getSize(atypes)
277 ! If we pass a default rcut, set all atypes to that cutoff distance
278 if(present(defaultRList)) then
279 InteractionMap(:,:)%rList = defaultRList
280 InteractionMap(:,:)%rListSq = defaultRList*defaultRList
281 haveRlist = .true.
282 return
283 end if
284
285 do map_i = 1,nAtypes
286 do map_j = map_i,nAtypes
287 iMap = InteractionMap(map_i, map_j)%InteractionHash
288
289 if ( iand(iMap, LJ_PAIR).ne.0 ) then
290 ! thisRCut = getLJCutOff(map_i,map_j)
291 if (thisRcut > actualCutoff) actualCutoff = thisRcut
292 endif
293
294 if ( iand(iMap, ELECTROSTATIC_PAIR).ne.0 ) then
295 ! thisRCut = getElectrostaticCutOff(map_i,map_j)
296 if (thisRcut > actualCutoff) actualCutoff = thisRcut
297 endif
298
299 if ( iand(iMap, STICKY_PAIR).ne.0 ) then
300 ! thisRCut = getStickyCutOff(map_i,map_j)
301 if (thisRcut > actualCutoff) actualCutoff = thisRcut
302 endif
303
304 if ( iand(iMap, STICKYPOWER_PAIR).ne.0 ) then
305 ! thisRCut = getStickyPowerCutOff(map_i,map_j)
306 if (thisRcut > actualCutoff) actualCutoff = thisRcut
307 endif
308
309 if ( iand(iMap, GAYBERNE_PAIR).ne.0 ) then
310 ! thisRCut = getGayberneCutOff(map_i,map_j)
311 if (thisRcut > actualCutoff) actualCutoff = thisRcut
312 endif
313
314 if ( iand(iMap, GAYBERNE_LJ).ne.0 ) then
315 ! thisRCut = getGaybrneLJCutOff(map_i,map_j)
316 if (thisRcut > actualCutoff) actualCutoff = thisRcut
317 endif
318
319 if ( iand(iMap, EAM_PAIR).ne.0 ) then
320 ! thisRCut = getEAMCutOff(map_i,map_j)
321 if (thisRcut > actualCutoff) actualCutoff = thisRcut
322 endif
323
324 if ( iand(iMap, SHAPE_PAIR).ne.0 ) then
325 ! thisRCut = getShapeCutOff(map_i,map_j)
326 if (thisRcut > actualCutoff) actualCutoff = thisRcut
327 endif
328
329 if ( iand(iMap, SHAPE_LJ).ne.0 ) then
330 ! thisRCut = getShapeLJCutOff(map_i,map_j)
331 if (thisRcut > actualCutoff) actualCutoff = thisRcut
332 endif
333 InteractionMap(map_i, map_j)%rList = actualCutoff
334 InteractionMap(map_i, map_j)%rListSq = actualCutoff * actualCutoff
335 end do
336 end do
337 haveRlist = .true.
338 end subroutine createRcuts
339
340
341 !!! THIS GOES AWAY FOR SIZE DEPENDENT CUTOFF
342 !!$ subroutine setRlistDF( this_rlist )
343 !!$
344 !!$ real(kind=dp) :: this_rlist
345 !!$
346 !!$ rlist = this_rlist
347 !!$ rlistsq = rlist * rlist
348 !!$
349 !!$ haveRlist = .true.
350 !!$
351 !!$ end subroutine setRlistDF
352
353
354 subroutine setSimVariables()
355 SIM_uses_DirectionalAtoms = SimUsesDirectionalAtoms()
356 SIM_uses_LennardJones = SimUsesLennardJones()
357 SIM_uses_Electrostatics = SimUsesElectrostatics()
358 SIM_uses_Charges = SimUsesCharges()
359 SIM_uses_Dipoles = SimUsesDipoles()
360 SIM_uses_Sticky = SimUsesSticky()
361 SIM_uses_StickyPower = SimUsesStickyPower()
362 SIM_uses_GayBerne = SimUsesGayBerne()
363 SIM_uses_EAM = SimUsesEAM()
364 SIM_uses_Shapes = SimUsesShapes()
365 SIM_uses_FLARB = SimUsesFLARB()
366 SIM_uses_RF = SimUsesRF()
367 SIM_requires_postpair_calc = SimRequiresPostpairCalc()
368 SIM_requires_prepair_calc = SimRequiresPrepairCalc()
369 SIM_uses_PBC = SimUsesPBC()
370
371 haveSIMvariables = .true.
372
373 return
374 end subroutine setSimVariables
375
376 subroutine doReadyCheck(error)
377 integer, intent(out) :: error
378
379 integer :: myStatus
380
381 error = 0
382
383 if (.not. haveInteractionMap) then
384
385 myStatus = 0
386
387 call createInteractionMap(myStatus)
388
389 if (myStatus .ne. 0) then
390 write(default_error, *) 'createInteractionMap failed in doForces!'
391 error = -1
392 return
393 endif
394 endif
395
396 if (.not. haveSIMvariables) then
397 call setSimVariables()
398 endif
399
400 if (.not. haveRlist) then
401 write(default_error, *) 'rList has not been set in doForces!'
402 error = -1
403 return
404 endif
405
406 if (.not. haveNeighborList) then
407 write(default_error, *) 'neighbor list has not been initialized in doForces!'
408 error = -1
409 return
410 end if
411
412 if (.not. haveSaneForceField) then
413 write(default_error, *) 'Force Field is not sane in doForces!'
414 error = -1
415 return
416 end if
417
418 #ifdef IS_MPI
419 if (.not. isMPISimSet()) then
420 write(default_error,*) "ERROR: mpiSimulation has not been initialized!"
421 error = -1
422 return
423 endif
424 #endif
425 return
426 end subroutine doReadyCheck
427
428
429 subroutine init_FF(use_RF_c, thisStat)
430
431 logical, intent(in) :: use_RF_c
432
433 integer, intent(out) :: thisStat
434 integer :: my_status, nMatches
435 integer, pointer :: MatchList(:) => null()
436 real(kind=dp) :: rcut, rrf, rt, dielect
437
438 !! assume things are copacetic, unless they aren't
439 thisStat = 0
440
441 !! Fortran's version of a cast:
442 FF_uses_RF = use_RF_c
443
444 !! init_FF is called *after* all of the atom types have been
445 !! defined in atype_module using the new_atype subroutine.
446 !!
447 !! this will scan through the known atypes and figure out what
448 !! interactions are used by the force field.
449
450 FF_uses_DirectionalAtoms = .false.
451 FF_uses_LennardJones = .false.
452 FF_uses_Electrostatics = .false.
453 FF_uses_Charges = .false.
454 FF_uses_Dipoles = .false.
455 FF_uses_Sticky = .false.
456 FF_uses_StickyPower = .false.
457 FF_uses_GayBerne = .false.
458 FF_uses_EAM = .false.
459 FF_uses_Shapes = .false.
460 FF_uses_FLARB = .false.
461
462 call getMatchingElementList(atypes, "is_Directional", .true., &
463 nMatches, MatchList)
464 if (nMatches .gt. 0) FF_uses_DirectionalAtoms = .true.
465
466 call getMatchingElementList(atypes, "is_LennardJones", .true., &
467 nMatches, MatchList)
468 if (nMatches .gt. 0) FF_uses_LennardJones = .true.
469
470 call getMatchingElementList(atypes, "is_Electrostatic", .true., &
471 nMatches, MatchList)
472 if (nMatches .gt. 0) then
473 FF_uses_Electrostatics = .true.
474 endif
475
476 call getMatchingElementList(atypes, "is_Charge", .true., &
477 nMatches, MatchList)
478 if (nMatches .gt. 0) then
479 FF_uses_Charges = .true.
480 FF_uses_Electrostatics = .true.
481 endif
482
483 call getMatchingElementList(atypes, "is_Dipole", .true., &
484 nMatches, MatchList)
485 if (nMatches .gt. 0) then
486 FF_uses_Dipoles = .true.
487 FF_uses_Electrostatics = .true.
488 FF_uses_DirectionalAtoms = .true.
489 endif
490
491 call getMatchingElementList(atypes, "is_Quadrupole", .true., &
492 nMatches, MatchList)
493 if (nMatches .gt. 0) then
494 FF_uses_Quadrupoles = .true.
495 FF_uses_Electrostatics = .true.
496 FF_uses_DirectionalAtoms = .true.
497 endif
498
499 call getMatchingElementList(atypes, "is_Sticky", .true., nMatches, &
500 MatchList)
501 if (nMatches .gt. 0) then
502 FF_uses_Sticky = .true.
503 FF_uses_DirectionalAtoms = .true.
504 endif
505
506 call getMatchingElementList(atypes, "is_StickyPower", .true., nMatches, &
507 MatchList)
508 if (nMatches .gt. 0) then
509 FF_uses_StickyPower = .true.
510 FF_uses_DirectionalAtoms = .true.
511 endif
512
513 call getMatchingElementList(atypes, "is_GayBerne", .true., &
514 nMatches, MatchList)
515 if (nMatches .gt. 0) then
516 FF_uses_GayBerne = .true.
517 FF_uses_DirectionalAtoms = .true.
518 endif
519
520 call getMatchingElementList(atypes, "is_EAM", .true., nMatches, MatchList)
521 if (nMatches .gt. 0) FF_uses_EAM = .true.
522
523 call getMatchingElementList(atypes, "is_Shape", .true., &
524 nMatches, MatchList)
525 if (nMatches .gt. 0) then
526 FF_uses_Shapes = .true.
527 FF_uses_DirectionalAtoms = .true.
528 endif
529
530 call getMatchingElementList(atypes, "is_FLARB", .true., &
531 nMatches, MatchList)
532 if (nMatches .gt. 0) FF_uses_FLARB = .true.
533
534 !! Assume sanity (for the sake of argument)
535 haveSaneForceField = .true.
536
537 !! check to make sure the FF_uses_RF setting makes sense
538
539 if (FF_uses_dipoles) then
540 if (FF_uses_RF) then
541 dielect = getDielect()
542 call initialize_rf(dielect)
543 endif
544 else
545 if (FF_uses_RF) then
546 write(default_error,*) 'Using Reaction Field with no dipoles? Huh?'
547 thisStat = -1
548 haveSaneForceField = .false.
549 return
550 endif
551 endif
552
553 !sticky module does not contain check_sticky_FF anymore
554 !if (FF_uses_sticky) then
555 ! call check_sticky_FF(my_status)
556 ! if (my_status /= 0) then
557 ! thisStat = -1
558 ! haveSaneForceField = .false.
559 ! return
560 ! end if
561 !endif
562
563 if (FF_uses_EAM) then
564 call init_EAM_FF(my_status)
565 if (my_status /= 0) then
566 write(default_error, *) "init_EAM_FF returned a bad status"
567 thisStat = -1
568 haveSaneForceField = .false.
569 return
570 end if
571 endif
572
573 if (FF_uses_GayBerne) then
574 call check_gb_pair_FF(my_status)
575 if (my_status .ne. 0) then
576 thisStat = -1
577 haveSaneForceField = .false.
578 return
579 endif
580 endif
581
582 if (FF_uses_GayBerne .and. FF_uses_LennardJones) then
583 endif
584
585 if (.not. haveNeighborList) then
586 !! Create neighbor lists
587 call expandNeighborList(nLocal, my_status)
588 if (my_Status /= 0) then
589 write(default_error,*) "SimSetup: ExpandNeighborList returned error."
590 thisStat = -1
591 return
592 endif
593 haveNeighborList = .true.
594 endif
595
596 end subroutine init_FF
597
598
599 !! Does force loop over i,j pairs. Calls do_pair to calculates forces.
600 !------------------------------------------------------------->
601 subroutine do_force_loop(q, q_group, A, eFrame, f, t, tau, pot, &
602 do_pot_c, do_stress_c, error)
603 !! Position array provided by C, dimensioned by getNlocal
604 real ( kind = dp ), dimension(3, nLocal) :: q
605 !! molecular center-of-mass position array
606 real ( kind = dp ), dimension(3, nGroups) :: q_group
607 !! Rotation Matrix for each long range particle in simulation.
608 real( kind = dp), dimension(9, nLocal) :: A
609 !! Unit vectors for dipoles (lab frame)
610 real( kind = dp ), dimension(9,nLocal) :: eFrame
611 !! Force array provided by C, dimensioned by getNlocal
612 real ( kind = dp ), dimension(3,nLocal) :: f
613 !! Torsion array provided by C, dimensioned by getNlocal
614 real( kind = dp ), dimension(3,nLocal) :: t
615
616 !! Stress Tensor
617 real( kind = dp), dimension(9) :: tau
618 real ( kind = dp ) :: pot
619 logical ( kind = 2) :: do_pot_c, do_stress_c
620 logical :: do_pot
621 logical :: do_stress
622 logical :: in_switching_region
623 #ifdef IS_MPI
624 real( kind = DP ) :: pot_local
625 integer :: nAtomsInRow
626 integer :: nAtomsInCol
627 integer :: nprocs
628 integer :: nGroupsInRow
629 integer :: nGroupsInCol
630 #endif
631 integer :: natoms
632 logical :: update_nlist
633 integer :: i, j, jstart, jend, jnab
634 integer :: istart, iend
635 integer :: ia, jb, atom1, atom2
636 integer :: nlist
637 real( kind = DP ) :: ratmsq, rgrpsq, rgrp, vpair, vij
638 real( kind = DP ) :: sw, dswdr, swderiv, mf
639 real(kind=dp),dimension(3) :: d_atm, d_grp, fpair, fij
640 real(kind=dp) :: rfpot, mu_i, virial
641 integer :: me_i, me_j, n_in_i, n_in_j
642 logical :: is_dp_i
643 integer :: neighborListSize
644 integer :: listerror, error
645 integer :: localError
646 integer :: propPack_i, propPack_j
647 integer :: loopStart, loopEnd, loop
648 integer :: iMap
649 real(kind=dp) :: listSkin = 1.0
650
651 !! initialize local variables
652
653 #ifdef IS_MPI
654 pot_local = 0.0_dp
655 nAtomsInRow = getNatomsInRow(plan_atom_row)
656 nAtomsInCol = getNatomsInCol(plan_atom_col)
657 nGroupsInRow = getNgroupsInRow(plan_group_row)
658 nGroupsInCol = getNgroupsInCol(plan_group_col)
659 #else
660 natoms = nlocal
661 #endif
662
663 call doReadyCheck(localError)
664 if ( localError .ne. 0 ) then
665 call handleError("do_force_loop", "Not Initialized")
666 error = -1
667 return
668 end if
669 call zero_work_arrays()
670
671 do_pot = do_pot_c
672 do_stress = do_stress_c
673
674 ! Gather all information needed by all force loops:
675
676 #ifdef IS_MPI
677
678 call gather(q, q_Row, plan_atom_row_3d)
679 call gather(q, q_Col, plan_atom_col_3d)
680
681 call gather(q_group, q_group_Row, plan_group_row_3d)
682 call gather(q_group, q_group_Col, plan_group_col_3d)
683
684 if (FF_UsesDirectionalAtoms() .and. SIM_uses_DirectionalAtoms) then
685 call gather(eFrame, eFrame_Row, plan_atom_row_rotation)
686 call gather(eFrame, eFrame_Col, plan_atom_col_rotation)
687
688 call gather(A, A_Row, plan_atom_row_rotation)
689 call gather(A, A_Col, plan_atom_col_rotation)
690 endif
691
692 #endif
693
694 !! Begin force loop timing:
695 #ifdef PROFILE
696 call cpu_time(forceTimeInitial)
697 nloops = nloops + 1
698 #endif
699
700 loopEnd = PAIR_LOOP
701 if (FF_RequiresPrepairCalc() .and. SIM_requires_prepair_calc) then
702 loopStart = PREPAIR_LOOP
703 else
704 loopStart = PAIR_LOOP
705 endif
706
707 do loop = loopStart, loopEnd
708
709 ! See if we need to update neighbor lists
710 ! (but only on the first time through):
711 if (loop .eq. loopStart) then
712 #ifdef IS_MPI
713 call checkNeighborList(nGroupsInRow, q_group_row, listSkin, &
714 update_nlist)
715 #else
716 call checkNeighborList(nGroups, q_group, listSkin, &
717 update_nlist)
718 #endif
719 endif
720
721 if (update_nlist) then
722 !! save current configuration and construct neighbor list
723 #ifdef IS_MPI
724 call saveNeighborList(nGroupsInRow, q_group_row)
725 #else
726 call saveNeighborList(nGroups, q_group)
727 #endif
728 neighborListSize = size(list)
729 nlist = 0
730 endif
731
732 istart = 1
733 #ifdef IS_MPI
734 iend = nGroupsInRow
735 #else
736 iend = nGroups - 1
737 #endif
738 outer: do i = istart, iend
739
740 if (update_nlist) point(i) = nlist + 1
741
742 n_in_i = groupStartRow(i+1) - groupStartRow(i)
743
744 if (update_nlist) then
745 #ifdef IS_MPI
746 me_i = atid_row(i)
747 jstart = 1
748 jend = nGroupsInCol
749 #else
750 me_i = atid(i)
751 jstart = i+1
752 jend = nGroups
753 #endif
754 else
755 jstart = point(i)
756 jend = point(i+1) - 1
757 ! make sure group i has neighbors
758 if (jstart .gt. jend) cycle outer
759 endif
760
761 do jnab = jstart, jend
762 if (update_nlist) then
763 j = jnab
764 else
765 j = list(jnab)
766 endif
767
768 #ifdef IS_MPI
769 me_j = atid_col(j)
770 call get_interatomic_vector(q_group_Row(:,i), &
771 q_group_Col(:,j), d_grp, rgrpsq)
772 #else
773 me_j = atid(j)
774 call get_interatomic_vector(q_group(:,i), &
775 q_group(:,j), d_grp, rgrpsq)
776 #endif
777
778 if (rgrpsq < InteractionMap(me_i,me_j)%rListsq) then
779 if (update_nlist) then
780 nlist = nlist + 1
781
782 if (nlist > neighborListSize) then
783 #ifdef IS_MPI
784 call expandNeighborList(nGroupsInRow, listerror)
785 #else
786 call expandNeighborList(nGroups, listerror)
787 #endif
788 if (listerror /= 0) then
789 error = -1
790 write(DEFAULT_ERROR,*) "ERROR: nlist > list size and max allocations exceeded."
791 return
792 end if
793 neighborListSize = size(list)
794 endif
795
796 list(nlist) = j
797 endif
798
799 if (loop .eq. PAIR_LOOP) then
800 vij = 0.0d0
801 fij(1:3) = 0.0d0
802 endif
803
804 call get_switch(rgrpsq, sw, dswdr, rgrp, group_switch, &
805 in_switching_region)
806
807 n_in_j = groupStartCol(j+1) - groupStartCol(j)
808
809 do ia = groupStartRow(i), groupStartRow(i+1)-1
810
811 atom1 = groupListRow(ia)
812
813 inner: do jb = groupStartCol(j), groupStartCol(j+1)-1
814
815 atom2 = groupListCol(jb)
816
817 if (skipThisPair(atom1, atom2)) cycle inner
818
819 if ((n_in_i .eq. 1).and.(n_in_j .eq. 1)) then
820 d_atm(1:3) = d_grp(1:3)
821 ratmsq = rgrpsq
822 else
823 #ifdef IS_MPI
824 call get_interatomic_vector(q_Row(:,atom1), &
825 q_Col(:,atom2), d_atm, ratmsq)
826 #else
827 call get_interatomic_vector(q(:,atom1), &
828 q(:,atom2), d_atm, ratmsq)
829 #endif
830 endif
831
832 if (loop .eq. PREPAIR_LOOP) then
833 #ifdef IS_MPI
834 call do_prepair(atom1, atom2, ratmsq, d_atm, sw, &
835 rgrpsq, d_grp, do_pot, do_stress, &
836 eFrame, A, f, t, pot_local)
837 #else
838 call do_prepair(atom1, atom2, ratmsq, d_atm, sw, &
839 rgrpsq, d_grp, do_pot, do_stress, &
840 eFrame, A, f, t, pot)
841 #endif
842 else
843 #ifdef IS_MPI
844 call do_pair(atom1, atom2, ratmsq, d_atm, sw, &
845 do_pot, &
846 eFrame, A, f, t, pot_local, vpair, fpair)
847 #else
848 call do_pair(atom1, atom2, ratmsq, d_atm, sw, &
849 do_pot, &
850 eFrame, A, f, t, pot, vpair, fpair)
851 #endif
852
853 vij = vij + vpair
854 fij(1:3) = fij(1:3) + fpair(1:3)
855 endif
856 enddo inner
857 enddo
858
859 if (loop .eq. PAIR_LOOP) then
860 if (in_switching_region) then
861 swderiv = vij*dswdr/rgrp
862 fij(1) = fij(1) + swderiv*d_grp(1)
863 fij(2) = fij(2) + swderiv*d_grp(2)
864 fij(3) = fij(3) + swderiv*d_grp(3)
865
866 do ia=groupStartRow(i), groupStartRow(i+1)-1
867 atom1=groupListRow(ia)
868 mf = mfactRow(atom1)
869 #ifdef IS_MPI
870 f_Row(1,atom1) = f_Row(1,atom1) + swderiv*d_grp(1)*mf
871 f_Row(2,atom1) = f_Row(2,atom1) + swderiv*d_grp(2)*mf
872 f_Row(3,atom1) = f_Row(3,atom1) + swderiv*d_grp(3)*mf
873 #else
874 f(1,atom1) = f(1,atom1) + swderiv*d_grp(1)*mf
875 f(2,atom1) = f(2,atom1) + swderiv*d_grp(2)*mf
876 f(3,atom1) = f(3,atom1) + swderiv*d_grp(3)*mf
877 #endif
878 enddo
879
880 do jb=groupStartCol(j), groupStartCol(j+1)-1
881 atom2=groupListCol(jb)
882 mf = mfactCol(atom2)
883 #ifdef IS_MPI
884 f_Col(1,atom2) = f_Col(1,atom2) - swderiv*d_grp(1)*mf
885 f_Col(2,atom2) = f_Col(2,atom2) - swderiv*d_grp(2)*mf
886 f_Col(3,atom2) = f_Col(3,atom2) - swderiv*d_grp(3)*mf
887 #else
888 f(1,atom2) = f(1,atom2) - swderiv*d_grp(1)*mf
889 f(2,atom2) = f(2,atom2) - swderiv*d_grp(2)*mf
890 f(3,atom2) = f(3,atom2) - swderiv*d_grp(3)*mf
891 #endif
892 enddo
893 endif
894
895 if (do_stress) call add_stress_tensor(d_grp, fij)
896 endif
897 end if
898 enddo
899 enddo outer
900
901 if (update_nlist) then
902 #ifdef IS_MPI
903 point(nGroupsInRow + 1) = nlist + 1
904 #else
905 point(nGroups) = nlist + 1
906 #endif
907 if (loop .eq. PREPAIR_LOOP) then
908 ! we just did the neighbor list update on the first
909 ! pass, so we don't need to do it
910 ! again on the second pass
911 update_nlist = .false.
912 endif
913 endif
914
915 if (loop .eq. PREPAIR_LOOP) then
916 call do_preforce(nlocal, pot)
917 endif
918
919 enddo
920
921 !! Do timing
922 #ifdef PROFILE
923 call cpu_time(forceTimeFinal)
924 forceTime = forceTime + forceTimeFinal - forceTimeInitial
925 #endif
926
927 #ifdef IS_MPI
928 !!distribute forces
929
930 f_temp = 0.0_dp
931 call scatter(f_Row,f_temp,plan_atom_row_3d)
932 do i = 1,nlocal
933 f(1:3,i) = f(1:3,i) + f_temp(1:3,i)
934 end do
935
936 f_temp = 0.0_dp
937 call scatter(f_Col,f_temp,plan_atom_col_3d)
938 do i = 1,nlocal
939 f(1:3,i) = f(1:3,i) + f_temp(1:3,i)
940 end do
941
942 if (FF_UsesDirectionalAtoms() .and. SIM_uses_DirectionalAtoms) then
943 t_temp = 0.0_dp
944 call scatter(t_Row,t_temp,plan_atom_row_3d)
945 do i = 1,nlocal
946 t(1:3,i) = t(1:3,i) + t_temp(1:3,i)
947 end do
948 t_temp = 0.0_dp
949 call scatter(t_Col,t_temp,plan_atom_col_3d)
950
951 do i = 1,nlocal
952 t(1:3,i) = t(1:3,i) + t_temp(1:3,i)
953 end do
954 endif
955
956 if (do_pot) then
957 ! scatter/gather pot_row into the members of my column
958 call scatter(pot_Row, pot_Temp, plan_atom_row)
959
960 ! scatter/gather pot_local into all other procs
961 ! add resultant to get total pot
962 do i = 1, nlocal
963 pot_local = pot_local + pot_Temp(i)
964 enddo
965
966 pot_Temp = 0.0_DP
967
968 call scatter(pot_Col, pot_Temp, plan_atom_col)
969 do i = 1, nlocal
970 pot_local = pot_local + pot_Temp(i)
971 enddo
972
973 endif
974 #endif
975
976 if (FF_RequiresPostpairCalc() .and. SIM_requires_postpair_calc) then
977
978 if (FF_uses_RF .and. SIM_uses_RF) then
979
980 #ifdef IS_MPI
981 call scatter(rf_Row,rf,plan_atom_row_3d)
982 call scatter(rf_Col,rf_Temp,plan_atom_col_3d)
983 do i = 1,nlocal
984 rf(1:3,i) = rf(1:3,i) + rf_Temp(1:3,i)
985 end do
986 #endif
987
988 do i = 1, nLocal
989
990 rfpot = 0.0_DP
991 #ifdef IS_MPI
992 me_i = atid_row(i)
993 #else
994 me_i = atid(i)
995 #endif
996 iMap = InteractionMap(me_i, me_j)%InteractionHash
997
998 if ( iand(iMap, ELECTROSTATIC_PAIR).ne.0 ) then
999
1000 mu_i = getDipoleMoment(me_i)
1001
1002 !! The reaction field needs to include a self contribution
1003 !! to the field:
1004 call accumulate_self_rf(i, mu_i, eFrame)
1005 !! Get the reaction field contribution to the
1006 !! potential and torques:
1007 call reaction_field_final(i, mu_i, eFrame, rfpot, t, do_pot)
1008 #ifdef IS_MPI
1009 pot_local = pot_local + rfpot
1010 #else
1011 pot = pot + rfpot
1012
1013 #endif
1014 endif
1015 enddo
1016 endif
1017 endif
1018
1019
1020 #ifdef IS_MPI
1021
1022 if (do_pot) then
1023 pot = pot + pot_local
1024 !! we assume the c code will do the allreduce to get the total potential
1025 !! we could do it right here if we needed to...
1026 endif
1027
1028 if (do_stress) then
1029 call mpi_allreduce(tau_Temp, tau, 9,mpi_double_precision,mpi_sum, &
1030 mpi_comm_world,mpi_err)
1031 call mpi_allreduce(virial_Temp, virial,1,mpi_double_precision,mpi_sum, &
1032 mpi_comm_world,mpi_err)
1033 endif
1034
1035 #else
1036
1037 if (do_stress) then
1038 tau = tau_Temp
1039 virial = virial_Temp
1040 endif
1041
1042 #endif
1043
1044 end subroutine do_force_loop
1045
1046 subroutine do_pair(i, j, rijsq, d, sw, do_pot, &
1047 eFrame, A, f, t, pot, vpair, fpair)
1048
1049 real( kind = dp ) :: pot, vpair, sw
1050 real( kind = dp ), dimension(3) :: fpair
1051 real( kind = dp ), dimension(nLocal) :: mfact
1052 real( kind = dp ), dimension(9,nLocal) :: eFrame
1053 real( kind = dp ), dimension(9,nLocal) :: A
1054 real( kind = dp ), dimension(3,nLocal) :: f
1055 real( kind = dp ), dimension(3,nLocal) :: t
1056
1057 logical, intent(inout) :: do_pot
1058 integer, intent(in) :: i, j
1059 real ( kind = dp ), intent(inout) :: rijsq
1060 real ( kind = dp ) :: r
1061 real ( kind = dp ), intent(inout) :: d(3)
1062 real ( kind = dp ) :: ebalance
1063 integer :: me_i, me_j
1064
1065 integer :: iMap
1066
1067 r = sqrt(rijsq)
1068 vpair = 0.0d0
1069 fpair(1:3) = 0.0d0
1070
1071 #ifdef IS_MPI
1072 me_i = atid_row(i)
1073 me_j = atid_col(j)
1074 #else
1075 me_i = atid(i)
1076 me_j = atid(j)
1077 #endif
1078
1079 iMap = InteractionMap(me_i, me_j)%InteractionHash
1080
1081 if ( iand(iMap, LJ_PAIR).ne.0 ) then
1082 call do_lj_pair(i, j, d, r, rijsq, sw, vpair, fpair, pot, f, do_pot)
1083 endif
1084
1085 if ( iand(iMap, ELECTROSTATIC_PAIR).ne.0 ) then
1086 call doElectrostaticPair(i, j, d, r, rijsq, sw, vpair, fpair, &
1087 pot, eFrame, f, t, do_pot)
1088
1089 if (FF_uses_RF .and. SIM_uses_RF) then
1090
1091 ! CHECK ME (RF needs to know about all electrostatic types)
1092 call accumulate_rf(i, j, r, eFrame, sw)
1093 call rf_correct_forces(i, j, d, r, eFrame, sw, f, fpair)
1094 endif
1095
1096 endif
1097
1098 if ( iand(iMap, STICKY_PAIR).ne.0 ) then
1099 call do_sticky_pair(i, j, d, r, rijsq, sw, vpair, fpair, &
1100 pot, A, f, t, do_pot)
1101 endif
1102
1103 if ( iand(iMap, STICKYPOWER_PAIR).ne.0 ) then
1104 call do_sticky_power_pair(i, j, d, r, rijsq, sw, vpair, fpair, &
1105 pot, A, f, t, do_pot)
1106 endif
1107
1108 if ( iand(iMap, GAYBERNE_PAIR).ne.0 ) then
1109 call do_gb_pair(i, j, d, r, rijsq, sw, vpair, fpair, &
1110 pot, A, f, t, do_pot)
1111 endif
1112
1113 if ( iand(iMap, GAYBERNE_LJ).ne.0 ) then
1114 ! call do_gblj_pair(i, j, d, r, rijsq, sw, vpair, fpair, &
1115 ! pot, A, f, t, do_pot)
1116 endif
1117
1118 if ( iand(iMap, EAM_PAIR).ne.0 ) then
1119 call do_eam_pair(i, j, d, r, rijsq, sw, vpair, fpair, pot, f, &
1120 do_pot)
1121 endif
1122
1123 if ( iand(iMap, SHAPE_PAIR).ne.0 ) then
1124 call do_shape_pair(i, j, d, r, rijsq, sw, vpair, fpair, &
1125 pot, A, f, t, do_pot)
1126 endif
1127
1128 if ( iand(iMap, SHAPE_LJ).ne.0 ) then
1129 call do_shape_pair(i, j, d, r, rijsq, sw, vpair, fpair, &
1130 pot, A, f, t, do_pot)
1131 endif
1132
1133 end subroutine do_pair
1134
1135 subroutine do_prepair(i, j, rijsq, d, sw, rcijsq, dc, &
1136 do_pot, do_stress, eFrame, A, f, t, pot)
1137
1138 real( kind = dp ) :: pot, sw
1139 real( kind = dp ), dimension(9,nLocal) :: eFrame
1140 real (kind=dp), dimension(9,nLocal) :: A
1141 real (kind=dp), dimension(3,nLocal) :: f
1142 real (kind=dp), dimension(3,nLocal) :: t
1143
1144 logical, intent(inout) :: do_pot, do_stress
1145 integer, intent(in) :: i, j
1146 real ( kind = dp ), intent(inout) :: rijsq, rcijsq
1147 real ( kind = dp ) :: r, rc
1148 real ( kind = dp ), intent(inout) :: d(3), dc(3)
1149
1150 integer :: me_i, me_j, iMap
1151
1152 #ifdef IS_MPI
1153 me_i = atid_row(i)
1154 me_j = atid_col(j)
1155 #else
1156 me_i = atid(i)
1157 me_j = atid(j)
1158 #endif
1159
1160 iMap = InteractionMap(me_i, me_j)%InteractionHash
1161
1162 if ( iand(iMap, EAM_PAIR).ne.0 ) then
1163 call calc_EAM_prepair_rho(i, j, d, r, rijsq )
1164 endif
1165
1166 end subroutine do_prepair
1167
1168
1169 subroutine do_preforce(nlocal,pot)
1170 integer :: nlocal
1171 real( kind = dp ) :: pot
1172
1173 if (FF_uses_EAM .and. SIM_uses_EAM) then
1174 call calc_EAM_preforce_Frho(nlocal,pot)
1175 endif
1176
1177
1178 end subroutine do_preforce
1179
1180
1181 subroutine get_interatomic_vector(q_i, q_j, d, r_sq)
1182
1183 real (kind = dp), dimension(3) :: q_i
1184 real (kind = dp), dimension(3) :: q_j
1185 real ( kind = dp ), intent(out) :: r_sq
1186 real( kind = dp ) :: d(3), scaled(3)
1187 integer i
1188
1189 d(1:3) = q_j(1:3) - q_i(1:3)
1190
1191 ! Wrap back into periodic box if necessary
1192 if ( SIM_uses_PBC ) then
1193
1194 if( .not.boxIsOrthorhombic ) then
1195 ! calc the scaled coordinates.
1196
1197 scaled = matmul(HmatInv, d)
1198
1199 ! wrap the scaled coordinates
1200
1201 scaled = scaled - anint(scaled)
1202
1203
1204 ! calc the wrapped real coordinates from the wrapped scaled
1205 ! coordinates
1206
1207 d = matmul(Hmat,scaled)
1208
1209 else
1210 ! calc the scaled coordinates.
1211
1212 do i = 1, 3
1213 scaled(i) = d(i) * HmatInv(i,i)
1214
1215 ! wrap the scaled coordinates
1216
1217 scaled(i) = scaled(i) - anint(scaled(i))
1218
1219 ! calc the wrapped real coordinates from the wrapped scaled
1220 ! coordinates
1221
1222 d(i) = scaled(i)*Hmat(i,i)
1223 enddo
1224 endif
1225
1226 endif
1227
1228 r_sq = dot_product(d,d)
1229
1230 end subroutine get_interatomic_vector
1231
1232 subroutine zero_work_arrays()
1233
1234 #ifdef IS_MPI
1235
1236 q_Row = 0.0_dp
1237 q_Col = 0.0_dp
1238
1239 q_group_Row = 0.0_dp
1240 q_group_Col = 0.0_dp
1241
1242 eFrame_Row = 0.0_dp
1243 eFrame_Col = 0.0_dp
1244
1245 A_Row = 0.0_dp
1246 A_Col = 0.0_dp
1247
1248 f_Row = 0.0_dp
1249 f_Col = 0.0_dp
1250 f_Temp = 0.0_dp
1251
1252 t_Row = 0.0_dp
1253 t_Col = 0.0_dp
1254 t_Temp = 0.0_dp
1255
1256 pot_Row = 0.0_dp
1257 pot_Col = 0.0_dp
1258 pot_Temp = 0.0_dp
1259
1260 rf_Row = 0.0_dp
1261 rf_Col = 0.0_dp
1262 rf_Temp = 0.0_dp
1263
1264 #endif
1265
1266 if (FF_uses_EAM .and. SIM_uses_EAM) then
1267 call clean_EAM()
1268 endif
1269
1270 rf = 0.0_dp
1271 tau_Temp = 0.0_dp
1272 virial_Temp = 0.0_dp
1273 end subroutine zero_work_arrays
1274
1275 function skipThisPair(atom1, atom2) result(skip_it)
1276 integer, intent(in) :: atom1
1277 integer, intent(in), optional :: atom2
1278 logical :: skip_it
1279 integer :: unique_id_1, unique_id_2
1280 integer :: me_i,me_j
1281 integer :: i
1282
1283 skip_it = .false.
1284
1285 !! there are a number of reasons to skip a pair or a particle
1286 !! mostly we do this to exclude atoms who are involved in short
1287 !! range interactions (bonds, bends, torsions), but we also need
1288 !! to exclude some overcounted interactions that result from
1289 !! the parallel decomposition
1290
1291 #ifdef IS_MPI
1292 !! in MPI, we have to look up the unique IDs for each atom
1293 unique_id_1 = AtomRowToGlobal(atom1)
1294 #else
1295 !! in the normal loop, the atom numbers are unique
1296 unique_id_1 = atom1
1297 #endif
1298
1299 !! We were called with only one atom, so just check the global exclude
1300 !! list for this atom
1301 if (.not. present(atom2)) then
1302 do i = 1, nExcludes_global
1303 if (excludesGlobal(i) == unique_id_1) then
1304 skip_it = .true.
1305 return
1306 end if
1307 end do
1308 return
1309 end if
1310
1311 #ifdef IS_MPI
1312 unique_id_2 = AtomColToGlobal(atom2)
1313 #else
1314 unique_id_2 = atom2
1315 #endif
1316
1317 #ifdef IS_MPI
1318 !! this situation should only arise in MPI simulations
1319 if (unique_id_1 == unique_id_2) then
1320 skip_it = .true.
1321 return
1322 end if
1323
1324 !! this prevents us from doing the pair on multiple processors
1325 if (unique_id_1 < unique_id_2) then
1326 if (mod(unique_id_1 + unique_id_2,2) == 0) then
1327 skip_it = .true.
1328 return
1329 endif
1330 else
1331 if (mod(unique_id_1 + unique_id_2,2) == 1) then
1332 skip_it = .true.
1333 return
1334 endif
1335 endif
1336 #endif
1337
1338 !! the rest of these situations can happen in all simulations:
1339 do i = 1, nExcludes_global
1340 if ((excludesGlobal(i) == unique_id_1) .or. &
1341 (excludesGlobal(i) == unique_id_2)) then
1342 skip_it = .true.
1343 return
1344 endif
1345 enddo
1346
1347 do i = 1, nSkipsForAtom(atom1)
1348 if (skipsForAtom(atom1, i) .eq. unique_id_2) then
1349 skip_it = .true.
1350 return
1351 endif
1352 end do
1353
1354 return
1355 end function skipThisPair
1356
1357 function FF_UsesDirectionalAtoms() result(doesit)
1358 logical :: doesit
1359 doesit = FF_uses_DirectionalAtoms .or. FF_uses_Dipoles .or. &
1360 FF_uses_Quadrupoles .or. FF_uses_Sticky .or. &
1361 FF_uses_StickyPower .or. FF_uses_GayBerne .or. FF_uses_Shapes
1362 end function FF_UsesDirectionalAtoms
1363
1364 function FF_RequiresPrepairCalc() result(doesit)
1365 logical :: doesit
1366 doesit = FF_uses_EAM
1367 end function FF_RequiresPrepairCalc
1368
1369 function FF_RequiresPostpairCalc() result(doesit)
1370 logical :: doesit
1371 doesit = FF_uses_RF
1372 end function FF_RequiresPostpairCalc
1373
1374 #ifdef PROFILE
1375 function getforcetime() result(totalforcetime)
1376 real(kind=dp) :: totalforcetime
1377 totalforcetime = forcetime
1378 end function getforcetime
1379 #endif
1380
1381 !! This cleans componets of force arrays belonging only to fortran
1382
1383 subroutine add_stress_tensor(dpair, fpair)
1384
1385 real( kind = dp ), dimension(3), intent(in) :: dpair, fpair
1386
1387 ! because the d vector is the rj - ri vector, and
1388 ! because fx, fy, fz are the force on atom i, we need a
1389 ! negative sign here:
1390
1391 tau_Temp(1) = tau_Temp(1) - dpair(1) * fpair(1)
1392 tau_Temp(2) = tau_Temp(2) - dpair(1) * fpair(2)
1393 tau_Temp(3) = tau_Temp(3) - dpair(1) * fpair(3)
1394 tau_Temp(4) = tau_Temp(4) - dpair(2) * fpair(1)
1395 tau_Temp(5) = tau_Temp(5) - dpair(2) * fpair(2)
1396 tau_Temp(6) = tau_Temp(6) - dpair(2) * fpair(3)
1397 tau_Temp(7) = tau_Temp(7) - dpair(3) * fpair(1)
1398 tau_Temp(8) = tau_Temp(8) - dpair(3) * fpair(2)
1399 tau_Temp(9) = tau_Temp(9) - dpair(3) * fpair(3)
1400
1401 virial_Temp = virial_Temp + &
1402 (tau_Temp(1) + tau_Temp(5) + tau_Temp(9))
1403
1404 end subroutine add_stress_tensor
1405
1406 end module doForces