4 * This source code is part of
8 * GROningen MAchine for Chemical Simulations
11 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
12 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
13 * Copyright (c) 2001-2004, The GROMACS development team,
14 * check out http://www.gromacs.org for more information.
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * If you want to redistribute modifications, please consider that
22 * scientific software is very special. Version control is crucial -
23 * bugs must be traceable. We will be happy to consider code for
24 * inclusion in the official distribution, but derived work must not
25 * be called official GROMACS. Details are found in the README & COPYING
26 * files - if they are missing, get the official version at www.gromacs.org.
28 * To help us fund GROMACS development, we humbly ask that you cite
29 * the papers on the package - you can find them in the top README file.
31 * For more info, check our website at http://www.gromacs.org
34 * GROningen Mixture of Alchemy and Childrens' Stories
36 /* This file is NOT threadsafe, but it is only used to create
37 * the innerloops during the build process, so it will never be
38 * executed by multiple threads.
45 char tabforcebuf
[255];
48 int calc_scalar_force(int i
,int j
)
53 /* calculate scalar force */
55 /* For softcore loops fs is calculated directly in the routine,
56 * due to the special construction with rinva and rivb. We also
57 * fix the extra flop added for each buffer. */
58 comment("Calculate the scalar force");
59 if(strlen(tabforcebuf
) && strlen(forcebuf
)) {
60 /* both table and non-table interactions */
61 assign("fs%d","((%s)*rinv%d-(%s))*rinv%d",ij
,forcebuf
,ij
,tabforcebuf
,ij
);
62 nflop
++; /* 3-2 flops */
64 else if(strlen(tabforcebuf
))
65 /* only table interactions */
66 assign("fs%d","-(%s)*rinv%d",ij
,tabforcebuf
,ij
); /* 1-1 flops */
67 else if(strlen(forcebuf
))
68 /* only non-table interactions */
69 assign("fs%d","(%s)*rinvsq%d",ij
,forcebuf
,ij
); /* 1-1 flops */
75 int update_inner_forces(int i
,int j
)
80 char src
[25],dest
[25];
83 comment("Convert scalar force to cartesian coords");
85 assign("tx%d","%s*fs%d",ij
,ARRAY(drbuf
,m3
),ij
);
87 assign("ty%d","%s*fs%d",ij
,ARRAY(drbuf
,m3
),ij
);
89 assign("tz%d","%s*fs%d",ij
,ARRAY(drbuf
,m3
),ij
);
92 assign("tx%d","dx%d*fs%d",ij
,ij
,ij
);
93 assign("ty%d","dy%d*fs%d",ij
,ij
,ij
);
94 assign("tz%d","dz%d*fs%d",ij
,ij
,ij
);
98 increment("fix%d","tx%d",i
,ij
);
99 increment("fiy%d","ty%d",i
,ij
);
100 increment("fiz%d","tz%d",i
,ij
);
103 /* and finally, j forces */
108 /* determine source array or variable */
109 if(!DO_PREFETCH
&& i
==1) /* source is faction or fbuf */
110 sprintf(src
, arch
.vectorcpu
? _array("fbuf","kk+%d",offset
) : _array("faction","j3+%d",offset
));
111 else /* source is fj */
112 sprintf(src
,"fj%c%d",m
+'x',j
);
114 /* determine destination array or variable */
115 if(i
==loop
.ni
) /* dest is faction or fbuf */
116 sprintf(dest
, _array("faction", "j3+%d",offset
));
117 else /* dest is fj */
118 sprintf(dest
,"fj%c%d",m
+'x',j
);
120 assign(dest
,"%s-t%c%d%d",src
,m
+'x',i
,j
); /* dest=src-tx */
127 int table_index(char *rin
)
130 #if (defined __GNUC__ && (defined i386 || defined __386__) && !defined DOUBLE && defined USE_X86TRUNC)
132 code("x86trunc(rt,n0);");
135 assign("n0","rt"); /* also used for g77 */
137 assign("eps","rt-n0");
138 assign("eps2","eps*eps");
143 int extract_table(char *pos
)
149 assign("Y",ARRAY(VFtab
,nnn
));
150 assign("F",ARRAY(VFtab
,nnn
+1));
151 sprintf(buf
,"eps*%s",ARRAY(VFtab
,nnn
+2));
153 sprintf(buf
,"eps2*%s",ARRAY(VFtab
,nnn
+3));
155 assign("Fp","F+Geps+Heps2");
156 assign("VV","Y+eps*Fp");
160 if(DO_FORCE
|| DO_SOFTCORE
) {
161 assign("FF","Fp+Geps+two*Heps2");
169 static int assign_chargeproduct(int i
,int j
,char *qq
)
171 /* Assign and return a string with the name of
172 * the variable containing the i charge * j charge. */
175 if(loop
.free
==FREE_LAMBDA
) {
176 assign("qqA","iqA*%s",ARRAY(charge
,jnr
));
177 assign("qqB","iqB*%s",ARRAY(chargeB
,jnr
));
180 } else if(loop
.free
==FREE_SOFTCORE
) {
186 assign("qq","%s*%s", arch
.simplewater
? "iqA" : ((i
==1) ? "qO" : "qH"),ARRAY(charge
,jnr
));
191 if(arch
.simplewater
) {
192 sprintf(qq
,"iqA*%s", (j
==1) ? "qO" : "qH");
196 else if(i
==1 || j
==1)
203 assign("qq","iqA*%s",ARRAY(charge
,jnr
));
212 int do_softcore(int i
, int j
)
215 char fabuf
[50],fbbuf
[50],dvdlbuf
[100];
219 fabuf
[0]=fbbuf
[0]=dvdlbuf
[0]=0;
221 comment("Tabulated Softcore free energy interactions");
223 assign("tjA","ntiA+%d*%s%s", N_VDWPARAM
, ARRAY(type
,jnr
) , bC
? "" : "+1");
224 assign("tjB","ntiB+%d*%s%s", N_VDWPARAM
, ARRAY(typeB
,jnr
) , bC
? "" : "+1");
225 assign("c6a",ARRAY(nbfp
,tjA
));
226 assign("c6b",ARRAY(nbfp
,tjB
));
227 assign( DO_BHAM
? "cexp1a" : "c12a",ARRAY(nbfp
,tjA
+1));
228 assign( DO_BHAM
? "cexp1b" : "c12b",ARRAY(nbfp
,tjB
+1));
230 assign("cexp2a",ARRAY(nbfp
,tjA
+2));
231 assign("cexp2b",ARRAY(nbfp
,tjB
+2));
232 assign("sigma6a","defsigma6");
233 assign("sigma6b","defsigma6");
237 start_if( bC
? "(c6a > 0) && (c12a > 0)" :
238 "(c6a.gt.0).and.(c12a.gt.0)");
239 /* check if we can compute sigma_a from c6/c12 */
240 assign("sigma6a","c12a/c6a");
241 do_else(); /* use default value for sigma_a */
243 assign("sigma6a","defsigma6");
245 end_if(); /* end of check for sigma_a */
247 start_if( bC
? "(c6b > 0) && (c12b > 0)" :
248 "(c6b.gt.0).and.(c12b.gt.0)");
249 /* corresponding check for sigma_b */
250 assign("sigma6b","c12b/c6b");
253 assign("sigma6b","defsigma6");
255 end_if(); /* end of sigma_b check */
257 assign("rfour","rsq%d*rsq%d",ij
,ij
);
258 assign("rsix","rfour*rsq%d",ij
);
263 assign("qqA","iqA*%s",ARRAY(charge
,jnr
));
264 sprintf(ifbuf
, bC
? "(qqA != 0)" : "(qqA.ne.0)");
268 strcat(ifbuf
, bC
? " || " : ".or.");
269 strcat(ifbuf
, bC
? "(c6a > 0) || " : "(c6a.gt.0).or.");
271 strcat(ifbuf
, bC
? "(cexp1a > 0)" : "(cexp1a.gt.0)");
273 strcat(ifbuf
, bC
? "(c12a > 0)" : "(c12a.gt.0)");
278 assign("rA", bC
? "pow(Alpha*sigma6a*lam2+rsix,onesixth)" :
279 "cpow(Alpha*sigma6a*lam2+rsix,onesixth)");
280 assign("rinva","1.0/rA");
281 assign("rinv5a","rinva*rinva");
282 assign("rinv5a","rinv5a*rinv5a*rinva");
284 /* Do the table lookup on r_A */
285 comment("Lookup on rA");
287 nflop
+= table_index("rA*tabscale");
288 assign("n1","%d*n0%s",table_element_size
, bC
? "" : "+1");
291 comment("Coulomb table");
292 nflop
+= extract_table("n1");
293 assign("VVCa","qqA*VV");
294 assign("FFCa","qqA*tabscale*FF");
298 comment("Dispersion");
299 nflop
+= extract_table( DO_COULTAB
? "n1+4" :"n1");
300 assign("VVDa","c6a*VV");
301 assign("FFDa","c6a*tabscale*FF");
304 comment("Buckingham repulsion");
305 /* Make a new lookup index for the exponential table */
306 nflop
+= table_index("cexp2a*rA*tabscale");
307 assign("n1","%d*n0%s",table_element_size
, bC
? "" : "+1");
308 extract_table( DO_COULTAB
? "n1+8" : "n1+4");
309 assign("VVRa","cexp1a*VV");
310 assign("FFRa","cexp1a*cexp2a*exptabscale*FF");
313 comment("Repulsion");
314 extract_table( DO_COULTAB
? "n1+8" : "n1+4");
315 assign("VVRa","c12a*VV");
316 assign("FFRa","c12a*tabscale*FF");
320 do_else(); /* If all A interaction parameters are 0 */
331 assign("rinv5a","0");
332 end_if(); /* finished A */
338 assign("qqB","iqB*%s",ARRAY(chargeB
,jnr
));
339 sprintf(ifbuf
, bC
? "(qqB != 0)" : "(qqB.ne.0)");
343 strcat(ifbuf
, bC
? " || " : ".or.");
344 strcat(ifbuf
, bC
? "(c6b > 0) || " : "(c6b.gt.0).or.");
346 strcat(ifbuf
, bC
? "(cexp1b > 0)" : "(cexp1b.gt.0)");
348 strcat(ifbuf
, bC
? "(c12b > 0)" : "(c12b.gt.0)");
353 assign("rB", bC
? "pow(Alpha*sigma6b*L12+rsix,onesixth)" :
354 "cpow(Alpha*sigma6b*L12+rsix,onesixth)");
355 assign("rinvb","1.0/rB");
356 assign("rinv5b","rinvb*rinvb");
357 assign("rinv5b","rinv5b*rinv5b*rinvb");
359 comment("Lookup on rB");
360 nflop
+= 1 + table_index("rB*tabscale");
362 assign("n1","%d*n0%s",table_element_size
, bC
? "" : "+1");
364 comment("Coulomb table");
365 nflop
+= extract_table("n1");
366 assign("VVCb","qqB*VV");
367 assign("FFCb","qqB*tabscale*FF");
371 comment("Dispersion");
372 nflop
+= extract_table( DO_COULTAB
? "n1+4" : "n1");
373 assign("VVDb","c6b*VV");
374 assign("FFDb","c6b*tabscale*FF");
377 comment("Buckingham repulsion");
378 /* Make a new lookup index for the exponential table */
379 nflop
+= 2 + table_index("cexp2b*rB*tabscale");
380 assign("n1","%d*n0%s",table_element_size
, bC
? "" : "+1");
381 nflop
+= extract_table( DO_COULTAB
? "n1+8" : "n1+4");
382 assign("VVRb","cexp1b*VV");
383 assign("FFRb","cexp1b*cexp2b*exptabscale*FF");
386 comment("Repulsion");
387 nflop
+= extract_table( DO_COULTAB
? "n1+8" : "n1+4");
388 assign("VVRb","c12b*VV");
389 assign("FFRb","c12b*tabscale*FF");
393 do_else(); /* If all B interaction parameters are 0 */
404 assign("rinv5b","0");
405 end_if(); /* finished B */
407 /* OK. Now we have all potential and force lookup values,
408 * all that is left is to calculate the actual force, potential
412 add_to_buffer(fabuf
,"FFCa");
413 add_to_buffer(fbbuf
,"FFCb");
414 assign("vcoul","lambda*VVCb+L1*VVCa");
415 add_to_buffer(dvdlbuf
,"VVCb-VVCa");
419 increment("vnbtot","lambda*(VVDb+VVRb)+L1*(VVDa+VVRa)");
420 add_to_buffer(dvdlbuf
,"VVDb+VVRb-VVDa-VVRa");
421 add_to_buffer(fabuf
,"FFDa+FFRa");
422 add_to_buffer(fbbuf
,"FFDb+FFRb");
425 assign("Fa","-(%s)",fabuf
);
426 assign("Fb","-(%s)",fbbuf
);
427 assign("fs%d","(L1*Fa*rinv5a + lambda*Fb*rinv5b)*rfour",ij
);
428 add_to_buffer(dvdlbuf
,"onethird*Alpha*lambda*L1*(Fb*sigma6b*rinv5b-Fa*sigma6a*rinv5a)");
429 increment("dvdl",dvdlbuf
);
436 int do_table(int i
,int j
)
439 char qq
[100],buf
[100];
440 char f1buf
[100],f2buf
[100];
445 comment("Tabulated interactions");
446 sprintf(buf
,"r%d*tabscale",ij
);
447 nflop
+= table_index(buf
) + 1;
449 assign("n1","%d*n0%s",table_element_size
, bC
? "" : "+1");
452 /* determine the name of and set the charge parameter q_i*q_j */
453 nflop
+= assign_chargeproduct(i
,j
,qq
);
455 assign("qqq","L1*qqA + lambda*qqB");
458 nflop
+= extract_table("n1");
459 assign("vcoul","%s*VV",qq
);
462 assign("fijC","%s*FF",qq
);
466 increment("dvdl","(qqB - qqA)*VV",j
,j
);
470 add_to_buffer(f1buf
,"fijC");
474 /* only do VDW for the first atom in water interactions */
476 (!DO_WATER
|| (loop
.sol
==SOL_WATER
&& i
==1) || (i
==1 && j
==1))) {
477 /* first determine nonbonded parameters */
479 assign("tjA","ntiA+%d*%s%s", N_VDWPARAM
, ARRAY(type
,jnr
) , bC
? "" : "+1");
480 assign("tjB","ntiB+%d*%s%s", N_VDWPARAM
, ARRAY(typeB
,jnr
) , bC
? "" : "+1");
481 assign("c6a",ARRAY(nbfp
,tjA
));
482 assign("c6b",ARRAY(nbfp
,tjB
));
483 assign("c6","L1*c6a + lambda*c6b");
485 assign( DO_BHAM
? "cexp1a" : "c12a" ,ARRAY(nbfp
,tjA
+1));
486 assign( DO_BHAM
? "cexp1b" : "c12b" ,ARRAY(nbfp
,tjB
+1));
488 assign("cexp2a",ARRAY(nbfp
,tjA
+2));
489 assign("cexp2b",ARRAY(nbfp
,tjB
+2));
491 assign("c12","L1*c12a + lambda*c12b");
494 } else if(loop
.sol
!=SOL_WATERWATER
) {
495 assign("tjA","ntiA+%d*%s%s", N_VDWPARAM
, ARRAY(type
,jnr
) , bC
? "" : "+1");
496 assign("c6",ARRAY(nbfp
,tjA
));
497 assign( DO_BHAM
? "cexp1" : "c12",ARRAY(nbfp
,tjA
+1));
499 assign("cexp2",ARRAY(nbfp
,tjA
+2));
501 comment("Dispersion");
502 nflop
+= extract_table( DO_COULTAB
? "n1+4" : "n1");
503 assign("vnb6","c6*VV");
506 assign("fijD","c6*FF");
510 increment("dvdl","(c6b - c6a)*VV");
514 add_to_buffer(f1buf
,"fijD");
518 comment("Buckingham repulsion");
519 /* Make a new lookup index for the exponential table */
520 sprintf(buf
, loop
.free
? "cexp2a*r%d*exptabscale" :
521 "cexp2*r%d*exptabscale",ij
);
522 nflop
+= 2 + table_index(buf
);
523 assign("n1","%d*n0%s",table_element_size
, bC
? "" : "+1");
524 nflop
+= extract_table( DO_COULTAB
? "n1+8" : "n1+4");
527 assign("vnbexpa","cexp1a*VV");
529 assign("fijRa","cexp1a*cexp2a*FF");
530 sprintf(buf
,"cexp2b*r%d*exptabscale",ij
);
531 nflop
+= table_index(buf
);
532 assign("n1","%d*n0%s",table_element_size
, bC
? "" : "+1");
533 nflop
+= extract_table( DO_COULTAB
? "n1+8" : "n1+4");
535 assign("vnbexpb","cexp1b*VV");
537 assign("fijRb","cexp1b*cexp2b*FF");
538 assign("fijR","L1*fijRa + lambda*fijRb");
540 increment("vnbtot","vnb6 + L1*vnbexpa + lambda*vnbexpb");
541 increment("dvdl","vnbexpb - vnbexpa");
544 assign("vnbexp","cexp1*VV");
547 assign("fijR","cexp1*cexp2*FF");
550 increment("vnbtot","vnb6 + vnbexp");
553 add_to_buffer(f2buf
,"fijR");
557 comment("Lennard-Jones repulsion");
558 nflop
+= extract_table( DO_COULTAB
? "n1+8" : "n1+4");
559 assign("vnb12","c12*VV");
561 assign("fijR","c12*FF");
564 increment("vnbtot","vnb6 + vnb12");
566 increment("dvdl","(c12b - c12a)*VV");
569 add_to_buffer(f1buf
,"fijR");
575 sprintf(buf
,"(%s)*tabscale",f1buf
);
577 add_to_buffer(tabforcebuf
,buf
);
579 sprintf(buf
,"(%s)*exptabscale",f2buf
);
581 add_to_buffer(tabforcebuf
,buf
);
589 int do_coul(int i
, int j
)
597 /* determine the name of and set the charge product iq*jq,
598 * depending on wheter we do water loops or not. */
599 nflop
+= assign_chargeproduct(i
,j
,qq
);
601 /* this short code is the actual interaction! */
602 assign("vcoul","%s*rinv%d",qq
,ij
);
603 /* concatenate the potential to the force buffer string.
604 * This is later multiplied by 1/r to get the force once
605 * all interactions for this pair have been calculated
610 add_to_buffer(forcebuf
,"vcoul");
616 int do_rf(int i
, int j
)
622 comment("Reaction field coulomb");
624 nflop
+= assign_chargeproduct(i
,j
,qq
);
626 /* this short code is the actual interaction! */
627 assign("krsq","krf*rsq%d",ij
);
628 assign("vcoul","%s*(rinv%d+krsq-crf)",qq
,ij
);
630 /* concatenate the potential to the force buffer string.
631 * This is later multiplied by 1/r to get the force once
632 * all interactions for this pair have been calculated
636 sprintf(buf
,"%s*(rinv%d-two*krsq)",qq
,ij
);
637 add_to_buffer(forcebuf
,buf
);
644 int do_lj(int i
, int j
)
650 /* calculate r^-6 from r^-2 */
651 assign("rinvsix","rinvsq%d*rinvsq%d*rinvsq%d",ij
,ij
,ij
);
652 /* for water-water loops this is only called for O-O interactions,
653 * and we have already extracted those c6/c12
655 if(loop
.sol
==SOL_WATERWATER
) {
656 assign("vnb6","c6*rinvsix");
657 assign("vnb12","c12*rinvsix*rinvsix");
659 assign("tjA","ntiA+2*%s%s", ARRAY(type
,jnr
) , bC
? "" : "+1");
660 assign("vnb6","rinvsix*%s",ARRAY(nbfp
,tjA
));
661 assign("vnb12","rinvsix*rinvsix*%s",ARRAY(nbfp
,tjA
+1));
666 add_to_buffer(forcebuf
,"twelve*vnb12-six*vnb6");
669 increment("vnbtot","vnb12-vnb6");
674 int do_bham(int i
, int j
)
679 comment("Buckingham");
681 /* calculate r^-6 from r^-2 */
682 assign("rinvsix","rinvsq%d*rinvsq%d*rinvsq%d",ij
,ij
,ij
);
683 if(loop
.sol
==SOL_WATERWATER
) {
684 assign("vnb6","c6*rinvsix");
685 assign("br","cexp2*r%d",ij
);
686 assign("vnbexp","cexp1*exp(-br)"); /* V=C_a*exp(-C_b*r) */
688 /* fortran indices start at one, instead of 0 for C.
689 * And there are three nb parameters for buckingham
691 assign("tjA","ntiA+3*%s%s", ARRAY(type
,jnr
) , bC
? "" : "+1");
692 assign("vnb6","rinvsix*%s",ARRAY(nbfp
,tjA
));
693 assign("br","r%d*%s",ij
,ARRAY(nbfp
,tjA
+2));
694 assign("vnbexp","exp(-br)*%s",ARRAY(nbfp
,tjA
+1));
698 add_to_buffer(forcebuf
,"br*vnbexp-six*vnb6");
701 increment("vnbtot","vnbexp-vnb6");
706 int calc_interactions(void)
708 /* Remember to set need_invsqrt, need_reciprocal and need_exp
709 * to the correct needs for your new interaction already
710 * in mkinl.c. Then add new entries in the switch statements
711 * and call your interaction implementation in a separate routine.
712 * Have a look at e.g. do_coul to see how it is done!
717 /* once we get here the distance measures
718 * we requested in mkinl.c are calculated, but we might
719 * have to extract them from the buffers
720 * used for vectorization.
722 for(i
=1;i
<=loop
.ni
;i
++)
723 for(j
=1;j
<=loop
.nj
;j
++) {
725 /* Dont do vdw for coul-only water atoms. For the general
726 * solvent we change the value of loop.vdw outside this loop */
728 (!DO_WATER
|| (loop
.sol
==SOL_WATER
&& i
==1) ||
729 (loop
.sol
==SOL_WATERWATER
&& i
==1 && j
==1));
731 forcebuf
[0]=tabforcebuf
[0]=0;
734 /* get rinv or rinvsq from temp buffer */
735 assign( loop
.invsqrt
? "rinv%d" : "rinvsq%d",
736 OVERWRITE_RSQ
? ARRAY(buf1
,m
) : ARRAY(buf2
,m
), ij
);
737 /* Do we need to copy rsq too? */
738 if((loop
.coul_needs_rsq
) || (loop
.vdw_needs_rsq
&& do_vdw
))
739 assign( "rsq%d" , ARRAY(buf1
,m
), ij
);
740 /* And what about r? */
741 if((loop
.coul_needs_r
) || (loop
.vdw_needs_r
&& do_vdw
))
742 assign( "r%d" ,"%s*%s",ij
,ARRAY(buf1
,m
),ARRAY(buf2
,m
));
744 } else { /* no vectorization */
745 /* If we aren't trying to hide the latency of invsqrt
746 * we calculate it here just before doing the
747 * interaction for each pair of atoms!
749 if(opt
.delay_invsqrt
) {
750 assign( loop
.invsqrt
? "rinv%d" : "rinvsq%d",
751 loop
.invsqrt
? "1.0/sqrt(rsq%d)" :
755 if((loop
.coul_needs_r
) || (loop
.vdw_needs_r
&& do_vdw
)) {
756 assign( "r%d","rsq%d*rinv%d", ij
,ij
,ij
);
760 /* Do we need rinvsq when calculating rinv? */
761 if(((loop
.coul_needs_rinvsq
) ||
762 (loop
.vdw_needs_rinvsq
&& do_vdw
)) && !loop
.recip
) {
763 assign("rinvsq%d","rinv%d*rinv%d",ij
,ij
,ij
);
766 /* which interactions should we call ? */
773 nflop
+= do_bham(i
,j
);
777 nflop
+= DO_SOFTCORE
? do_softcore(i
,j
) : do_table(i
,j
);
780 comment("No nonbonded interaction");
786 nflop
+= do_coul(i
,j
);
792 if(!(do_vdw
&& DO_VDWTAB
))
793 nflop
+= DO_SOFTCORE
? do_softcore(i
,j
) : do_table(i
,j
);
794 /* coul table was done simultaneous with
795 * vdw if the latter was tabulated.
799 comment("No coulomb interaction");
802 /* The total coulomb energy is incremented after calculating
803 * the scalar force to hide a flop.
807 nflop
+= calc_scalar_force(i
,j
);
810 increment("vctot","vcoul");
814 nflop
+= update_inner_forces(i
,j
);