APBS  1.4.1
vpmg.c
Go to the documentation of this file.
1 
73 #include "vpmg.h"
74 
75 VEMBED(rcsid="$Id$")
76 
77 #if !defined(VINLINE_VPMG)
78 
79 VPUBLIC unsigned long int Vpmg_memChk(Vpmg *thee) {
80  if (thee == VNULL) return 0;
81  return Vmem_bytes(thee->vmem);
82 }
83 
84 #endif /* if !defined(VINLINE_VPMG) */
85 
86 
87 VPUBLIC void Vpmg_printColComp(Vpmg *thee, char path[72], char title[72],
88  char mxtype[3], int flag) {
89 
90  int nn, nxm2, nym2, nzm2, ncol, nrow, nonz;
91  double *nzval;
92  int *colptr, *rowind;
93 
94  /* Calculate the total number of unknowns */
95  nxm2 = thee->pmgp->nx - 2;
96  nym2 = thee->pmgp->ny - 2;
97  nzm2 = thee->pmgp->nz - 2;
98  nn = nxm2*nym2*nzm2;
99  ncol = nn;
100  nrow = nn;
101 
102  /* Calculate the number of non-zero matrix entries:
103  * nn nonzeros on diagonal
104  * nn-1 nonzeros on first off-diagonal
105  * nn-nx nonzeros on second off-diagonal
106  * nn-nx*ny nonzeros on third off-diagonal
107  *
108  * 7*nn-2*nx*ny-2*nx-2 TOTAL non-zeros
109  */
110  nonz = 7*nn - 2*nxm2*nym2 - 2*nxm2 - 2;
111  nzval = (double*)Vmem_malloc(thee->vmem, nonz, sizeof(double));
112  rowind = (int*)Vmem_malloc(thee->vmem, nonz, sizeof(int));
113  colptr = (int*)Vmem_malloc(thee->vmem, (ncol+1), sizeof(int));
114 
115 #ifndef VAPBSQUIET
116  Vnm_print(1, "Vpmg_printColComp: Allocated space for %d nonzeros\n",
117  nonz);
118 #endif
119 
120  bcolcomp(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
121  nzval, rowind, colptr, &flag);
122 
123 
124 #if 0
125  for (i=0; i<nn; i++) {
126  Vnm_print(1, "nnz(%d) = %g\n", i, nzval[i]);
127  }
128 #endif
129 
130  /* I do not understand why I need to pass nzval in this way, but it
131  * works... */
132  pcolcomp(&nrow, &ncol, &nonz, &(nzval[0]), rowind, colptr, path, title,
133  mxtype);
134 
135  Vmem_free(thee->vmem, (ncol+1), sizeof(int), (void **)&colptr);
136  Vmem_free(thee->vmem, nonz, sizeof(int), (void **)&rowind);
137  Vmem_free(thee->vmem, nonz, sizeof(double), (void **)&nzval);
138 
139 }
140 
141 VPUBLIC Vpmg* Vpmg_ctor(Vpmgp *pmgp, Vpbe *pbe, int focusFlag,
142  Vpmg *pmgOLD, MGparm *mgparm, PBEparm_calcEnergy energyFlag) {
143 
144  Vpmg *thee = VNULL;
145 
146  thee = (Vpmg*)Vmem_malloc(VNULL, 1, sizeof(Vpmg) );
147  VASSERT(thee != VNULL);
148  VASSERT( Vpmg_ctor2(thee, pmgp, pbe, focusFlag, pmgOLD, mgparm,
149  energyFlag) );
150  return thee;
151 }
152 
153 VPUBLIC int Vpmg_ctor2(Vpmg *thee, Vpmgp *pmgp, Vpbe *pbe, int focusFlag,
154  Vpmg *pmgOLD, MGparm *mgparm, PBEparm_calcEnergy energyFlag) {
155 
156  int i, j, nion;
157  double ionConc[MAXION], ionQ[MAXION], ionRadii[MAXION], zkappa2, zks2;
158  double ionstr, partMin[3], partMax[3];
159 
160  /* Get the parameters */
161  VASSERT(pmgp != VNULL);
162  VASSERT(pbe != VNULL);
163  thee->pmgp = pmgp;
164  thee->pbe = pbe;
165 
166  /* Set up the memory */
167  thee->vmem = Vmem_ctor("APBS:VPMG");
168 
169 
170 
172  /* Initialize ion concentrations and valencies in PMG routines */
173  zkappa2 = Vpbe_getZkappa2(thee->pbe);
174  ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
175  if (ionstr > 0.0) zks2 = 0.5/ionstr;
176  else zks2 = 0.0;
177  Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
178 
179  /* TEMPORARY USEAQUA */
180  /* Calculate storage requirements */
181  if(mgparm->useAqua == 0){
182  Vpmgp_size(thee->pmgp);
183  }else{
184  VABORT_MSG0("Aqua is currently disabled");
185  }
186 
187  /* We need some additional storage if: nonlinear & newton OR cgmg */
188  /* SMPBE Added - nonlin = 2 added since it mimics NPBE */
189  if ( ( ((thee->pmgp->nonlin == NONLIN_NPBE) || (thee->pmgp->nonlin == NONLIN_SMPBE))
190  && (thee->pmgp->meth == VSOL_Newton) ) || (thee->pmgp->meth == VSOL_CGMG) )
191  {
192  thee->pmgp->nrwk += (2*(thee->pmgp->nf));
193  }
194 
195 
196  if (thee->pmgp->iinfo > 1) {
197  Vnm_print(2, "Vpmg_ctor2: PMG chose nx = %d, ny = %d, nz = %d\n",
198  thee->pmgp->nx, thee->pmgp->ny, thee->pmgp->nz);
199  Vnm_print(2, "Vpmg_ctor2: PMG chose nlev = %d\n",
200  thee->pmgp->nlev);
201  Vnm_print(2, "Vpmg_ctor2: PMG chose nxc = %d, nyc = %d, nzc = %d\n",
202  thee->pmgp->nxc, thee->pmgp->nyc, thee->pmgp->nzc);
203  Vnm_print(2, "Vpmg_ctor2: PMG chose nf = %d, nc = %d\n",
204  thee->pmgp->nf, thee->pmgp->nc);
205  Vnm_print(2, "Vpmg_ctor2: PMG chose narr = %d, narrc = %d\n",
206  thee->pmgp->narr, thee->pmgp->narrc);
207  Vnm_print(2, "Vpmg_ctor2: PMG chose n_rpc = %d, n_iz = %d, n_ipc = %d\n",
208  thee->pmgp->n_rpc, thee->pmgp->n_iz, thee->pmgp->n_ipc);
209  Vnm_print(2, "Vpmg_ctor2: PMG chose nrwk = %d, niwk = %d\n",
210  thee->pmgp->nrwk, thee->pmgp->niwk);
211  }
212 
213 
214 
215  /* Allocate boundary storage */
216  thee->gxcf = (double *)Vmem_malloc(
217  thee->vmem,
218  10*(thee->pmgp->ny)*(thee->pmgp->nz),
219  sizeof(double)
220  );
221 
222  thee->gycf = (double *)Vmem_malloc(
223  thee->vmem,
224  10*(thee->pmgp->nx)*(thee->pmgp->nz),
225  sizeof(double)
226  );
227 
228  thee->gzcf = (double *)Vmem_malloc(
229  thee->vmem,
230  10*(thee->pmgp->nx)*(thee->pmgp->ny),
231  sizeof(double)
232  );
233 
234 
235 
236  /* Warn users if they are using BCFL_MAP that
237  we do not include external energies */
238  if (thee->pmgp->bcfl == BCFL_MAP)
239  Vnm_print(2,"Vpmg_ctor2: \nWarning: External energies are not used in BCFL_MAP calculations!\n");
240 
241  if (focusFlag) {
242 
243  /* Overwrite any default or user-specified boundary condition
244  * arguments; we are now committed to a calculation via focusing */
245  if (thee->pmgp->bcfl != BCFL_FOCUS) {
246  Vnm_print(2,
247  "Vpmg_ctor2: reset boundary condition flag to BCFL_FOCUS!\n");
248  thee->pmgp->bcfl = BCFL_FOCUS;
249  }
250 
251  /* Fill boundaries */
252  Vnm_print(0, "Vpmg_ctor2: Filling boundary with old solution!\n");
253  focusFillBound(thee, pmgOLD);
254 
255  /* Calculate energetic contributions from region outside focusing
256  * domain */
257  if (energyFlag != PCE_NO) {
258 
259  if (mgparm->type == MCT_PARALLEL) {
260 
261  for (j=0; j<3; j++) {
262  partMin[j] = mgparm->partDisjCenter[j]
263  - 0.5*mgparm->partDisjLength[j];
264  partMax[j] = mgparm->partDisjCenter[j]
265  + 0.5*mgparm->partDisjLength[j];
266  }
267 
268  } else {
269  for (j=0; j<3; j++) {
270  partMin[j] = mgparm->center[j] - 0.5*mgparm->glen[j];
271  partMax[j] = mgparm->center[j] + 0.5*mgparm->glen[j];
272  }
273  }
274  extEnergy(thee, pmgOLD, energyFlag, partMin, partMax,
275  mgparm->partDisjOwnSide);
276  }
277 
278  } else {
279 
280  /* Ignore external energy contributions */
281  thee->extQmEnergy = 0;
282  thee->extDiEnergy = 0;
283  thee->extQfEnergy = 0;
284  }
285 
286  /* Allocate partition vector storage */
287  thee->pvec = (double *)Vmem_malloc(
288  thee->vmem,
289  (thee->pmgp->nx)*(thee->pmgp->ny)*(thee->pmgp->nz),
290  sizeof(double)
291  );
292 
293  /* Allocate remaining storage */
294  thee->iparm = ( int *)Vmem_malloc(thee->vmem, 100, sizeof( int));
295  thee->rparm = (double *)Vmem_malloc(thee->vmem, 100, sizeof(double));
296  thee->iwork = ( int *)Vmem_malloc(thee->vmem, thee->pmgp->niwk, sizeof( int));
297  thee->rwork = (double *)Vmem_malloc(thee->vmem, thee->pmgp->nrwk, sizeof(double));
298  thee->charge = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
299  thee->kappa = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
300  thee->pot = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
301  thee->epsx = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
302  thee->epsy = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
303  thee->epsz = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
304  thee->a1cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
305  thee->a2cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
306  thee->a3cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
307  thee->ccf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
308  thee->fcf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
309  thee->tcf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
310  thee->u = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
311  thee->xf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->nx), sizeof(double));
312  thee->yf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->ny), sizeof(double));
313  thee->zf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->nz), sizeof(double));
314 
315 
316 
317  /* Packs parameters into the iparm and rparm arrays */
318  Vpackmg(thee->iparm, thee->rparm, &(thee->pmgp->nrwk), &(thee->pmgp->niwk),
319  &(thee->pmgp->nx), &(thee->pmgp->ny), &(thee->pmgp->nz),
320  &(thee->pmgp->nlev), &(thee->pmgp->nu1), &(thee->pmgp->nu2),
321  &(thee->pmgp->mgkey), &(thee->pmgp->itmax), &(thee->pmgp->istop),
322  &(thee->pmgp->ipcon), &(thee->pmgp->nonlin), &(thee->pmgp->mgsmoo),
323  &(thee->pmgp->mgprol), &(thee->pmgp->mgcoar), &(thee->pmgp->mgsolv),
324  &(thee->pmgp->mgdisc), &(thee->pmgp->iinfo), &(thee->pmgp->errtol),
325  &(thee->pmgp->ipkey), &(thee->pmgp->omegal), &(thee->pmgp->omegan),
326  &(thee->pmgp->irite), &(thee->pmgp->iperf));
327 
328 
329 
330  /* Currently for SMPBE type calculations we do not want to apply a scale
331  factor to the ionConc */
338  switch(pmgp->ipkey){
339 
340  case IPKEY_SMPBE:
341 
342  Vmypdefinitsmpbe(&nion, ionQ, ionConc, &pbe->smvolume, &pbe->smsize);
343  break;
344 
345 
346 
347  case IPKEY_NPBE:
348 
349  /* Else adjust the inoConc by scaling factor zks2 */
350  for (i=0; i<nion; i++)
351  ionConc[i] = zks2 * ionConc[i];
352 
353  Vmypdefinitnpbe(&nion, ionQ, ionConc);
354  break;
355 
356 
357 
358  case IPKEY_LPBE:
359 
360  /* Else adjust the inoConc by scaling factor zks2 */
361  for (i=0; i<nion; i++)
362  ionConc[i] = zks2 * ionConc[i];
363 
364  Vmypdefinitlpbe(&nion, ionQ, ionConc);
365  break;
366 
367 
368 
369  default:
370  Vnm_print(2, "PMG: Warning: PBE structure not initialized!\n");
371  /* Else adjust the inoConc by scaling factor zks2 */
372  for (i=0; i<nion; i++)
373  ionConc[i] = zks2 * ionConc[i];
374  break;
375  }
376 
377  /* Set the default chargeSrc for 5th order splines */
378  thee->chargeSrc = mgparm->chgs;
379 
380  /* Turn off restriction of observable calculations to a specific
381  * partition */
382  Vpmg_unsetPart(thee);
383 
384  /* The coefficient arrays have not been filled */
385  thee->filled = 0;
386 
387 
388  /*
389  * TODO: Move the dtor out of here. The current ctor is done in routines.c,
390  * This was originally moved out to kill a memory leak. The dtor has
391  * has been removed from initMG and placed back here to keep memory
392  * usage low. killMG has been modified accordingly.
393  */
394  Vpmg_dtor(&pmgOLD);
395 
396  return 1;
397 }
398 
399 VPUBLIC int Vpmg_solve(Vpmg *thee) {
400 
401  int i,
402  nx,
403  ny,
404  nz,
405  n;
406  double zkappa2;
407 
408  nx = thee->pmgp->nx;
409  ny = thee->pmgp->ny;
410  nz = thee->pmgp->nz;
411  n = nx*ny*nz;
412 
413  if (!(thee->filled)) {
414  Vnm_print(2, "Vpmg_solve: Need to call Vpmg_fillco()!\n");
415  return 0;
416  }
417 
418  /* Fill the "true solution" array */
419  for (i=0; i<n; i++) {
420  thee->tcf[i] = 0.0;
421  }
422 
423  /* Fill the RHS array */
424  for (i=0; i<n; i++) {
425  thee->fcf[i] = thee->charge[i];
426  }
427 
428  /* Fill the operator coefficient array. */
429  for (i=0; i<n; i++) {
430  thee->a1cf[i] = thee->epsx[i];
431  thee->a2cf[i] = thee->epsy[i];
432  thee->a3cf[i] = thee->epsz[i];
433  }
434 
435  /* Fill the nonlinear coefficient array by multiplying the kappa
436  * accessibility array (containing values between 0 and 1) by zkappa2. */
437  zkappa2 = Vpbe_getZkappa2(thee->pbe);
438  if (zkappa2 > VPMGSMALL) {
439  for (i=0; i<n; i++) {
440  thee->ccf[i] = zkappa2*thee->kappa[i];
441  }
442  } else {
443  for (i=0; i<n; i++) {
444  thee->ccf[i] = 0.0;
445  }
446  }
447 
448  switch(thee->pmgp->meth) {
449  /* CGMG (linear) */
450  case VSOL_CGMG:
451 
452  if (thee->pmgp->iinfo > 1)
453  Vnm_print(2, "Driving with CGMGDRIV\n");
454 
455  VABORT_MSG0("CGMGDRIV is not currently supported");
456  break;
457 
458  /* Newton (nonlinear) */
459  case VSOL_Newton:
460 
461  if (thee->pmgp->iinfo > 1)
462  Vnm_print(2, "Driving with NEWDRIV\n");
463 
464  Vnewdriv
465  (thee->iparm, thee->rparm, thee->iwork, thee->rwork,
466  thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
467  thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
468  thee->fcf, thee->tcf);
469  break;
470 
471  /* MG (linear/nonlinear) */
472  case VSOL_MG:
473 
474  if (thee->pmgp->iinfo > 1)
475  Vnm_print(2, "Driving with MGDRIV\n");
476 
477  Vmgdriv(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
478  thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
479  thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
480  thee->fcf, thee->tcf);
481  break;
482 
483  /* CGHS (linear/nonlinear) */
484  case VSOL_CG:
485 
486  if (thee->pmgp->iinfo > 1)
487  Vnm_print(2, "Driving with NCGHSDRIV\n");
488 
489  VABORT_MSG0("NCGHSDRIV is not currently supported");
490  break;
491 
492  /* SOR (linear/nonlinear) */
493  case VSOL_SOR:
494 
495  if (thee->pmgp->iinfo > 1)
496  Vnm_print(2, "Driving with NSORDRIV\n");
497 
498  VABORT_MSG0("NSORDRIV is not currently supported");
499  break;
500 
501  /* GSRB (linear/nonlinear) */
502  case VSOL_RBGS:
503 
504  if (thee->pmgp->iinfo > 1)
505  Vnm_print(2, "Driving with NGSRBDRIV\n");
506 
507  VABORT_MSG0("NGSRBDRIV is not currently supported");
508  break;
509 
510  /* WJAC (linear/nonlinear) */
511  case VSOL_WJ:
512 
513  if (thee->pmgp->iinfo > 1)
514  Vnm_print(2, "Driving with NWJACDRIV\n");
515 
516  VABORT_MSG0("NWJACDRIV is not currently supported");
517  break;
518 
519  /* RICH (linear/nonlinear) */
520  case VSOL_Richardson:
521 
522  if (thee->pmgp->iinfo > 1)
523  Vnm_print(2, "Driving with NRICHDRIV\n");
524 
525  VABORT_MSG0("NRICHDRIV is not currently supported");
526  break;
527 
528  /* CGMG (linear) TEMPORARY USEAQUA */
529  case VSOL_CGMGAqua:
530 
531  if (thee->pmgp->iinfo > 1)
532  Vnm_print(2, "Driving with CGMGDRIVAQUA\n");
533 
534  VABORT_MSG0("CGMGDRIVAQUA is not currently supported");
535  break;
536 
537  /* Newton (nonlinear) TEMPORARY USEAQUA */
538  case VSOL_NewtonAqua:
539 
540  if (thee->pmgp->iinfo > 1)
541  Vnm_print(2, "Driving with NEWDRIVAQUA\n");
542 
543  VABORT_MSG0("NEWDRIVAQUA is not currently supported");
544  break;
545 
546  /* Error handling */
547  default:
548  Vnm_print(2, "Vpmg_solve: invalid solver method key (%d)\n",
549  thee->pmgp->key);
550  return 0;
551  break;
552  }
553 
554  return 1;
555 
556 }
557 
558 
559 VPUBLIC void Vpmg_dtor(Vpmg **thee) {
560 
561  if ((*thee) != VNULL) {
562  Vpmg_dtor2(*thee);
563  Vmem_free(VNULL, 1, sizeof(Vpmg), (void **)thee);
564  (*thee) = VNULL;
565  }
566 
567 }
568 
569 VPUBLIC void Vpmg_dtor2(Vpmg *thee) {
570 
571  /* Clean up the storage */
572 
573  Vmem_free(thee->vmem, 100, sizeof(int),
574  (void **)&(thee->iparm));
575  Vmem_free(thee->vmem, 100, sizeof(double),
576  (void **)&(thee->rparm));
577  Vmem_free(thee->vmem, thee->pmgp->niwk, sizeof(int),
578  (void **)&(thee->iwork));
579  Vmem_free(thee->vmem, thee->pmgp->nrwk, sizeof(double),
580  (void **)&(thee->rwork));
581  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
582  (void **)&(thee->charge));
583  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
584  (void **)&(thee->kappa));
585  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
586  (void **)&(thee->pot));
587  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
588  (void **)&(thee->epsx));
589  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
590  (void **)&(thee->epsy));
591  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
592  (void **)&(thee->epsz));
593  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
594  (void **)&(thee->a1cf));
595  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
596  (void **)&(thee->a2cf));
597  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
598  (void **)&(thee->a3cf));
599  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
600  (void **)&(thee->ccf));
601  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
602  (void **)&(thee->fcf));
603  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
604  (void **)&(thee->tcf));
605  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
606  (void **)&(thee->u));
607  Vmem_free(thee->vmem, 5*(thee->pmgp->nx), sizeof(double),
608  (void **)&(thee->xf));
609  Vmem_free(thee->vmem, 5*(thee->pmgp->ny), sizeof(double),
610  (void **)&(thee->yf));
611  Vmem_free(thee->vmem, 5*(thee->pmgp->nz), sizeof(double),
612  (void **)&(thee->zf));
613  Vmem_free(thee->vmem, 10*(thee->pmgp->ny)*(thee->pmgp->nz), sizeof(double),
614  (void **)&(thee->gxcf));
615  Vmem_free(thee->vmem, 10*(thee->pmgp->nx)*(thee->pmgp->nz), sizeof(double),
616  (void **)&(thee->gycf));
617  Vmem_free(thee->vmem, 10*(thee->pmgp->nx)*(thee->pmgp->ny), sizeof(double),
618  (void **)&(thee->gzcf));
619  Vmem_free(thee->vmem, (thee->pmgp->nx)*(thee->pmgp->ny)*(thee->pmgp->nz),
620  sizeof(double), (void **)&(thee->pvec));
621 
622  Vmem_dtor(&(thee->vmem));
623 }
624 
625 VPUBLIC void Vpmg_setPart(Vpmg *thee, double lowerCorner[3],
626  double upperCorner[3], int bflags[6]) {
627 
628  Valist *alist;
629  Vatom *atom;
630  int i, j, k, nx, ny, nz;
631  double xmin, ymin, zmin, x, y, z, hx, hy, hzed, xok, yok, zok;
632  double x0,x1,y0,y1,z0,z1;
633 
634  nx = thee->pmgp->nx;
635  ny = thee->pmgp->ny;
636  nz = thee->pmgp->nz;
637  hx = thee->pmgp->hx;
638  hy = thee->pmgp->hy;
639  hzed = thee->pmgp->hzed;
640  xmin = thee->pmgp->xcent - 0.5*hx*(nx-1);
641  ymin = thee->pmgp->ycent - 0.5*hy*(ny-1);
642  zmin = thee->pmgp->zcent - 0.5*hzed*(nz-1);
643 
644  xok = 0;
645  yok = 0;
646  zok = 0;
647 
648  /* We need have called Vpmg_fillco first */
649 
650  alist = thee->pbe->alist;
651 
652  Vnm_print(0, "Vpmg_setPart: lower corner = (%g, %g, %g)\n",
653  lowerCorner[0], lowerCorner[1], lowerCorner[2]);
654  Vnm_print(0, "Vpmg_setPart: upper corner = (%g, %g, %g)\n",
655  upperCorner[0], upperCorner[1], upperCorner[2]);
656  Vnm_print(0, "Vpmg_setPart: actual minima = (%g, %g, %g)\n",
657  xmin, ymin, zmin);
658  Vnm_print(0, "Vpmg_setPart: actual maxima = (%g, %g, %g)\n",
659  xmin+hx*(nx-1), ymin+hy*(ny-1), zmin+hzed*(nz-1));
660  Vnm_print(0, "Vpmg_setPart: bflag[FRONT] = %d\n",
661  bflags[VAPBS_FRONT]);
662  Vnm_print(0, "Vpmg_setPart: bflag[BACK] = %d\n",
663  bflags[VAPBS_BACK]);
664  Vnm_print(0, "Vpmg_setPart: bflag[LEFT] = %d\n",
665  bflags[VAPBS_LEFT]);
666  Vnm_print(0, "Vpmg_setPart: bflag[RIGHT] = %d\n",
667  bflags[VAPBS_RIGHT]);
668  Vnm_print(0, "Vpmg_setPart: bflag[UP] = %d\n",
669  bflags[VAPBS_UP]);
670  Vnm_print(0, "Vpmg_setPart: bflag[DOWN] = %d\n",
671  bflags[VAPBS_DOWN]);
672 
673  /* Identify atoms as inside, outside, or on the border
674  If on the border, use the bflags to determine if there
675  is an adjacent processor - if so, this atom should be equally
676  shared. */
677 
678  for (i=0; i<Valist_getNumberAtoms(alist); i++) {
679  atom = Valist_getAtom(alist, i);
680 
681  if ((atom->position[0] < upperCorner[0]) &&
682  (atom->position[0] > lowerCorner[0])) xok = 1;
683  else {
684  if ((VABS(atom->position[0] - lowerCorner[0]) < VPMGSMALL) &&
685  (bflags[VAPBS_LEFT] == 0)) xok = 1;
686  else if ((VABS(atom->position[0] - lowerCorner[0]) < VPMGSMALL) &&
687  (bflags[VAPBS_LEFT] == 1)) xok = 0.5;
688  else if ((VABS(atom->position[0] - upperCorner[0]) < VPMGSMALL) &&
689  (bflags[VAPBS_RIGHT] == 0)) xok = 1;
690  else if ((VABS(atom->position[0] - upperCorner[0]) < VPMGSMALL) &&
691  (bflags[VAPBS_RIGHT] == 1)) xok = 0.5;
692  else xok = 0;
693  }
694  if ((atom->position[1] < upperCorner[1]) &&
695  (atom->position[1] > lowerCorner[1])) yok = 1;
696  else {
697  if ((VABS(atom->position[1] - lowerCorner[1]) < VPMGSMALL) &&
698  (bflags[VAPBS_BACK] == 0)) yok = 1;
699  else if ((VABS(atom->position[1] - lowerCorner[1]) < VPMGSMALL) &&
700  (bflags[VAPBS_BACK] == 1)) yok = 0.5;
701  else if ((VABS(atom->position[1] - upperCorner[1]) < VPMGSMALL) &&
702  (bflags[VAPBS_FRONT] == 0)) yok = 1;
703  else if ((VABS(atom->position[1] - upperCorner[1]) < VPMGSMALL) &&
704  (bflags[VAPBS_FRONT] == 1)) yok = 0.5;
705  else yok = 0;
706  }
707  if ((atom->position[2] < upperCorner[2]) &&
708  (atom->position[2] > lowerCorner[2])) zok = 1;
709  else {
710  if ((VABS(atom->position[2] - lowerCorner[2]) < VPMGSMALL) &&
711  (bflags[VAPBS_DOWN] == 0)) zok = 1;
712  else if ((VABS(atom->position[2] - lowerCorner[2]) < VPMGSMALL) &&
713  (bflags[VAPBS_DOWN] == 1)) zok = 0.5;
714  else if ((VABS(atom->position[2] - upperCorner[2]) < VPMGSMALL) &&
715  (bflags[VAPBS_UP] == 0)) zok = 1;
716  else if ((VABS(atom->position[2] - upperCorner[2]) < VPMGSMALL) &&
717  (bflags[VAPBS_UP] == 1)) zok = 0.5;
718  else zok = 0;
719  }
720 
721  atom->partID = xok*yok*zok;
722  /*
723  Vnm_print(1, "DEBUG (%s, %d): atom->position[0] - upperCorner[0] = %g\n",
724  __FILE__, __LINE__, atom->position[0] - upperCorner[0]);
725  Vnm_print(1, "DEBUG (%s, %d): atom->position[0] - lowerCorner[0] = %g\n",
726  __FILE__, __LINE__, atom->position[0] - lowerCorner[0]);
727  Vnm_print(1, "DEBUG (%s, %d): atom->position[1] - upperCorner[1] = %g\n",
728  __FILE__, __LINE__, atom->position[1] - upperCorner[1]);
729  Vnm_print(1, "DEBUG (%s, %d): atom->position[1] - lowerCorner[1] = %g\n",
730  __FILE__, __LINE__, atom->position[1] - lowerCorner[1]);
731  Vnm_print(1, "DEBUG (%s, %d): atom->position[2] - upperCorner[2] = %g\n",
732  __FILE__, __LINE__, atom->position[2] - upperCorner[2]);
733  Vnm_print(1, "DEBUG (%s, %d): atom->position[2] - lowerCorner[0] = %g\n",
734  __FILE__, __LINE__, atom->position[2] - lowerCorner[2]);
735  Vnm_print(1, "DEBUG (%s, %d): xok = %g, yok = %g, zok = %g\n",
736  __FILE__, __LINE__, xok, yok, zok);
737  */
738 
739  }
740 
741  /* Load up pvec -
742  For all points within h{axis}/2 of a border - use a gradient
743  to determine the pvec weight.
744  Points on the boundary depend on the presence of an adjacent
745  processor. */
746 
747  for (i=0; i<(nx*ny*nz); i++) thee->pvec[i] = 0.0;
748 
749  for (i=0; i<nx; i++) {
750  xok = 0.0;
751  x = i*hx + xmin;
752  if ( (x < (upperCorner[0]-hx/2)) &&
753  (x > (lowerCorner[0]+hx/2))
754  ) xok = 1.0;
755  else if ( (VABS(x - lowerCorner[0]) < VPMGSMALL) &&
756  (bflags[VAPBS_LEFT] == 0)) xok = 1.0;
757  else if ((VABS(x - lowerCorner[0]) < VPMGSMALL) &&
758  (bflags[VAPBS_LEFT] == 1)) xok = 0.5;
759  else if ((VABS(x - upperCorner[0]) < VPMGSMALL) &&
760  (bflags[VAPBS_RIGHT] == 0)) xok = 1.0;
761  else if ((VABS(x - upperCorner[0]) < VPMGSMALL) &&
762  (bflags[VAPBS_RIGHT] == 1)) xok = 0.5;
763  else if ((x > (upperCorner[0] + hx/2)) || (x < (lowerCorner[0] - hx/2))) xok = 0.0;
764  else if ((x < (upperCorner[0] + hx/2)) || (x > (lowerCorner[0] - hx/2))) {
765  x0 = VMAX2(x - hx/2, lowerCorner[0]);
766  x1 = VMIN2(x + hx/2, upperCorner[0]);
767  xok = VABS(x1-x0)/hx;
768 
769  if (xok < 0.0) {
770  if (VABS(xok) < VPMGSMALL) xok = 0.0;
771  else {
772  Vnm_print(2, "Vpmg_setPart: fell off x-interval (%1.12E)!\n",
773  xok);
774  VASSERT(0);
775  }
776  }
777  if (xok > 1.0) {
778  if (VABS(xok - 1.0) < VPMGSMALL) xok = 1.0;
779  else {
780  Vnm_print(2, "Vpmg_setPart: fell off x-interval (%1.12E)!\n",
781  xok);
782  VASSERT(0);
783  }
784  }
785 
786  } else xok = 0.0;
787 
788  for (j=0; j<ny; j++) {
789  yok = 0.0;
790  y = j*hy + ymin;
791  if ((y < (upperCorner[1]-hy/2)) && (y > (lowerCorner[1]+hy/2))) yok = 1.0;
792  else if ((VABS(y - lowerCorner[1]) < VPMGSMALL) &&
793  (bflags[VAPBS_BACK] == 0)) yok = 1.0;
794  else if ((VABS(y - lowerCorner[1]) < VPMGSMALL) &&
795  (bflags[VAPBS_BACK] == 1)) yok = 0.5;
796  else if ((VABS(y - upperCorner[1]) < VPMGSMALL) &&
797  (bflags[VAPBS_FRONT] == 0)) yok = 1.0;
798  else if ((VABS(y - upperCorner[1]) < VPMGSMALL) &&
799  (bflags[VAPBS_FRONT] == 1)) yok = 0.5;
800  else if ((y > (upperCorner[1] + hy/2)) || (y < (lowerCorner[1] - hy/2))) yok=0.0;
801  else if ((y < (upperCorner[1] + hy/2)) || (y > (lowerCorner[1] - hy/2))){
802  y0 = VMAX2(y - hy/2, lowerCorner[1]);
803  y1 = VMIN2(y + hy/2, upperCorner[1]);
804  yok = VABS(y1-y0)/hy;
805 
806  if (yok < 0.0) {
807  if (VABS(yok) < VPMGSMALL) yok = 0.0;
808  else {
809  Vnm_print(2, "Vpmg_setPart: fell off y-interval (%1.12E)!\n",
810  yok);
811  VASSERT(0);
812  }
813  }
814  if (yok > 1.0) {
815  if (VABS(yok - 1.0) < VPMGSMALL) yok = 1.0;
816  else {
817  Vnm_print(2, "Vpmg_setPart: fell off y-interval (%1.12E)!\n",
818  yok);
819  VASSERT(0);
820  }
821  }
822  }
823  else yok=0.0;
824 
825  for (k=0; k<nz; k++) {
826  zok = 0.0;
827  z = k*hzed + zmin;
828  if ((z < (upperCorner[2]-hzed/2)) && (z > (lowerCorner[2]+hzed/2))) zok = 1.0;
829  else if ((VABS(z - lowerCorner[2]) < VPMGSMALL) &&
830  (bflags[VAPBS_DOWN] == 0)) zok = 1.0;
831  else if ((VABS(z - lowerCorner[2]) < VPMGSMALL) &&
832  (bflags[VAPBS_DOWN] == 1)) zok = 0.5;
833  else if ((VABS(z - upperCorner[2]) < VPMGSMALL) &&
834  (bflags[VAPBS_UP] == 0)) zok = 1.0;
835  else if ((VABS(z - upperCorner[2]) < VPMGSMALL) &&
836  (bflags[VAPBS_UP] == 1)) zok = 0.5;
837  else if ((z > (upperCorner[2] + hzed/2)) || (z < (lowerCorner[2] - hzed/2))) zok=0.0;
838  else if ((z < (upperCorner[2] + hzed/2)) || (z > (lowerCorner[2] - hzed/2))){
839  z0 = VMAX2(z - hzed/2, lowerCorner[2]);
840  z1 = VMIN2(z + hzed/2, upperCorner[2]);
841  zok = VABS(z1-z0)/hzed;
842 
843  if (zok < 0.0) {
844  if (VABS(zok) < VPMGSMALL) zok = 0.0;
845  else {
846  Vnm_print(2, "Vpmg_setPart: fell off z-interval (%1.12E)!\n",
847  zok);
848  VASSERT(0);
849  }
850  }
851  if (zok > 1.0) {
852  if (VABS(zok - 1.0) < VPMGSMALL) zok = 1.0;
853  else {
854  Vnm_print(2, "Vpmg_setPart: fell off z-interval (%1.12E)!\n",
855  zok);
856  VASSERT(0);
857  }
858  }
859  }
860  else zok = 0.0;
861 
862  if (VABS(xok*yok*zok) < VPMGSMALL) thee->pvec[IJK(i,j,k)] = 0.0;
863  else thee->pvec[IJK(i,j,k)] = xok*yok*zok;
864 
865  }
866  }
867  }
868 }
869 
870 VPUBLIC void Vpmg_unsetPart(Vpmg *thee) {
871 
872  int i, nx, ny, nz;
873  Vatom *atom;
874  Valist *alist;
875 
876  VASSERT(thee != VNULL);
877 
878  nx = thee->pmgp->nx;
879  ny = thee->pmgp->ny;
880  nz = thee->pmgp->nz;
881  alist = thee->pbe->alist;
882 
883  for (i=0; i<(nx*ny*nz); i++) thee->pvec[i] = 1;
884  for (i=0; i<Valist_getNumberAtoms(alist); i++) {
885  atom = Valist_getAtom(alist, i);
886  atom->partID = 1;
887  }
888 }
889 
890 VPUBLIC int Vpmg_fillArray(Vpmg *thee, double *vec, Vdata_Type type,
891  double parm, Vhal_PBEType pbetype, PBEparm *pbeparm) {
892 
893  Vacc *acc = VNULL;
894  Vpbe *pbe = VNULL;
895  Vgrid *grid = VNULL;
896  Vatom *atoms = VNULL;
897  Valist *alist = VNULL;
898  double position[3], hx, hy, hzed, xmin, ymin, zmin;
899  double grad[3], eps, epsp, epss, zmagic;
900  int i, j, k, l, nx, ny, nz, ichop;
901 
902  pbe = thee->pbe;
903  acc = Vpbe_getVacc(pbe);
904  nx = thee->pmgp->nx;
905  ny = thee->pmgp->ny;
906  nz = thee->pmgp->nz;
907  hx = thee->pmgp->hx;
908  hy = thee->pmgp->hy;
909  hzed = thee->pmgp->hzed;
910  xmin = thee->pmgp->xmin;
911  ymin = thee->pmgp->ymin;
912  zmin = thee->pmgp->zmin;
913  epsp = Vpbe_getSoluteDiel(pbe);
914  epss = Vpbe_getSolventDiel(pbe);
915  zmagic = Vpbe_getZmagic(pbe);
916 
917  if (!(thee->filled)) {
918  Vnm_print(2, "Vpmg_fillArray: need to call Vpmg_fillco first!\n");
919  return 0;
920  }
921 
922  switch (type) {
923 
924  case VDT_CHARGE:
925 
926  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->charge[i]/zmagic;
927  break;
928 
929  case VDT_DIELX:
930 
931  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsx[i];
932  break;
933 
934  case VDT_DIELY:
935 
936  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsy[i];
937  break;
938 
939  case VDT_DIELZ:
940 
941  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsz[i];
942  break;
943 
944  case VDT_KAPPA:
945 
946  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->kappa[i];
947  break;
948 
949  case VDT_POT:
950 
951  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->u[i];
952  break;
953 
954  case VDT_ATOMPOT:
955  alist = thee->pbe->alist;
956  atoms = alist[pbeparm->molid-1].atoms;
957  grid = Vgrid_ctor(nx, ny, nz, hx, hy,
958  hzed, xmin, ymin, zmin,thee->u);
959  for (i=0; i<alist[pbeparm->molid-1].number;i++) {
960  position[0] = atoms[i].position[0];
961  position[1] = atoms[i].position[1];
962  position[2] = atoms[i].position[2];
963 
964  Vgrid_value(grid, position, &vec[i]);
965  }
966  Vgrid_dtor(&grid);
967  break;
968 
969  case VDT_SMOL:
970 
971  for (k=0; k<nz; k++) {
972  for (j=0; j<ny; j++) {
973  for (i=0; i<nx; i++) {
974 
975  position[0] = i*hx + xmin;
976  position[1] = j*hy + ymin;
977  position[2] = k*hzed + zmin;
978 
979  vec[IJK(i,j,k)] = (Vacc_molAcc(acc,position,parm));
980  }
981  }
982  }
983  break;
984 
985  case VDT_SSPL:
986 
987  for (k=0; k<nz; k++) {
988  for (j=0; j<ny; j++) {
989  for (i=0; i<nx; i++) {
990 
991  position[0] = i*hx + xmin;
992  position[1] = j*hy + ymin;
993  position[2] = k*hzed + zmin;
994 
995  vec[IJK(i,j,k)] = Vacc_splineAcc(acc,position,parm,0);
996  }
997  }
998  }
999  break;
1000 
1001  case VDT_VDW:
1002 
1003  for (k=0; k<nz; k++) {
1004  for (j=0; j<ny; j++) {
1005  for (i=0; i<nx; i++) {
1006 
1007  position[0] = i*hx + xmin;
1008  position[1] = j*hy + ymin;
1009  position[2] = k*hzed + zmin;
1010 
1011  vec[IJK(i,j,k)] = Vacc_vdwAcc(acc,position);
1012  }
1013  }
1014  }
1015  break;
1016 
1017  case VDT_IVDW:
1018 
1019  for (k=0; k<nz; k++) {
1020  for (j=0; j<ny; j++) {
1021  for (i=0; i<nx; i++) {
1022 
1023  position[0] = i*hx + xmin;
1024  position[1] = j*hy + ymin;
1025  position[2] = k*hzed + zmin;
1026 
1027  vec[IJK(i,j,k)] = Vacc_ivdwAcc(acc,position,parm);
1028  }
1029  }
1030  }
1031  break;
1032 
1033  case VDT_LAP:
1034 
1035  grid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin,
1036  thee->u);
1037  for (k=0; k<nz; k++) {
1038  for (j=0; j<ny; j++) {
1039  for (i=0; i<nx; i++) {
1040 
1041  if ((k==0) || (k==(nz-1)) ||
1042  (j==0) || (j==(ny-1)) ||
1043  (i==0) || (i==(nx-1))) {
1044 
1045  vec[IJK(i,j,k)] = 0;
1046 
1047  } else {
1048  position[0] = i*hx + xmin;
1049  position[1] = j*hy + ymin;
1050  position[2] = k*hzed + zmin;
1051  VASSERT(Vgrid_curvature(grid,position, 1,
1052  &(vec[IJK(i,j,k)])));
1053  }
1054  }
1055  }
1056  }
1057  Vgrid_dtor(&grid);
1058  break;
1059 
1060  case VDT_EDENS:
1061 
1062  grid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin,
1063  thee->u);
1064  for (k=0; k<nz; k++) {
1065  for (j=0; j<ny; j++) {
1066  for (i=0; i<nx; i++) {
1067 
1068  position[0] = i*hx + xmin;
1069  position[1] = j*hy + ymin;
1070  position[2] = k*hzed + zmin;
1071  VASSERT(Vgrid_gradient(grid, position, grad));
1072  eps = epsp + (epss-epsp)*Vacc_molAcc(acc, position,
1073  pbe->solventRadius);
1074  vec[IJK(i,j,k)] = 0.0;
1075  for (l=0; l<3; l++)
1076  vec[IJK(i,j,k)] += eps*VSQR(grad[l]);
1077  }
1078  }
1079  }
1080  Vgrid_dtor(&grid);
1081  break;
1082 
1083  case VDT_NDENS:
1084 
1085  for (k=0; k<nz; k++) {
1086  for (j=0; j<ny; j++) {
1087  for (i=0; i<nx; i++) {
1088 
1089  position[0] = i*hx + xmin;
1090  position[1] = j*hy + ymin;
1091  position[2] = k*hzed + zmin;
1092  vec[IJK(i,j,k)] = 0.0;
1093  if ( VABS(Vacc_ivdwAcc(acc,
1094  position, pbe->maxIonRadius) - 1.0) < VSMALL) {
1095  for (l=0; l<pbe->numIon; l++) {
1096  if (pbetype == PBE_NPBE || pbetype == PBE_SMPBE /* SMPBE Added */) {
1097  vec[IJK(i,j,k)] += (pbe->ionConc[l]
1098  * Vcap_exp(-pbe->ionQ[l]*thee->u[IJK(i,j,k)],
1099  &ichop));
1100  } else if (pbetype == PBE_LPBE){
1101  vec[IJK(i,j,k)] += (pbe->ionConc[l]
1102  * (1 - pbe->ionQ[l]*thee->u[IJK(i,j,k)]));
1103  }
1104  }
1105  }
1106  }
1107  }
1108  }
1109  break;
1110 
1111  case VDT_QDENS:
1112 
1113  for (k=0; k<nz; k++) {
1114  for (j=0; j<ny; j++) {
1115  for (i=0; i<nx; i++) {
1116 
1117  position[0] = i*hx + xmin;
1118  position[1] = j*hy + ymin;
1119  position[2] = k*hzed + zmin;
1120  vec[IJK(i,j,k)] = 0.0;
1121  if ( VABS(Vacc_ivdwAcc(acc,
1122  position, pbe->maxIonRadius) - 1.0) < VSMALL) {
1123  for (l=0; l<pbe->numIon; l++) {
1124  if (pbetype == PBE_NPBE || pbetype == PBE_SMPBE /* SMPBE Added */) {
1125  vec[IJK(i,j,k)] += (pbe->ionConc[l]
1126  * pbe->ionQ[l]
1127  * Vcap_exp(-pbe->ionQ[l]*thee->u[IJK(i,j,k)],
1128  &ichop));
1129  } else if (pbetype == PBE_LPBE) {
1130  vec[IJK(i,j,k)] += (pbe->ionConc[l]
1131  * pbe->ionQ[l]
1132  * (1 - pbe->ionQ[l]*thee->u[IJK(i,j,k)]));
1133  }
1134  }
1135  }
1136  }
1137  }
1138  }
1139  break;
1140 
1141  default:
1142 
1143  Vnm_print(2, "main: Bogus data type (%d)!\n", type);
1144  return 0;
1145  break;
1146 
1147  }
1148 
1149  return 1;
1150 
1151 }
1152 
1153 VPRIVATE double Vpmg_polarizEnergy(Vpmg *thee,
1154  int extFlag
1155  ) {
1156 
1157  int i,
1158  j,
1159  k,
1160  ijk,
1161  nx,
1162  ny,
1163  nz,
1164  iatom;
1165  double xmin,
1166  ymin,
1167  zmin,
1168  //x, // gcc: not used
1169  //y,
1170  //z,
1171  hx,
1172  hy,
1173  hzed,
1174  epsp,
1175  lap,
1176  pt[3],
1177  T,
1178  pre,
1179  polq,
1180  dist2,
1181  dist,
1182  energy,
1183  q,
1184  *charge,
1185  *pos,
1186  eps_w;
1187  Vgrid *potgrid;
1188  Vpbe *pbe;
1189  Valist *alist;
1190  Vatom *atom;
1191 
1192  xmin = thee->pmgp->xmin;
1193  ymin = thee->pmgp->ymin;
1194  zmin = thee->pmgp->ymin;
1195  hx = thee->pmgp->hx;
1196  hy = thee->pmgp->hy;
1197  hzed = thee->pmgp->hzed;
1198  nx = thee->pmgp->nx;
1199  ny = thee->pmgp->ny;
1200  nz = thee->pmgp->nz;
1201  pbe = thee->pbe;
1202  epsp = Vpbe_getSoluteDiel(pbe);
1203  eps_w = Vpbe_getSolventDiel(pbe);
1204  alist = pbe->alist;
1205  charge = thee->charge;
1206 
1207  /* Calculate the prefactor for Coulombic calculations */
1208  T = Vpbe_getTemperature(pbe);
1209  pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
1210  pre = pre*(1.0e10);
1211 
1212  /* Set up Vgrid object with solution */
1213  potgrid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin, thee->u);
1214 
1215  /* Calculate polarization charge */
1216  energy = 0.0;
1217  for (i=1; i<(nx-1); i++) {
1218  pt[0] = xmin + hx*i;
1219  for (j=1; j<(ny-1); j++) {
1220  pt[1] = ymin + hy*j;
1221  for (k=1; k<(nz-1); k++) {
1222  pt[2] = zmin + hzed*k;
1223 
1224  /* Calculate polarization charge */
1225  VASSERT(Vgrid_curvature(potgrid, pt, 1, &lap));
1226  ijk = IJK(i,j,k);
1227  polq = charge[ijk] + epsp*lap*3.0;
1228 
1229  /* Calculate interaction energy with atoms */
1230  if (VABS(polq) > VSMALL) {
1231  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
1232  atom = Valist_getAtom(alist, iatom);
1233  q = Vatom_getCharge(atom);
1234  pos = Vatom_getPosition(atom);
1235  dist2 = VSQR(pos[0]-pt[0]) + VSQR(pos[1]-pt[1]) \
1236  + VSQR(pos[2]-pt[2]);
1237  dist = VSQRT(dist2);
1238 
1239  if (dist < VSMALL) {
1240  Vnm_print(2, "Vpmg_polarizEnergy: atom on grid point; ignoring!\n");
1241  } else {
1242  energy = energy + polq*q/dist;
1243  }
1244  }
1245  }
1246  }
1247  }
1248  }
1249 
1250  return pre*energy;
1251 }
1252 
1253 VPUBLIC double Vpmg_energy(Vpmg *thee,
1254  int extFlag
1255  ) {
1256 
1257  double totEnergy = 0.0,
1258  dielEnergy = 0.0,
1259  qmEnergy = 0.0,
1260  qfEnergy = 0.0;
1261 
1262  VASSERT(thee != VNULL);
1263 
1264  if ((thee->pmgp->nonlin) && (Vpbe_getBulkIonicStrength(thee->pbe) > 0.)) {
1265  Vnm_print(0, "Vpmg_energy: calculating full PBE energy\n");
1266  qmEnergy = Vpmg_qmEnergy(thee, extFlag);
1267  Vnm_print(0, "Vpmg_energy: qmEnergy = %1.12E kT\n", qmEnergy);
1268  qfEnergy = Vpmg_qfEnergy(thee, extFlag);
1269  Vnm_print(0, "Vpmg_energy: qfEnergy = %1.12E kT\n", qfEnergy);
1270  dielEnergy = Vpmg_dielEnergy(thee, extFlag);
1271  Vnm_print(0, "Vpmg_energy: dielEnergy = %1.12E kT\n", dielEnergy);
1272  totEnergy = qfEnergy - dielEnergy - qmEnergy;
1273  } else {
1274  Vnm_print(0, "Vpmg_energy: calculating only q-phi energy\n");
1275  qfEnergy = Vpmg_qfEnergy(thee, extFlag);
1276  Vnm_print(0, "Vpmg_energy: qfEnergy = %1.12E kT\n", qfEnergy);
1277  totEnergy = 0.5*qfEnergy;
1278  }
1279 
1280  return totEnergy;
1281 
1282 }
1283 
1284 VPUBLIC double Vpmg_dielEnergy(Vpmg *thee,
1285  int extFlag
1286  ) {
1287 
1288  double hx,
1289  hy,
1290  hzed,
1291  energy,
1292  nrgx,
1293  nrgy,
1294  nrgz,
1295  pvecx,
1296  pvecy,
1297  pvecz;
1298  int i,
1299  j,
1300  k,
1301  nx,
1302  ny,
1303  nz;
1304 
1305  VASSERT(thee != VNULL);
1306 
1307  /* Get the mesh information */
1308  nx = thee->pmgp->nx;
1309  ny = thee->pmgp->ny;
1310  nz = thee->pmgp->nz;
1311  hx = thee->pmgp->hx;
1312  hy = thee->pmgp->hy;
1313  hzed = thee->pmgp->hzed;
1314 
1315  energy = 0.0;
1316 
1317  if (!thee->filled) {
1318  Vnm_print(2, "Vpmg_dielEnergy: Need to call Vpmg_fillco!\n");
1319  VASSERT(0);
1320  }
1321 
1322  for (k=0; k<(nz-1); k++) {
1323  for (j=0; j<(ny-1); j++) {
1324  for (i=0; i<(nx-1); i++) {
1325  pvecx = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i+1,j,k)]);
1326  pvecy = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j+1,k)]);
1327  pvecz = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j,k+1)]);
1328  nrgx = thee->epsx[IJK(i,j,k)]*pvecx
1329  * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i+1,j,k)])/hx);
1330  nrgy = thee->epsy[IJK(i,j,k)]*pvecy
1331  * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i,j+1,k)])/hy);
1332  nrgz = thee->epsz[IJK(i,j,k)]*pvecz
1333  * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i,j,k+1)])/hzed);
1334  energy += (nrgx + nrgy + nrgz);
1335  }
1336  }
1337  }
1338 
1339  energy = 0.5*energy*hx*hy*hzed;
1340  energy = energy/Vpbe_getZmagic(thee->pbe);
1341 
1342  if (extFlag == 1) energy += (thee->extDiEnergy);
1343 
1344  return energy;
1345 }
1346 
1347 VPUBLIC double Vpmg_dielGradNorm(Vpmg *thee) {
1348 
1349  double hx, hy, hzed, energy, nrgx, nrgy, nrgz, pvecx, pvecy, pvecz;
1350  int i, j, k, nx, ny, nz;
1351 
1352  VASSERT(thee != VNULL);
1353 
1354  /* Get the mesh information */
1355  nx = thee->pmgp->nx;
1356  ny = thee->pmgp->ny;
1357  nz = thee->pmgp->nz;
1358  hx = thee->pmgp->hx;
1359  hy = thee->pmgp->hy;
1360  hzed = thee->pmgp->hzed;
1361 
1362  energy = 0.0;
1363 
1364  if (!thee->filled) {
1365  Vnm_print(2, "Vpmg_dielGradNorm: Need to call Vpmg_fillco!\n");
1366  VASSERT(0);
1367  }
1368 
1369  for (k=1; k<nz; k++) {
1370  for (j=1; j<ny; j++) {
1371  for (i=1; i<nx; i++) {
1372  pvecx = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i-1,j,k)]);
1373  pvecy = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j-1,k)]);
1374  pvecz = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j,k-1)]);
1375  nrgx = pvecx
1376  * VSQR((thee->epsx[IJK(i,j,k)]-thee->epsx[IJK(i-1,j,k)])/hx);
1377  nrgy = pvecy
1378  * VSQR((thee->epsy[IJK(i,j,k)]-thee->epsy[IJK(i,j-1,k)])/hy);
1379  nrgz = pvecz
1380  * VSQR((thee->epsz[IJK(i,j,k)]-thee->epsz[IJK(i,j,k-1)])/hzed);
1381  energy += VSQRT(nrgx + nrgy + nrgz);
1382  }
1383  }
1384  }
1385 
1386  energy = energy*hx*hy*hzed;
1387 
1388  return energy;
1389 }
1390 
1391 VPUBLIC double Vpmg_qmEnergy(Vpmg *thee,
1392  int extFlag
1393  ) {
1394 
1395  double energy;
1396 
1397  if(thee->pbe->ipkey == IPKEY_SMPBE){
1398  energy = Vpmg_qmEnergySMPBE(thee,extFlag);
1399  }else{
1400  energy = Vpmg_qmEnergyNONLIN(thee,extFlag);
1401  }
1402 
1403  return energy;
1404 }
1405 
1406 VPRIVATE double Vpmg_qmEnergyNONLIN(Vpmg *thee,
1407  int extFlag
1408  ) {
1409 
1410  double hx,
1411  hy,
1412  hzed,
1413  energy,
1414  ionConc[MAXION],
1415  ionRadii[MAXION],
1416  ionQ[MAXION],
1417  zkappa2,
1418  ionstr,
1419  zks2;
1420  int i, /* Loop variable */
1421  j,
1422  nx,
1423  ny,
1424  nz,
1425  nion,
1426  ichop,
1427  nchop,
1428  len; /* Stores number of iterations for loops to avoid multiple recalculations */
1429 
1430  VASSERT(thee != VNULL);
1431 
1432  /* Get the mesh information */
1433  nx = thee->pmgp->nx;
1434  ny = thee->pmgp->ny;
1435  nz = thee->pmgp->nz;
1436  hx = thee->pmgp->hx;
1437  hy = thee->pmgp->hy;
1438  hzed = thee->pmgp->hzed;
1439  zkappa2 = Vpbe_getZkappa2(thee->pbe);
1440  ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
1441 
1442  /* Bail if we're at zero ionic strength */
1443  if (zkappa2 < VSMALL) {
1444 
1445 #ifndef VAPBSQUIET
1446  Vnm_print(0, "Vpmg_qmEnergy: Zero energy for zero ionic strength!\n");
1447 #endif
1448 
1449  return 0.0;
1450  }
1451  zks2 = 0.5*zkappa2/ionstr;
1452 
1453  if (!thee->filled) {
1454  Vnm_print(2, "Vpmg_qmEnergy: Need to call Vpmg_fillco()!\n");
1455  VASSERT(0);
1456  }
1457 
1458  energy = 0.0;
1459  nchop = 0;
1460  Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
1461  if (thee->pmgp->nonlin) {
1462  Vnm_print(0, "Vpmg_qmEnergy: Calculating nonlinear energy\n");
1463  for (i=0, len=nx*ny*nz; i<len; i++) {
1464  if (thee->pvec[i]*thee->kappa[i] > VSMALL) {
1465  for (j=0; j<nion; j++) {
1466  energy += (thee->pvec[i]*thee->kappa[i]*zks2
1467  * ionConc[j]
1468  * (Vcap_exp(-ionQ[j]*thee->u[i], &ichop)-1.0));
1469  nchop += ichop;
1470  }
1471  }
1472  }
1473  if (nchop > 0){
1474  Vnm_print(2, "Vpmg_qmEnergy: Chopped EXP %d times!\n",nchop);
1475  Vnm_print(2, "\nERROR! Detected large potential values in energy evaluation! \nERROR! This calculation failed -- please report to the APBS developers!\n\n");
1476  VASSERT(0);
1477  }
1478  } else {
1479  /* Zkappa2 OK here b/c LPBE approx */
1480  Vnm_print(0, "Vpmg_qmEnergy: Calculating linear energy\n");
1481  for (i=0, len=nx*ny*nz; i<len; i++) {
1482  if (thee->pvec[i]*thee->kappa[i] > VSMALL)
1483  energy += (thee->pvec[i]*zkappa2*thee->kappa[i]*VSQR(thee->u[i]));
1484  }
1485  energy = 0.5*energy;
1486  }
1487  energy = energy*hx*hy*hzed;
1488  energy = energy/Vpbe_getZmagic(thee->pbe);
1489 
1490  if (extFlag == 1) energy += thee->extQmEnergy;
1491 
1492  return energy;
1493 }
1494 
1495 VPUBLIC double Vpmg_qmEnergySMPBE(Vpmg *thee,
1496  int extFlag
1497  ) {
1498 
1499  double hx,
1500  hy,
1501  hzed,
1502  energy,
1503  ionConc[MAXION],
1504  ionRadii[MAXION],
1505  ionQ[MAXION],
1506  zkappa2,
1507  ionstr,
1508  zks2;
1509  int i,
1510  //j, // gcc: not used
1511  nx,
1512  ny,
1513  nz,
1514  nion,
1515  //ichop, // gcc: not used
1516  nchop,
1517  len; /* Loop variable */
1518 
1519  /* SMPB Modification (vchu, 09/21/06)*/
1520  /* variable declarations for SMPB energy terms */
1521  double a,
1522  k,
1523  z1,
1524  z2,
1525  z3,
1526  cb1,
1527  cb2,
1528  cb3,
1529  a1,
1530  a2,
1531  a3,
1532  c1,
1533  c2,
1534  c3,
1535  currEnergy,
1536  fracOccA,
1537  fracOccB,
1538  fracOccC,
1539  phi,
1540  gpark,
1541  denom;
1542  // Na; /**< @todo remove if no conflicts are caused - This constant is already defined in vpde.h. no need to redefine. */
1543  int ichop1,
1544  ichop2,
1545  ichop3;
1546 
1547  VASSERT(thee != VNULL);
1548 
1549  /* Get the mesh information */
1550  nx = thee->pmgp->nx;
1551  ny = thee->pmgp->ny;
1552  nz = thee->pmgp->nz;
1553  hx = thee->pmgp->hx;
1554  hy = thee->pmgp->hy;
1555  hzed = thee->pmgp->hzed;
1556  zkappa2 = Vpbe_getZkappa2(thee->pbe);
1557  ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
1558 
1559  /* Bail if we're at zero ionic strength */
1560  if (zkappa2 < VSMALL) {
1561 
1562 #ifndef VAPBSQUIET
1563  Vnm_print(0, "Vpmg_qmEnergySMPBE: Zero energy for zero ionic strength!\n");
1564 #endif
1565 
1566  return 0.0;
1567  }
1568  zks2 = 0.5*zkappa2/ionstr;
1569 
1570  if (!thee->filled) {
1571  Vnm_print(2, "Vpmg_qmEnergySMPBE: Need to call Vpmg_fillco()!\n");
1572  VASSERT(0);
1573  }
1574 
1575  energy = 0.0;
1576  nchop = 0;
1577  Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
1578 
1579  /* SMPB Modification (vchu, 09/21/06) */
1580  /* Extensive modification to the first part of the if statement
1581  where that handles the thee->pmgp->nonlin part. Basically, I've
1582  deleted all of the original code and written my own code that computes
1583  the electrostatic free energy in the SMPB framework. Definitely really hacky
1584  at this stage of the game, but gets the job done. The second part of the
1585  if statement (the part that handles linear poisson-boltzmann) has been deleted
1586  because there will be no linearized SMPB energy.. */
1587 
1588  z1 = ionQ[0];
1589  z2 = ionQ[1];
1590  z3 = ionQ[2];
1591  cb1 = ionConc[0];
1592  cb2 = ionConc[1];
1593  cb3 = ionConc[2];
1594  a = thee->pbe->smvolume;
1595  k = thee->pbe->smsize;
1596 
1598  // This constant is defined in vpde.h Do not need to redefine
1599  //Na = 6.022045000e-04; /* Converts from Molar to N/A^3 */
1600 
1601  fracOccA = Na*cb1*VCUB(a);
1602  fracOccB = Na*cb2*VCUB(a);
1603  fracOccC = Na*cb3*VCUB(a);
1604 
1605  phi = (fracOccA/k) + fracOccB + fracOccC;
1606 
1607  if (thee->pmgp->nonlin) {
1608  Vnm_print(0, "Vpmg_qmEnergySMPBE: Calculating nonlinear energy using SMPB functional!\n");
1609  for (i=0, len=nx*ny*nz; i<len; i++) {
1610  if (((k-1) > VSMALL) && (thee->pvec[i]*thee->kappa[i] > VSMALL)) {
1611 
1612  a1 = Vcap_exp(-1.0*z1*thee->u[i], &ichop1);
1613  a2 = Vcap_exp(-1.0*z2*thee->u[i], &ichop2);
1614  a3 = Vcap_exp(-1.0*z3*thee->u[i], &ichop3);
1615 
1616  nchop += ichop1 + ichop2 + ichop3;
1617 
1618  gpark = (1 - phi + (fracOccA/k)*a1);
1619  denom = VPOW(gpark, k) + VPOW(1-fracOccB-fracOccC, k-1)*(fracOccB*a2+fracOccC*a3);
1620 
1621  if (cb1 > VSMALL) {
1622  c1 = Na*cb1*VPOW(gpark, k-1)*a1/denom;
1623  if(c1 != c1) c1 = 0.;
1624  } else c1 = 0.;
1625 
1626  if (cb2 > VSMALL) {
1627  c2 = Na*cb2*VPOW(1-fracOccB-fracOccC,k-1)*a2/denom;
1628  if(c2 != c2) c2 = 0.;
1629  } else c2 = 0.;
1630 
1631  if (cb3 > VSMALL) {
1632  c3 = Na*cb3*VPOW(1-fracOccB-fracOccC,k-1)*a3/denom;
1633  if(c3 != c3) c3 = 0.;
1634  } else c3 = 0.;
1635 
1636  currEnergy = k*VLOG((1-(c1*VCUB(a)/k)-c2*VCUB(a)-c3*VCUB(a))/(1-phi))
1637  -(k-1)*VLOG((1-c2*VCUB(a)-c3*VCUB(a))/(1-phi+(fracOccA/k)));
1638 
1639  energy += thee->pvec[i]*thee->kappa[i]*currEnergy;
1640 
1641  } else if (thee->pvec[i]*thee->kappa[i] > VSMALL){
1642 
1643  a1 = Vcap_exp(-1.0*z1*thee->u[i], &ichop1);
1644  a2 = Vcap_exp(-1.0*z2*thee->u[i], &ichop2);
1645  a3 = Vcap_exp(-1.0*z3*thee->u[i], &ichop3);
1646 
1647  nchop += ichop1 + ichop2 + ichop3;
1648 
1649  gpark = (1 - phi + (fracOccA)*a1);
1650  denom = gpark + (fracOccB*a2+fracOccC*a3);
1651 
1652  if (cb1 > VSMALL) {
1653  c1 = Na*cb1*a1/denom;
1654  if(c1 != c1) c1 = 0.;
1655  } else c1 = 0.;
1656 
1657  if (cb2 > VSMALL) {
1658  c2 = Na*cb2*a2/denom;
1659  if(c2 != c2) c2 = 0.;
1660  } else c2 = 0.;
1661 
1662  if (cb3 > VSMALL) {
1663  c3 = Na*cb3*a3/denom;
1664  if(c3 != c3) c3 = 0.;
1665  } else c3 = 0.;
1666 
1667  currEnergy = VLOG((1-c1*VCUB(a)-c2*VCUB(a)-c3*VCUB(a))/(1-fracOccA-fracOccB-fracOccC));
1668 
1669  energy += thee->pvec[i]*thee->kappa[i]*currEnergy;
1670  }
1671  }
1672 
1673  energy = -energy/VCUB(a);
1674 
1675  if (nchop > 0) Vnm_print(2, "Vpmg_qmEnergySMPBE: Chopped EXP %d times!\n",
1676  nchop);
1677 
1678  } else {
1679  /* Zkappa2 OK here b/c LPBE approx */
1680  Vnm_print(0, "Vpmg_qmEnergySMPBE: ERROR: NO LINEAR ENERGY!! Returning 0!\n");
1681 
1682  energy = 0.0;
1683 
1684  }
1685  energy = energy*hx*hy*hzed;
1686 
1687  if (extFlag == 1) energy += thee->extQmEnergy;
1688 
1689  return energy;
1690 }
1691 
1692 VPUBLIC double Vpmg_qfEnergy(Vpmg *thee,
1693  int extFlag
1694  ) {
1695 
1696  double energy = 0.0;
1697 
1698  VASSERT(thee != VNULL);
1699 
1700  if ((thee->useChargeMap) || (thee->chargeMeth == VCM_BSPL2)) {
1701  energy = Vpmg_qfEnergyVolume(thee, extFlag);
1702  } else {
1703  energy = Vpmg_qfEnergyPoint(thee, extFlag);
1704  }
1705 
1706  return energy;
1707 }
1708 
1709 VPRIVATE double Vpmg_qfEnergyPoint(Vpmg *thee,
1710  int extFlag
1711  ) {
1712 
1713  int iatom, nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
1714  double xmax, ymax, zmax, xmin, ymin, zmin, hx, hy, hzed, ifloat, jfloat;
1715  double charge, kfloat, dx, dy, dz, energy, uval, *position;
1716  double *u;
1717  double *pvec;
1718  Valist *alist;
1719  Vatom *atom;
1720  Vpbe *pbe;
1721 
1722  pbe = thee->pbe;
1723  alist = pbe->alist;
1724  VASSERT(alist != VNULL);
1725 
1726  /* Get the mesh information */
1727  nx = thee->pmgp->nx;
1728  ny = thee->pmgp->ny;
1729  nz = thee->pmgp->nz;
1730  hx = thee->pmgp->hx;
1731  hy = thee->pmgp->hy;
1732  hzed = thee->pmgp->hzed;
1733  xmax = thee->pmgp->xmax;
1734  ymax = thee->pmgp->ymax;
1735  zmax = thee->pmgp->zmax;
1736  xmin = thee->pmgp->xmin;
1737  ymin = thee->pmgp->ymin;
1738  zmin = thee->pmgp->zmin;
1739 
1740  u = thee->u;
1741  pvec = thee->pvec;
1742 
1743  energy = 0.0;
1744 
1745  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
1746 
1747  /* Get atomic information */
1748  atom = Valist_getAtom(alist, iatom);
1749 
1750  position = Vatom_getPosition(atom);
1751  charge = Vatom_getCharge(atom);
1752 
1753  /* Figure out which vertices we're next to */
1754  ifloat = (position[0] - xmin)/hx;
1755  jfloat = (position[1] - ymin)/hy;
1756  kfloat = (position[2] - zmin)/hzed;
1757  ihi = (int)ceil(ifloat);
1758  ilo = (int)floor(ifloat);
1759  jhi = (int)ceil(jfloat);
1760  jlo = (int)floor(jfloat);
1761  khi = (int)ceil(kfloat);
1762  klo = (int)floor(kfloat);
1763 
1764  if (atom->partID > 0) {
1765 
1766  if ((ihi<nx) && (jhi<ny) && (khi<nz) &&
1767  (ilo>=0) && (jlo>=0) && (klo>=0)) {
1768 
1769  /* Now get trilinear interpolation constants */
1770  dx = ifloat - (double)(ilo);
1771  dy = jfloat - (double)(jlo);
1772  dz = kfloat - (double)(klo);
1773  uval =
1774  dx*dy*dz*u[IJK(ihi,jhi,khi)]
1775  + dx*(1.0-dy)*dz*u[IJK(ihi,jlo,khi)]
1776  + dx*dy*(1.0-dz)*u[IJK(ihi,jhi,klo)]
1777  + dx*(1.0-dy)*(1.0-dz)*u[IJK(ihi,jlo,klo)]
1778  + (1.0-dx)*dy*dz*u[IJK(ilo,jhi,khi)]
1779  + (1.0-dx)*(1.0-dy)*dz*u[IJK(ilo,jlo,khi)]
1780  + (1.0-dx)*dy*(1.0-dz)*u[IJK(ilo,jhi,klo)]
1781  + (1.0-dx)*(1.0-dy)*(1.0-dz)*u[IJK(ilo,jlo,klo)];
1782  energy += (uval*charge*atom->partID);
1783  } else if (thee->pmgp->bcfl != BCFL_FOCUS) {
1784  Vnm_print(2, "Vpmg_qfEnergy: Atom #%d at (%4.3f, %4.3f, \
1785 %4.3f) is off the mesh (ignoring)!\n",
1786  iatom, position[0], position[1], position[2]);
1787  }
1788  }
1789  }
1790 
1791  if (extFlag) energy += thee->extQfEnergy;
1792 
1793  return energy;
1794 }
1795 
1796 VPUBLIC double Vpmg_qfAtomEnergy(Vpmg *thee, Vatom *atom) {
1797 
1798  int nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
1799  double xmax, xmin, ymax, ymin, zmax, zmin, hx, hy, hzed, ifloat, jfloat;
1800  double charge, kfloat, dx, dy, dz, energy, uval, *position;
1801  double *u;
1802 
1803 
1804  /* Get the mesh information */
1805  nx = thee->pmgp->nx;
1806  ny = thee->pmgp->ny;
1807  nz = thee->pmgp->nz;
1808  hx = thee->pmgp->hx;
1809  hy = thee->pmgp->hy;
1810  hzed = thee->pmgp->hzed;
1811  xmax = thee->xf[nx-1];
1812  ymax = thee->yf[ny-1];
1813  zmax = thee->zf[nz-1];
1814  xmin = thee->xf[0];
1815  ymin = thee->yf[0];
1816  zmin = thee->zf[0];
1817 
1818  u = thee->u;
1819 
1820  energy = 0.0;
1821 
1822 
1823  position = Vatom_getPosition(atom);
1824  charge = Vatom_getCharge(atom);
1825 
1826  /* Figure out which vertices we're next to */
1827  ifloat = (position[0] - xmin)/hx;
1828  jfloat = (position[1] - ymin)/hy;
1829  kfloat = (position[2] - zmin)/hzed;
1830  ihi = (int)ceil(ifloat);
1831  ilo = (int)floor(ifloat);
1832  jhi = (int)ceil(jfloat);
1833  jlo = (int)floor(jfloat);
1834  khi = (int)ceil(kfloat);
1835  klo = (int)floor(kfloat);
1836 
1837  if (atom->partID > 0) {
1838 
1839  if ((ihi<nx) && (jhi<ny) && (khi<nz) &&
1840  (ilo>=0) && (jlo>=0) && (klo>=0)) {
1841 
1842  /* Now get trilinear interpolation constants */
1843  dx = ifloat - (double)(ilo);
1844  dy = jfloat - (double)(jlo);
1845  dz = kfloat - (double)(klo);
1846  uval =
1847  dx*dy*dz*u[IJK(ihi,jhi,khi)]
1848  + dx*(1.0-dy)*dz*u[IJK(ihi,jlo,khi)]
1849  + dx*dy*(1.0-dz)*u[IJK(ihi,jhi,klo)]
1850  + dx*(1.0-dy)*(1.0-dz)*u[IJK(ihi,jlo,klo)]
1851  + (1.0-dx)*dy*dz*u[IJK(ilo,jhi,khi)]
1852  + (1.0-dx)*(1.0-dy)*dz*u[IJK(ilo,jlo,khi)]
1853  + (1.0-dx)*dy*(1.0-dz)*u[IJK(ilo,jhi,klo)]
1854  + (1.0-dx)*(1.0-dy)*(1.0-dz)*u[IJK(ilo,jlo,klo)];
1855  energy += (uval*charge*atom->partID);
1856  } else if (thee->pmgp->bcfl != BCFL_FOCUS) {
1857  Vnm_print(2, "Vpmg_qfAtomEnergy: Atom at (%4.3f, %4.3f, \
1858 %4.3f) is off the mesh (ignoring)!\n",
1859  position[0], position[1], position[2]);
1860  }
1861  }
1862 
1863  return energy;
1864 }
1865 
1866 VPRIVATE double Vpmg_qfEnergyVolume(Vpmg *thee, int extFlag) {
1867 
1868  double hx, hy, hzed, energy;
1869  int i, nx, ny, nz;
1870 
1871  VASSERT(thee != VNULL);
1872 
1873  /* Get the mesh information */
1874  nx = thee->pmgp->nx;
1875  ny = thee->pmgp->ny;
1876  nz = thee->pmgp->nz;
1877  hx = thee->pmgp->hx;
1878  hy = thee->pmgp->hy;
1879  hzed = thee->pmgp->hzed;
1880 
1881  if (!thee->filled) {
1882  Vnm_print(2, "Vpmg_qfEnergyVolume: need to call Vpmg_fillco!\n");
1883  VASSERT(0);
1884  }
1885 
1886  energy = 0.0;
1887  Vnm_print(0, "Vpmg_qfEnergyVolume: Calculating energy\n");
1888  for (i=0; i<(nx*ny*nz); i++) {
1889  energy += (thee->pvec[i]*thee->u[i]*thee->charge[i]);
1890  }
1891  energy = energy*hx*hy*hzed/Vpbe_getZmagic(thee->pbe);
1892 
1893  if (extFlag == 1) energy += thee->extQfEnergy;
1894 
1895  return energy;
1896 }
1897 
1898 VPRIVATE void Vpmg_splineSelect(int srfm,Vacc *acc,double *gpos,double win,
1899  double infrad,Vatom *atom,double *force){
1900 
1901  switch (srfm) {
1902  case VSM_SPLINE :
1903  Vacc_splineAccGradAtomNorm(acc, gpos, win, infrad, atom, force);
1904  break;
1905  case VSM_SPLINE3:
1906  Vacc_splineAccGradAtomNorm3(acc, gpos, win, infrad, atom, force);
1907  break;
1908  case VSM_SPLINE4 :
1909  Vacc_splineAccGradAtomNorm4(acc, gpos, win, infrad, atom, force);
1910  break;
1911  default:
1912  Vnm_print(2, "Vpmg_dbnbForce: Unknown surface method.\n");
1913  return;
1914  }
1915 
1916  return;
1917 }
1918 
1919 VPRIVATE void focusFillBound(Vpmg *thee,
1920  Vpmg *pmgOLD
1921  ) {
1922 
1923  Vpbe *pbe;
1924  double hxOLD,
1925  hyOLD,
1926  hzOLD,
1927  xminOLD,
1928  yminOLD,
1929  zminOLD,
1930  xmaxOLD,
1931  ymaxOLD,
1932  zmaxOLD,
1933  hxNEW,
1934  hyNEW,
1935  hzNEW,
1936  xminNEW,
1937  yminNEW,
1938  zminNEW,
1939  xmaxNEW,
1940  ymaxNEW,
1941  zmaxNEW,
1942  x,
1943  y,
1944  z,
1945  dx,
1946  dy,
1947  dz,
1948  ifloat,
1949  jfloat,
1950  kfloat,
1951  uval,
1952  eps_w,
1953  T,
1954  pre1,
1955  xkappa,
1956  size,
1957  *apos,
1958  charge,
1959  //pos[3], // gcc: not used
1960  uvalMin,
1961  uvalMax,
1962  *data;
1963  int nxOLD,
1964  nyOLD,
1965  nzOLD,
1966  nxNEW,
1967  nyNEW,
1968  nzNEW,
1969  i,
1970  j,
1971  k,
1972  ihi,
1973  ilo,
1974  jhi,
1975  jlo,
1976  khi,
1977  klo,
1978  nx,
1979  ny,
1980  nz;
1981 
1982  /* Calculate new problem dimensions */
1983  hxNEW = thee->pmgp->hx;
1984  hyNEW = thee->pmgp->hy;
1985  hzNEW = thee->pmgp->hzed;
1986  nx = thee->pmgp->nx;
1987  ny = thee->pmgp->ny;
1988  nz = thee->pmgp->nz;
1989  nxNEW = thee->pmgp->nx;
1990  nyNEW = thee->pmgp->ny;
1991  nzNEW = thee->pmgp->nz;
1992  xminNEW = thee->pmgp->xcent - ((double)(nxNEW-1)*hxNEW)/2.0;
1993  xmaxNEW = thee->pmgp->xcent + ((double)(nxNEW-1)*hxNEW)/2.0;
1994  yminNEW = thee->pmgp->ycent - ((double)(nyNEW-1)*hyNEW)/2.0;
1995  ymaxNEW = thee->pmgp->ycent + ((double)(nyNEW-1)*hyNEW)/2.0;
1996  zminNEW = thee->pmgp->zcent - ((double)(nzNEW-1)*hzNEW)/2.0;
1997  zmaxNEW = thee->pmgp->zcent + ((double)(nzNEW-1)*hzNEW)/2.0;
1998 
1999  if(pmgOLD != VNULL){
2000  /* Relevant old problem parameters */
2001  hxOLD = pmgOLD->pmgp->hx;
2002  hyOLD = pmgOLD->pmgp->hy;
2003  hzOLD = pmgOLD->pmgp->hzed;
2004  nxOLD = pmgOLD->pmgp->nx;
2005  nyOLD = pmgOLD->pmgp->ny;
2006  nzOLD = pmgOLD->pmgp->nz;
2007  xminOLD = pmgOLD->pmgp->xcent - ((double)(nxOLD-1)*hxOLD)/2.0;
2008  xmaxOLD = pmgOLD->pmgp->xcent + ((double)(nxOLD-1)*hxOLD)/2.0;
2009  yminOLD = pmgOLD->pmgp->ycent - ((double)(nyOLD-1)*hyOLD)/2.0;
2010  ymaxOLD = pmgOLD->pmgp->ycent + ((double)(nyOLD-1)*hyOLD)/2.0;
2011  zminOLD = pmgOLD->pmgp->zcent - ((double)(nzOLD-1)*hzOLD)/2.0;
2012  zmaxOLD = pmgOLD->pmgp->zcent + ((double)(nzOLD-1)*hzOLD)/2.0;
2013 
2014  data = pmgOLD->u;
2015  }else{
2016  /* Relevant old problem parameters */
2017  hxOLD = thee->potMap->hx;
2018  hyOLD = thee->potMap->hy;
2019  hzOLD = thee->potMap->hzed;
2020  nxOLD = thee->potMap->nx;
2021  nyOLD = thee->potMap->ny;
2022  nzOLD = thee->potMap->nz;
2023  xminOLD = thee->potMap->xmin;
2024  xmaxOLD = thee->potMap->xmax;
2025  yminOLD = thee->potMap->ymin;
2026  ymaxOLD = thee->potMap->ymax;
2027  zminOLD = thee->potMap->zmin;
2028  zmaxOLD = thee->potMap->zmax;
2029 
2030  data = thee->potMap->data;
2031  }
2032  /* BOUNDARY CONDITION SETUP FOR POINTS OFF OLD MESH:
2033  * For each "atom" (only one for bcfl=1), we use the following formula to
2034  * calculate the boundary conditions:
2035  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2036  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2037  * * 1/d
2038  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
2039  * We only need to evaluate some of these prefactors once:
2040  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2041  * which gives the potential as
2042  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2043  */
2044  pbe = thee->pbe;
2045  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
2046  T = Vpbe_getTemperature(pbe); /* K */
2047  pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
2048 
2049  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
2050  * m/A, then we will only need to deal with distances and sizes in
2051  * Angstroms rather than meters. */
2052  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
2053  pre1 = pre1*(1.0e10);
2054  size = Vpbe_getSoluteRadius(pbe);
2055  apos = Vpbe_getSoluteCenter(pbe);
2056  charge = Vunit_ec*Vpbe_getSoluteCharge(pbe);
2057 
2058  /* Check for rounding error */
2059  if (VABS(xminOLD-xminNEW) < VSMALL) xminNEW = xminOLD;
2060  if (VABS(xmaxOLD-xmaxNEW) < VSMALL) xmaxNEW = xmaxOLD;
2061  if (VABS(yminOLD-yminNEW) < VSMALL) yminNEW = yminOLD;
2062  if (VABS(ymaxOLD-ymaxNEW) < VSMALL) ymaxNEW = ymaxOLD;
2063  if (VABS(zminOLD-zminNEW) < VSMALL) zminNEW = zminOLD;
2064  if (VABS(zmaxOLD-zmaxNEW) < VSMALL) zmaxNEW = zmaxOLD;
2065 
2066 
2067  /* Sanity check: make sure we're within the old mesh */
2068  Vnm_print(0, "VPMG::focusFillBound -- New mesh mins = %g, %g, %g\n",
2069  xminNEW, yminNEW, zminNEW);
2070  Vnm_print(0, "VPMG::focusFillBound -- New mesh maxs = %g, %g, %g\n",
2071  xmaxNEW, ymaxNEW, zmaxNEW);
2072  Vnm_print(0, "VPMG::focusFillBound -- Old mesh mins = %g, %g, %g\n",
2073  xminOLD, yminOLD, zminOLD);
2074  Vnm_print(0, "VPMG::focusFillBound -- Old mesh maxs = %g, %g, %g\n",
2075  xmaxOLD, ymaxOLD, zmaxOLD);
2076 
2077  /* The following is obsolete; we'll substitute analytical boundary
2078  * condition values when the new mesh falls outside the old */
2079  if ((xmaxNEW>xmaxOLD) || (ymaxNEW>ymaxOLD) || (zmaxNEW>zmaxOLD) ||
2080  (xminOLD>xminNEW) || (yminOLD>yminNEW) || (zminOLD>zminNEW)) {
2081 
2082  Vnm_print(2, "Vpmg::focusFillBound -- new mesh not contained in old!\n");
2083  Vnm_print(2, "Vpmg::focusFillBound -- old mesh min = (%g, %g, %g)\n",
2084  xminOLD, yminOLD, zminOLD);
2085  Vnm_print(2, "Vpmg::focusFillBound -- old mesh max = (%g, %g, %g)\n",
2086  xmaxOLD, ymaxOLD, zmaxOLD);
2087  Vnm_print(2, "Vpmg::focusFillBound -- new mesh min = (%g, %g, %g)\n",
2088  xminNEW, yminNEW, zminNEW);
2089  Vnm_print(2, "Vpmg::focusFillBound -- new mesh max = (%g, %g, %g)\n",
2090  xmaxNEW, ymaxNEW, zmaxNEW);
2091  fflush(stderr);
2092  VASSERT(0);
2093  }
2094 
2095  uvalMin = VPMGSMALL;
2096  uvalMax = -VPMGSMALL;
2097 
2098  /* Fill the "i" boundaries (dirichlet) */
2099  for (k=0; k<nzNEW; k++) {
2100  for (j=0; j<nyNEW; j++) {
2101  /* Low X face */
2102  x = xminNEW;
2103  y = yminNEW + j*hyNEW;
2104  z = zminNEW + k*hzNEW;
2105  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2106  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2107  ifloat = (x - xminOLD)/hxOLD;
2108  jfloat = (y - yminOLD)/hyOLD;
2109  kfloat = (z - zminOLD)/hzOLD;
2110  ihi = (int)ceil(ifloat);
2111  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2112  ilo = (int)floor(ifloat);
2113  if (ilo < 0) ilo = 0;
2114  jhi = (int)ceil(jfloat);
2115  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2116  jlo = (int)floor(jfloat);
2117  if (jlo < 0) jlo = 0;
2118  khi = (int)ceil(kfloat);
2119  if (khi > (nzOLD-1)) khi = nzOLD-1;
2120  klo = (int)floor(kfloat);
2121  if (klo < 0) klo = 0;
2122  dx = ifloat - (double)(ilo);
2123  dy = jfloat - (double)(jlo);
2124  dz = kfloat - (double)(klo);
2125  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2126  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2127  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2128  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2129  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2130  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2131  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2132  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2133  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2134  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2135  } else {
2136  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2137  %g!\n", __FILE__, __LINE__, x, y, z);
2138  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2139  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2140  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2141  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2142  VASSERT(0);
2143  }
2144  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2145  thee->gxcf[IJKx(j,k,0)] = uval;
2146  if(uval < uvalMin) uvalMin = uval;
2147  if(uval > uvalMax) uvalMax = uval;
2148 
2149  /* High X face */
2150  x = xmaxNEW;
2151  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2152  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2153  ifloat = (x - xminOLD)/hxOLD;
2154  jfloat = (y - yminOLD)/hyOLD;
2155  kfloat = (z - zminOLD)/hzOLD;
2156  ihi = (int)ceil(ifloat);
2157  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2158  ilo = (int)floor(ifloat);
2159  if (ilo < 0) ilo = 0;
2160  jhi = (int)ceil(jfloat);
2161  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2162  jlo = (int)floor(jfloat);
2163  if (jlo < 0) jlo = 0;
2164  khi = (int)ceil(kfloat);
2165  if (khi > (nzOLD-1)) khi = nzOLD-1;
2166  klo = (int)floor(kfloat);
2167  if (klo < 0) klo = 0;
2168  dx = ifloat - (double)(ilo);
2169  dy = jfloat - (double)(jlo);
2170  dz = kfloat - (double)(klo);
2171  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2172  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2173  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2174  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2175  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2176  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2177  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2178  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2179  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2180  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2181  } else {
2182  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2183  %g!\n", __FILE__, __LINE__, x, y, z);
2184  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2185  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2186  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2187  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2188  VASSERT(0);
2189  }
2190  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2191  thee->gxcf[IJKx(j,k,1)] = uval;
2192  if(uval < uvalMin) uvalMin = uval;
2193  if(uval > uvalMax) uvalMax = uval;
2194 
2195  /* Zero Neumann conditions */
2196  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2197  thee->gxcf[IJKx(j,k,2)] = 0.0;
2198  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2199  thee->gxcf[IJKx(j,k,3)] = 0.0;
2200  }
2201  }
2202 
2203  /* Fill the "j" boundaries (dirichlet) */
2204  for (k=0; k<nzNEW; k++) {
2205  for (i=0; i<nxNEW; i++) {
2206  /* Low Y face */
2207  x = xminNEW + i*hxNEW;
2208  y = yminNEW;
2209  z = zminNEW + k*hzNEW;
2210  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2211  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2212  ifloat = (x - xminOLD)/hxOLD;
2213  jfloat = (y - yminOLD)/hyOLD;
2214  kfloat = (z - zminOLD)/hzOLD;
2215  ihi = (int)ceil(ifloat);
2216  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2217  ilo = (int)floor(ifloat);
2218  if (ilo < 0) ilo = 0;
2219  jhi = (int)ceil(jfloat);
2220  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2221  jlo = (int)floor(jfloat);
2222  if (jlo < 0) jlo = 0;
2223  khi = (int)ceil(kfloat);
2224  if (khi > (nzOLD-1)) khi = nzOLD-1;
2225  klo = (int)floor(kfloat);
2226  if (klo < 0) klo = 0;
2227  dx = ifloat - (double)(ilo);
2228  dy = jfloat - (double)(jlo);
2229  dz = kfloat - (double)(klo);
2230  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2231  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2232  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2233  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2234  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2235  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2236  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2237  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2238  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2239  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2240  } else {
2241  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2242  %g!\n", __FILE__, __LINE__, x, y, z);
2243  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2244  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2245  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2246  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2247  VASSERT(0);
2248  }
2249  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2250  thee->gycf[IJKy(i,k,0)] = uval;
2251  if(uval < uvalMin) uvalMin = uval;
2252  if(uval > uvalMax) uvalMax = uval;
2253 
2254  /* High Y face */
2255  y = ymaxNEW;
2256  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2257  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2258  ifloat = (x - xminOLD)/hxOLD;
2259  jfloat = (y - yminOLD)/hyOLD;
2260  kfloat = (z - zminOLD)/hzOLD;
2261  ihi = (int)ceil(ifloat);
2262  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2263  ilo = (int)floor(ifloat);
2264  if (ilo < 0) ilo = 0;
2265  jhi = (int)ceil(jfloat);
2266  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2267  jlo = (int)floor(jfloat);
2268  if (jlo < 0) jlo = 0;
2269  khi = (int)ceil(kfloat);
2270  if (khi > (nzOLD-1)) khi = nzOLD-1;
2271  klo = (int)floor(kfloat);
2272  if (klo < 0) klo = 0;
2273  dx = ifloat - (double)(ilo);
2274  dy = jfloat - (double)(jlo);
2275  dz = kfloat - (double)(klo);
2276  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2277  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2278  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2279  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2280  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2281  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2282  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2283  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2284  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2285  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2286  } else {
2287  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2288  %g!\n", __FILE__, __LINE__, x, y, z);
2289  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2290  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2291  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2292  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2293  VASSERT(0);
2294  }
2295  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2296  thee->gycf[IJKy(i,k,1)] = uval;
2297  if(uval < uvalMin) uvalMin = uval;
2298  if(uval > uvalMax) uvalMax = uval;
2299 
2300  /* Zero Neumann conditions */
2301  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2302  thee->gycf[IJKy(i,k,2)] = 0.0;
2303  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2304  thee->gycf[IJKy(i,k,3)] = 0.0;
2305  }
2306  }
2307 
2308  /* Fill the "k" boundaries (dirichlet) */
2309  for (j=0; j<nyNEW; j++) {
2310  for (i=0; i<nxNEW; i++) {
2311  /* Low Z face */
2312  x = xminNEW + i*hxNEW;
2313  y = yminNEW + j*hyNEW;
2314  z = zminNEW;
2315  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2316  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2317  ifloat = (x - xminOLD)/hxOLD;
2318  jfloat = (y - yminOLD)/hyOLD;
2319  kfloat = (z - zminOLD)/hzOLD;
2320  ihi = (int)ceil(ifloat);
2321  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2322  ilo = (int)floor(ifloat);
2323  if (ilo < 0) ilo = 0;
2324  jhi = (int)ceil(jfloat);
2325  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2326  jlo = (int)floor(jfloat);
2327  if (jlo < 0) jlo = 0;
2328  khi = (int)ceil(kfloat);
2329  if (khi > (nzOLD-1)) khi = nzOLD-1;
2330  klo = (int)floor(kfloat);
2331  if (klo < 0) klo = 0;
2332  dx = ifloat - (double)(ilo);
2333  dy = jfloat - (double)(jlo);
2334  dz = kfloat - (double)(klo);
2335  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2336  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2337  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2338  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2339  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2340  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2341  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2342  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2343  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2344  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2345  } else {
2346  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2347  %g!\n", __FILE__, __LINE__, x, y, z);
2348  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2349  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2350  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2351  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2352  VASSERT(0);
2353  }
2354  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2355  thee->gzcf[IJKz(i,j,0)] = uval;
2356  if(uval < uvalMin) uvalMin = uval;
2357  if(uval > uvalMax) uvalMax = uval;
2358 
2359  /* High Z face */
2360  z = zmaxNEW;
2361  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2362  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2363  ifloat = (x - xminOLD)/hxOLD;
2364  jfloat = (y - yminOLD)/hyOLD;
2365  kfloat = (z - zminOLD)/hzOLD;
2366  ihi = (int)ceil(ifloat);
2367  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2368  ilo = (int)floor(ifloat);
2369  if (ilo < 0) ilo = 0;
2370  jhi = (int)ceil(jfloat);
2371  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2372  jlo = (int)floor(jfloat);
2373  if (jlo < 0) jlo = 0;
2374  khi = (int)ceil(kfloat);
2375  if (khi > (nzOLD-1)) khi = nzOLD-1;
2376  klo = (int)floor(kfloat);
2377  if (klo < 0) klo = 0;
2378  dx = ifloat - (double)(ilo);
2379  dy = jfloat - (double)(jlo);
2380  dz = kfloat - (double)(klo);
2381  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2382  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2383  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2384  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2385  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2386  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2387  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2388  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2389  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2390  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2391  } else {
2392  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2393  %g!\n", __FILE__, __LINE__, x, y, z);
2394  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2395  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2396  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2397  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2398  VASSERT(0);
2399  }
2400  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2401  thee->gzcf[IJKz(i,j,1)] = uval;
2402  if(uval < uvalMin) uvalMin = uval;
2403  if(uval > uvalMax) uvalMax = uval;
2404 
2405  /* Zero Neumann conditions */
2406  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2407  thee->gzcf[IJKz(i,j,2)] = 0.0;
2408  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2409  thee->gzcf[IJKz(i,j,3)] = 0.0;
2410  }
2411  }
2412 
2413  VWARN_MSG0(
2414  uvalMin >= SINH_MIN && uvalMax <= SINH_MAX,
2415  "Unusually large potential values\n"
2416  " detected on the focusing boundary!\n"
2417  " Convergence not guaranteed for NPBE/NRPBE calculations!"
2418  );
2419 }
2420 
2421 VPRIVATE void extEnergy(Vpmg *thee, Vpmg *pmgOLD, PBEparm_calcEnergy extFlag,
2422  double partMin[3], double partMax[3], int bflags[6]) {
2423 
2424  Vatom *atom;
2425  double hxNEW, hyNEW, hzNEW;
2426  double lowerCorner[3], upperCorner[3];
2427  int nxNEW, nyNEW, nzNEW;
2428  int nxOLD, nyOLD, nzOLD;
2429  int i,j,k;
2430  double xmin, xmax, ymin, ymax, zmin, zmax;
2431  double hxOLD, hyOLD, hzOLD;
2432  double xval, yval, zval;
2433  double x,y,z;
2434  int nx, ny, nz;
2435 
2436  /* Set the new external energy contribution to zero. Any external
2437  * contributions from higher levels will be included in the appropriate
2438  * energy function call. */
2439  thee->extQmEnergy = 0;
2440  thee->extQfEnergy = 0;
2441  thee->extDiEnergy = 0;
2442 
2443  /* New problem dimensions */
2444  hxNEW = thee->pmgp->hx;
2445  hyNEW = thee->pmgp->hy;
2446  hzNEW = thee->pmgp->hzed;
2447  nxNEW = thee->pmgp->nx;
2448  nyNEW = thee->pmgp->ny;
2449  nzNEW = thee->pmgp->nz;
2450  lowerCorner[0] = thee->pmgp->xcent - ((double)(nxNEW-1)*hxNEW)/2.0;
2451  upperCorner[0] = thee->pmgp->xcent + ((double)(nxNEW-1)*hxNEW)/2.0;
2452  lowerCorner[1] = thee->pmgp->ycent - ((double)(nyNEW-1)*hyNEW)/2.0;
2453  upperCorner[1] = thee->pmgp->ycent + ((double)(nyNEW-1)*hyNEW)/2.0;
2454  lowerCorner[2] = thee->pmgp->zcent - ((double)(nzNEW-1)*hzNEW)/2.0;
2455  upperCorner[2] = thee->pmgp->zcent + ((double)(nzNEW-1)*hzNEW)/2.0;
2456 
2457  Vnm_print(0, "VPMG::extEnergy: energy flag = %d\n", extFlag);
2458 
2459  /* Old problem dimensions */
2460  nxOLD = pmgOLD->pmgp->nx;
2461  nyOLD = pmgOLD->pmgp->ny;
2462  nzOLD = pmgOLD->pmgp->nz;
2463 
2464  /* Create a partition based on the new problem dimensions */
2465  /* Vnm_print(1, "DEBUG (%s, %d): extEnergy calling Vpmg_setPart for old PMG.\n",
2466  __FILE__, __LINE__); */
2467  Vpmg_setPart(pmgOLD, lowerCorner, upperCorner, bflags);
2468 
2469 
2470  Vnm_print(0,"VPMG::extEnergy: Finding extEnergy dimensions...\n");
2471  Vnm_print(0,"VPMG::extEnergy Disj part lower corner = (%g, %g, %g)\n",
2472  partMin[0], partMin[1], partMin[2]);
2473  Vnm_print(0,"VPMG::extEnergy Disj part upper corner = (%g, %g, %g)\n",
2474  partMax[0], partMax[1], partMax[2]);
2475 
2476  /* Find the old dimensions */
2477 
2478  hxOLD = pmgOLD->pmgp->hx;
2479  hyOLD = pmgOLD->pmgp->hy;
2480  hzOLD = pmgOLD->pmgp->hzed;
2481  xmin = pmgOLD->pmgp->xcent - 0.5*hxOLD*(nxOLD-1);
2482  ymin = pmgOLD->pmgp->ycent - 0.5*hyOLD*(nyOLD-1);
2483  zmin = pmgOLD->pmgp->zcent - 0.5*hzOLD*(nzOLD-1);
2484  xmax = xmin+hxOLD*(nxOLD-1);
2485  ymax = ymin+hyOLD*(nyOLD-1);
2486  zmax = zmin+hzOLD*(nzOLD-1);
2487 
2488  Vnm_print(0,"VPMG::extEnergy Old lower corner = (%g, %g, %g)\n",
2489  xmin, ymin, zmin);
2490  Vnm_print(0,"VPMG::extEnergy Old upper corner = (%g, %g, %g)\n",
2491  xmax, ymax, zmax);
2492 
2493  /* Flip the partition, but do not include any points that will
2494  be included by another processor */
2495 
2496  nx = nxOLD;
2497  ny = nyOLD;
2498  nz = nzOLD;
2499 
2500  for(i=0; i<nx; i++) {
2501  xval = 1;
2502  x = i*hxOLD + xmin;
2503  if (x < partMin[0] && bflags[VAPBS_LEFT] == 1) xval = 0;
2504  else if (x > partMax[0] && bflags[VAPBS_RIGHT] == 1) xval = 0;
2505 
2506  for(j=0; j<ny; j++) {
2507  yval = 1;
2508  y = j*hyOLD + ymin;
2509  if (y < partMin[1] && bflags[VAPBS_BACK] == 1) yval = 0;
2510  else if (y > partMax[1] && bflags[VAPBS_FRONT] == 1) yval = 0;
2511 
2512  for(k=0; k<nz; k++) {
2513  zval = 1;
2514  z = k*hzOLD + zmin;
2515  if (z < partMin[2] && bflags[VAPBS_DOWN] == 1) zval = 0;
2516  else if (z > partMax[2] && bflags[VAPBS_UP] == 1) zval = 0;
2517 
2518  if (pmgOLD->pvec[IJK(i,j,k)] > VSMALL) pmgOLD->pvec[IJK(i,j,k)] = 1.0;
2519  pmgOLD->pvec[IJK(i,j,k)] = (1 - (pmgOLD->pvec[IJK(i,j,k)])) * (xval*yval*zval);
2520  }
2521  }
2522  }
2523 
2524  for (i=0; i<Valist_getNumberAtoms(thee->pbe->alist); i++) {
2525  xval=1;
2526  yval=1;
2527  zval=1;
2528  atom = Valist_getAtom(thee->pbe->alist, i);
2529  x = atom->position[0];
2530  y = atom->position[1];
2531  z = atom->position[2];
2532  if (x < partMin[0] && bflags[VAPBS_LEFT] == 1) xval = 0;
2533  else if (x > partMax[0] && bflags[VAPBS_RIGHT] == 1) xval = 0;
2534  if (y < partMin[1] && bflags[VAPBS_BACK] == 1) yval = 0;
2535  else if (y > partMax[1] && bflags[VAPBS_FRONT] == 1) yval = 0;
2536  if (z < partMin[2] && bflags[VAPBS_DOWN] == 1) zval = 0;
2537  else if (z > partMax[2] && bflags[VAPBS_UP] == 1) zval = 0;
2538  if (atom->partID > VSMALL) atom->partID = 1.0;
2539  atom->partID = (1 - atom->partID) * (xval*yval*zval);
2540  }
2541 
2542  /* Now calculate the energy on inverted subset of the domain */
2543  thee->extQmEnergy = Vpmg_qmEnergy(pmgOLD, 1);
2544  Vnm_print(0, "VPMG::extEnergy: extQmEnergy = %g kT\n", thee->extQmEnergy);
2545  thee->extQfEnergy = Vpmg_qfEnergy(pmgOLD, 1);
2546  Vnm_print(0, "VPMG::extEnergy: extQfEnergy = %g kT\n", thee->extQfEnergy);
2547  thee->extDiEnergy = Vpmg_dielEnergy(pmgOLD, 1);
2548  Vnm_print(0, "VPMG::extEnergy: extDiEnergy = %g kT\n", thee->extDiEnergy);
2549  Vpmg_unsetPart(pmgOLD);
2550 }
2551 
2552 VPRIVATE double bcfl1sp(double size, double *apos, double charge,
2553  double xkappa, double pre1, double *pos) {
2554 
2555  double dist, val;
2556 
2557  dist = VSQRT(VSQR(pos[0]-apos[0]) + VSQR(pos[1]-apos[1])
2558  + VSQR(pos[2]-apos[2]));
2559  if (xkappa > VSMALL) {
2560  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2561  / (1+xkappa*size);
2562  } else {
2563  val = pre1*(charge/dist);
2564  }
2565 
2566  return val;
2567 }
2568 
2569 VPRIVATE void bcfl1(double size, double *apos, double charge,
2570  double xkappa, double pre1, double *gxcf, double *gycf, double *gzcf,
2571  double *xf, double *yf, double *zf, int nx, int ny, int nz) {
2572 
2573  int i, j, k;
2574  double dist, val;
2575  double gpos[3];
2576 
2577  /* the "i" boundaries (dirichlet) */
2578  for (k=0; k<nz; k++) {
2579  gpos[2] = zf[k];
2580  for (j=0; j<ny; j++) {
2581  gpos[1] = yf[j];
2582  gpos[0] = xf[0];
2583  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2584  + VSQR(gpos[2]-apos[2]));
2585  if (xkappa > VSMALL) {
2586  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2587  / (1+xkappa*size);
2588  } else {
2589  val = pre1*(charge/dist);
2590  }
2591  gxcf[IJKx(j,k,0)] += val;
2592  gpos[0] = xf[nx-1];
2593  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2594  + VSQR(gpos[2]-apos[2]));
2595  if (xkappa > VSMALL) {
2596  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2597  / (1+xkappa*size);
2598  } else {
2599  val = pre1*(charge/dist);
2600  }
2601  gxcf[IJKx(j,k,1)] += val;
2602  }
2603  }
2604 
2605  /* the "j" boundaries (dirichlet) */
2606  for (k=0; k<nz; k++) {
2607  gpos[2] = zf[k];
2608  for (i=0; i<nx; i++) {
2609  gpos[0] = xf[i];
2610  gpos[1] = yf[0];
2611  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2612  + VSQR(gpos[2]-apos[2]));
2613  if (xkappa > VSMALL) {
2614  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2615  / (1+xkappa*size);
2616  } else {
2617  val = pre1*(charge/dist);
2618  }
2619  gycf[IJKy(i,k,0)] += val;
2620  gpos[1] = yf[ny-1];
2621  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2622  + VSQR(gpos[2]-apos[2]));
2623  if (xkappa > VSMALL) {
2624  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2625  / (1+xkappa*size);
2626  } else {
2627  val = pre1*(charge/dist);
2628  }
2629  gycf[IJKy(i,k,1)] += val;
2630  }
2631  }
2632 
2633  /* the "k" boundaries (dirichlet) */
2634  for (j=0; j<ny; j++) {
2635  gpos[1] = yf[j];
2636  for (i=0; i<nx; i++) {
2637  gpos[0] = xf[i];
2638  gpos[2] = zf[0];
2639  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2640  + VSQR(gpos[2]-apos[2]));
2641  if (xkappa > VSMALL) {
2642  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2643  / (1+xkappa*size);
2644  } else {
2645  val = pre1*(charge/dist);
2646  }
2647  gzcf[IJKz(i,j,0)] += val;
2648  gpos[2] = zf[nz-1];
2649  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2650  + VSQR(gpos[2]-apos[2]));
2651  if (xkappa > VSMALL) {
2652  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2653  / (1+xkappa*size);
2654  } else {
2655  val = pre1*(charge/dist);
2656  }
2657  gzcf[IJKz(i,j,1)] += val;
2658  }
2659  }
2660 }
2661 
2662 VPRIVATE void bcfl2(double size, double *apos,
2663  double charge, double *dipole, double *quad,
2664  double xkappa, double eps_p, double eps_w, double T,
2665  double *gxcf, double *gycf, double *gzcf,
2666  double *xf, double *yf, double *zf,
2667  int nx, int ny, int nz) {
2668 
2669  int i, j, k;
2670  double val;
2671  double gpos[3],tensor[3];
2672  double ux,uy,uz,xr,yr,zr;
2673  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
2674  double dist, pre;
2675 
2676  VASSERT(dipole != VNULL);
2677  ux = dipole[0];
2678  uy = dipole[1];
2679  uz = dipole[2];
2680  if (quad != VNULL) {
2681  /* The factor of 1/3 results from using a
2682  traceless quadrupole definition. See, for example,
2683  "The Theory of Intermolecular Forces" by A.J. Stone,
2684  Chapter 3. */
2685  qxx = quad[0] / 3.0;
2686  qxy = quad[1] / 3.0;
2687  qxz = quad[2] / 3.0;
2688  qyx = quad[3] / 3.0;
2689  qyy = quad[4] / 3.0;
2690  qyz = quad[5] / 3.0;
2691  qzx = quad[6] / 3.0;
2692  qzy = quad[7] / 3.0;
2693  qzz = quad[8] / 3.0;
2694  } else {
2695  qxx = 0.0;
2696  qxy = 0.0;
2697  qxz = 0.0;
2698  qyx = 0.0;
2699  qyy = 0.0;
2700  qyz = 0.0;
2701  qzx = 0.0;
2702  qzy = 0.0;
2703  qzz = 0.0;
2704  }
2705 
2706  pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
2707  pre = pre*(1.0e10);
2708 
2709  /* the "i" boundaries (dirichlet) */
2710  for (k=0; k<nz; k++) {
2711  gpos[2] = zf[k];
2712  for (j=0; j<ny; j++) {
2713  gpos[1] = yf[j];
2714  gpos[0] = xf[0];
2715  xr = gpos[0] - apos[0];
2716  yr = gpos[1] - apos[1];
2717  zr = gpos[2] - apos[2];
2718  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2719  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2720  val = pre*charge*tensor[0];
2721  val -= pre*ux*xr*tensor[1];
2722  val -= pre*uy*yr*tensor[1];
2723  val -= pre*uz*zr*tensor[1];
2724  val += pre*qxx*xr*xr*tensor[2];
2725  val += pre*qyy*yr*yr*tensor[2];
2726  val += pre*qzz*zr*zr*tensor[2];
2727  val += pre*2.0*qxy*xr*yr*tensor[2];
2728  val += pre*2.0*qxz*xr*zr*tensor[2];
2729  val += pre*2.0*qyz*yr*zr*tensor[2];
2730  gxcf[IJKx(j,k,0)] += val;
2731 
2732  gpos[0] = xf[nx-1];
2733  xr = gpos[0] - apos[0];
2734  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2735  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2736  val = pre*charge*tensor[0];
2737  val -= pre*ux*xr*tensor[1];
2738  val -= pre*uy*yr*tensor[1];
2739  val -= pre*uz*zr*tensor[1];
2740  val += pre*qxx*xr*xr*tensor[2];
2741  val += pre*qyy*yr*yr*tensor[2];
2742  val += pre*qzz*zr*zr*tensor[2];
2743  val += pre*2.0*qxy*xr*yr*tensor[2];
2744  val += pre*2.0*qxz*xr*zr*tensor[2];
2745  val += pre*2.0*qyz*yr*zr*tensor[2];
2746  gxcf[IJKx(j,k,1)] += val;
2747  }
2748  }
2749 
2750  /* the "j" boundaries (dirichlet) */
2751  for (k=0; k<nz; k++) {
2752  gpos[2] = zf[k];
2753  for (i=0; i<nx; i++) {
2754  gpos[0] = xf[i];
2755  gpos[1] = yf[0];
2756  xr = gpos[0] - apos[0];
2757  yr = gpos[1] - apos[1];
2758  zr = gpos[2] - apos[2];
2759  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2760  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2761  val = pre*charge*tensor[0];
2762  val -= pre*ux*xr*tensor[1];
2763  val -= pre*uy*yr*tensor[1];
2764  val -= pre*uz*zr*tensor[1];
2765  val += pre*qxx*xr*xr*tensor[2];
2766  val += pre*qyy*yr*yr*tensor[2];
2767  val += pre*qzz*zr*zr*tensor[2];
2768  val += pre*2.0*qxy*xr*yr*tensor[2];
2769  val += pre*2.0*qxz*xr*zr*tensor[2];
2770  val += pre*2.0*qyz*yr*zr*tensor[2];
2771  gycf[IJKy(i,k,0)] += val;
2772 
2773  gpos[1] = yf[ny-1];
2774  yr = gpos[1] - apos[1];
2775  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2776  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2777  val = pre*charge*tensor[0];
2778  val -= pre*ux*xr*tensor[1];
2779  val -= pre*uy*yr*tensor[1];
2780  val -= pre*uz*zr*tensor[1];
2781  val += pre*qxx*xr*xr*tensor[2];
2782  val += pre*qyy*yr*yr*tensor[2];
2783  val += pre*qzz*zr*zr*tensor[2];
2784  val += pre*2.0*qxy*xr*yr*tensor[2];
2785  val += pre*2.0*qxz*xr*zr*tensor[2];
2786  val += pre*2.0*qyz*yr*zr*tensor[2];
2787  gycf[IJKy(i,k,1)] += val;
2788  }
2789  }
2790 
2791  /* the "k" boundaries (dirichlet) */
2792  for (j=0; j<ny; j++) {
2793  gpos[1] = yf[j];
2794  for (i=0; i<nx; i++) {
2795  gpos[0] = xf[i];
2796  gpos[2] = zf[0];
2797  xr = gpos[0] - apos[0];
2798  yr = gpos[1] - apos[1];
2799  zr = gpos[2] - apos[2];
2800  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2801  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2802  val = pre*charge*tensor[0];
2803  val -= pre*ux*xr*tensor[1];
2804  val -= pre*uy*yr*tensor[1];
2805  val -= pre*uz*zr*tensor[1];
2806  val += pre*qxx*xr*xr*tensor[2];
2807  val += pre*qyy*yr*yr*tensor[2];
2808  val += pre*qzz*zr*zr*tensor[2];
2809  val += pre*2.0*qxy*xr*yr*tensor[2];
2810  val += pre*2.0*qxz*xr*zr*tensor[2];
2811  val += pre*2.0*qyz*yr*zr*tensor[2];
2812  gzcf[IJKz(i,j,0)] += val;
2813 
2814  gpos[2] = zf[nz-1];
2815  zr = gpos[2] - apos[2];
2816  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2817  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2818  val = pre*charge*tensor[0];
2819  val -= pre*ux*xr*tensor[1];
2820  val -= pre*uy*yr*tensor[1];
2821  val -= pre*uz*zr*tensor[1];
2822  val += pre*qxx*xr*xr*tensor[2];
2823  val += pre*qyy*yr*yr*tensor[2];
2824  val += pre*qzz*zr*zr*tensor[2];
2825  val += pre*2.0*qxy*xr*yr*tensor[2];
2826  val += pre*2.0*qxz*xr*zr*tensor[2];
2827  val += pre*2.0*qyz*yr*zr*tensor[2];
2828  gzcf[IJKz(i,j,1)] += val;
2829  }
2830  }
2831 }
2832 
2833 VPRIVATE void bcCalcOrig(Vpmg *thee) {
2834 
2835  int nx, ny, nz;
2836  double size, *position, charge, xkappa, eps_w, T, pre1;
2837  double *dipole, *quadrupole, debye, eps_p;
2838  double xr,yr,zr,qave,*apos;
2839  double sdhcharge, sdhdipole[3], traced[9], sdhquadrupole[9];
2840  int i, j, k, iatom;
2841  Vpbe *pbe;
2842  Vatom *atom;
2843  Valist *alist;
2844 
2845  pbe = thee->pbe;
2846  alist = thee->pbe->alist;
2847  nx = thee->pmgp->nx;
2848  ny = thee->pmgp->ny;
2849  nz = thee->pmgp->nz;
2850 
2851  /* Zero out the boundaries */
2852  /* the "i" boundaries (dirichlet) */
2853  for (k=0; k<nz; k++) {
2854  for (j=0; j<ny; j++) {
2855  thee->gxcf[IJKx(j,k,0)] = 0.0;
2856  thee->gxcf[IJKx(j,k,1)] = 0.0;
2857  thee->gxcf[IJKx(j,k,2)] = 0.0;
2858  thee->gxcf[IJKx(j,k,3)] = 0.0;
2859  }
2860  }
2861 
2862  /* the "j" boundaries (dirichlet) */
2863  for (k=0; k<nz; k++) {
2864  for (i=0; i<nx; i++) {
2865  thee->gycf[IJKy(i,k,0)] = 0.0;
2866  thee->gycf[IJKy(i,k,1)] = 0.0;
2867  thee->gycf[IJKy(i,k,2)] = 0.0;
2868  thee->gycf[IJKy(i,k,3)] = 0.0;
2869  }
2870  }
2871 
2872  /* the "k" boundaries (dirichlet) */
2873  for (j=0; j<ny; j++) {
2874  for (i=0; i<nx; i++) {
2875  thee->gzcf[IJKz(i,j,0)] = 0.0;
2876  thee->gzcf[IJKz(i,j,1)] = 0.0;
2877  thee->gzcf[IJKz(i,j,2)] = 0.0;
2878  thee->gzcf[IJKz(i,j,3)] = 0.0;
2879  }
2880  }
2881 
2882  /* For each "atom" (only one for bcfl=1), we use the following formula to
2883  * calculate the boundary conditions:
2884  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2885  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2886  * * 1/d
2887  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
2888  * We only need to evaluate some of these prefactors once:
2889  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2890  * which gives the potential as
2891  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2892  */
2893  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
2894  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
2895  T = Vpbe_getTemperature(pbe); /* K */
2896  pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
2897 
2898  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
2899  * m/A, then we will only need to deal with distances and sizes in
2900  * Angstroms rather than meters. */
2901  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
2902  pre1 = pre1*(1.0e10);
2903 
2904  switch (thee->pmgp->bcfl) {
2905  /* If we have zero boundary conditions, we're done */
2906  case BCFL_ZERO:
2907  return;
2908 
2909  /* For single DH sphere BC's, we only have one "atom" to deal with;
2910  * get its information and */
2911  case BCFL_SDH:
2912  size = Vpbe_getSoluteRadius(pbe);
2913  position = Vpbe_getSoluteCenter(pbe);
2914 
2915  /*
2916  For AMOEBA SDH boundary conditions, we need to find the
2917  total monopole, dipole and traceless quadrupole moments
2918  of either the permanent multipoles, induced dipoles or
2919  non-local induced dipoles.
2920  */
2921 
2922  sdhcharge = 0.0;
2923  for (i=0; i<3; i++) sdhdipole[i] = 0.0;
2924  for (i=0; i<9; i++) sdhquadrupole[i] = 0.0;
2925 
2926  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
2927  atom = Valist_getAtom(alist, iatom);
2928  apos = Vatom_getPosition(atom);
2929  xr = apos[0] - position[0];
2930  yr = apos[1] - position[1];
2931  zr = apos[2] - position[2];
2932  switch (thee->chargeSrc) {
2933  case VCM_CHARGE:
2934  charge = Vatom_getCharge(atom);
2935  sdhcharge += charge;
2936  sdhdipole[0] += xr * charge;
2937  sdhdipole[1] += yr * charge;
2938  sdhdipole[2] += zr * charge;
2939  traced[0] = xr*xr*charge;
2940  traced[1] = xr*yr*charge;
2941  traced[2] = xr*zr*charge;
2942  traced[3] = yr*xr*charge;
2943  traced[4] = yr*yr*charge;
2944  traced[5] = yr*zr*charge;
2945  traced[6] = zr*xr*charge;
2946  traced[7] = zr*yr*charge;
2947  traced[8] = zr*zr*charge;
2948  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
2949  sdhquadrupole[0] += 1.5*(traced[0] - qave);
2950  sdhquadrupole[1] += 1.5*(traced[1]);
2951  sdhquadrupole[2] += 1.5*(traced[2]);
2952  sdhquadrupole[3] += 1.5*(traced[3]);
2953  sdhquadrupole[4] += 1.5*(traced[4] - qave);
2954  sdhquadrupole[5] += 1.5*(traced[5]);
2955  sdhquadrupole[6] += 1.5*(traced[6]);
2956  sdhquadrupole[7] += 1.5*(traced[7]);
2957  sdhquadrupole[8] += 1.5*(traced[8] - qave);
2958 #if defined(WITH_TINKER)
2959  case VCM_PERMANENT:
2960  charge = Vatom_getCharge(atom);
2961  dipole = Vatom_getDipole(atom);
2962  quadrupole = Vatom_getQuadrupole(atom);
2963  sdhcharge += charge;
2964  sdhdipole[0] += xr * charge;
2965  sdhdipole[1] += yr * charge;
2966  sdhdipole[2] += zr * charge;
2967  traced[0] = xr*xr*charge;
2968  traced[1] = xr*yr*charge;
2969  traced[2] = xr*zr*charge;
2970  traced[3] = yr*xr*charge;
2971  traced[4] = yr*yr*charge;
2972  traced[5] = yr*zr*charge;
2973  traced[6] = zr*xr*charge;
2974  traced[7] = zr*yr*charge;
2975  traced[8] = zr*zr*charge;
2976  sdhdipole[0] += dipole[0];
2977  sdhdipole[1] += dipole[1];
2978  sdhdipole[2] += dipole[2];
2979  traced[0] += 2.0*xr*dipole[0];
2980  traced[1] += xr*dipole[1] + yr*dipole[0];
2981  traced[2] += xr*dipole[2] + zr*dipole[0];
2982  traced[3] += yr*dipole[0] + xr*dipole[1];
2983  traced[4] += 2.0*yr*dipole[1];
2984  traced[5] += yr*dipole[2] + zr*dipole[1];
2985  traced[6] += zr*dipole[0] + xr*dipole[2];
2986  traced[7] += zr*dipole[1] + yr*dipole[2];
2987  traced[8] += 2.0*zr*dipole[2];
2988  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
2989  sdhquadrupole[0] += 1.5*(traced[0] - qave);
2990  sdhquadrupole[1] += 1.5*(traced[1]);
2991  sdhquadrupole[2] += 1.5*(traced[2]);
2992  sdhquadrupole[3] += 1.5*(traced[3]);
2993  sdhquadrupole[4] += 1.5*(traced[4] - qave);
2994  sdhquadrupole[5] += 1.5*(traced[5]);
2995  sdhquadrupole[6] += 1.5*(traced[6]);
2996  sdhquadrupole[7] += 1.5*(traced[7]);
2997  sdhquadrupole[8] += 1.5*(traced[8] - qave);
2998  sdhquadrupole[0] += quadrupole[0];
2999  sdhquadrupole[1] += quadrupole[1];
3000  sdhquadrupole[2] += quadrupole[2];
3001  sdhquadrupole[3] += quadrupole[3];
3002  sdhquadrupole[4] += quadrupole[4];
3003  sdhquadrupole[5] += quadrupole[5];
3004  sdhquadrupole[6] += quadrupole[6];
3005  sdhquadrupole[7] += quadrupole[7];
3006  sdhquadrupole[8] += quadrupole[8];
3007  case VCM_INDUCED:
3008  dipole = Vatom_getInducedDipole(atom);
3009  sdhdipole[0] += dipole[0];
3010  sdhdipole[1] += dipole[1];
3011  sdhdipole[2] += dipole[2];
3012  traced[0] = 2.0*xr*dipole[0];
3013  traced[1] = xr*dipole[1] + yr*dipole[0];
3014  traced[2] = xr*dipole[2] + zr*dipole[0];
3015  traced[3] = yr*dipole[0] + xr*dipole[1];
3016  traced[4] = 2.0*yr*dipole[1];
3017  traced[5] = yr*dipole[2] + zr*dipole[1];
3018  traced[6] = zr*dipole[0] + xr*dipole[2];
3019  traced[7] = zr*dipole[1] + yr*dipole[2];
3020  traced[8] = 2.0*zr*dipole[2];
3021  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3022  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3023  sdhquadrupole[1] += 1.5*(traced[1]);
3024  sdhquadrupole[2] += 1.5*(traced[2]);
3025  sdhquadrupole[3] += 1.5*(traced[3]);
3026  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3027  sdhquadrupole[5] += 1.5*(traced[5]);
3028  sdhquadrupole[6] += 1.5*(traced[6]);
3029  sdhquadrupole[7] += 1.5*(traced[7]);
3030  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3031  case VCM_NLINDUCED:
3032  dipole = Vatom_getNLInducedDipole(atom);
3033  sdhdipole[0] += dipole[0];
3034  sdhdipole[1] += dipole[1];
3035  sdhdipole[2] += dipole[2];
3036  traced[0] = 2.0*xr*dipole[0];
3037  traced[1] = xr*dipole[1] + yr*dipole[0];
3038  traced[2] = xr*dipole[2] + zr*dipole[0];
3039  traced[3] = yr*dipole[0] + xr*dipole[1];
3040  traced[4] = 2.0*yr*dipole[1];
3041  traced[5] = yr*dipole[2] + zr*dipole[1];
3042  traced[6] = zr*dipole[0] + xr*dipole[2];
3043  traced[7] = zr*dipole[1] + yr*dipole[2];
3044  traced[8] = 2.0*zr*dipole[2];
3045  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3046  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3047  sdhquadrupole[1] += 1.5*(traced[1]);
3048  sdhquadrupole[2] += 1.5*(traced[2]);
3049  sdhquadrupole[3] += 1.5*(traced[3]);
3050  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3051  sdhquadrupole[5] += 1.5*(traced[5]);
3052  sdhquadrupole[6] += 1.5*(traced[6]);
3053  sdhquadrupole[7] += 1.5*(traced[7]);
3054  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3055 #endif /* if defined(WITH_TINKER) */
3056  }
3057  }
3058  /* SDH dipole and traceless quadrupole values
3059  were checked against similar routines in TINKER
3060  for large proteins.
3061 
3062  debye=4.8033324;
3063  printf("%6.3f, %6.3f, %6.3f\n", sdhdipole[0]*debye,
3064  sdhdipole[1]*debye, sdhdipole[2]*debye);
3065  printf("%6.3f\n", sdhquadrupole[0]*debye);
3066  printf("%6.3f %6.3f\n", sdhquadrupole[3]*debye,
3067  sdhquadrupole[4]*debye);
3068  printf("%6.3f %6.3f %6.3f\n", sdhquadrupole[6]*debye,
3069  sdhquadrupole[7]*debye, sdhquadrupole[8]*debye);
3070  */
3071 
3072  bcfl2(size, position, sdhcharge, sdhdipole, sdhquadrupole,
3073  xkappa, eps_p, eps_w, T, thee->gxcf, thee->gycf,
3074  thee->gzcf, thee->xf, thee->yf, thee->zf, nx, ny, nz);
3075  break;
3076 
3077  case BCFL_MDH:
3078  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
3079  atom = Valist_getAtom(alist, iatom);
3080  position = Vatom_getPosition(atom);
3081  charge = Vunit_ec*Vatom_getCharge(atom);
3082  dipole = VNULL;
3083  quadrupole = VNULL;
3084  size = Vatom_getRadius(atom);
3085  switch (thee->chargeSrc)
3086  {
3087  case VCM_CHARGE:
3088  ;
3089 #if defined(WITH_TINKER)
3090  case VCM_PERMANENT:
3091  dipole = Vatom_getDipole(atom);
3092  quadrupole = Vatom_getQuadrupole(atom);
3093 
3094  case VCM_INDUCED:
3095  dipole = Vatom_getInducedDipole(atom);
3096 
3097  case VCM_NLINDUCED:
3098  dipole = Vatom_getNLInducedDipole(atom);
3099 #endif
3100  }
3101  bcfl1(size, position, charge, xkappa, pre1,
3102  thee->gxcf, thee->gycf, thee->gzcf,
3103  thee->xf, thee->yf, thee->zf, nx, ny, nz);
3104  }
3105  break;
3106 
3107  case BCFL_UNUSED:
3108  Vnm_print(2, "bcCalc: Invalid bcfl (%d)!\n", thee->pmgp->bcfl);
3109  VASSERT(0);
3110 
3111  case BCFL_FOCUS:
3112  Vnm_print(2, "VPMG::bcCalc -- not appropriate for focusing!\n");
3113  VASSERT(0);
3114 
3115  default:
3116  Vnm_print(2, "VPMG::bcCalc -- invalid boundary condition \
3117 flag (%d)!\n", thee->pmgp->bcfl);
3118  VASSERT(0);
3119  }
3120 }
3121 
3122 /*
3123  Used by bcflnew
3124  */
3125 VPRIVATE int gridPointIsValid(int i, int j, int k, int nx, int ny, int nz){
3126 
3127  int isValid = 0;
3128 
3129  if((k==0) || (k==nz-1)){
3130  isValid = 1;
3131  }else if((j==0) || (j==ny-1)){
3132  isValid = 1;
3133  }else if((i==0) || (i==nx-1)){
3134  isValid = 1;
3135  }
3136 
3137  return isValid;
3138 }
3139 
3140 /*
3141  Used by bcflnew
3142  */
3143 #ifdef DEBUG_MAC_OSX_OCL
3144 #include "mach_chud.h"
3145 VPRIVATE void packAtomsOpenCL(float *ax, float *ay, float *az,
3146  float *charge, float *size, Vpmg *thee){
3147 
3148  int i;
3149  int natoms;
3150 
3151  Vatom *atom = VNULL;
3152  Valist *alist = VNULL;
3153 
3154  alist = thee->pbe->alist;
3155  natoms = Valist_getNumberAtoms(alist);
3156 
3157  for(i=0;i<natoms;i++){
3158  atom = &(alist->atoms[i]);
3159  charge[i] = Vunit_ec*atom->charge;
3160  ax[i] = atom->position[0];
3161  ay[i] = atom->position[1];
3162  az[i] = atom->position[2];
3163  size[i] = atom->radius;
3164  }
3165 }
3166 
3167 /*
3168  Used by bcflnew
3169  */
3170 VPRIVATE void packUnpackOpenCL(int nx, int ny, int nz, int ngrid,
3171  float *gx, float *gy, float *gz, float *value,
3172  Vpmg *thee, int pack){
3173 
3174  int i,j,k,igrid;
3175  int x0,x1,y0,y1,z0,z1;
3176 
3177  float gpos[3];
3178  double *xf, *yf, *zf;
3179  double *gxcf, *gycf, *gzcf;
3180 
3181  xf = thee->xf;
3182  yf = thee->yf;
3183  zf = thee->zf;
3184 
3185  gxcf = thee->gxcf;
3186  gycf = thee->gycf;
3187  gzcf = thee->gzcf;
3188 
3189  igrid = 0;
3190  for(k=0;k<nz;k++){
3191  gpos[2] = zf[k];
3192  for(j=0;j<ny;j++){
3193  gpos[1] = yf[j];
3194  for(i=0;i<nx;i++){
3195  gpos[0] = xf[i];
3196  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3197  if(pack != 0){
3198  gx[igrid] = gpos[0];
3199  gy[igrid] = gpos[1];
3200  gz[igrid] = gpos[2];
3201 
3202  value[igrid] = 0.0;
3203  }else{
3204  x0 = IJKx(j,k,0);
3205  x1 = IJKx(j,k,1);
3206  y0 = IJKy(i,k,0);
3207  y1 = IJKy(i,k,1);
3208  z0 = IJKz(i,j,0);
3209  z1 = IJKz(i,j,1);
3210 
3211  if(i==0){
3212  gxcf[x0] += value[igrid];
3213  }
3214  if(i==nx-1){
3215  gxcf[x1] += value[igrid];
3216  }
3217  if(j==0){
3218  gycf[y0] += value[igrid];
3219  }
3220  if(j==ny-1){
3221  gycf[y1] += value[igrid];
3222  }
3223  if(k==0){
3224  gzcf[z0] += value[igrid];
3225  }
3226  if(k==nz-1){
3227  gzcf[z1] += value[igrid];
3228  }
3229  }
3230 
3231  igrid++;
3232  } //end is valid point
3233  } //end i
3234  } //end j
3235  } //end k
3236 
3237 }
3238 
3239 /*
3240  bcflnew is an optimized replacement for bcfl1. bcfl1 is still used when TINKER
3241  support is compiled in.
3242  bcflnew uses: packUnpack, packAtoms, gridPointIsValid
3243  */
3244 VPRIVATE void bcflnewOpenCL(Vpmg *thee){
3245 
3246  int i,j,k, iatom, igrid;
3247  int x0, x1, y0, y1, z0, z1;
3248 
3249  int nx, ny, nz;
3250  int natoms, ngrid, ngadj;
3251 
3252  float dist, pre1, eps_w, eps_p, T, xkappa;
3253 
3254  float *ax, *ay, *az;
3255  float *charge, *size, *val;
3256 
3257  float *gx, *gy, *gz;
3258 
3259  Vpbe *pbe = thee->pbe;
3260 
3261  nx = thee->pmgp->nx;
3262  ny = thee->pmgp->ny;
3263  nz = thee->pmgp->nz;
3264 
3265  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3266  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3267  T = Vpbe_getTemperature(pbe); /* K */
3268  pre1 = ((Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T))*(1.0e10);
3269  xkappa = Vpbe_getXkappa(pbe);
3270 
3271  natoms = Valist_getNumberAtoms(thee->pbe->alist);
3272  ngrid = 2*((nx*ny) + (ny*nz) + (nx*nz));
3273  ngadj = ngrid + (512 - (ngrid & 511));
3274 
3275  ax = (float*)malloc(natoms * sizeof(float));
3276  ay = (float*)malloc(natoms * sizeof(float));
3277  az = (float*)malloc(natoms * sizeof(float));
3278 
3279  charge = (float*)malloc(natoms * sizeof(float));
3280  size = (float*)malloc(natoms * sizeof(float));
3281 
3282  gx = (float*)malloc(ngrid * sizeof(float));
3283  gy = (float*)malloc(ngrid * sizeof(float));
3284  gz = (float*)malloc(ngrid * sizeof(float));
3285 
3286  val = (float*)malloc(ngrid * sizeof(float));
3287 
3288  packAtomsOpenCL(ax,ay,az,charge,size,thee);
3289  packUnpackOpenCL(nx,ny,nz,ngrid,gx,gy,gz,val,thee,1);
3290 
3291  runMDHCL(ngrid,natoms,ngadj,ax,ay,az,gx,gy,gz,charge,size,xkappa,pre1,val);
3292 
3293  packUnpackOpenCL(nx,ny,nz,ngrid,gx,gy,gz,val,thee,0);
3294 
3295  free(ax);
3296  free(ay);
3297  free(az);
3298  free(charge);
3299  free(size);
3300 
3301  free(gx);
3302  free(gy);
3303  free(gz);
3304  free(val);
3305 }
3306 #endif
3307 
3308 VPRIVATE void packAtoms(double *ax, double *ay, double *az,
3309  double *charge, double *size, Vpmg *thee){
3310 
3311  int i;
3312  int natoms;
3313 
3314  Vatom *atom = VNULL;
3315  Valist *alist = VNULL;
3316 
3317  alist = thee->pbe->alist;
3318  natoms = Valist_getNumberAtoms(alist);
3319 
3320  for(i=0;i<natoms;i++){
3321  atom = &(alist->atoms[i]);
3322  charge[i] = Vunit_ec*atom->charge;
3323  ax[i] = atom->position[0];
3324  ay[i] = atom->position[1];
3325  az[i] = atom->position[2];
3326  size[i] = atom->radius;
3327  }
3328 }
3329 
3330 /*
3331  Used by bcflnew
3332  */
3333 VPRIVATE void packUnpack(int nx, int ny, int nz, int ngrid,
3334  double *gx, double *gy, double *gz, double *value,
3335  Vpmg *thee, int pack){
3336 
3337  int i,j,k,igrid;
3338  int x0,x1,y0,y1,z0,z1;
3339 
3340  double gpos[3];
3341  double *xf, *yf, *zf;
3342  double *gxcf, *gycf, *gzcf;
3343 
3344  xf = thee->xf;
3345  yf = thee->yf;
3346  zf = thee->zf;
3347 
3348  gxcf = thee->gxcf;
3349  gycf = thee->gycf;
3350  gzcf = thee->gzcf;
3351 
3352  igrid = 0;
3353  for(k=0;k<nz;k++){
3354  gpos[2] = zf[k];
3355  for(j=0;j<ny;j++){
3356  gpos[1] = yf[j];
3357  for(i=0;i<nx;i++){
3358  gpos[0] = xf[i];
3359  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3360  if(pack != 0){
3361  gx[igrid] = gpos[0];
3362  gy[igrid] = gpos[1];
3363  gz[igrid] = gpos[2];
3364 
3365  value[igrid] = 0.0;
3366  }else{
3367  x0 = IJKx(j,k,0);
3368  x1 = IJKx(j,k,1);
3369  y0 = IJKy(i,k,0);
3370  y1 = IJKy(i,k,1);
3371  z0 = IJKz(i,j,0);
3372  z1 = IJKz(i,j,1);
3373 
3374  if(i==0){
3375  gxcf[x0] += value[igrid];
3376  }
3377  if(i==nx-1){
3378  gxcf[x1] += value[igrid];
3379  }
3380  if(j==0){
3381  gycf[y0] += value[igrid];
3382  }
3383  if(j==ny-1){
3384  gycf[y1] += value[igrid];
3385  }
3386  if(k==0){
3387  gzcf[z0] += value[igrid];
3388  }
3389  if(k==nz-1){
3390  gzcf[z1] += value[igrid];
3391  }
3392  }
3393 
3394  igrid++;
3395  } //end is valid point
3396  } //end i
3397  } //end j
3398  } //end k
3399 
3400 }
3401 
3402 VPRIVATE void bcflnew(Vpmg *thee){
3403 
3404  int i,j,k, iatom, igrid;
3405  int x0, x1, y0, y1, z0, z1;
3406 
3407  int nx, ny, nz;
3408  int natoms, ngrid;
3409 
3410  double dist, pre1, eps_w, eps_p, T, xkappa;
3411 
3412  double *ax, *ay, *az;
3413  double *charge, *size, *val;
3414 
3415  double *gx, *gy, *gz;
3416 
3417  Vpbe *pbe = thee->pbe;
3418 
3419  nx = thee->pmgp->nx;
3420  ny = thee->pmgp->ny;
3421  nz = thee->pmgp->nz;
3422 
3423  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3424  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3425  T = Vpbe_getTemperature(pbe); /* K */
3426  pre1 = ((Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T))*(1.0e10);
3427  xkappa = Vpbe_getXkappa(pbe);
3428 
3429  natoms = Valist_getNumberAtoms(thee->pbe->alist);
3430  ngrid = 2*((nx*ny) + (ny*nz) + (nx*nz));
3431 
3432  ax = (double*)malloc(natoms * sizeof(double));
3433  ay = (double*)malloc(natoms * sizeof(double));
3434  az = (double*)malloc(natoms * sizeof(double));
3435 
3436  charge = (double*)malloc(natoms * sizeof(double));
3437  size = (double*)malloc(natoms * sizeof(double));
3438 
3439  gx = (double*)malloc(ngrid * sizeof(double));
3440  gy = (double*)malloc(ngrid * sizeof(double));
3441  gz = (double*)malloc(ngrid * sizeof(double));
3442 
3443  val = (double*)malloc(ngrid * sizeof(double));
3444 
3445  packAtoms(ax,ay,az,charge,size,thee);
3446  packUnpack(nx,ny,nz,ngrid,gx,gy,gz,val,thee,1);
3447 
3448  if(xkappa > VSMALL){
3449 #pragma omp parallel for default(shared) private(igrid,iatom,dist)
3450  for(igrid=0;igrid<ngrid;igrid++){
3451  for(iatom=0; iatom<natoms; iatom++){
3452  dist = VSQRT(VSQR(gx[igrid]-ax[iatom]) + VSQR(gy[igrid]-ay[iatom])
3453  + VSQR(gz[igrid]-az[iatom]));
3454  val[igrid] += pre1*(charge[iatom]/dist)*VEXP(-xkappa*(dist-size[iatom]))
3455  / (1+xkappa*size[iatom]);
3456  }
3457  }
3458  }else{
3459 #pragma omp parallel for default(shared) private(igrid,iatom,dist)
3460  for(igrid=0;igrid<ngrid;igrid++){
3461  for(iatom=0; iatom<natoms; iatom++){
3462  dist = VSQRT(VSQR(gx[igrid]-ax[iatom]) + VSQR(gy[igrid]-ay[iatom])
3463  + VSQR(gz[igrid]-az[iatom]));
3464  val[igrid] += pre1*(charge[iatom]/dist);
3465  }
3466  }
3467  }
3468  packUnpack(nx,ny,nz,ngrid,gx,gy,gz,val,thee,0);
3469 
3470  free(ax);
3471  free(ay);
3472  free(az);
3473  free(charge);
3474  free(size);
3475 
3476  free(gx);
3477  free(gy);
3478  free(gz);
3479  free(val);
3480 }
3481 
3482 VPRIVATE void multipolebc(double r, double kappa, double eps_p,
3483  double eps_w, double rad, double tsr[3]) {
3484  double r2,r3,r5;
3485  double eps_r;
3486  double ka,ka2,ka3;
3487  double kr,kr2,kr3;
3488 
3489  /*
3490  Below an attempt is made to explain the potential outside of a
3491  multipole located at the center of spherical cavity of dieletric
3492  eps_p, with dielectric eps_w outside (and possibly kappa > 0).
3493 
3494 
3495  First, eps_p = 1.0
3496  eps_w = 1.0
3497  kappa = 0.0
3498 
3499  The general form for the potential of a traceless multipole tensor
3500  of rank n in vacuum is:
3501 
3502  V(r) = (-1)^n * u . n . Del^n (1/r)
3503 
3504  where
3505  u is a multipole of order n (3^n components)
3506  u . n. Del^n (1/r) is the contraction of u with the nth
3507  derivative of 1/r
3508 
3509  for example, if n = 1, the dipole potential is
3510  V_vac(r) = (-1)*[ux*x + uy*y + uz*z]/r^3
3511 
3512  This function returns the parts of V(r) for multipoles of
3513  order 0, 1 and 2 that are independent of the contraction.
3514 
3515  For the vacuum example, this would be 1/r, -1/r^3 and 3/r^5
3516  respectively.
3517 
3518  *** Note that this requires that the quadrupole is
3519  traceless. If not, the diagonal quadrupole potential changes
3520  from
3521  qaa * 3*a^2/r^5
3522  to
3523  qaa * (3*a^2/r^5 - 1/r^3a )
3524  where we sum over the trace; a = x, y and z.
3525 
3526  (In other words, the -1/r^3 term cancels for a traceless quadrupole.
3527  qxx + qyy + qzz = 0
3528  such that
3529  -(qxx + qyy + qzz)/r^3 = 0
3530 
3531  For quadrupole with trace:
3532  qxx + qyy + qzz != 0
3533  such that
3534  -(qxx + qyy + qzz)/r^3 != 0
3535  )
3536 
3537  ========================================================================
3538 
3539  eps_p != 1 or eps_w != 1
3540  kappa = 0.0
3541 
3542  If the multipole is placed at the center of a sphere with
3543  dieletric eps_p in a solvent of dielectric eps_w, the potential
3544  outside the sphere is the solution to the Laplace equation:
3545 
3546  V(r) = 1/eps_w * (2*n+1)*eps_r/(n+(n+1)*eps_r)
3547  * (-1)^n * u . n . Del^n (1/r)
3548  where
3549  eps_r = eps_w / eps_p
3550  is the ratio of solvent to solute dielectric
3551 
3552  ========================================================================
3553 
3554  kappa > 0
3555 
3556  Finally, if the region outside the sphere is treated by the linearized
3557  PB equation with Debye-Huckel parameter kappa, the solution is:
3558 
3559  V(r) = kappa/eps_w * alpha_n(kappa*a) * K_n(kappa*r) * r^(n+1)/a^n
3560  * (-1)^n * u . n . Del^n (1/r)
3561  where
3562  alpha_n(x) is [(2n + 1) / x] / [(n*K_n(x)/eps_r) - x*K_n'(x)]
3563  K_n(x) are modified spherical Bessel functions of the third kind.
3564  K_n'(x) is the derivative of K_n(x)
3565  */
3566 
3567  eps_r = eps_w/eps_p;
3568  r2 = r*r;
3569  r3 = r2*r;
3570  r5 = r3*r2;
3571  tsr[0] = (1.0/eps_w)/r;
3572  tsr[1] = (1.0/eps_w)*(-1.0)/r3;
3573  tsr[2] = (1.0/eps_w)*(3.0)/r5;
3574  if (kappa < VSMALL) {
3575  tsr[1] = (3.0*eps_r)/(1.0 + 2.0*eps_r)*tsr[1];
3576  tsr[2] = (5.0*eps_r)/(2.0 + 3.0*eps_r)*tsr[2];
3577  } else {
3578  ka = kappa*rad;
3579  ka2 = ka*ka;
3580  ka3 = ka2*ka;
3581  kr = kappa*r;
3582  kr2 = kr*kr;
3583  kr3 = kr2*kr;
3584  tsr[0] = exp(ka-kr) / (1.0 + ka) * tsr[0];
3585  tsr[1] = 3.0*eps_r*exp(ka-kr)*(1.0 + kr) * tsr[1];
3586  tsr[1] = tsr[1] / (1.0 + ka + eps_r*(2.0 + 2.0*ka + ka2));
3587  tsr[2] = 5.0*eps_r*exp(ka-kr)*(3.0 + 3.0*kr + kr2) * tsr[2];
3588  tsr[2] = tsr[2]/(6.0+6.0*ka+2.0*ka2+eps_r*(9.0+9.0*ka+4.0*ka2+ka3));
3589  }
3590 }
3591 
3592 VPRIVATE void bcfl_sdh(Vpmg *thee){
3593 
3594  int i,j,k,iatom;
3595  int nx, ny, nz;
3596 
3597  double size, *position, charge, xkappa, eps_w, eps_p, T, pre, dist;
3598  double sdhcharge, sdhdipole[3], traced[9], sdhquadrupole[9];
3599  double *dipole, *quadrupole;
3600 
3601  double val, *apos, gpos[3], tensor[3], qave;
3602  double ux, uy, uz, xr, yr, zr;
3603  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
3604 
3605  double *xf, *yf, *zf;
3606  double *gxcf, *gycf, *gzcf;
3607 
3608  Vpbe *pbe;
3609  Vatom *atom;
3610  Valist *alist;
3611 
3612  pbe = thee->pbe;
3613  alist = thee->pbe->alist;
3614  nx = thee->pmgp->nx;
3615  ny = thee->pmgp->ny;
3616  nz = thee->pmgp->nz;
3617 
3618  xf = thee->xf;
3619  yf = thee->yf;
3620  zf = thee->zf;
3621 
3622  gxcf = thee->gxcf;
3623  gycf = thee->gycf;
3624  gzcf = thee->gzcf;
3625 
3626  /* For each "atom" (only one for bcfl=1), we use the following formula to
3627  * calculate the boundary conditions:
3628  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3629  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3630  * * 1/d
3631  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
3632  * We only need to evaluate some of these prefactors once:
3633  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3634  * which gives the potential as
3635  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3636  */
3637  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3638  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3639  T = Vpbe_getTemperature(pbe); /* K */
3640 
3641  pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
3642  pre = pre*(1.0e10);
3643 
3644  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
3645  * m/A, then we will only need to deal with distances and sizes in
3646  * Angstroms rather than meters. */
3647  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
3648 
3649  /* Solute size and position */
3650  size = Vpbe_getSoluteRadius(pbe);
3651  position = Vpbe_getSoluteCenter(pbe);
3652 
3653  sdhcharge = 0.0;
3654  for (i=0; i<3; i++) sdhdipole[i] = 0.0;
3655  for (i=0; i<9; i++) sdhquadrupole[i] = 0.0;
3656 
3657  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
3658  atom = Valist_getAtom(alist, iatom);
3659  apos = Vatom_getPosition(atom);
3660  xr = apos[0] - position[0];
3661  yr = apos[1] - position[1];
3662  zr = apos[2] - position[2];
3663  switch (thee->chargeSrc) {
3664  case VCM_CHARGE:
3665  charge = Vatom_getCharge(atom);
3666  sdhcharge += charge;
3667  sdhdipole[0] += xr * charge;
3668  sdhdipole[1] += yr * charge;
3669  sdhdipole[2] += zr * charge;
3670  traced[0] = xr*xr*charge;
3671  traced[1] = xr*yr*charge;
3672  traced[2] = xr*zr*charge;
3673  traced[3] = yr*xr*charge;
3674  traced[4] = yr*yr*charge;
3675  traced[5] = yr*zr*charge;
3676  traced[6] = zr*xr*charge;
3677  traced[7] = zr*yr*charge;
3678  traced[8] = zr*zr*charge;
3679  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3680  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3681  sdhquadrupole[1] += 1.5*(traced[1]);
3682  sdhquadrupole[2] += 1.5*(traced[2]);
3683  sdhquadrupole[3] += 1.5*(traced[3]);
3684  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3685  sdhquadrupole[5] += 1.5*(traced[5]);
3686  sdhquadrupole[6] += 1.5*(traced[6]);
3687  sdhquadrupole[7] += 1.5*(traced[7]);
3688  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3689 #if defined(WITH_TINKER)
3690  case VCM_PERMANENT:
3691  charge = Vatom_getCharge(atom);
3692  dipole = Vatom_getDipole(atom);
3693  quadrupole = Vatom_getQuadrupole(atom);
3694  sdhcharge += charge;
3695  sdhdipole[0] += xr * charge;
3696  sdhdipole[1] += yr * charge;
3697  sdhdipole[2] += zr * charge;
3698  traced[0] = xr*xr*charge;
3699  traced[1] = xr*yr*charge;
3700  traced[2] = xr*zr*charge;
3701  traced[3] = yr*xr*charge;
3702  traced[4] = yr*yr*charge;
3703  traced[5] = yr*zr*charge;
3704  traced[6] = zr*xr*charge;
3705  traced[7] = zr*yr*charge;
3706  traced[8] = zr*zr*charge;
3707  sdhdipole[0] += dipole[0];
3708  sdhdipole[1] += dipole[1];
3709  sdhdipole[2] += dipole[2];
3710  traced[0] += 2.0*xr*dipole[0];
3711  traced[1] += xr*dipole[1] + yr*dipole[0];
3712  traced[2] += xr*dipole[2] + zr*dipole[0];
3713  traced[3] += yr*dipole[0] + xr*dipole[1];
3714  traced[4] += 2.0*yr*dipole[1];
3715  traced[5] += yr*dipole[2] + zr*dipole[1];
3716  traced[6] += zr*dipole[0] + xr*dipole[2];
3717  traced[7] += zr*dipole[1] + yr*dipole[2];
3718  traced[8] += 2.0*zr*dipole[2];
3719  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3720  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3721  sdhquadrupole[1] += 1.5*(traced[1]);
3722  sdhquadrupole[2] += 1.5*(traced[2]);
3723  sdhquadrupole[3] += 1.5*(traced[3]);
3724  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3725  sdhquadrupole[5] += 1.5*(traced[5]);
3726  sdhquadrupole[6] += 1.5*(traced[6]);
3727  sdhquadrupole[7] += 1.5*(traced[7]);
3728  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3729  sdhquadrupole[0] += quadrupole[0];
3730  sdhquadrupole[1] += quadrupole[1];
3731  sdhquadrupole[2] += quadrupole[2];
3732  sdhquadrupole[3] += quadrupole[3];
3733  sdhquadrupole[4] += quadrupole[4];
3734  sdhquadrupole[5] += quadrupole[5];
3735  sdhquadrupole[6] += quadrupole[6];
3736  sdhquadrupole[7] += quadrupole[7];
3737  sdhquadrupole[8] += quadrupole[8];
3738  case VCM_INDUCED:
3739  dipole = Vatom_getInducedDipole(atom);
3740  sdhdipole[0] += dipole[0];
3741  sdhdipole[1] += dipole[1];
3742  sdhdipole[2] += dipole[2];
3743  traced[0] = 2.0*xr*dipole[0];
3744  traced[1] = xr*dipole[1] + yr*dipole[0];
3745  traced[2] = xr*dipole[2] + zr*dipole[0];
3746  traced[3] = yr*dipole[0] + xr*dipole[1];
3747  traced[4] = 2.0*yr*dipole[1];
3748  traced[5] = yr*dipole[2] + zr*dipole[1];
3749  traced[6] = zr*dipole[0] + xr*dipole[2];
3750  traced[7] = zr*dipole[1] + yr*dipole[2];
3751  traced[8] = 2.0*zr*dipole[2];
3752  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3753  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3754  sdhquadrupole[1] += 1.5*(traced[1]);
3755  sdhquadrupole[2] += 1.5*(traced[2]);
3756  sdhquadrupole[3] += 1.5*(traced[3]);
3757  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3758  sdhquadrupole[5] += 1.5*(traced[5]);
3759  sdhquadrupole[6] += 1.5*(traced[6]);
3760  sdhquadrupole[7] += 1.5*(traced[7]);
3761  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3762  case VCM_NLINDUCED:
3763  dipole = Vatom_getNLInducedDipole(atom);
3764  sdhdipole[0] += dipole[0];
3765  sdhdipole[1] += dipole[1];
3766  sdhdipole[2] += dipole[2];
3767  traced[0] = 2.0*xr*dipole[0];
3768  traced[1] = xr*dipole[1] + yr*dipole[0];
3769  traced[2] = xr*dipole[2] + zr*dipole[0];
3770  traced[3] = yr*dipole[0] + xr*dipole[1];
3771  traced[4] = 2.0*yr*dipole[1];
3772  traced[5] = yr*dipole[2] + zr*dipole[1];
3773  traced[6] = zr*dipole[0] + xr*dipole[2];
3774  traced[7] = zr*dipole[1] + yr*dipole[2];
3775  traced[8] = 2.0*zr*dipole[2];
3776  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3777  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3778  sdhquadrupole[1] += 1.5*(traced[1]);
3779  sdhquadrupole[2] += 1.5*(traced[2]);
3780  sdhquadrupole[3] += 1.5*(traced[3]);
3781  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3782  sdhquadrupole[5] += 1.5*(traced[5]);
3783  sdhquadrupole[6] += 1.5*(traced[6]);
3784  sdhquadrupole[7] += 1.5*(traced[7]);
3785  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3786 #endif /* if defined(WITH_TINKER) */
3787  }
3788  }
3789 
3790  ux = sdhdipole[0];
3791  uy = sdhdipole[1];
3792  uz = sdhdipole[2];
3793 
3794  /* The factor of 1/3 results from using a
3795  traceless quadrupole definition. See, for example,
3796  "The Theory of Intermolecular Forces" by A.J. Stone,
3797  Chapter 3. */
3798  qxx = sdhquadrupole[0] / 3.0;
3799  qxy = sdhquadrupole[1] / 3.0;
3800  qxz = sdhquadrupole[2] / 3.0;
3801  qyx = sdhquadrupole[3] / 3.0;
3802  qyy = sdhquadrupole[4] / 3.0;
3803  qyz = sdhquadrupole[5] / 3.0;
3804  qzx = sdhquadrupole[6] / 3.0;
3805  qzy = sdhquadrupole[7] / 3.0;
3806  qzz = sdhquadrupole[8] / 3.0;
3807 
3808  for(k=0;k<nz;k++){
3809  gpos[2] = zf[k];
3810  for(j=0;j<ny;j++){
3811  gpos[1] = yf[j];
3812  for(i=0;i<nx;i++){
3813  gpos[0] = xf[i];
3814  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3815  xr = gpos[0] - position[0];
3816  yr = gpos[1] - position[1];
3817  zr = gpos[2] - position[2];
3818 
3819  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
3820  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
3821 
3822  val = pre*sdhcharge*tensor[0];
3823  val -= pre*ux*xr*tensor[1];
3824  val -= pre*uy*yr*tensor[1];
3825  val -= pre*uz*zr*tensor[1];
3826  val += pre*qxx*xr*xr*tensor[2];
3827  val += pre*qyy*yr*yr*tensor[2];
3828  val += pre*qzz*zr*zr*tensor[2];
3829  val += pre*2.0*qxy*xr*yr*tensor[2];
3830  val += pre*2.0*qxz*xr*zr*tensor[2];
3831  val += pre*2.0*qyz*yr*zr*tensor[2];
3832 
3833  if(i==0){
3834  gxcf[IJKx(j,k,0)] = val;
3835  }
3836  if(i==nx-1){
3837  gxcf[IJKx(j,k,1)] = val;
3838  }
3839  if(j==0){
3840  gycf[IJKy(i,k,0)] = val;
3841  }
3842  if(j==ny-1){
3843  gycf[IJKy(i,k,1)] = val;
3844  }
3845  if(k==0){
3846  gzcf[IJKz(i,j,0)] = val;
3847  }
3848  if(k==nz-1){
3849  gzcf[IJKz(i,j,1)] = val;
3850  }
3851  } /* End grid point is valid */
3852  } /* End i loop */
3853  } /* End j loop */
3854  } /* End k loop */
3855 
3856 }
3857 
3858 VPRIVATE void bcfl_mdh(Vpmg *thee){
3859 
3860  int i,j,k,iatom;
3861  int nx, ny, nz;
3862 
3863  double val, *apos, gpos[3];
3864  double *dipole, *quadrupole;
3865  double size, charge, xkappa, eps_w, eps_p, T, pre1, dist;
3866 
3867  double *xf, *yf, *zf;
3868  double *gxcf, *gycf, *gzcf;
3869 
3870  Vpbe *pbe;
3871  Vatom *atom;
3872  Valist *alist;
3873 
3874  pbe = thee->pbe;
3875  alist = thee->pbe->alist;
3876  nx = thee->pmgp->nx;
3877  ny = thee->pmgp->ny;
3878  nz = thee->pmgp->nz;
3879 
3880  xf = thee->xf;
3881  yf = thee->yf;
3882  zf = thee->zf;
3883 
3884  gxcf = thee->gxcf;
3885  gycf = thee->gycf;
3886  gzcf = thee->gzcf;
3887 
3888  /* For each "atom" (only one for bcfl=1), we use the following formula to
3889  * calculate the boundary conditions:
3890  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3891  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3892  * * 1/d
3893  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
3894  * We only need to evaluate some of these prefactors once:
3895  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3896  * which gives the potential as
3897  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3898  */
3899  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3900  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3901  T = Vpbe_getTemperature(pbe); /* K */
3902  pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
3903 
3904  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
3905  * m/A, then we will only need to deal with distances and sizes in
3906  * Angstroms rather than meters. */
3907  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
3908  pre1 = pre1*(1.0e10);
3909 
3910  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
3911  * m/A, then we will only need to deal with distances and sizes in
3912  * Angstroms rather than meters. */
3913  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
3914 
3915  for(k=0;k<nz;k++){
3916  gpos[2] = zf[k];
3917  for(j=0;j<ny;j++){
3918  gpos[1] = yf[j];
3919  for(i=0;i<nx;i++){
3920  gpos[0] = xf[i];
3921  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3922 
3923  val = 0.0;
3924 
3925  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
3926  atom = Valist_getAtom(alist, iatom);
3927  apos = Vatom_getPosition(atom);
3928  charge = Vunit_ec*Vatom_getCharge(atom);
3929  size = Vatom_getRadius(atom);
3930 
3931  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
3932  + VSQR(gpos[2]-apos[2]));
3933  if (xkappa > VSMALL) {
3934  val += pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
3935  / (1+xkappa*size);
3936  } else {
3937  val += pre1*(charge/dist);
3938  }
3939 
3940  }
3941 
3942  if(i==0){
3943  gxcf[IJKx(j,k,0)] = val;
3944  }
3945  if(i==nx-1){
3946  gxcf[IJKx(j,k,1)] = val;
3947  }
3948  if(j==0){
3949  gycf[IJKy(i,k,0)] = val;
3950  }
3951  if(j==ny-1){
3952  gycf[IJKy(i,k,1)] = val;
3953  }
3954  if(k==0){
3955  gzcf[IJKz(i,j,0)] = val;
3956  }
3957  if(k==nz-1){
3958  gzcf[IJKz(i,j,1)] = val;
3959  }
3960  } /* End grid point is valid */
3961  } /* End i loop */
3962  } /* End j loop */
3963  } /* End k loop */
3964 
3965 }
3966 
3967 /* ///////////////////////////////////////////////////////////////////////////
3968  // Routine: bcfl_mem
3969  //
3970  // Purpose: Increment all the boundary points by the
3971  // analytic expression for a membrane system in
3972  // the presence of a membrane potential. This
3973  // Boundary flag should only be used for systems
3974  // that explicitly have membranes in the dielectric
3975  // and solvent maps.
3976  //
3977  // There should be several input variables add to this
3978  // function such as membrane potential, membrane thickness
3979  // and height.
3980  //
3981  // Args: apos is a 3-vector
3982  //
3983  // Author: Michael Grabe
3985 VPRIVATE void bcfl_mem(double zmem, double L, double eps_m, double eps_w,
3986  double V, double xkappa, double *gxcf, double *gycf, double *gzcf,
3987  double *xf, double *yf, double *zf, int nx, int ny, int nz) {
3988 
3990  /* some definitions */
3991  /* L = total length of the membrane */
3992  /* xkappa = inverse Debeye length */
3993  /* zmem = z value of membrane bottom (Cytoplasm) */
3994  /* V = electrical potential inside the cell */
3996  int i, j, k;
3997  double dist, val, z_low, z_high, z_shift;
3998  double A, B, C, D, edge_L, l;
3999  double G, z_0, z_rel;
4000  double gpos[3];
4001 
4002  Vnm_print(0, "Here is the value of kappa: %f\n",xkappa);
4003  Vnm_print(0, "Here is the value of L: %f\n",L);
4004  Vnm_print(0, "Here is the value of zmem: %f\n",zmem);
4005  Vnm_print(0, "Here is the value of mdie: %f\n",eps_m);
4006  Vnm_print(0, "Here is the value of memv: %f\n",V);
4007 
4008  /* no salt symmetric BC's at +/- infinity */
4009  // B=V/(edge_L - l*(1-eps_w/eps_m));
4010  // A=V + B*edge_L;
4011  // D=eps_w/eps_m*B;
4012  z_low = zmem; /* this defines the bottom of the membrane */
4013  z_high = zmem + L; /* this is the top of the membrane */
4014 
4015  /******************************************************/
4016  /* proper boundary conditions for V = 0 extracellular */
4017  /* and psi=-V cytoplasm. */
4018  /* Implicit in this formulation is that the membrane */
4019  /* center be at z = 0 */
4020  /******************************************************/
4021 
4022  l=L/2; /* half of the membrane length */
4023  z_0 = z_low + l; /* center of the membrane */
4024  G=l*eps_w/eps_m*xkappa;
4025  A=-V/2*(1/(G+1))*exp(xkappa*l);
4026  B=V/2;
4027  C=-V/2*eps_w/eps_m*xkappa*(1/(G+1));
4028  D=-A;
4029  /* The analytic expression for the boundary conditions */
4030  /* had the cytoplasmic surface of the membrane set to zero. */
4031  /* This requires an off-set of the BC equations. */
4032 
4033  /* the "i" boundaries (dirichlet) */
4034  for (k=0; k<nz; k++) {
4035  gpos[2] = zf[k];
4036  z_rel = gpos[2] - z_0; /* relative position for BCs */
4037 
4038  for (j=0; j<ny; j++) {
4039 
4040  if (gpos[2] <= z_low) { /* cytoplasmic */
4041 
4042  val = A*exp(xkappa*z_rel) + V;
4043  gxcf[IJKx(j,k,0)] += val; /* assign low side BC */
4044  gxcf[IJKx(j,k,1)] += val; /* assign high side BC */
4045 
4046  }
4047 
4048  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4049 
4050  val = B + C*z_rel;
4051  gxcf[IJKx(j,k,0)] += val; /* assign low side BC */
4052  gxcf[IJKx(j,k,1)] += val; /* assign high side BC */
4053 
4054  }
4055 
4056  else if (gpos[2] > z_high) { /* extracellular */
4057 
4058  val = D*exp(-xkappa*z_rel);
4059  gxcf[IJKx(j,k,0)] += val; /* assign low side BC */
4060  gxcf[IJKx(j,k,1)] += val; /* assign high side BC */
4061 
4062  }
4063 
4064  }
4065  }
4066 
4067  /* the "j" boundaries (dirichlet) */
4068  for (k=0; k<nz; k++) {
4069  gpos[2] = zf[k];
4070  z_rel = gpos[2] - z_0;
4071  for (i=0; i<nx; i++) {
4072 
4073  if (gpos[2] <= z_low) { /* cytoplasmic */
4074 
4075  val = A*exp(xkappa*z_rel) + V;
4076  gycf[IJKy(i,k,0)] += val; /* assign low side BC */
4077  gycf[IJKy(i,k,1)] += val; /* assign high side BC */
4078  //printf("%f \n",val);
4079 
4080  }
4081 
4082  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4083 
4084  val = B + C*z_rel;
4085  gycf[IJKy(i,k,0)] += val; /* assign low side BC */
4086  gycf[IJKy(i,k,1)] += val; /* assign high side BC */
4087  //printf("%f \n",val);
4088 
4089  }
4090  else if (gpos[2] > z_high) { /* extracellular */
4091 
4092  val = D*exp(-xkappa*z_rel);
4093  gycf[IJKy(i,k,0)] += val; /* assign low side BC */
4094  gycf[IJKy(i,k,1)] += val; /* assign high side BC */
4095  //printf("%f \n",val);
4096 
4097  }
4098 
4099  }
4100  }
4101 
4102  /* the "k" boundaries (dirichlet) */
4103  for (j=0; j<ny; j++) {
4104  for (i=0; i<nx; i++) {
4105 
4106  /* first assign the bottom boundary */
4107 
4108  gpos[2] = zf[0];
4109  z_rel = gpos[2] - z_0;
4110 
4111  if (gpos[2] <= z_low) { /* cytoplasmic */
4112 
4113  val = A*exp(xkappa*z_rel) + V;
4114  gzcf[IJKz(i,j,0)] += val; /* assign low side BC */
4115  //printf("%f \n",val);
4116 
4117  }
4118 
4119  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4120 
4121  val = B + C*z_rel;
4122  gzcf[IJKz(i,j,0)] += val; /* assign low side BC */
4123 
4124  }
4125 
4126  else if (gpos[2] > z_high) { /* extracellular */
4127 
4128  val = D*exp(-xkappa*z_rel);
4129  gzcf[IJKz(i,j,0)] += val; /* assign low side BC */
4130 
4131  }
4132 
4133  /* now assign the top boundary */
4134 
4135  gpos[2] = zf[nz-1];
4136  z_rel = gpos[2] - z_0;
4137 
4138  if (gpos[2] <= z_low) { /* cytoplasmic */
4139 
4140  val = A*exp(xkappa*z_rel) + V;
4141  gzcf[IJKz(i,j,1)] += val; /* assign high side BC */
4142 
4143  }
4144 
4145  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4146 
4147  val = B + C*z_rel;
4148  gzcf[IJKz(i,j,1)] += val; /* assign high side BC */
4149 
4150  }
4151 
4152  else if (gpos[2] > z_high) { /* extracellular */
4153 
4154  val = D*exp(-xkappa*z_rel);
4155  gzcf[IJKz(i,j,1)] += val; /* assign high side BC */
4156  //printf("%f \n",val);
4157 
4158  }
4159 
4160  }
4161  }
4162 }
4163 
4164 VPRIVATE void bcfl_map(Vpmg *thee){
4165 
4166  Vpbe *pbe;
4167  double position[3], pot, hx, hy, hzed;
4168  int i, j, k, nx, ny, nz, rc;
4169 
4170 
4171  VASSERT(thee != VNULL);
4172 
4173  /* Mesh info */
4174  nx = thee->pmgp->nx;
4175  ny = thee->pmgp->ny;
4176  nz = thee->pmgp->nz;
4177  hx = thee->pmgp->hx;
4178  hy = thee->pmgp->hy;
4179  hzed = thee->pmgp->hzed;
4180 
4181  /* Reset the potential array */
4182  for (i=0; i<(nx*ny*nz); i++) thee->pot[i] = 0.0;
4183 
4184  /* Fill in the source term (atomic potentials) */
4185  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
4186  for (k=0; k<nz; k++) {
4187  for (j=0; j<ny; j++) {
4188  for (i=0; i<nx; i++) {
4189  position[0] = thee->xf[i];
4190  position[1] = thee->yf[j];
4191  position[2] = thee->zf[k];
4192  rc = Vgrid_value(thee->potMap, position, &pot);
4193  if (!rc) {
4194  Vnm_print(2, "fillcoChargeMap: Error -- fell off of potential map at (%g, %g, %g)!\n",
4195  position[0], position[1], position[2]);
4196  VASSERT(0);
4197  }
4198  thee->pot[IJK(i,j,k)] = pot;
4199  }
4200  }
4201  }
4202 
4203 }
4204 
4205 #if defined(WITH_TINKER)
4206 VPRIVATE void bcfl_mdh_tinker(Vpmg *thee){
4207 
4208  int i,j,k,iatom;
4209  int nx, ny, nz;
4210 
4211  double val, *apos, gpos[3], tensor[9];
4212  double *dipole, *quadrupole;
4213  double size, charge, xkappa, eps_w, eps_p, T, pre1, dist;
4214 
4215  double ux,uy,uz,xr,yr,zr;
4216  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
4217 
4218  double *xf, *yf, *zf;
4219  double *gxcf, *gycf, *gzcf;
4220 
4221  Vpbe *pbe;
4222  Vatom *atom;
4223  Valist *alist;
4224 
4225  pbe = thee->pbe;
4226  alist = thee->pbe->alist;
4227  nx = thee->pmgp->nx;
4228  ny = thee->pmgp->ny;
4229  nz = thee->pmgp->nz;
4230 
4231  xf = thee->xf;
4232  yf = thee->yf;
4233  zf = thee->zf;
4234 
4235  gxcf = thee->gxcf;
4236  gycf = thee->gycf;
4237  gzcf = thee->gzcf;
4238 
4239  /* For each "atom" (only one for bcfl=1), we use the following formula to
4240  * calculate the boundary conditions:
4241  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
4242  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
4243  * * 1/d
4244  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
4245  * We only need to evaluate some of these prefactors once:
4246  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
4247  * which gives the potential as
4248  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
4249  */
4250  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
4251  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
4252  T = Vpbe_getTemperature(pbe); /* K */
4253  pre1 = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
4254 
4255  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
4256  * m/A, then we will only need to deal with distances and sizes in
4257  * Angstroms rather than meters. */
4258  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
4259  pre1 = pre1*(1.0e10);
4260 
4261  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
4262  * m/A, then we will only need to deal with distances and sizes in
4263  * Angstroms rather than meters. */
4264  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
4265 
4266  for(k=0;k<nz;k++){
4267  gpos[2] = zf[k];
4268  for(j=0;j<ny;j++){
4269  gpos[1] = yf[j];
4270  for(i=0;i<nx;i++){
4271  gpos[0] = xf[i];
4272  if(gridPointIsValid(i, j, k, nx, ny, nz)){
4273 
4274  val = 0.0;
4275 
4276  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4277  atom = Valist_getAtom(alist, iatom);
4278  apos = Vatom_getPosition(atom);
4279  size = Vatom_getRadius(atom);
4280 
4281  charge = 0.0;
4282 
4283  dipole = VNULL;
4284  quadrupole = VNULL;
4285 
4286  if (thee->chargeSrc == VCM_PERMANENT) {
4287  charge = Vatom_getCharge(atom);
4288  dipole = Vatom_getDipole(atom);
4289  quadrupole = Vatom_getQuadrupole(atom);
4290  } else if (thee->chargeSrc == VCM_INDUCED) {
4291  dipole = Vatom_getInducedDipole(atom);
4292  } else {
4293  dipole = Vatom_getNLInducedDipole(atom);
4294  }
4295 
4296  ux = dipole[0];
4297  uy = dipole[1];
4298  uz = dipole[2];
4299 
4300  if (quadrupole != VNULL) {
4301  /* The factor of 1/3 results from using a
4302  traceless quadrupole definition. See, for example,
4303  "The Theory of Intermolecular Forces" by A.J. Stone,
4304  Chapter 3. */
4305  qxx = quadrupole[0] / 3.0;
4306  qxy = quadrupole[1] / 3.0;
4307  qxz = quadrupole[2] / 3.0;
4308  qyx = quadrupole[3] / 3.0;
4309  qyy = quadrupole[4] / 3.0;
4310  qyz = quadrupole[5] / 3.0;
4311  qzx = quadrupole[6] / 3.0;
4312  qzy = quadrupole[7] / 3.0;
4313  qzz = quadrupole[8] / 3.0;
4314  } else {
4315  qxx = 0.0;
4316  qxy = 0.0;
4317  qxz = 0.0;
4318  qyx = 0.0;
4319  qyy = 0.0;
4320  qyz = 0.0;
4321  qzx = 0.0;
4322  qzy = 0.0;
4323  qzz = 0.0;
4324  }
4325 
4326  xr = gpos[0] - apos[0];
4327  yr = gpos[1] - apos[1];
4328  zr = gpos[2] - apos[2];
4329 
4330  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
4331  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
4332 
4333  val += pre1*charge*tensor[0];
4334  val -= pre1*ux*xr*tensor[1];
4335  val -= pre1*uy*yr*tensor[1];
4336  val -= pre1*uz*zr*tensor[1];
4337  val += pre1*qxx*xr*xr*tensor[2];
4338  val += pre1*qyy*yr*yr*tensor[2];
4339  val += pre1*qzz*zr*zr*tensor[2];
4340  val += pre1*2.0*qxy*xr*yr*tensor[2];
4341  val += pre1*2.0*qxz*xr*zr*tensor[2];
4342  val += pre1*2.0*qyz*yr*zr*tensor[2];
4343 
4344  }
4345 
4346  if(i==0){
4347  gxcf[IJKx(j,k,0)] = val;
4348  }
4349  if(i==nx-1){
4350  gxcf[IJKx(j,k,1)] = val;
4351  }
4352  if(j==0){
4353  gycf[IJKy(i,k,0)] = val;
4354  }
4355  if(j==ny-1){
4356  gycf[IJKy(i,k,1)] = val;
4357  }
4358  if(k==0){
4359  gzcf[IJKz(i,j,0)] = val;
4360  }
4361  if(k==nz-1){
4362  gzcf[IJKz(i,j,1)] = val;
4363  }
4364  } /* End grid point is valid */
4365  } /* End i loop */
4366  } /* End j loop */
4367  } /* End k loop */
4368 
4369 }
4370 #endif
4371 
4372 VPRIVATE void bcCalc(Vpmg *thee){
4373 
4374  int i, j, k;
4375  int nx, ny, nz;
4376 
4377  double zmem, eps_m, Lmem, memv, eps_w, xkappa;
4378 
4379  nx = thee->pmgp->nx;
4380  ny = thee->pmgp->ny;
4381  nz = thee->pmgp->nz;
4382 
4383  /* Zero out the boundaries */
4384  /* the "i" boundaries (dirichlet) */
4385  for (k=0; k<nz; k++) {
4386  for (j=0; j<ny; j++) {
4387  thee->gxcf[IJKx(j,k,0)] = 0.0;
4388  thee->gxcf[IJKx(j,k,1)] = 0.0;
4389  thee->gxcf[IJKx(j,k,2)] = 0.0;
4390  thee->gxcf[IJKx(j,k,3)] = 0.0;
4391  }
4392  }
4393 
4394  /* the "j" boundaries (dirichlet) */
4395  for (k=0; k<nz; k++) {
4396  for (i=0; i<nx; i++) {
4397  thee->gycf[IJKy(i,k,0)] = 0.0;
4398  thee->gycf[IJKy(i,k,1)] = 0.0;
4399  thee->gycf[IJKy(i,k,2)] = 0.0;
4400  thee->gycf[IJKy(i,k,3)] = 0.0;
4401  }
4402  }
4403 
4404  /* the "k" boundaries (dirichlet) */
4405  for (j=0; j<ny; j++) {
4406  for (i=0; i<nx; i++) {
4407  thee->gzcf[IJKz(i,j,0)] = 0.0;
4408  thee->gzcf[IJKz(i,j,1)] = 0.0;
4409  thee->gzcf[IJKz(i,j,2)] = 0.0;
4410  thee->gzcf[IJKz(i,j,3)] = 0.0;
4411  }
4412  }
4413 
4414  switch (thee->pmgp->bcfl) {
4415  /* If we have zero boundary conditions, we're done */
4416  case BCFL_ZERO:
4417  return;
4418  case BCFL_SDH:
4419  bcfl_sdh(thee);
4420  break;
4421  case BCFL_MDH:
4422 #if defined(WITH_TINKER)
4423  bcfl_mdh_tinker(thee);
4424 #else
4425 
4426 #ifdef DEBUG_MAC_OSX_OCL
4427 #include "mach_chud.h"
4428  uint64_t mbeg = mach_absolute_time();
4429 
4430  /*
4431  * If OpenCL is available we use it, otherwise fall back to
4432  * normal route (CPU multithreaded w/ OpenMP)
4433  */
4434  if (kOpenCLAvailable == 1) bcflnewOpenCL(thee);
4435  else bcflnew(thee);
4436 
4437  mets_(&mbeg, "MDH");
4438 #else
4439  /* bcfl_mdh(thee); */
4440  bcflnew(thee);
4441 #endif /* DEBUG_MAC_OSX_OCL */
4442 
4443 #endif /* WITH_TINKER */
4444  break;
4445  case BCFL_MEM:
4446 
4447  zmem = Vpbe_getzmem(thee->pbe);
4448  Lmem = Vpbe_getLmem(thee->pbe);
4449  eps_m = Vpbe_getmembraneDiel(thee->pbe);
4450  memv = Vpbe_getmemv(thee->pbe);
4451 
4452  eps_w = Vpbe_getSolventDiel(thee->pbe);
4453  xkappa = Vpbe_getXkappa(thee->pbe);
4454 
4455  bcfl_mem(zmem, Lmem, eps_m, eps_w, memv, xkappa,
4456  thee->gxcf, thee->gycf, thee->gzcf,
4457  thee->xf, thee->yf, thee->zf, nx, ny, nz);
4458  break;
4459  case BCFL_UNUSED:
4460  Vnm_print(2, "bcCalc: Invalid bcfl (%d)!\n", thee->pmgp->bcfl);
4461  VASSERT(0);
4462  break;
4463  case BCFL_FOCUS:
4464  Vnm_print(2, "VPMG::bcCalc -- not appropriate for focusing!\n");
4465  VASSERT(0);
4466  break;
4467  case BCFL_MAP:
4468  bcfl_map(thee);
4469  focusFillBound(thee,VNULL);
4470  break;
4471  default:
4472  Vnm_print(2, "VPMG::bcCalc -- invalid boundary condition \
4473  flag (%d)!\n", thee->pmgp->bcfl);
4474  VASSERT(0);
4475  break;
4476  }
4477 }
4478 
4479 VPRIVATE void fillcoCoefMap(Vpmg *thee) {
4480 
4481  Vpbe *pbe;
4482  double ionstr, position[3], tkappa, eps, pot, hx, hy, hzed;
4483  int i, j, k, nx, ny, nz;
4484  double kappamax;
4485  VASSERT(thee != VNULL);
4486 
4487  /* Get PBE info */
4488  pbe = thee->pbe;
4489  ionstr = Vpbe_getBulkIonicStrength(pbe);
4490 
4491  /* Mesh info */
4492  nx = thee->pmgp->nx;
4493  ny = thee->pmgp->ny;
4494  nz = thee->pmgp->nz;
4495  hx = thee->pmgp->hx;
4496  hy = thee->pmgp->hy;
4497  hzed = thee->pmgp->hzed;
4498 
4499  if ((!thee->useDielXMap) || (!thee->useDielYMap)
4500  || (!thee->useDielZMap) || ((!thee->useKappaMap) && (ionstr>VPMGSMALL))) {
4501 
4502  Vnm_print(2, "fillcoCoefMap: You need to use all coefficient maps!\n");
4503  VASSERT(0);
4504 
4505  }
4506 
4507  /* Scale the kappa map to values between 0 and 1
4508  Thus get the maximum value in the map - this
4509  is theoretically unnecessary, but a good check.*/
4510  kappamax = -1.00;
4511  for (k=0; k<nz; k++) {
4512  for (j=0; j<ny; j++) {
4513  for (i=0; i<nx; i++) {
4514  if (ionstr > VPMGSMALL) {
4515  position[0] = thee->xf[i];
4516  position[1] = thee->yf[j];
4517  position[2] = thee->zf[k];
4518  if (!Vgrid_value(thee->kappaMap, position, &tkappa)) {
4519  Vnm_print(2, "Vpmg_fillco: Off kappaMap at:\n");
4520  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4521  position[0], position[1], position[2]);
4522  VASSERT(0);
4523  }
4524  if (tkappa > kappamax) {
4525  kappamax = tkappa;
4526  }
4527  if (tkappa < 0.0){
4528  Vnm_print(2, "Vpmg_fillcoCoefMap: Kappa map less than 0\n");
4529  Vnm_print(2, "Vpmg_fillcoCoefMap: at (x,y,z) = (%g,%g %g)\n",
4530  position[0], position[1], position[2]);
4531  VASSERT(0);
4532  }
4533  }
4534  }
4535  }
4536  }
4537 
4538  if (kappamax > 1.0){
4539  Vnm_print(2, "Vpmg_fillcoCoefMap: Maximum Kappa value\n");
4540  Vnm_print(2, "%g is greater than 1 - will scale appropriately!\n",
4541  kappamax);
4542  }
4543  else {
4544  kappamax = 1.0;
4545  }
4546 
4547  for (k=0; k<nz; k++) {
4548  for (j=0; j<ny; j++) {
4549  for (i=0; i<nx; i++) {
4550 
4551  if (ionstr > VPMGSMALL) {
4552  position[0] = thee->xf[i];
4553  position[1] = thee->yf[j];
4554  position[2] = thee->zf[k];
4555  if (!Vgrid_value(thee->kappaMap, position, &tkappa)) {
4556  Vnm_print(2, "Vpmg_fillco: Off kappaMap at:\n");
4557  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4558  position[0], position[1], position[2]);
4559  VASSERT(0);
4560  }
4561  if (tkappa < VPMGSMALL) tkappa = 0.0;
4562  thee->kappa[IJK(i,j,k)] = (tkappa / kappamax);
4563  }
4564 
4565  position[0] = thee->xf[i] + 0.5*hx;
4566  position[1] = thee->yf[j];
4567  position[2] = thee->zf[k];
4568  if (!Vgrid_value(thee->dielXMap, position, &eps)) {
4569  Vnm_print(2, "Vpmg_fillco: Off dielXMap at:\n");
4570  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4571  position[0], position[1], position[2]);
4572  VASSERT(0);
4573  }
4574  thee->epsx[IJK(i,j,k)] = eps;
4575 
4576  position[0] = thee->xf[i];
4577  position[1] = thee->yf[j] + 0.5*hy;
4578  position[2] = thee->zf[k];
4579  if (!Vgrid_value(thee->dielYMap, position, &eps)) {
4580  Vnm_print(2, "Vpmg_fillco: Off dielYMap at:\n");
4581  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4582  position[0], position[1], position[2]);
4583  VASSERT(0);
4584  }
4585  thee->epsy[IJK(i,j,k)] = eps;
4586 
4587  position[0] = thee->xf[i];
4588  position[1] = thee->yf[j];
4589  position[2] = thee->zf[k] + 0.5*hzed;
4590  if (!Vgrid_value(thee->dielZMap, position, &eps)) {
4591  Vnm_print(2, "Vpmg_fillco: Off dielZMap at:\n");
4592  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4593  position[0], position[1], position[2]);
4594  VASSERT(0);
4595  }
4596  thee->epsz[IJK(i,j,k)] = eps;
4597  }
4598  }
4599  }
4600 }
4601 
4602 VPRIVATE void fillcoCoefMol(Vpmg *thee) {
4603 
4604  if (thee->useDielXMap || thee->useDielYMap || thee->useDielZMap ||
4605  thee->useKappaMap) {
4606 
4607  fillcoCoefMap(thee);
4608 
4609  } else {
4610 
4611  fillcoCoefMolDiel(thee);
4612  fillcoCoefMolIon(thee);
4613 
4614  }
4615 
4616 }
4617 
4618 VPRIVATE void fillcoCoefMolIon(Vpmg *thee) {
4619 
4620  Vacc *acc;
4621  Valist *alist;
4622  Vpbe *pbe;
4623  Vatom *atom;
4624  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr;
4625  double xlen, ylen, zlen, irad;
4626  double hx, hy, hzed, *apos, arad;
4627  int i, nx, ny, nz, iatom;
4628  Vsurf_Meth surfMeth;
4629 
4630  VASSERT(thee != VNULL);
4631  surfMeth = thee->surfMeth;
4632 
4633  /* Get PBE info */
4634  pbe = thee->pbe;
4635  acc = pbe->acc;
4636  alist = pbe->alist;
4637  irad = Vpbe_getMaxIonRadius(pbe);
4638  ionstr = Vpbe_getBulkIonicStrength(pbe);
4639 
4640  /* Mesh info */
4641  nx = thee->pmgp->nx;
4642  ny = thee->pmgp->ny;
4643  nz = thee->pmgp->nz;
4644  hx = thee->pmgp->hx;
4645  hy = thee->pmgp->hy;
4646  hzed = thee->pmgp->hzed;
4647 
4648  /* Define the total domain size */
4649  xlen = thee->pmgp->xlen;
4650  ylen = thee->pmgp->ylen;
4651  zlen = thee->pmgp->zlen;
4652 
4653  /* Define the min/max dimensions */
4654  xmin = thee->pmgp->xcent - (xlen/2.0);
4655  ymin = thee->pmgp->ycent - (ylen/2.0);
4656  zmin = thee->pmgp->zcent - (zlen/2.0);
4657  xmax = thee->pmgp->xcent + (xlen/2.0);
4658  ymax = thee->pmgp->ycent + (ylen/2.0);
4659  zmax = thee->pmgp->zcent + (zlen/2.0);
4660 
4661  /* This is a floating point parameter related to the non-zero nature of the
4662  * bulk ionic strength. If the ionic strength is greater than zero; this
4663  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
4664  * Otherwise, this parameter is set to 0.0 */
4665  if (ionstr > VPMGSMALL) ionmask = 1.0;
4666  else ionmask = 0.0;
4667 
4668  /* Reset the kappa array, marking everything accessible */
4669  for (i=0; i<(nx*ny*nz); i++) thee->kappa[i] = ionmask;
4670 
4671  if (ionstr < VPMGSMALL) return;
4672 
4673  /* Loop through the atoms and set kappa = 0.0 (inaccessible) if a point
4674  * is inside the ion-inflated van der Waals radii */
4675  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4676 
4677  atom = Valist_getAtom(alist, iatom);
4678  apos = Vatom_getPosition(atom);
4679  arad = Vatom_getRadius(atom);
4680 
4681  if (arad > VSMALL) {
4682 
4683  /* Make sure we're on the grid */
4684  if ((apos[0]<(xmin-irad-arad)) || (apos[0]>(xmax+irad+arad)) || \
4685  (apos[1]<(ymin-irad-arad)) || (apos[1]>(ymax+irad+arad)) || \
4686  (apos[2]<(zmin-irad-arad)) || (apos[2]>(zmax+irad+arad))) {
4687  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
4688  (thee->pmgp->bcfl != BCFL_MAP)) {
4689  Vnm_print(2,
4690  "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
4691  iatom, apos[0], apos[1], apos[2]);
4692  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
4693  xmin, xmax);
4694  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
4695  ymin, ymax);
4696  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
4697  zmin, zmax);
4698  }
4699  fflush(stderr);
4700 
4701  } else { /* if we're on the mesh */
4702 
4703  /* Mark ions */
4704  markSphere((irad+arad), apos,
4705  nx, ny, nz,
4706  hx, hy, hzed,
4707  xmin, ymin, zmin,
4708  thee->kappa, 0.0);
4709 
4710  } /* endif (on the mesh) */
4711  }
4712  } /* endfor (over all atoms) */
4713 
4714 }
4715 
4716 VPRIVATE void fillcoCoefMolDiel(Vpmg *thee) {
4717 
4718  /* Always call NoSmooth to fill the epsilon arrays */
4720 
4721  /* Call the smoothing algorithm as needed */
4722  if (thee->surfMeth == VSM_MOLSMOOTH) {
4724  }
4725 }
4726 
4727 VPRIVATE void fillcoCoefMolDielNoSmooth(Vpmg *thee) {
4728 
4729  Vacc *acc;
4730  VaccSurf *asurf;
4731  Valist *alist;
4732  Vpbe *pbe;
4733  Vatom *atom;
4734  double xmin, xmax, ymin, ymax, zmin, zmax;
4735  double xlen, ylen, zlen, position[3];
4736  double srad, epsw, epsp, deps, area;
4737  double hx, hy, hzed, *apos, arad;
4738  int i, nx, ny, nz, ntot, iatom, ipt;
4739 
4740  /* Get PBE info */
4741  pbe = thee->pbe;
4742  acc = pbe->acc;
4743  alist = pbe->alist;
4744  srad = Vpbe_getSolventRadius(pbe);
4745  epsw = Vpbe_getSolventDiel(pbe);
4746  epsp = Vpbe_getSoluteDiel(pbe);
4747 
4748  /* Mesh info */
4749  nx = thee->pmgp->nx;
4750  ny = thee->pmgp->ny;
4751  nz = thee->pmgp->nz;
4752  hx = thee->pmgp->hx;
4753  hy = thee->pmgp->hy;
4754  hzed = thee->pmgp->hzed;
4755 
4756  /* Define the total domain size */
4757  xlen = thee->pmgp->xlen;
4758  ylen = thee->pmgp->ylen;
4759  zlen = thee->pmgp->zlen;
4760 
4761  /* Define the min/max dimensions */
4762  xmin = thee->pmgp->xcent - (xlen/2.0);
4763  ymin = thee->pmgp->ycent - (ylen/2.0);
4764  zmin = thee->pmgp->zcent - (zlen/2.0);
4765  xmax = thee->pmgp->xcent + (xlen/2.0);
4766  ymax = thee->pmgp->ycent + (ylen/2.0);
4767  zmax = thee->pmgp->zcent + (zlen/2.0);
4768 
4769  /* Reset the arrays */
4770  ntot = nx*ny*nz;
4771  for (i=0; i<ntot; i++) {
4772  thee->epsx[i] = epsw;
4773  thee->epsy[i] = epsw;
4774  thee->epsz[i] = epsw;
4775  }
4776 
4777  /* Loop through the atoms and set a{123}cf = 0.0 (inaccessible)
4778  * if a point is inside the solvent-inflated van der Waals radii */
4779 #pragma omp parallel for default(shared) private(iatom,atom,apos,arad)
4780  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4781 
4782  atom = Valist_getAtom(alist, iatom);
4783  apos = Vatom_getPosition(atom);
4784  arad = Vatom_getRadius(atom);
4785 
4786  /* Make sure we're on the grid */
4787  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
4788  (apos[1]<=ymin) || (apos[1]>=ymax) || \
4789  (apos[2]<=zmin) || (apos[2]>=zmax)) {
4790  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
4791  (thee->pmgp->bcfl != BCFL_MAP)) {
4792  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
4793  %4.3f) is off the mesh (ignoring):\n",
4794  iatom, apos[0], apos[1], apos[2]);
4795  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
4796  xmin, xmax);
4797  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
4798  ymin, ymax);
4799  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
4800  zmin, zmax);
4801  }
4802  fflush(stderr);
4803 
4804  } else { /* if we're on the mesh */
4805 
4806  if (arad > VSMALL) {
4807  /* Mark x-shifted dielectric */
4808  markSphere((arad+srad), apos,
4809  nx, ny, nz,
4810  hx, hy, hzed,
4811  (xmin+0.5*hx), ymin, zmin,
4812  thee->epsx, epsp);
4813 
4814  /* Mark y-shifted dielectric */
4815  markSphere((arad+srad), apos,
4816  nx, ny, nz,
4817  hx, hy, hzed,
4818  xmin, (ymin+0.5*hy), zmin,
4819  thee->epsy, epsp);
4820 
4821  /* Mark z-shifted dielectric */
4822  markSphere((arad+srad), apos,
4823  nx, ny, nz,
4824  hx, hy, hzed,
4825  xmin, ymin, (zmin+0.5*hzed),
4826  thee->epsz, epsp);
4827  }
4828 
4829  } /* endif (on the mesh) */
4830  } /* endfor (over all atoms) */
4831 
4832  area = Vacc_SASA(acc, srad);
4833 
4834  /* We only need to do the next step for non-zero solvent radii */
4835  if (srad > VSMALL) {
4836 
4837  /* Now loop over the solvent accessible surface points */
4838 
4839 #pragma omp parallel for default(shared) private(iatom,atom,area,asurf,ipt,position)
4840  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4841  atom = Valist_getAtom(alist, iatom);
4842  area = Vacc_atomSASA(acc, srad, atom);
4843  if (area > 0.0 ) {
4844  asurf = Vacc_atomSASPoints(acc, srad, atom);
4845 
4846  /* Use each point on the SAS to reset the solvent accessibility */
4847  /* TODO: Make sure we're not still wasting time here. */
4848  for (ipt=0; ipt<(asurf->npts); ipt++) {
4849 
4850  position[0] = asurf->xpts[ipt];
4851  position[1] = asurf->ypts[ipt];
4852  position[2] = asurf->zpts[ipt];
4853 
4854  /* Mark x-shifted dielectric */
4855  markSphere(srad, position,
4856  nx, ny, nz,
4857  hx, hy, hzed,
4858  (xmin+0.5*hx), ymin, zmin,
4859  thee->epsx, epsw);
4860 
4861  /* Mark y-shifted dielectric */
4862  markSphere(srad, position,
4863  nx, ny, nz,
4864  hx, hy, hzed,
4865  xmin, (ymin+0.5*hy), zmin,
4866  thee->epsy, epsw);
4867 
4868  /* Mark z-shifted dielectric */
4869  markSphere(srad, position,
4870  nx, ny, nz,
4871  hx, hy, hzed,
4872  xmin, ymin, (zmin+0.5*hzed),
4873  thee->epsz, epsw);
4874 
4875  }
4876  }
4877  }
4878  }
4879 }
4880 
4881 VPRIVATE void fillcoCoefMolDielSmooth(Vpmg *thee) {
4882 
4883  /* This function smoothes using a 9 point method based on
4884  Bruccoleri, et al. J Comput Chem 18 268-276 (1997). The nine points
4885  used are the shifted grid point and the 8 points that are 1/sqrt(2)
4886  grid spacings away. The harmonic mean of the 9 points is then used to
4887  find the overall dielectric value for the point in question. The use of
4888  this function assumes that the non-smoothed values were placed in the
4889  dielectric arrays by the fillcoCoefMolDielNoSmooth function.*/
4890 
4891  Vpbe *pbe;
4892  double frac, epsw;
4893  int i, j, k, nx, ny, nz, numpts;
4894 
4895  /* Mesh info */
4896  nx = thee->pmgp->nx;
4897  ny = thee->pmgp->ny;
4898  nz = thee->pmgp->nz;
4899 
4900  pbe = thee->pbe;
4901  epsw = Vpbe_getSolventDiel(pbe);
4902 
4903  /* Copy the existing diel arrays to work arrays */
4904  for (i=0; i<(nx*ny*nz); i++) {
4905  thee->a1cf[i] = thee->epsx[i];
4906  thee->a2cf[i] = thee->epsy[i];
4907  thee->a3cf[i] = thee->epsz[i];
4908  thee->epsx[i] = epsw;
4909  thee->epsy[i] = epsw;
4910  thee->epsz[i] = epsw;
4911  }
4912 
4913  /* Smooth the dielectric values */
4914  for (i=0; i<nx; i++) {
4915  for (j=0; j<ny; j++) {
4916  for (k=0; k<nz; k++) {
4917 
4918  /* Get the 8 points that are 1/sqrt(2) grid spacings away */
4919 
4920  /* Points for the X-shifted array */
4921  frac = 1.0/thee->a1cf[IJK(i,j,k)];
4922  frac += 1.0/thee->a2cf[IJK(i,j,k)];
4923  frac += 1.0/thee->a3cf[IJK(i,j,k)];
4924  numpts = 3;
4925 
4926  if (j > 0) {
4927  frac += 1.0/thee->a2cf[IJK(i,j-1,k)];
4928  numpts += 1;
4929  }
4930  if (k > 0) {
4931  frac += 1.0/thee->a3cf[IJK(i,j,k-1)];
4932  numpts += 1;
4933  }
4934  if (i < (nx-1)){
4935  frac += 1.0/thee->a2cf[IJK(i+1,j,k)];
4936  frac += 1.0/thee->a3cf[IJK(i+1,j,k)];
4937  numpts += 2;
4938  if (j > 0) {
4939  frac += 1.0/thee->a2cf[IJK(i+1,j-1,k)];
4940  numpts += 1;
4941  }
4942  if (k > 0) {
4943  frac += 1.0/thee->a3cf[IJK(i+1,j,k-1)];
4944  numpts += 1;
4945  }
4946  }
4947  thee->epsx[IJK(i,j,k)] = numpts/frac;
4948 
4949  /* Points for the Y-shifted array */
4950  frac = 1.0/thee->a2cf[IJK(i,j,k)];
4951  frac += 1.0/thee->a1cf[IJK(i,j,k)];
4952  frac += 1.0/thee->a3cf[IJK(i,j,k)];
4953  numpts = 3;
4954 
4955  if (i > 0) {
4956  frac += 1.0/thee->a1cf[IJK(i-1,j,k)];
4957  numpts += 1;
4958  }
4959  if (k > 0) {
4960  frac += 1.0/thee->a3cf[IJK(i,j,k-1)];
4961  numpts += 1;
4962  }
4963  if (j < (ny-1)){
4964  frac += 1.0/thee->a1cf[IJK(i,j+1,k)];
4965  frac += 1.0/thee->a3cf[IJK(i,j+1,k)];
4966  numpts += 2;
4967  if (i > 0) {
4968  frac += 1.0/thee->a1cf[IJK(i-1,j+1,k)];
4969  numpts += 1;
4970  }
4971  if (k > 0) {
4972  frac += 1.0/thee->a3cf[IJK(i,j+1,k-1)];
4973  numpts += 1;
4974  }
4975  }
4976  thee->epsy[IJK(i,j,k)] = numpts/frac;
4977 
4978  /* Points for the Z-shifted array */
4979  frac = 1.0/thee->a3cf[IJK(i,j,k)];
4980  frac += 1.0/thee->a1cf[IJK(i,j,k)];
4981  frac += 1.0/thee->a2cf[IJK(i,j,k)];
4982  numpts = 3;
4983 
4984  if (i > 0) {
4985  frac += 1.0/thee->a1cf[IJK(i-1,j,k)];
4986  numpts += 1;
4987  }
4988  if (j > 0) {
4989  frac += 1.0/thee->a2cf[IJK(i,j-1,k)];
4990  numpts += 1;
4991  }
4992  if (k < (nz-1)){
4993  frac += 1.0/thee->a1cf[IJK(i,j,k+1)];
4994  frac += 1.0/thee->a2cf[IJK(i,j,k+1)];
4995  numpts += 2;
4996  if (i > 0) {
4997  frac += 1.0/thee->a1cf[IJK(i-1,j,k+1)];
4998  numpts += 1;
4999  }
5000  if (j > 0) {
5001  frac += 1.0/thee->a2cf[IJK(i,j-1,k+1)];
5002  numpts += 1;
5003  }
5004  }
5005  thee->epsz[IJK(i,j,k)] = numpts/frac;
5006  }
5007  }
5008  }
5009 }
5010 
5011 
5012 VPRIVATE void fillcoCoefSpline(Vpmg *thee) {
5013 
5014  Valist *alist;
5015  Vpbe *pbe;
5016  Vatom *atom;
5017  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
5018  double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
5019  double irad, dx, dy, dz, epsw, epsp, w2i;
5020  double hx, hy, hzed, *apos, arad, sctot2;
5021  double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin, w3i;
5022  double dist, value, sm, sm2;
5023  int i, j, k, nx, ny, nz, iatom;
5024  int imin, imax, jmin, jmax, kmin, kmax;
5025 
5026  VASSERT(thee != VNULL);
5027  splineWin = thee->splineWin;
5028  w2i = 1.0/(splineWin*splineWin);
5029  w3i = 1.0/(splineWin*splineWin*splineWin);
5030 
5031  /* Get PBE info */
5032  pbe = thee->pbe;
5033  alist = pbe->alist;
5034  irad = Vpbe_getMaxIonRadius(pbe);
5035  ionstr = Vpbe_getBulkIonicStrength(pbe);
5036  epsw = Vpbe_getSolventDiel(pbe);
5037  epsp = Vpbe_getSoluteDiel(pbe);
5038 
5039  /* Mesh info */
5040  nx = thee->pmgp->nx;
5041  ny = thee->pmgp->ny;
5042  nz = thee->pmgp->nz;
5043  hx = thee->pmgp->hx;
5044  hy = thee->pmgp->hy;
5045  hzed = thee->pmgp->hzed;
5046 
5047  /* Define the total domain size */
5048  xlen = thee->pmgp->xlen;
5049  ylen = thee->pmgp->ylen;
5050  zlen = thee->pmgp->zlen;
5051 
5052  /* Define the min/max dimensions */
5053  xmin = thee->pmgp->xcent - (xlen/2.0);
5054  ymin = thee->pmgp->ycent - (ylen/2.0);
5055  zmin = thee->pmgp->zcent - (zlen/2.0);
5056  xmax = thee->pmgp->xcent + (xlen/2.0);
5057  ymax = thee->pmgp->ycent + (ylen/2.0);
5058  zmax = thee->pmgp->zcent + (zlen/2.0);
5059 
5060  /* This is a floating point parameter related to the non-zero nature of the
5061  * bulk ionic strength. If the ionic strength is greater than zero; this
5062  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
5063  * Otherwise, this parameter is set to 0.0 */
5064  if (ionstr > VPMGSMALL) ionmask = 1.0;
5065  else ionmask = 0.0;
5066 
5067  /* Reset the kappa, epsx, epsy, and epsz arrays */
5068  for (i=0; i<(nx*ny*nz); i++) {
5069  thee->kappa[i] = 1.0;
5070  thee->epsx[i] = 1.0;
5071  thee->epsy[i] = 1.0;
5072  thee->epsz[i] = 1.0;
5073  }
5074 
5075  /* Loop through the atoms and do assign the dielectric */
5076  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
5077 
5078  atom = Valist_getAtom(alist, iatom);
5079  apos = Vatom_getPosition(atom);
5080  arad = Vatom_getRadius(atom);
5081 
5082  /* Make sure we're on the grid */
5083  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
5084  (apos[1]<=ymin) || (apos[1]>=ymax) || \
5085  (apos[2]<=zmin) || (apos[2]>=zmax)) {
5086  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5087  (thee->pmgp->bcfl != BCFL_MAP)) {
5088  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
5089  %4.3f) is off the mesh (ignoring):\n",
5090  iatom, apos[0], apos[1], apos[2]);
5091  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
5092  xmin, xmax);
5093  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
5094  ymin, ymax);
5095  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
5096  zmin, zmax);
5097  }
5098  fflush(stderr);
5099 
5100  } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
5101 
5102  /* Convert the atom position to grid reference frame */
5103  position[0] = apos[0] - xmin;
5104  position[1] = apos[1] - ymin;
5105  position[2] = apos[2] - zmin;
5106 
5107  /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
5108  * ASSIGNMENT (Steps #1-3) */
5109  itot = irad + arad + splineWin;
5110  itot2 = VSQR(itot);
5111  ictot = VMAX2(0, (irad + arad - splineWin));
5112  ictot2 = VSQR(ictot);
5113  stot = arad + splineWin;
5114  stot2 = VSQR(stot);
5115  sctot = VMAX2(0, (arad - splineWin));
5116  sctot2 = VSQR(sctot);
5117 
5118  /* We'll search over grid points which are in the greater of
5119  * these two radii */
5120  rtot = VMAX2(itot, stot);
5121  rtot2 = VMAX2(itot2, stot2);
5122  dx = rtot + 0.5*hx;
5123  dy = rtot + 0.5*hy;
5124  dz = rtot + 0.5*hzed;
5125  imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
5126  imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
5127  jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
5128  jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
5129  kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
5130  kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
5131  for (i=imin; i<=imax; i++) {
5132  dx2 = VSQR(position[0] - hx*i);
5133  for (j=jmin; j<=jmax; j++) {
5134  dy2 = VSQR(position[1] - hy*j);
5135  for (k=kmin; k<=kmax; k++) {
5136  dz2 = VSQR(position[2] - k*hzed);
5137 
5138  /* ASSIGN CCF */
5139  if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
5140  dist2 = dz2 + dy2 + dx2;
5141  if (dist2 >= itot2) {
5142  ;
5143  }
5144  if (dist2 <= ictot2) {
5145  thee->kappa[IJK(i,j,k)] = 0.0;
5146  }
5147  if ((dist2 < itot2) && (dist2 > ictot2)) {
5148  dist = VSQRT(dist2);
5149  sm = dist - (arad + irad) + splineWin;
5150  sm2 = VSQR(sm);
5151  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5152  thee->kappa[IJK(i,j,k)] *= value;
5153  }
5154  }
5155 
5156  /* ASSIGN A1CF */
5157  if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
5158  dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
5159  if (dist2 >= stot2) {
5160  thee->epsx[IJK(i,j,k)] *= 1.0;
5161  }
5162  if (dist2 <= sctot2) {
5163  thee->epsx[IJK(i,j,k)] = 0.0;
5164  }
5165  if ((dist2 > sctot2) && (dist2 < stot2)) {
5166  dist = VSQRT(dist2);
5167  sm = dist - arad + splineWin;
5168  sm2 = VSQR(sm);
5169  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5170  thee->epsx[IJK(i,j,k)] *= value;
5171  }
5172  }
5173 
5174  /* ASSIGN A2CF */
5175  if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
5176  dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
5177  if (dist2 >= stot2) {
5178  thee->epsy[IJK(i,j,k)] *= 1.0;
5179  }
5180  if (dist2 <= sctot2) {
5181  thee->epsy[IJK(i,j,k)] = 0.0;
5182  }
5183  if ((dist2 > sctot2) && (dist2 < stot2)) {
5184  dist = VSQRT(dist2);
5185  sm = dist - arad + splineWin;
5186  sm2 = VSQR(sm);
5187  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5188  thee->epsy[IJK(i,j,k)] *= value;
5189  }
5190  }
5191 
5192  /* ASSIGN A3CF */
5193  if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
5194  dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
5195  if (dist2 >= stot2) {
5196  thee->epsz[IJK(i,j,k)] *= 1.0;
5197  }
5198  if (dist2 <= sctot2) {
5199  thee->epsz[IJK(i,j,k)] = 0.0;
5200  }
5201  if ((dist2 > sctot2) && (dist2 < stot2)) {
5202  dist = VSQRT(dist2);
5203  sm = dist - arad + splineWin;
5204  sm2 = VSQR(sm);
5205  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5206  thee->epsz[IJK(i,j,k)] *= value;
5207  }
5208  }
5209 
5210 
5211  } /* k loop */
5212  } /* j loop */
5213  } /* i loop */
5214  } /* endif (on the mesh) */
5215  } /* endfor (over all atoms) */
5216 
5217  Vnm_print(0, "Vpmg_fillco: filling coefficient arrays\n");
5218  /* Interpret markings and fill the coefficient arrays */
5219  for (k=0; k<nz; k++) {
5220  for (j=0; j<ny; j++) {
5221  for (i=0; i<nx; i++) {
5222 
5223  thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
5224  thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)]
5225  + epsp;
5226  thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)]
5227  + epsp;
5228  thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)]
5229  + epsp;
5230 
5231  } /* i loop */
5232  } /* j loop */
5233  } /* k loop */
5234 
5235 }
5236 
5237 VPRIVATE void fillcoCoef(Vpmg *thee) {
5238 
5239  VASSERT(thee != VNULL);
5240 
5241  if (thee->useDielXMap || thee->useDielYMap ||
5242  thee->useDielZMap || thee->useKappaMap) {
5243  fillcoCoefMap(thee);
5244  return;
5245  }
5246 
5247  switch(thee->surfMeth) {
5248  case VSM_MOL:
5249  Vnm_print(0, "fillcoCoef: Calling fillcoCoefMol...\n");
5250  fillcoCoefMol(thee);
5251  break;
5252  case VSM_MOLSMOOTH:
5253  Vnm_print(0, "fillcoCoef: Calling fillcoCoefMol...\n");
5254  fillcoCoefMol(thee);
5255  break;
5256  case VSM_SPLINE:
5257  Vnm_print(0, "fillcoCoef: Calling fillcoCoefSpline...\n");
5258  fillcoCoefSpline(thee);
5259  break;
5260  case VSM_SPLINE3:
5261  Vnm_print(0, "fillcoCoef: Calling fillcoCoefSpline3...\n");
5262  fillcoCoefSpline3(thee);
5263  break;
5264  case VSM_SPLINE4:
5265  Vnm_print(0, "fillcoCoef: Calling fillcoCoefSpline4...\n");
5266  fillcoCoefSpline4(thee);
5267  break;
5268  default:
5269  Vnm_print(2, "fillcoCoef: Invalid surfMeth (%d)!\n",
5270  thee->surfMeth);
5271  VASSERT(0);
5272  break;
5273  }
5274 }
5275 
5276 
5277 VPRIVATE Vrc_Codes fillcoCharge(Vpmg *thee) {
5278 
5279  Vrc_Codes rc;
5280 
5281  VASSERT(thee != VNULL);
5282 
5283  if (thee->useChargeMap) {
5284  return fillcoChargeMap(thee);
5285  }
5286 
5287  switch(thee->chargeMeth) {
5288  case VCM_TRIL:
5289  Vnm_print(0, "fillcoCharge: Calling fillcoChargeSpline1...\n");
5290  fillcoChargeSpline1(thee);
5291  break;
5292  case VCM_BSPL2:
5293  Vnm_print(0, "fillcoCharge: Calling fillcoChargeSpline2...\n");
5294  fillcoChargeSpline2(thee);
5295  break;
5296  case VCM_BSPL4:
5297  switch (thee->chargeSrc) {
5298  case VCM_CHARGE:
5299  Vnm_print(0, "fillcoCharge: Calling fillcoPermanentMultipole...\n");
5301  break;
5302 #if defined(WITH_TINKER)
5303  case VCM_PERMANENT:
5304  Vnm_print(0, "fillcoCharge: Calling fillcoPermanentMultipole...\n");
5306  break;
5307  case VCM_INDUCED:
5308  Vnm_print(0, "fillcoCharge: Calling fillcoInducedDipole...\n");
5309  fillcoInducedDipole(thee);
5310  break;
5311  case VCM_NLINDUCED:
5312  Vnm_print(0, "fillcoCharge: Calling fillcoNLInducedDipole...\n");
5313  fillcoNLInducedDipole(thee);
5314  break;
5315 #endif /* if defined(WITH_TINKER) */
5316  default:
5317  Vnm_print(2, "fillcoCharge: Invalid chargeSource (%d)!\n",
5318  thee->chargeSrc);
5319  return VRC_FAILURE;
5320  break;
5321  }
5322  break;
5323  default:
5324  Vnm_print(2, "fillcoCharge: Invalid chargeMeth (%d)!\n",
5325  thee->chargeMeth);
5326  return VRC_FAILURE;
5327  break;
5328  }
5329 
5330  return VRC_SUCCESS;
5331 }
5332 
5333 VPRIVATE Vrc_Codes fillcoChargeMap(Vpmg *thee) {
5334 
5335  Vpbe *pbe;
5336  double position[3], charge, zmagic, hx, hy, hzed;
5337  int i, j, k, nx, ny, nz, rc;
5338 
5339 
5340  VASSERT(thee != VNULL);
5341 
5342  /* Get PBE info */
5343  pbe = thee->pbe;
5344  zmagic = Vpbe_getZmagic(pbe);
5345 
5346  /* Mesh info */
5347  nx = thee->pmgp->nx;
5348  ny = thee->pmgp->ny;
5349  nz = thee->pmgp->nz;
5350  hx = thee->pmgp->hx;
5351  hy = thee->pmgp->hy;
5352  hzed = thee->pmgp->hzed;
5353 
5354  /* Reset the charge array */
5355  for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
5356 
5357  /* Fill in the source term (atomic charges) */
5358  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5359  for (k=0; k<nz; k++) {
5360  for (j=0; j<ny; j++) {
5361  for (i=0; i<nx; i++) {
5362  position[0] = thee->xf[i];
5363  position[1] = thee->yf[j];
5364  position[2] = thee->zf[k];
5365  rc = Vgrid_value(thee->chargeMap, position, &charge);
5366  if (!rc) {
5367  Vnm_print(2, "fillcoChargeMap: Error -- fell off of charge map at (%g, %g, %g)!\n",
5368  position[0], position[1], position[2]);
5369  return VRC_FAILURE;
5370  }
5371  /* Scale the charge to internal units */
5372  charge = charge*zmagic;
5373  thee->charge[IJK(i,j,k)] = charge;
5374  }
5375  }
5376  }
5377 
5378  return VRC_SUCCESS;
5379 }
5380 
5381 VPRIVATE void fillcoChargeSpline1(Vpmg *thee) {
5382 
5383  Valist *alist;
5384  Vpbe *pbe;
5385  Vatom *atom;
5386  double xmin, xmax, ymin, ymax, zmin, zmax;
5387  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
5388  double charge, dx, dy, dz, zmagic, hx, hy, hzed, *apos;
5389  int i, nx, ny, nz, iatom, ihi, ilo, jhi, jlo, khi, klo;
5390 
5391 
5392  VASSERT(thee != VNULL);
5393 
5394  /* Get PBE info */
5395  pbe = thee->pbe;
5396  alist = pbe->alist;
5397  zmagic = Vpbe_getZmagic(pbe);
5398 
5399  /* Mesh info */
5400  nx = thee->pmgp->nx;
5401  ny = thee->pmgp->ny;
5402  nz = thee->pmgp->nz;
5403  hx = thee->pmgp->hx;
5404  hy = thee->pmgp->hy;
5405  hzed = thee->pmgp->hzed;
5406 
5407  /* Define the total domain size */
5408  xlen = thee->pmgp->xlen;
5409  ylen = thee->pmgp->ylen;
5410  zlen = thee->pmgp->zlen;
5411 
5412  /* Define the min/max dimensions */
5413  xmin = thee->pmgp->xcent - (xlen/2.0);
5414  ymin = thee->pmgp->ycent - (ylen/2.0);
5415  zmin = thee->pmgp->zcent - (zlen/2.0);
5416  xmax = thee->pmgp->xcent + (xlen/2.0);
5417  ymax = thee->pmgp->ycent + (ylen/2.0);
5418  zmax = thee->pmgp->zcent + (zlen/2.0);
5419 
5420  /* Reset the charge array */
5421  for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
5422 
5423  /* Fill in the source term (atomic charges) */
5424  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5425  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
5426 
5427  atom = Valist_getAtom(alist, iatom);
5428  apos = Vatom_getPosition(atom);
5429  charge = Vatom_getCharge(atom);
5430 
5431  /* Make sure we're on the grid */
5432  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
5433  (apos[1]<=ymin) || (apos[1]>=ymax) || \
5434  (apos[2]<=zmin) || (apos[2]>=zmax)) {
5435  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5436  (thee->pmgp->bcfl != BCFL_MAP)) {
5437  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f, \
5438 %4.3f) is off the mesh (ignoring):\n",
5439  iatom, apos[0], apos[1], apos[2]);
5440  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
5441  xmin, xmax);
5442  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
5443  ymin, ymax);
5444  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
5445  zmin, zmax);
5446  }
5447  fflush(stderr);
5448  } else {
5449 
5450  /* Convert the atom position to grid reference frame */
5451  position[0] = apos[0] - xmin;
5452  position[1] = apos[1] - ymin;
5453  position[2] = apos[2] - zmin;
5454 
5455  /* Scale the charge to be a delta function */
5456  charge = charge*zmagic/(hx*hy*hzed);
5457 
5458  /* Figure out which vertices we're next to */
5459  ifloat = position[0]/hx;
5460  jfloat = position[1]/hy;
5461  kfloat = position[2]/hzed;
5462 
5463  ihi = (int)ceil(ifloat);
5464  ilo = (int)floor(ifloat);
5465  jhi = (int)ceil(jfloat);
5466  jlo = (int)floor(jfloat);
5467  khi = (int)ceil(kfloat);
5468  klo = (int)floor(kfloat);
5469 
5470  /* Now assign fractions of the charge to the nearby verts */
5471  dx = ifloat - (double)(ilo);
5472  dy = jfloat - (double)(jlo);
5473  dz = kfloat - (double)(klo);
5474  thee->charge[IJK(ihi,jhi,khi)] += (dx*dy*dz*charge);
5475  thee->charge[IJK(ihi,jlo,khi)] += (dx*(1.0-dy)*dz*charge);
5476  thee->charge[IJK(ihi,jhi,klo)] += (dx*dy*(1.0-dz)*charge);
5477  thee->charge[IJK(ihi,jlo,klo)] += (dx*(1.0-dy)*(1.0-dz)*charge);
5478  thee->charge[IJK(ilo,jhi,khi)] += ((1.0-dx)*dy*dz *charge);
5479  thee->charge[IJK(ilo,jlo,khi)] += ((1.0-dx)*(1.0-dy)*dz *charge);
5480  thee->charge[IJK(ilo,jhi,klo)] += ((1.0-dx)*dy*(1.0-dz)*charge);
5481  thee->charge[IJK(ilo,jlo,klo)] += ((1.0-dx)*(1.0-dy)*(1.0-dz)*charge);
5482  } /* endif (on the mesh) */
5483  } /* endfor (each atom) */
5484 }
5485 
5486 VPRIVATE double bspline2(double x) {
5487 
5488  double m2m, m2, m3;
5489 
5490  if ((x >= 0.0) && (x <= 2.0)) m2m = 1.0 - VABS(x - 1.0);
5491  else m2m = 0.0;
5492  if ((x >= 1.0) && (x <= 3.0)) m2 = 1.0 - VABS(x - 2.0);
5493  else m2 = 0.0;
5494 
5495  if ((x >= 0.0) && (x <= 3.0)) m3 = 0.5*x*m2m + 0.5*(3.0-x)*m2;
5496  else m3 = 0.0;
5497 
5498  return m3;
5499 
5500 }
5501 
5502 VPRIVATE double dbspline2(double x) {
5503 
5504  double m2m, m2, dm3;
5505 
5506  if ((x >= 0.0) && (x <= 2.0)) m2m = 1.0 - VABS(x - 1.0);
5507  else m2m = 0.0;
5508  if ((x >= 1.0) && (x <= 3.0)) m2 = 1.0 - VABS(x - 2.0);
5509  else m2 = 0.0;
5510 
5511  dm3 = m2m - m2;
5512 
5513  return dm3;
5514 
5515 }
5516 
5517 
5518 VPRIVATE void fillcoChargeSpline2(Vpmg *thee) {
5519 
5520  Valist *alist;
5521  Vpbe *pbe;
5522  Vatom *atom;
5523  double xmin, xmax, ymin, ymax, zmin, zmax, zmagic;
5524  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
5525  double charge, hx, hy, hzed, *apos, mx, my, mz;
5526  int i, ii, jj, kk, nx, ny, nz, iatom;
5527  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
5528 
5529 
5530  VASSERT(thee != VNULL);
5531 
5532  /* Get PBE info */
5533  pbe = thee->pbe;
5534  alist = pbe->alist;
5535  zmagic = Vpbe_getZmagic(pbe);
5536 
5537  /* Mesh info */
5538  nx = thee->pmgp->nx;
5539  ny = thee->pmgp->ny;
5540  nz = thee->pmgp->nz;
5541  hx = thee->pmgp->hx;
5542  hy = thee->pmgp->hy;
5543  hzed = thee->pmgp->hzed;
5544 
5545  /* Define the total domain size */
5546  xlen = thee->pmgp->xlen;
5547  ylen = thee->pmgp->ylen;
5548  zlen = thee->pmgp->zlen;
5549 
5550  /* Define the min/max dimensions */
5551  xmin = thee->pmgp->xcent - (xlen/2.0);
5552  ymin = thee->pmgp->ycent - (ylen/2.0);
5553  zmin = thee->pmgp->zcent - (zlen/2.0);
5554  xmax = thee->pmgp->xcent + (xlen/2.0);
5555  ymax = thee->pmgp->ycent + (ylen/2.0);
5556  zmax = thee->pmgp->zcent + (zlen/2.0);
5557 
5558  /* Reset the charge array */
5559  for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
5560 
5561  /* Fill in the source term (atomic charges) */
5562  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5563  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
5564 
5565  atom = Valist_getAtom(alist, iatom);
5566  apos = Vatom_getPosition(atom);
5567  charge = Vatom_getCharge(atom);
5568 
5569  /* Make sure we're on the grid */
5570  if ((apos[0]<=(xmin-hx)) || (apos[0]>=(xmax+hx)) || \
5571  (apos[1]<=(ymin-hy)) || (apos[1]>=(ymax+hy)) || \
5572  (apos[2]<=(zmin-hzed)) || (apos[2]>=(zmax+hzed))) {
5573  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5574  (thee->pmgp->bcfl != BCFL_MAP)) {
5575  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f, \
5576 %4.3f) is off the mesh (for cubic splines!!) (ignoring this atom):\n",
5577  iatom, apos[0], apos[1], apos[2]);
5578  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
5579  xmin, xmax);
5580  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
5581  ymin, ymax);
5582  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
5583  zmin, zmax);
5584  }
5585  fflush(stderr);
5586  } else {
5587 
5588  /* Convert the atom position to grid reference frame */
5589  position[0] = apos[0] - xmin;
5590  position[1] = apos[1] - ymin;
5591  position[2] = apos[2] - zmin;
5592 
5593  /* Scale the charge to be a delta function */
5594  charge = charge*zmagic/(hx*hy*hzed);
5595 
5596  /* Figure out which vertices we're next to */
5597  ifloat = position[0]/hx;
5598  jfloat = position[1]/hy;
5599  kfloat = position[2]/hzed;
5600 
5601  ip1 = (int)ceil(ifloat);
5602  ip2 = ip1 + 1;
5603  im1 = (int)floor(ifloat);
5604  im2 = im1 - 1;
5605  jp1 = (int)ceil(jfloat);
5606  jp2 = jp1 + 1;
5607  jm1 = (int)floor(jfloat);
5608  jm2 = jm1 - 1;
5609  kp1 = (int)ceil(kfloat);
5610  kp2 = kp1 + 1;
5611  km1 = (int)floor(kfloat);
5612  km2 = km1 - 1;
5613 
5614  /* This step shouldn't be necessary, but it saves nasty debugging
5615  * later on if something goes wrong */
5616  ip2 = VMIN2(ip2,nx-1);
5617  ip1 = VMIN2(ip1,nx-1);
5618  im1 = VMAX2(im1,0);
5619  im2 = VMAX2(im2,0);
5620  jp2 = VMIN2(jp2,ny-1);
5621  jp1 = VMIN2(jp1,ny-1);
5622  jm1 = VMAX2(jm1,0);
5623  jm2 = VMAX2(jm2,0);
5624  kp2 = VMIN2(kp2,nz-1);
5625  kp1 = VMIN2(kp1,nz-1);
5626  km1 = VMAX2(km1,0);
5627  km2 = VMAX2(km2,0);
5628 
5629  /* Now assign fractions of the charge to the nearby verts */
5630  for (ii=im2; ii<=ip2; ii++) {
5631  mx = bspline2(VFCHI(ii,ifloat));
5632  for (jj=jm2; jj<=jp2; jj++) {
5633  my = bspline2(VFCHI(jj,jfloat));
5634  for (kk=km2; kk<=kp2; kk++) {
5635  mz = bspline2(VFCHI(kk,kfloat));
5636  thee->charge[IJK(ii,jj,kk)] += (charge*mx*my*mz);
5637  }
5638  }
5639  }
5640 
5641  } /* endif (on the mesh) */
5642  } /* endfor (each atom) */
5643 }
5644 
5645 VPUBLIC int Vpmg_fillco(Vpmg *thee,
5646  Vsurf_Meth surfMeth,
5647  double splineWin,
5648  Vchrg_Meth chargeMeth,
5649  int useDielXMap,
5650  Vgrid *dielXMap,
5651  int useDielYMap,
5652  Vgrid *dielYMap,
5653  int useDielZMap,
5654  Vgrid *dielZMap,
5655  int useKappaMap,
5656  Vgrid *kappaMap,
5657  int usePotMap,
5658  Vgrid *potMap,
5659  int useChargeMap,
5660  Vgrid *chargeMap
5661  ) {
5662 
5663  Vpbe *pbe;
5664  double xmin,
5665  xmax,
5666  ymin,
5667  ymax,
5668  zmin,
5669  zmax,
5670  xlen,
5671  ylen,
5672  zlen,
5673  hx,
5674  hy,
5675  hzed,
5676  epsw,
5677  epsp,
5678  ionstr;
5679  int i,
5680  nx,
5681  ny,
5682  nz,
5683  islap;
5684  Vrc_Codes rc;
5685 
5686  if (thee == VNULL) {
5687  Vnm_print(2, "Vpmg_fillco: got NULL thee!\n");
5688  return 0;
5689  }
5690 
5691  thee->surfMeth = surfMeth;
5692  thee->splineWin = splineWin;
5693  thee->chargeMeth = chargeMeth;
5694  thee->useDielXMap = useDielXMap;
5695  if (thee->useDielXMap) thee->dielXMap = dielXMap;
5696  thee->useDielYMap = useDielYMap;
5697  if (thee->useDielYMap) thee->dielYMap = dielYMap;
5698  thee->useDielZMap = useDielZMap;
5699  if (thee->useDielZMap) thee->dielZMap = dielZMap;
5700  thee->useKappaMap = useKappaMap;
5701  if (thee->useKappaMap) thee->kappaMap = kappaMap;
5702  thee->usePotMap = usePotMap;
5703  if (thee->usePotMap) thee->potMap = potMap;
5704  thee->useChargeMap = useChargeMap;
5705  if (thee->useChargeMap) thee->chargeMap = chargeMap;
5706 
5707  /* Get PBE info */
5708  pbe = thee->pbe;
5709  ionstr = Vpbe_getBulkIonicStrength(pbe);
5710  epsw = Vpbe_getSolventDiel(pbe);
5711  epsp = Vpbe_getSoluteDiel(pbe);
5712 
5713  /* Mesh info */
5714  nx = thee->pmgp->nx;
5715  ny = thee->pmgp->ny;
5716  nz = thee->pmgp->nz;
5717  hx = thee->pmgp->hx;
5718  hy = thee->pmgp->hy;
5719  hzed = thee->pmgp->hzed;
5720 
5721  /* Define the total domain size */
5722  xlen = thee->pmgp->xlen;
5723  ylen = thee->pmgp->ylen;
5724  zlen = thee->pmgp->zlen;
5725 
5726  /* Define the min/max dimensions */
5727  xmin = thee->pmgp->xcent - (xlen/2.0);
5728  thee->pmgp->xmin = xmin;
5729  ymin = thee->pmgp->ycent - (ylen/2.0);
5730  thee->pmgp->ymin = ymin;
5731  zmin = thee->pmgp->zcent - (zlen/2.0);
5732  thee->pmgp->zmin = zmin;
5733  xmax = thee->pmgp->xcent + (xlen/2.0);
5734  thee->pmgp->xmax = xmax;
5735  ymax = thee->pmgp->ycent + (ylen/2.0);
5736  thee->pmgp->ymax = ymax;
5737  zmax = thee->pmgp->zcent + (zlen/2.0);
5738  thee->pmgp->zmax = zmax;
5739  thee->rparm[2] = xmin;
5740  thee->rparm[3] = xmax;
5741  thee->rparm[4] = ymin;
5742  thee->rparm[5] = ymax;
5743  thee->rparm[6] = zmin;
5744  thee->rparm[7] = zmax;
5745 
5746  /* This is a flag that gets set if the operator is a simple Laplacian;
5747  * i.e., in the case of a homogenous dielectric and zero ionic strength
5748  * The operator cannot be a simple Laplacian if maps are read in. */
5749  if(thee->useDielXMap || thee->useDielYMap || thee->useDielZMap ||
5750  thee->useKappaMap || thee->usePotMap){
5751  islap = 0;
5752  }else if ( (ionstr < VPMGSMALL) && (VABS(epsp-epsw) < VPMGSMALL) ){
5753  islap = 1;
5754  }else{
5755  islap = 0;
5756  }
5757 
5758  /* Fill the mesh point coordinate arrays */
5759  for (i=0; i<nx; i++) thee->xf[i] = xmin + i*hx;
5760  for (i=0; i<ny; i++) thee->yf[i] = ymin + i*hy;
5761  for (i=0; i<nz; i++) thee->zf[i] = zmin + i*hzed;
5762 
5763  /* Reset the tcf array */
5764  for (i=0; i<(nx*ny*nz); i++) thee->tcf[i] = 0.0;
5765 
5766  /* Fill in the source term (atomic charges) */
5767  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5768  rc = fillcoCharge(thee);
5769  switch(rc) {
5770  case VRC_SUCCESS:
5771  break;
5772  case VRC_WARNING:
5773  Vnm_print(2, "Vpmg_fillco: non-fatal errors while filling charge map!\n");
5774  break;
5775  case VRC_FAILURE:
5776  Vnm_print(2, "Vpmg_fillco: fatal errors while filling charge map!\n");
5777  return 0;
5778  break;
5779  }
5780 
5781  /* THE FOLLOWING NEEDS TO BE DONE IF WE'RE NOT USING A SIMPLE LAPLACIAN
5782  * OPERATOR */
5783  if (!islap) {
5784  Vnm_print(0, "Vpmg_fillco: marking ion and solvent accessibility.\n");
5785  fillcoCoef(thee);
5786  Vnm_print(0, "Vpmg_fillco: done filling coefficient arrays\n");
5787 
5788  } else { /* else (!islap) ==> It's a Laplacian operator! */
5789 
5790  for (i=0; i<(nx*ny*nz); i++) {
5791  thee->kappa[i] = 0.0;
5792  thee->epsx[i] = epsp;
5793  thee->epsy[i] = epsp;
5794  thee->epsz[i] = epsp;
5795  }
5796 
5797  } /* endif (!islap) */
5798 
5799  /* Fill the boundary arrays (except when focusing, bcfl = 4) */
5800  if (thee->pmgp->bcfl != BCFL_FOCUS) {
5801  Vnm_print(0, "Vpmg_fillco: filling boundary arrays\n");
5802  bcCalc(thee);
5803  Vnm_print(0, "Vpmg_fillco: done filling boundary arrays\n");
5804  }
5805 
5806  thee->filled = 1;
5807 
5808  return 1;
5809 }
5810 
5811 
5812 VPUBLIC int Vpmg_force(Vpmg *thee, double *force, int atomID,
5813  Vsurf_Meth srfm, Vchrg_Meth chgm) {
5814 
5815  int rc = 1;
5816  double qfF[3]; /* Charge-field force */
5817  double dbF[3]; /* Dielectric boundary force */
5818  double ibF[3]; /* Ion boundary force */
5819  double npF[3]; /* Non-polar boundary force */
5820 
5821  VASSERT(thee != VNULL);
5822 
5823  rc = rc && Vpmg_dbForce(thee, qfF, atomID, srfm);
5824  rc = rc && Vpmg_ibForce(thee, dbF, atomID, srfm);
5825  rc = rc && Vpmg_qfForce(thee, ibF, atomID, chgm);
5826 
5827  force[0] = qfF[0] + dbF[0] + ibF[0];
5828  force[1] = qfF[1] + dbF[1] + ibF[1];
5829  force[2] = qfF[2] + dbF[2] + ibF[2];
5830 
5831  return rc;
5832 
5833 }
5834 
5835 VPUBLIC int Vpmg_ibForce(Vpmg *thee, double *force, int atomID,
5836  Vsurf_Meth srfm) {
5837 
5838  Valist *alist;
5839  Vacc *acc;
5840  Vpbe *pbe;
5841  Vatom *atom;
5842 
5843  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
5844  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
5845  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
5846  double izmagic;
5847  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
5848 
5849  /* For nonlinear forces */
5850  int ichop, nchop, nion, m;
5851  double ionConc[MAXION], ionRadii[MAXION], ionQ[MAXION], ionstr;
5852 
5853  VASSERT(thee != VNULL);
5854 
5855  acc = thee->pbe->acc;
5856  atom = Valist_getAtom(thee->pbe->alist, atomID);
5857  apos = Vatom_getPosition(atom);
5858  arad = Vatom_getRadius(atom);
5859 
5860  /* Reset force */
5861  force[0] = 0.0;
5862  force[1] = 0.0;
5863  force[2] = 0.0;
5864 
5865  /* Check surface definition */
5866  if ((srfm != VSM_SPLINE) && (srfm!=VSM_SPLINE3) && (srfm!=VSM_SPLINE4)) {
5867  Vnm_print(2, "Vpmg_ibForce: Forces *must* be calculated with \
5868 spline-based surfaces!\n");
5869  Vnm_print(2, "Vpmg_ibForce: Skipping ionic boundary force \
5870 calculation!\n");
5871  return 0;
5872  }
5873 
5874  /* If we aren't in the current position, then we're done */
5875  if (atom->partID == 0) return 1;
5876 
5877  /* Get PBE info */
5878  pbe = thee->pbe;
5879  acc = pbe->acc;
5880  alist = pbe->alist;
5881  irad = Vpbe_getMaxIonRadius(pbe);
5882  zkappa2 = Vpbe_getZkappa2(pbe);
5883  izmagic = 1.0/Vpbe_getZmagic(pbe);
5884 
5885  ionstr = Vpbe_getBulkIonicStrength(pbe);
5886  Vpbe_getIons(pbe, &nion, ionConc, ionRadii, ionQ);
5887 
5888  /* Mesh info */
5889  nx = thee->pmgp->nx;
5890  ny = thee->pmgp->ny;
5891  nz = thee->pmgp->nz;
5892  hx = thee->pmgp->hx;
5893  hy = thee->pmgp->hy;
5894  hzed = thee->pmgp->hzed;
5895  xlen = thee->pmgp->xlen;
5896  ylen = thee->pmgp->ylen;
5897  zlen = thee->pmgp->zlen;
5898  xmin = thee->pmgp->xmin;
5899  ymin = thee->pmgp->ymin;
5900  zmin = thee->pmgp->zmin;
5901  xmax = thee->pmgp->xmax;
5902  ymax = thee->pmgp->ymax;
5903  zmax = thee->pmgp->zmax;
5904 
5905  /* Sanity check: there is no force if there is zero ionic strength */
5906  if (zkappa2 < VPMGSMALL) {
5907 #ifndef VAPBSQUIET
5908  Vnm_print(2, "Vpmg_ibForce: No force for zero ionic strength!\n");
5909 #endif
5910  return 1;
5911  }
5912 
5913  /* Make sure we're on the grid */
5914  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
5915  (apos[1]<=ymin) || (apos[1]>=ymax) || \
5916  (apos[2]<=zmin) || (apos[2]>=zmax)) {
5917  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5918  (thee->pmgp->bcfl != BCFL_MAP)) {
5919  Vnm_print(2, "Vpmg_ibForce: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
5920  atom, apos[0], apos[1], apos[2]);
5921  Vnm_print(2, "Vpmg_ibForce: xmin = %g, xmax = %g\n",
5922  xmin, xmax);
5923  Vnm_print(2, "Vpmg_ibForce: ymin = %g, ymax = %g\n",
5924  ymin, ymax);
5925  Vnm_print(2, "Vpmg_ibForce: zmin = %g, zmax = %g\n",
5926  zmin, zmax);
5927  }
5928  fflush(stderr);
5929  } else {
5930 
5931  /* Convert the atom position to grid reference frame */
5932  position[0] = apos[0] - xmin;
5933  position[1] = apos[1] - ymin;
5934  position[2] = apos[2] - zmin;
5935 
5936  /* Integrate over points within this atom's (inflated) radius */
5937  rtot = (irad + arad + thee->splineWin);
5938  rtot2 = VSQR(rtot);
5939  dx = rtot + 0.5*hx;
5940  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
5941  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
5942  for (i=imin; i<=imax; i++) {
5943  dx2 = VSQR(position[0] - hx*i);
5944  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
5945  else dy = 0.5*hy;
5946  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
5947  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
5948  for (j=jmin; j<=jmax; j++) {
5949  dy2 = VSQR(position[1] - hy*j);
5950  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
5951  else dz = 0.5*hzed;
5952  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
5953  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
5954  for (k=kmin; k<=kmax; k++) {
5955  dz2 = VSQR(k*hzed - position[2]);
5956  /* See if grid point is inside ivdw radius and set kappa
5957  * accordingly (do spline assignment here) */
5958  if ((dz2 + dy2 + dx2) <= rtot2) {
5959  gpos[0] = i*hx + xmin;
5960  gpos[1] = j*hy + ymin;
5961  gpos[2] = k*hzed + zmin;
5962 
5963  /* Select the correct function based on the surface definition
5964  * (now including the 7th order polynomial) */
5965  Vpmg_splineSelect(srfm,acc, gpos,thee->splineWin, irad, atom, tgrad);
5966 
5967  if (thee->pmgp->nonlin) {
5968  /* Nonlinear forces */
5969  fmag = 0.0;
5970  nchop = 0;
5971  for (m=0; m<nion; m++) {
5972  fmag += (thee->kappa[IJK(i,j,k)])*ionConc[m]*(Vcap_exp(-ionQ[m]*thee->u[IJK(i,j,k)], &ichop)-1.0)/ionstr;
5973  nchop += ichop;
5974  }
5975  /* if (nchop > 0) Vnm_print(2, "Vpmg_ibForece: Chopped EXP %d times!\n", nchop);*/
5976  force[0] += (zkappa2*fmag*tgrad[0]);
5977  force[1] += (zkappa2*fmag*tgrad[1]);
5978  force[2] += (zkappa2*fmag*tgrad[2]);
5979  } else {
5980  /* Use of bulk factor (zkappa2) OK here becuase
5981  * LPBE force approximation */
5982  /* NAB -- did we forget a kappa factor here??? */
5983  fmag = VSQR(thee->u[IJK(i,j,k)])*(thee->kappa[IJK(i,j,k)]);
5984  force[0] += (zkappa2*fmag*tgrad[0]);
5985  force[1] += (zkappa2*fmag*tgrad[1]);
5986  force[2] += (zkappa2*fmag*tgrad[2]);
5987  }
5988  }
5989  } /* k loop */
5990  } /* j loop */
5991  } /* i loop */
5992  }
5993  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
5994  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
5995  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
5996 
5997  return 1;
5998 }
5999 
6000 VPUBLIC int Vpmg_dbForce(Vpmg *thee, double *dbForce, int atomID,
6001  Vsurf_Meth srfm) {
6002 
6003  Vacc *acc;
6004  Vpbe *pbe;
6005  Vatom *atom;
6006 
6007  double *apos, position[3], arad, srad, hx, hy, hzed, izmagic, deps, depsi;
6008  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
6009  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
6010  double *u, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
6011  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
6012  double dHzijkm1[3];
6013  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
6014 
6015  VASSERT(thee != VNULL);
6016  if (!thee->filled) {
6017  Vnm_print(2, "Vpmg_dbForce: Need to callVpmg_fillco!\n");
6018  return 0;
6019  }
6020 
6021  acc = thee->pbe->acc;
6022  atom = Valist_getAtom(thee->pbe->alist, atomID);
6023  apos = Vatom_getPosition(atom);
6024  arad = Vatom_getRadius(atom);
6025  srad = Vpbe_getSolventRadius(thee->pbe);
6026 
6027  /* Reset force */
6028  dbForce[0] = 0.0;
6029  dbForce[1] = 0.0;
6030  dbForce[2] = 0.0;
6031 
6032  /* Check surface definition */
6033  if ((srfm != VSM_SPLINE) && (srfm!=VSM_SPLINE3) && (srfm!=VSM_SPLINE4)) {
6034  Vnm_print(2, "Vpmg_dbForce: Forces *must* be calculated with \
6035 spline-based surfaces!\n");
6036  Vnm_print(2, "Vpmg_dbForce: Skipping dielectric/apolar boundary \
6037 force calculation!\n");
6038  return 0;
6039  }
6040 
6041 
6042  /* If we aren't in the current position, then we're done */
6043  if (atom->partID == 0) return 1;
6044 
6045  /* Get PBE info */
6046  pbe = thee->pbe;
6047  acc = pbe->acc;
6048  epsp = Vpbe_getSoluteDiel(pbe);
6049  epsw = Vpbe_getSolventDiel(pbe);
6050  kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
6051  izmagic = 1.0/Vpbe_getZmagic(pbe);
6052 
6053  /* Mesh info */
6054  nx = thee->pmgp->nx;
6055  ny = thee->pmgp->ny;
6056  nz = thee->pmgp->nz;
6057  hx = thee->pmgp->hx;
6058  hy = thee->pmgp->hy;
6059  hzed = thee->pmgp->hzed;
6060  xlen = thee->pmgp->xlen;
6061  ylen = thee->pmgp->ylen;
6062  zlen = thee->pmgp->zlen;
6063  xmin = thee->pmgp->xmin;
6064  ymin = thee->pmgp->ymin;
6065  zmin = thee->pmgp->zmin;
6066  xmax = thee->pmgp->xmax;
6067  ymax = thee->pmgp->ymax;
6068  zmax = thee->pmgp->zmax;
6069  u = thee->u;
6070 
6071  /* Sanity check: there is no force if there is zero ionic strength */
6072  if (VABS(epsp-epsw) < VPMGSMALL) {
6073  Vnm_print(0, "Vpmg_dbForce: No force for uniform dielectric!\n");
6074  return 1;
6075  }
6076  deps = (epsw - epsp);
6077  depsi = 1.0/deps;
6078  rtot = (arad + thee->splineWin + srad);
6079 
6080  /* Make sure we're on the grid */
6081  /* Grid checking modified by Matteo Rotter */
6082  if ((apos[0]<=xmin + rtot) || (apos[0]>=xmax - rtot) || \
6083  (apos[1]<=ymin + rtot) || (apos[1]>=ymax - rtot) || \
6084  (apos[2]<=zmin + rtot) || (apos[2]>=zmax - rtot)) {
6085  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
6086  (thee->pmgp->bcfl != BCFL_MAP)) {
6087  Vnm_print(2, "Vpmg_dbForce: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
6088  atomID, apos[0], apos[1], apos[2]);
6089  Vnm_print(2, "Vpmg_dbForce: xmin = %g, xmax = %g\n",
6090  xmin, xmax);
6091  Vnm_print(2, "Vpmg_dbForce: ymin = %g, ymax = %g\n",
6092  ymin, ymax);
6093  Vnm_print(2, "Vpmg_dbForce: zmin = %g, zmax = %g\n",
6094  zmin, zmax);
6095  }
6096  fflush(stderr);
6097  } else {
6098 
6099  /* Convert the atom position to grid reference frame */
6100  position[0] = apos[0] - xmin;
6101  position[1] = apos[1] - ymin;
6102  position[2] = apos[2] - zmin;
6103 
6104  /* Integrate over points within this atom's (inflated) radius */
6105  rtot2 = VSQR(rtot);
6106  dx = rtot/hx;
6107  imin = (int)floor((position[0]-rtot)/hx);
6108  if (imin < 1) {
6109  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6110  return 0;
6111  }
6112  imax = (int)ceil((position[0]+rtot)/hx);
6113  if (imax > (nx-2)) {
6114  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6115  return 0;
6116  }
6117  jmin = (int)floor((position[1]-rtot)/hy);
6118  if (jmin < 1) {
6119  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6120  return 0;
6121  }
6122  jmax = (int)ceil((position[1]+rtot)/hy);
6123  if (jmax > (ny-2)) {
6124  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6125  return 0;
6126  }
6127  kmin = (int)floor((position[2]-rtot)/hzed);
6128  if (kmin < 1) {
6129  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6130  return 0;
6131  }
6132  kmax = (int)ceil((position[2]+rtot)/hzed);
6133  if (kmax > (nz-2)) {
6134  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6135  return 0;
6136  }
6137  for (i=imin; i<=imax; i++) {
6138  for (j=jmin; j<=jmax; j++) {
6139  for (k=kmin; k<=kmax; k++) {
6140  /* i,j,k */
6141  gpos[0] = (i+0.5)*hx + xmin;
6142  gpos[1] = j*hy + ymin;
6143  gpos[2] = k*hzed + zmin;
6144  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
6145 
6146  /* Select the correct function based on the surface definition
6147  * (now including the 7th order polynomial) */
6148  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHxijk);
6149  /*
6150  switch (srfm) {
6151  case VSM_SPLINE :
6152  Vacc_splineAccGradAtomNorm(acc, gpos, thee->splineWin, 0.,
6153  atom, dHxijk);
6154  break;
6155  case VSM_SPLINE4 :
6156  Vacc_splineAccGradAtomNorm4(acc, gpos, thee->splineWin, 0.,
6157  atom, dHxijk);
6158  break;
6159  default:
6160  Vnm_print(2, "Vpmg_dbnbForce: Unknown surface method.\n");
6161  return;
6162  }
6163  */
6164  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
6165  gpos[0] = i*hx + xmin;
6166  gpos[1] = (j+0.5)*hy + ymin;
6167  gpos[2] = k*hzed + zmin;
6168  Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
6169 
6170  /* Select the correct function based on the surface definition
6171  * (now including the 7th order polynomial) */
6172  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHyijk);
6173 
6174  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
6175  gpos[0] = i*hx + xmin;
6176  gpos[1] = j*hy + ymin;
6177  gpos[2] = (k+0.5)*hzed + zmin;
6178  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
6179 
6180  /* Select the correct function based on the surface definition
6181  * (now including the 7th order polynomial) */
6182  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHzijk);
6183 
6184  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
6185  /* i-1,j,k */
6186  gpos[0] = (i-0.5)*hx + xmin;
6187  gpos[1] = j*hy + ymin;
6188  gpos[2] = k*hzed + zmin;
6189  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
6190 
6191  /* Select the correct function based on the surface definition
6192  * (now including the 7th order polynomial) */
6193  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHxim1jk);
6194 
6195  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
6196  /* i,j-1,k */
6197  gpos[0] = i*hx + xmin;
6198  gpos[1] = (j-0.5)*hy + ymin;
6199  gpos[2] = k*hzed + zmin;
6200  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
6201 
6202  /* Select the correct function based on the surface definition
6203  * (now including the 7th order polynomial) */
6204  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHyijm1k);
6205 
6206  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
6207  /* i,j,k-1 */
6208  gpos[0] = i*hx + xmin;
6209  gpos[1] = j*hy + ymin;
6210  gpos[2] = (k-0.5)*hzed + zmin;
6211  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
6212 
6213  /* Select the correct function based on the surface definition
6214  * (now including the 7th order polynomial) */
6215  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHzijkm1);
6216 
6217  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
6218  /* *** CALCULATE DIELECTRIC BOUNDARY FORCES *** */
6219  dbFmag = u[IJK(i,j,k)];
6220  tgrad[0] =
6221  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
6222  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
6223  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
6224  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
6225  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
6226  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
6227  tgrad[1] =
6228  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
6229  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
6230  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
6231  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
6232  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
6233  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
6234  tgrad[2] =
6235  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
6236  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
6237  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
6238  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
6239  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
6240  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
6241  dbForce[0] += (dbFmag*tgrad[0]);
6242  dbForce[1] += (dbFmag*tgrad[1]);
6243  dbForce[2] += (dbFmag*tgrad[2]);
6244 
6245  } /* k loop */
6246  } /* j loop */
6247  } /* i loop */
6248 
6249  dbForce[0] = -dbForce[0]*hx*hy*hzed*deps*0.5*izmagic;
6250  dbForce[1] = -dbForce[1]*hx*hy*hzed*deps*0.5*izmagic;
6251  dbForce[2] = -dbForce[2]*hx*hy*hzed*deps*0.5*izmagic;
6252  }
6253 
6254  return 1;
6255 }
6256 
6257 VPUBLIC int Vpmg_qfForce(Vpmg *thee, double *force, int atomID,
6258  Vchrg_Meth chgm) {
6259 
6260  double tforce[3];
6261 
6262  /* Reset force */
6263  force[0] = 0.0;
6264  force[1] = 0.0;
6265  force[2] = 0.0;
6266 
6267  /* Check surface definition */
6268  if (chgm != VCM_BSPL2) {
6269  Vnm_print(2, "Vpmg_qfForce: It is recommended that forces be \
6270 calculated with the\n");
6271  Vnm_print(2, "Vpmg_qfForce: cubic spline charge discretization \
6272 scheme\n");
6273  }
6274 
6275  switch (chgm) {
6276  case VCM_TRIL:
6277  qfForceSpline1(thee, tforce, atomID);
6278  break;
6279  case VCM_BSPL2:
6280  qfForceSpline2(thee, tforce, atomID);
6281  break;
6282  case VCM_BSPL4:
6283  qfForceSpline4(thee, tforce, atomID);
6284  break;
6285  default:
6286  Vnm_print(2, "Vpmg_qfForce: Undefined charge discretization \
6287 method (%d)!\n", chgm);
6288  Vnm_print(2, "Vpmg_qfForce: Forces not calculated!\n");
6289  return 0;
6290  }
6291 
6292  /* Assign forces */
6293  force[0] = tforce[0];
6294  force[1] = tforce[1];
6295  force[2] = tforce[2];
6296 
6297  return 1;
6298 }
6299 
6300 
6301 VPRIVATE void qfForceSpline1(Vpmg *thee, double *force, int atomID) {
6302 
6303  Vatom *atom;
6304 
6305  double *apos, position[3], hx, hy, hzed;
6306  double xmin, ymin, zmin, xmax, ymax, zmax;
6307  double dx, dy, dz;
6308  double *u, charge, ifloat, jfloat, kfloat;
6309  int nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
6310 
6311  VASSERT(thee != VNULL);
6312 
6313  atom = Valist_getAtom(thee->pbe->alist, atomID);
6314  apos = Vatom_getPosition(atom);
6315  charge = Vatom_getCharge(atom);
6316 
6317  /* Reset force */
6318  force[0] = 0.0;
6319  force[1] = 0.0;
6320  force[2] = 0.0;
6321 
6322  /* If we aren't in the current position, then we're done */
6323  if (atom->partID == 0) return;
6324 
6325  /* Mesh info */
6326  nx = thee->pmgp->nx;
6327  ny = thee->pmgp->ny;
6328  nz = thee->pmgp->nz;
6329  hx = thee->pmgp->hx;
6330  hy = thee->pmgp->hy;
6331  hzed = thee->pmgp->hzed;
6332  xmin = thee->pmgp->xmin;
6333  ymin = thee->pmgp->ymin;
6334  zmin = thee->pmgp->zmin;
6335  xmax = thee->pmgp->xmax;
6336  ymax = thee->pmgp->ymax;
6337  zmax = thee->pmgp->zmax;
6338  u = thee->u;
6339 
6340  /* Make sure we're on the grid */
6341  if ((apos[0]<=xmin) || (apos[0]>=xmax) || (apos[1]<=ymin) || \
6342  (apos[1]>=ymax) || (apos[2]<=zmin) || (apos[2]>=zmax)) {
6343  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
6344  (thee->pmgp->bcfl != BCFL_MAP)) {
6345  Vnm_print(2, "Vpmg_qfForce: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", atomID, apos[0], apos[1], apos[2]);
6346  Vnm_print(2, "Vpmg_qfForce: xmin = %g, xmax = %g\n", xmin, xmax);
6347  Vnm_print(2, "Vpmg_qfForce: ymin = %g, ymax = %g\n", ymin, ymax);
6348  Vnm_print(2, "Vpmg_qfForce: zmin = %g, zmax = %g\n", zmin, zmax);
6349  }
6350  fflush(stderr);
6351  } else {
6352 
6353  /* Convert the atom position to grid coordinates */
6354  position[0] = apos[0] - xmin;
6355  position[1] = apos[1] - ymin;
6356  position[2] = apos[2] - zmin;
6357  ifloat = position[0]/hx;
6358  jfloat = position[1]/hy;
6359  kfloat = position[2]/hzed;
6360  ihi = (int)ceil(ifloat);
6361  ilo = (int)floor(ifloat);
6362  jhi = (int)ceil(jfloat);
6363  jlo = (int)floor(jfloat);
6364  khi = (int)ceil(kfloat);
6365  klo = (int)floor(kfloat);
6366  VASSERT((ihi < nx) && (ihi >=0));
6367  VASSERT((ilo < nx) && (ilo >=0));
6368  VASSERT((jhi < ny) && (jhi >=0));
6369  VASSERT((jlo < ny) && (jlo >=0));
6370  VASSERT((khi < nz) && (khi >=0));
6371  VASSERT((klo < nz) && (klo >=0));
6372  dx = ifloat - (double)(ilo);
6373  dy = jfloat - (double)(jlo);
6374  dz = kfloat - (double)(klo);
6375 
6376 
6377 #if 0
6378  Vnm_print(1, "Vpmg_qfForce: (DEBUG) u ~ %g\n",
6379  dx *dy *dz *u[IJK(ihi,jhi,khi)]
6380  +dx *dy *(1-dz)*u[IJK(ihi,jhi,klo)]
6381  +dx *(1-dy)*dz *u[IJK(ihi,jlo,khi)]
6382  +dx *(1-dy)*(1-dz)*u[IJK(ihi,jlo,klo)]
6383  +(1-dx)*dy *dz *u[IJK(ilo,jhi,khi)]
6384  +(1-dx)*dy *(1-dz)*u[IJK(ilo,jhi,klo)]
6385  +(1-dx)*(1-dy)*dz *u[IJK(ilo,jlo,khi)]
6386  +(1-dx)*(1-dy)*(1-dz)*u[IJK(ilo,jlo,klo)]);
6387 #endif
6388 
6389 
6390  if ((dx > VPMGSMALL) && (VABS(1.0-dx) > VPMGSMALL)) {
6391  force[0] =
6392  -charge*(dy *dz *u[IJK(ihi,jhi,khi)]
6393  + dy *(1-dz)*u[IJK(ihi,jhi,klo)]
6394  + (1-dy)*dz *u[IJK(ihi,jlo,khi)]
6395  + (1-dy)*(1-dz)*u[IJK(ihi,jlo,klo)]
6396  - dy *dz *u[IJK(ilo,jhi,khi)]
6397  - dy *(1-dz)*u[IJK(ilo,jhi,klo)]
6398  - (1-dy)*dz *u[IJK(ilo,jlo,khi)]
6399  - (1-dy)*(1-dz)*u[IJK(ilo,jlo,klo)])/hx;
6400  } else {
6401  force[0] = 0;
6402  Vnm_print(0,
6403  "Vpmg_qfForce: Atom %d on x gridline; zero x-force\n", atomID);
6404  }
6405  if ((dy > VPMGSMALL) && (VABS(1.0-dy) > VPMGSMALL)) {
6406  force[1] =
6407  -charge*(dx *dz *u[IJK(ihi,jhi,khi)]
6408  + dx *(1-dz)*u[IJK(ihi,jhi,klo)]
6409  - dx *dz *u[IJK(ihi,jlo,khi)]
6410  - dx *(1-dz)*u[IJK(ihi,jlo,klo)]
6411  + (1-dx)*dz *u[IJK(ilo,jhi,khi)]
6412  + (1-dx)*(1-dz)*u[IJK(ilo,jhi,klo)]
6413  - (1-dx)*dz *u[IJK(ilo,jlo,khi)]
6414  - (1-dx)*(1-dz)*u[IJK(ilo,jlo,klo)])/hy;
6415  } else {
6416  force[1] = 0;
6417  Vnm_print(0,
6418  "Vpmg_qfForce: Atom %d on y gridline; zero y-force\n", atomID);
6419  }
6420  if ((dz > VPMGSMALL) && (VABS(1.0-dz) > VPMGSMALL)) {
6421  force[2] =
6422  -charge*(dy *dx *u[IJK(ihi,jhi,khi)]
6423  - dy *dx *u[IJK(ihi,jhi,klo)]
6424  + (1-dy)*dx *u[IJK(ihi,jlo,khi)]
6425  - (1-dy)*dx *u[IJK(ihi,jlo,klo)]
6426  + dy *(1-dx)*u[IJK(ilo,jhi,khi)]
6427  - dy *(1-dx)*u[IJK(ilo,jhi,klo)]
6428  + (1-dy)*(1-dx)*u[IJK(ilo,jlo,khi)]
6429  - (1-dy)*(1-dx)*u[IJK(ilo,jlo,klo)])/hzed;
6430  } else {
6431  force[2] = 0;
6432  Vnm_print(0,
6433  "Vpmg_qfForce: Atom %d on z gridline; zero z-force\n", atomID);
6434  }
6435  }
6436 }
6437 
6438 VPRIVATE void qfForceSpline2(Vpmg *thee, double *force, int atomID) {
6439 
6440  Vatom *atom;
6441 
6442  double *apos, position[3], hx, hy, hzed;
6443  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
6444  double mx, my, mz, dmx, dmy, dmz;
6445  double *u, charge, ifloat, jfloat, kfloat;
6446  int nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1;
6447  int kp1, kp2, ii, jj, kk;
6448 
6449  VASSERT(thee != VNULL);
6450 
6451  atom = Valist_getAtom(thee->pbe->alist, atomID);
6452  apos = Vatom_getPosition(atom);
6453  charge = Vatom_getCharge(atom);
6454 
6455  /* Reset force */
6456  force[0] = 0.0;
6457  force[1] = 0.0;
6458  force[2] = 0.0;
6459 
6460  /* If we aren't in the current position, then we're done */
6461  if (atom->partID == 0) return;
6462 
6463  /* Mesh info */
6464  nx = thee->pmgp->nx;
6465  ny = thee->pmgp->ny;
6466  nz = thee->pmgp->nz;
6467  hx = thee->pmgp->hx;
6468  hy = thee->pmgp->hy;
6469  hzed = thee->pmgp->hzed;
6470  xlen = thee->pmgp->xlen;
6471  ylen = thee->pmgp->ylen;
6472  zlen = thee->pmgp->zlen;
6473  xmin = thee->pmgp->xmin;
6474  ymin = thee->pmgp->ymin;
6475  zmin = thee->pmgp->zmin;
6476  xmax = thee->pmgp->xmax;
6477  ymax = thee->pmgp->ymax;
6478  zmax = thee->pmgp->zmax;
6479  u = thee->u;
6480 
6481  /* Make sure we're on the grid */
6482  if ((apos[0]<=(xmin+hx)) || (apos[0]>=(xmax-hx)) \
6483  || (apos[1]<=(ymin+hy)) || (apos[1]>=(ymax-hy)) \
6484  || (apos[2]<=(zmin+hzed)) || (apos[2]>=(zmax-hzed))) {
6485  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
6486  (thee->pmgp->bcfl != BCFL_MAP)) {
6487  Vnm_print(2, "qfForceSpline2: Atom #%d off the mesh \
6488  (ignoring)\n", atomID);
6489  }
6490  fflush(stderr);
6491 
6492  } else {
6493 
6494  /* Convert the atom position to grid coordinates */
6495  position[0] = apos[0] - xmin;
6496  position[1] = apos[1] - ymin;
6497  position[2] = apos[2] - zmin;
6498  ifloat = position[0]/hx;
6499  jfloat = position[1]/hy;
6500  kfloat = position[2]/hzed;
6501  ip1 = (int)ceil(ifloat);
6502  ip2 = ip1 + 1;
6503  im1 = (int)floor(ifloat);
6504  im2 = im1 - 1;
6505  jp1 = (int)ceil(jfloat);
6506  jp2 = jp1 + 1;
6507  jm1 = (int)floor(jfloat);
6508  jm2 = jm1 - 1;
6509  kp1 = (int)ceil(kfloat);
6510  kp2 = kp1 + 1;
6511  km1 = (int)floor(kfloat);
6512  km2 = km1 - 1;
6513 
6514  /* This step shouldn't be necessary, but it saves nasty debugging
6515  * later on if something goes wrong */
6516  ip2 = VMIN2(ip2,nx-1);
6517  ip1 = VMIN2(ip1,nx-1);
6518  im1 = VMAX2(im1,0);
6519  im2 = VMAX2(im2,0);
6520  jp2 = VMIN2(jp2,ny-1);
6521  jp1 = VMIN2(jp1,ny-1);
6522  jm1 = VMAX2(jm1,0);
6523  jm2 = VMAX2(jm2,0);
6524  kp2 = VMIN2(kp2,nz-1);
6525  kp1 = VMIN2(kp1,nz-1);
6526  km1 = VMAX2(km1,0);
6527  km2 = VMAX2(km2,0);
6528 
6529 
6530  for (ii=im2; ii<=ip2; ii++) {
6531  mx = bspline2(VFCHI(ii,ifloat));
6532  dmx = dbspline2(VFCHI(ii,ifloat));
6533  for (jj=jm2; jj<=jp2; jj++) {
6534  my = bspline2(VFCHI(jj,jfloat));
6535  dmy = dbspline2(VFCHI(jj,jfloat));
6536  for (kk=km2; kk<=kp2; kk++) {
6537  mz = bspline2(VFCHI(kk,kfloat));
6538  dmz = dbspline2(VFCHI(kk,kfloat));
6539 
6540  force[0] += (charge*dmx*my*mz*u[IJK(ii,jj,kk)])/hx;
6541  force[1] += (charge*mx*dmy*mz*u[IJK(ii,jj,kk)])/hy;
6542  force[2] += (charge*mx*my*dmz*u[IJK(ii,jj,kk)])/hzed;
6543 
6544  }
6545  }
6546  }
6547 
6548  }
6549 }
6550 
6551 VPRIVATE void qfForceSpline4(Vpmg *thee, double *force, int atomID) {
6552 
6553  Vatom *atom;
6554  double f, c, *u, *apos, position[3];
6555 
6556  /* Grid variables */
6557  int nx,ny,nz;
6558  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
6559  double hx, hy, hzed, ifloat, jfloat, kfloat;
6560 
6561  /* B-spline weights */
6562  double mx, my, mz, dmx, dmy, dmz;
6563  double mi, mj, mk;
6564 
6565  /* Loop indeces */
6566  int i, j, k, ii, jj, kk;
6567  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
6568 
6569  /* field */
6570  double e[3];
6571 
6572  VASSERT(thee != VNULL);
6573  VASSERT(thee->filled);
6574 
6575  atom = Valist_getAtom(thee->pbe->alist, atomID);
6576  apos = Vatom_getPosition(atom);
6577  c = Vatom_getCharge(atom);
6578 
6579  for (i=0;i<3;i++){
6580  e[i] = 0.0;
6581  }
6582 
6583  /* Mesh info */
6584  nx = thee->pmgp->nx;
6585  ny = thee->pmgp->ny;
6586  nz = thee->pmgp->nz;
6587  hx = thee->pmgp->hx;
6588  hy = thee->pmgp->hy;
6589  hzed = thee->pmgp->hzed;
6590  xlen = thee->pmgp->xlen;
6591  ylen = thee->pmgp->ylen;
6592  zlen = thee->pmgp->zlen;
6593  xmin = thee->pmgp->xmin;
6594  ymin = thee->pmgp->ymin;
6595  zmin = thee->pmgp->zmin;
6596  xmax = thee->pmgp->xmax;
6597  ymax = thee->pmgp->ymax;
6598  zmax = thee->pmgp->zmax;
6599  u = thee->u;
6600 
6601  /* Make sure we're on the grid */
6602  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
6603  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
6604  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
6605  Vnm_print(2, "qfForceSpline4: Atom off the mesh \
6606  (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
6607  fflush(stderr);
6608  } else {
6609 
6610  /* Convert the atom position to grid coordinates */
6611  position[0] = apos[0] - xmin;
6612  position[1] = apos[1] - ymin;
6613  position[2] = apos[2] - zmin;
6614  ifloat = position[0]/hx;
6615  jfloat = position[1]/hy;
6616  kfloat = position[2]/hzed;
6617  ip1 = (int)ceil(ifloat);
6618  ip2 = ip1 + 2;
6619  im1 = (int)floor(ifloat);
6620  im2 = im1 - 2;
6621  jp1 = (int)ceil(jfloat);
6622  jp2 = jp1 + 2;
6623  jm1 = (int)floor(jfloat);
6624  jm2 = jm1 - 2;
6625  kp1 = (int)ceil(kfloat);
6626  kp2 = kp1 + 2;
6627  km1 = (int)floor(kfloat);
6628  km2 = km1 - 2;
6629 
6630  /* This step shouldn't be necessary, but it saves nasty debugging
6631  * later on if something goes wrong */
6632  ip2 = VMIN2(ip2,nx-1);
6633  ip1 = VMIN2(ip1,nx-1);
6634  im1 = VMAX2(im1,0);
6635  im2 = VMAX2(im2,0);
6636  jp2 = VMIN2(jp2,ny-1);
6637  jp1 = VMIN2(jp1,ny-1);
6638  jm1 = VMAX2(jm1,0);
6639  jm2 = VMAX2(jm2,0);
6640  kp2 = VMIN2(kp2,nz-1);
6641  kp1 = VMIN2(kp1,nz-1);
6642  km1 = VMAX2(km1,0);
6643  km2 = VMAX2(km2,0);
6644 
6645  for (ii=im2; ii<=ip2; ii++) {
6646  mi = VFCHI4(ii,ifloat);
6647  mx = bspline4(mi);
6648  dmx = dbspline4(mi);
6649  for (jj=jm2; jj<=jp2; jj++) {
6650  mj = VFCHI4(jj,jfloat);
6651  my = bspline4(mj);
6652  dmy = dbspline4(mj);
6653  for (kk=km2; kk<=kp2; kk++) {
6654  mk = VFCHI4(kk,kfloat);
6655  mz = bspline4(mk);
6656  dmz = dbspline4(mk);
6657  f = u[IJK(ii,jj,kk)];
6658  /* Field */
6659  e[0] += f*dmx*my*mz/hx;
6660  e[1] += f*mx*dmy*mz/hy;
6661  e[2] += f*mx*my*dmz/hzed;
6662  }
6663  }
6664  }
6665  }
6666 
6667  /* Monopole Force */
6668  force[0] = e[0]*c;
6669  force[1] = e[1]*c;
6670  force[2] = e[2]*c;
6671 
6672 }
6673 
6674 VPRIVATE void markFrac(
6675  double rtot, double *tpos,
6676  int nx, int ny, int nz,
6677  double hx, double hy, double hzed,
6678  double xmin, double ymin, double zmin,
6679  double *xarray, double *yarray, double *zarray) {
6680 
6681  int i, j, k, imin, imax, jmin, jmax, kmin, kmax;
6682  double dx, dx2, dy, dy2, dz, dz2, a000, a001, a010, a100, r2;
6683  double x, xp, xm, y, yp, ym, zp, z, zm, xspan, yspan, zspan;
6684  double rtot2, pos[3];
6685 
6686  /* Convert to grid reference frame */
6687  pos[0] = tpos[0] - xmin;
6688  pos[1] = tpos[1] - ymin;
6689  pos[2] = tpos[2] - zmin;
6690 
6691  rtot2 = VSQR(rtot);
6692 
6693  xspan = rtot + 2*hx;
6694  imin = VMAX2(0, (int)ceil((pos[0] - xspan)/hx));
6695  imax = VMIN2(nx-1, (int)floor((pos[0] + xspan)/hx));
6696  for (i=imin; i<=imax; i++) {
6697  x = hx*i;
6698  dx2 = VSQR(pos[0] - x);
6699  if (rtot2 > dx2) {
6700  yspan = VSQRT(rtot2 - dx2) + 2*hy;
6701  } else {
6702  yspan = 2*hy;
6703  }
6704  jmin = VMAX2(0,(int)ceil((pos[1] - yspan)/hy));
6705  jmax = VMIN2(ny-1,(int)floor((pos[1] + yspan)/hy));
6706  for (j=jmin; j<=jmax; j++) {
6707  y = hy*j;
6708  dy2 = VSQR(pos[1] - y);
6709  if (rtot2 > (dx2+dy2)) {
6710  zspan = VSQRT(rtot2-dx2-dy2) + 2*hzed;
6711  } else {
6712  zspan = 2*hzed;
6713  }
6714  kmin = VMAX2(0,(int)ceil((pos[2] - zspan)/hzed));
6715  kmax = VMIN2(nz-1,(int)floor((pos[2] + zspan)/hzed));
6716  for (k=kmin; k<=kmax; k++) {
6717  z = hzed*k;
6718  dz2 = VSQR(pos[2] - z);
6719 
6720  r2 = dx2 + dy2 + dz2;
6721 
6722  /* We need to determine the inclusion value a000 at (i,j,k) */
6723  if (r2 < rtot2) a000 = 1.0;
6724  else a000 = 0.0;
6725 
6726  /* We need to evaluate the values of x which intersect the
6727  * sphere and determine if these are in the interval
6728  * [(i,j,k), (i+1,j,k)] */
6729  if (r2 < (rtot2 - hx*hx)) a100 = 1.0;
6730  else if (r2 > (rtot2 + hx*hx)) a100 = 0.0;
6731  else if (rtot2 > (dy2 + dz2)) {
6732  dx = VSQRT(rtot2 - dy2 - dz2);
6733  xm = pos[0] - dx;
6734  xp = pos[0] + dx;
6735  if ((xm < x+hx) && (xm > x)) {
6736  a100 = (xm - x)/hx;
6737  } else if ((xp < x+hx) && (xp > x)) {
6738  a100 = (xp - x)/hx;
6739  }
6740  } else a100 = 0.0;
6741 
6742  /* We need to evaluate the values of y which intersect the
6743  * sphere and determine if these are in the interval
6744  * [(i,j,k), (i,j+1,k)] */
6745  if (r2 < (rtot2 - hy*hy)) a010 = 1.0;
6746  else if (r2 > (rtot2 + hy*hy)) a010 = 0.0;
6747  else if (rtot2 > (dx2 + dz2)) {
6748  dy = VSQRT(rtot2 - dx2 - dz2);
6749  ym = pos[1] - dy;
6750  yp = pos[1] + dy;
6751  if ((ym < y+hy) && (ym > y)) {
6752  a010 = (ym - y)/hy;
6753  } else if ((yp < y+hy) && (yp > y)) {
6754  a010 = (yp - y)/hy;
6755  }
6756  } else a010 = 0.0;
6757 
6758  /* We need to evaluate the values of y which intersect the
6759  * sphere and determine if these are in the interval
6760  * [(i,j,k), (i,j,k+1)] */
6761  if (r2 < (rtot2 - hzed*hzed)) a001 = 1.0;
6762  else if (r2 > (rtot2 + hzed*hzed)) a001 = 0.0;
6763  else if (rtot2 > (dx2 + dy2)) {
6764  dz = VSQRT(rtot2 - dx2 - dy2);
6765  zm = pos[2] - dz;
6766  zp = pos[2] + dz;
6767  if ((zm < z+hzed) && (zm > z)) {
6768  a001 = (zm - z)/hzed;
6769  } else if ((zp < z+hzed) && (zp > z)) {
6770  a001 = (zp - z)/hzed;
6771  }
6772  } else a001 = 0.0;
6773 
6774  if (a100 < xarray[IJK(i,j,k)]) xarray[IJK(i,j,k)] = a100;
6775  if (a010 < yarray[IJK(i,j,k)]) yarray[IJK(i,j,k)] = a010;
6776  if (a001 < zarray[IJK(i,j,k)]) zarray[IJK(i,j,k)] = a001;
6777 
6778  } /* k loop */
6779  } /* j loop */
6780  } /* i loop */
6781 }
6782 
6783 /*
6784 
6785  NOTE: This is the original version of the markSphere function. It's in here
6786  for reference and in case a reversion to the original code is needed.
6787  D. Gohara (2/14/08)
6788  */
6789 /*
6790 VPRIVATE void markSphere(
6791  double rtot, double *tpos,
6792  int nx, int ny, int nz,
6793  double hx, double hy, double hzed,
6794  double xmin, double ymin, double zmin,
6795  double *array, double markVal) {
6796 
6797  int i, j, k, imin, imax, jmin, jmax, kmin, kmax;
6798  double dx, dx2, dy, dy2, dz, dz2;
6799  double rtot2, pos[3];
6800 
6801  // Convert to grid reference frame
6802  pos[0] = tpos[0] - xmin;
6803  pos[1] = tpos[1] - ymin;
6804  pos[2] = tpos[2] - zmin;
6805 
6806  rtot2 = VSQR(rtot);
6807 
6808  dx = rtot + 0.5*hx;
6809  imin = VMAX2(0,(int)ceil((pos[0] - dx)/hx));
6810  imax = VMIN2(nx-1,(int)floor((pos[0] + dx)/hx));
6811  for (i=imin; i<=imax; i++) {
6812  dx2 = VSQR(pos[0] - hx*i);
6813  if (rtot2 > dx2) {
6814  dy = VSQRT(rtot2 - dx2) + 0.5*hy;
6815  } else {
6816  dy = 0.5*hy;
6817  }
6818  jmin = VMAX2(0,(int)ceil((pos[1] - dy)/hy));
6819  jmax = VMIN2(ny-1,(int)floor((pos[1] + dy)/hy));
6820  for (j=jmin; j<=jmax; j++) {
6821  dy2 = VSQR(pos[1] - hy*j);
6822  if (rtot2 > (dx2+dy2)) {
6823  dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
6824  } else {
6825  dz = 0.5*hzed;
6826  }
6827  kmin = VMAX2(0,(int)ceil((pos[2] - dz)/hzed));
6828  kmax = VMIN2(nz-1,(int)floor((pos[2] + dz)/hzed));
6829  for (k=kmin; k<=kmax; k++) {
6830  dz2 = VSQR(k*hzed - pos[2]);
6831  if ((dz2 + dy2 + dx2) <= rtot2) {
6832  array[IJK(i,j,k)] = markVal;
6833  }
6834  } // k loop
6835  } // j loop
6836  } // i loop
6837 }
6838 */
6839 VPRIVATE void markSphere(double rtot, double *tpos,
6840  int nx, int ny, int nz,
6841  double hx, double hy, double hz,
6842  double xmin, double ymin, double zmin,
6843  double *array, double markVal) {
6844 
6845  int i, j, k;
6846  double fi,fj,fk;
6847  int imin, imax;
6848  int jmin, jmax;
6849  int kmin, kmax;
6850  double dx2, dy2, dz2;
6851  double xrange, yrange, zrange;
6852  double rtot2, posx, posy, posz;
6853 
6854  /* Convert to grid reference frame */
6855  posx = tpos[0] - xmin;
6856  posy = tpos[1] - ymin;
6857  posz = tpos[2] - zmin;
6858 
6859  rtot2 = VSQR(rtot);
6860 
6861  xrange = rtot + 0.5 * hx;
6862  yrange = rtot + 0.5 * hy;
6863  zrange = rtot + 0.5 * hz;
6864 
6865  imin = VMAX2(0, (int)ceil((posx - xrange)/hx));
6866  jmin = VMAX2(0, (int)ceil((posy - yrange)/hy));
6867  kmin = VMAX2(0, (int)ceil((posz - zrange)/hz));
6868 
6869  imax = VMIN2(nx-1, (int)floor((posx + xrange)/hx));
6870  jmax = VMIN2(ny-1, (int)floor((posy + yrange)/hy));
6871  kmax = VMIN2(nz-1, (int)floor((posz + zrange)/hz));
6872 
6873  for (i=imin,fi=imin; i<=imax; i++, fi+=1.) {
6874  dx2 = VSQR(posx - hx*fi);
6875  for (j=jmin,fj=jmin; j<=jmax; j++, fj+=1.) {
6876  dy2 = VSQR(posy - hy*fj);
6877  if((dx2 + dy2) > rtot2) continue;
6878  for (k=kmin, fk=kmin; k<=kmax; k++, fk+=1.) {
6879  dz2 = VSQR(posz - hz*fk);
6880  if ((dz2 + dy2 + dx2) <= rtot2) {
6881  array[IJK(i,j,k)] = markVal;
6882  }
6883  }
6884  }
6885  }
6886 }
6887 
6888 VPRIVATE void zlapSolve(
6889  Vpmg *thee,
6890  double **solution,
6891  double **source,
6892  double **work1
6893  ) {
6894 
6895  /* NOTE: this is an incredibly inefficient algorithm. The next
6896  * improvement is to focus on only non-zero entries in the source term.
6897  * The best improvement is to use a fast sine transform */
6898 
6899  int n, nx, ny, nz, i, j, k, kx, ky, kz;
6900  double hx, hy, hzed, wx, wy, wz, xlen, ylen, zlen;
6901  double phix, phixp1, phixm1, phiy, phiym1, phiyp1, phiz, phizm1, phizp1;
6902  double norm, coef, proj, eigx, eigy, eigz;
6903  double ihx2, ihy2, ihzed2;
6904  double *u, *f, *phi;
6905 
6906  /* Snarf grid parameters */
6907  nx = thee->pmgp->nx;
6908  ny = thee->pmgp->ny;
6909  nz = thee->pmgp->nz;
6910  n = nx*ny*nz;
6911  hx = thee->pmgp->hx;
6912  ihx2 = 1.0/hx/hx;
6913  hy = thee->pmgp->hy;
6914  ihy2 = 1.0/hy/hy;
6915  hzed = thee->pmgp->hzed;
6916  ihzed2 = 1.0/hzed/hzed;
6917  xlen = thee->pmgp->xlen;
6918  ylen = thee->pmgp->ylen;
6919  zlen = thee->pmgp->zlen;
6920 
6921  /* Set solution and source array pointers */
6922  u = *solution;
6923  f = *source;
6924  phi = *work1;
6925 
6926  /* Zero out the solution vector */
6927  for (i=0; i<n; i++) thee->u[i] = 0.0;
6928 
6929  /* Iterate through the wavenumbers */
6930  for (kx=1; kx<(nx-1); kx++) {
6931 
6932  wx = (VPI*(double)kx)/((double)nx - 1.0);
6933  eigx = 2.0*ihx2*(1.0 - cos(wx));
6934 
6935  for (ky=1; ky<(ny-1); ky++) {
6936 
6937  wy = (VPI*(double)ky)/((double)ny - 1.0);
6938  eigy = 2.0*ihy2*(1.0 - cos(wy));
6939 
6940  for (kz=1; kz<(nz-1); kz++) {
6941 
6942  wz = (VPI*(double)kz)/((double)nz - 1.0);
6943  eigz = 2.0*ihzed2*(1.0 - cos(wz));
6944 
6945  /* Calculate the basis function.
6946  * We could calculate each basis function as
6947  * phix(i) = sin(wx*i)
6948  * phiy(j) = sin(wy*j)
6949  * phiz(k) = sin(wz*k)
6950  * However, this is likely to be very expensive.
6951  * Therefore, we can use the fact that
6952  * phix(i+1) = (2-hx*hx*eigx)*phix(i) - phix(i-1)
6953  * */
6954  for (i=1; i<(nx-1); i++) {
6955  if (i == 1) {
6956  phix = sin(wx*(double)i);
6957  phixm1 = 0.0;
6958  } else {
6959  phixp1 = (2.0-hx*hx*eigx)*phix - phixm1;
6960  phixm1 = phix;
6961  phix = phixp1;
6962  }
6963  /* phix = sin(wx*(double)i); */
6964  for (j=1; j<(ny-1); j++) {
6965  if (j == 1) {
6966  phiy = sin(wy*(double)j);
6967  phiym1 = 0.0;
6968  } else {
6969  phiyp1 = (2.0-hy*hy*eigy)*phiy - phiym1;
6970  phiym1 = phiy;
6971  phiy = phiyp1;
6972  }
6973  /* phiy = sin(wy*(double)j); */
6974  for (k=1; k<(nz-1); k++) {
6975  if (k == 1) {
6976  phiz = sin(wz*(double)k);
6977  phizm1 = 0.0;
6978  } else {
6979  phizp1 = (2.0-hzed*hzed*eigz)*phiz - phizm1;
6980  phizm1 = phiz;
6981  phiz = phizp1;
6982  }
6983  /* phiz = sin(wz*(double)k); */
6984 
6985  phi[IJK(i,j,k)] = phix*phiy*phiz;
6986 
6987  }
6988  }
6989  }
6990 
6991  /* Calculate the projection of the source function on this
6992  * basis function */
6993  proj = 0.0;
6994  for (i=1; i<(nx-1); i++) {
6995  for (j=1; j<(ny-1); j++) {
6996  for (k=1; k<(nz-1); k++) {
6997 
6998  proj += f[IJK(i,j,k)]*phi[IJK(i,j,k)];
6999 
7000  } /* k loop */
7001  } /* j loop */
7002  } /* i loop */
7003 
7004  /* Assemble the coefficient to weight the contribution of this
7005  * basis function to the solution */
7006  /* The first contribution is the projection */
7007  coef = proj;
7008  /* The second contribution is the eigenvalue */
7009  coef = coef/(eigx + eigy + eigz);
7010  /* The third contribution is the normalization factor */
7011  coef = (8.0/xlen/ylen/zlen)*coef;
7012  /* The fourth contribution is from scaling the diagonal */
7013  /* coef = hx*hy*hzed*coef; */
7014 
7015  /* Evaluate the basis function at each grid point */
7016  for (i=1; i<(nx-1); i++) {
7017  for (j=1; j<(ny-1); j++) {
7018  for (k=1; k<(nz-1); k++) {
7019 
7020  u[IJK(i,j,k)] += coef*phi[IJK(i,j,k)];
7021 
7022  } /* k loop */
7023  } /* j loop */
7024  } /* i loop */
7025 
7026  } /* kz loop */
7027  } /* ky loop */
7028  } /* kx loop */
7029 
7030 }
7031 
7032 VPUBLIC int Vpmg_solveLaplace(Vpmg *thee) {
7033 
7034  int i, j, k, ijk, nx, ny, nz, n, dilo, dihi, djlo, djhi, dklo, dkhi;
7035  double hx, hy, hzed, epsw, iepsw, scal, scalx, scaly, scalz;
7036 
7037  nx = thee->pmgp->nx;
7038  ny = thee->pmgp->ny;
7039  nz = thee->pmgp->nz;
7040  n = nx*ny*nz;
7041  hx = thee->pmgp->hx;
7042  hy = thee->pmgp->hy;
7043  hzed = thee->pmgp->hzed;
7044  epsw = Vpbe_getSolventDiel(thee->pbe);
7045  iepsw = 1.0/epsw;
7046  scal = hx*hy*hzed;
7047  scalx = hx*hy/hzed;
7048  scaly = hx*hzed/hy;
7049  scalz = hx*hy/hzed;
7050 
7051  if (!(thee->filled)) {
7052  Vnm_print(2, "Vpmg_solve: Need to call Vpmg_fillco()!\n");
7053  return 0;
7054  }
7055 
7056  /* Load boundary conditions into the RHS array */
7057  for (i=1; i<(nx-1); i++) {
7058 
7059  if (i == 1) dilo = 1;
7060  else dilo = 0;
7061  if (i == nx-2) dihi = 1;
7062  else dihi = 0;
7063 
7064  for (j=1; j<(ny-1); j++) {
7065 
7066  if (j == 1) djlo = 1;
7067  else djlo = 0;
7068  if (j == ny-2) djhi = 1;
7069  else djhi = 0;
7070 
7071  for (k=1; k<(nz-1); k++) {
7072 
7073  if (k == 1) dklo = 1;
7074  else dklo = 0;
7075  if (k == nz-2) dkhi = 1;
7076  else dkhi = 0;
7077 
7079  thee->fcf[IJK(i,j,k)] = \
7080  iepsw*scal*thee->charge[IJK(i,j,k)] \
7081  + dilo*scalx*thee->gxcf[IJKx(j,k,0)] \
7082  + dihi*scalx*thee->gxcf[IJKx(j,k,1)] \
7083  + djlo*scaly*thee->gycf[IJKy(i,k,0)] \
7084  + djhi*scaly*thee->gycf[IJKy(i,k,1)] \
7085  + dklo*scalz*thee->gzcf[IJKz(i,j,0)] \
7086  + dkhi*scalz*thee->gzcf[IJKz(i,j,1)] ;
7087 
7088  }
7089  }
7090  }
7091 
7092  /* Solve */
7093  zlapSolve( thee, &(thee->u), &(thee->fcf), &(thee->tcf) );
7094 
7095  /* Add boundary conditions to solution */
7096  /* i faces */
7097  for (j=0; j<ny; j++) {
7098  for (k=0; k<nz; k++) {
7099  thee->u[IJK(0,j,k)] = thee->gxcf[IJKx(j,k,0)];
7100  thee->u[IJK(nx-1,j,k)] = thee->gycf[IJKx(j,k,1)];
7101  }
7102  }
7103  /* j faces */
7104  for (i=0; i<nx; i++) {
7105  for (k=0; k<nz; k++) {
7106  thee->u[IJK(i,0,k)] = thee->gycf[IJKy(i,k,0)];
7107  thee->u[IJK(i,ny-1,k)] = thee->gycf[IJKy(i,k,1)];
7108  }
7109  }
7110  /* k faces */
7111  for (i=0; i<nx; i++) {
7112  for (j=0; j<ny; j++) {
7113  thee->u[IJK(i,j,0)] = thee->gzcf[IJKz(i,j,0)];
7114  thee->u[IJK(i,j,nz-1)] = thee->gzcf[IJKz(i,j,1)];
7115  }
7116  }
7117 
7118  return 1;
7119 
7120 }
7121 
7122 VPRIVATE double VFCHI4(int i, double f) {
7123  return (2.5+((double)(i)-(f)));
7124 }
7125 
7126 VPRIVATE double bspline4(double x) {
7127 
7128  double m, m2;
7129  static double one6 = 1.0/6.0;
7130  static double one8 = 1.0/8.0;
7131  static double one24 = 1.0/24.0;
7132  static double thirteen24 = 13.0/24.0;
7133  static double fourtyseven24 = 47.0/24.0;
7134  static double seventeen24 = 17.0/24.0;
7135 
7136  if ((x > 0.0) && (x <= 1.0)){
7137  m = x*x;
7138  return one24*m*m;
7139  } else if ((x > 1.0) && (x <= 2.0)){
7140  m = x - 1.0;
7141  m2 = m*m;
7142  return -one8 + one6*x + m2*(0.25 + one6*m - one6*m2);
7143  } else if ((x > 2.0) && (x <= 3.0)){
7144  m = x - 2.0;
7145  m2 = m*m;
7146  return -thirteen24 + 0.5*x + m2*(-0.25 - 0.5*m + 0.25*m2);
7147  } else if ((x > 3.0) && (x <= 4.0)){
7148  m = x - 3.0;
7149  m2 = m*m;
7150  return fourtyseven24 - 0.5*x + m2*(-0.25 + 0.5*m - one6*m2);
7151  } else if ((x > 4.0) && (x <= 5.0)){
7152  m = x - 4.0;
7153  m2 = m*m;
7154  return seventeen24 - one6*x + m2*(0.25 - one6*m + one24*m2);
7155  } else {
7156  return 0.0;
7157  }
7158 }
7159 
7160 VPUBLIC double dbspline4(double x) {
7161 
7162  double m, m2;
7163  static double one6 = 1.0/6.0;
7164  static double one3 = 1.0/3.0;
7165  static double two3 = 2.0/3.0;
7166  static double thirteen6 = 13.0/6.0;
7167 
7168  if ((x > 0.0) && (x <= 1.0)){
7169  m2 = x*x;
7170  return one6*x*m2;
7171  } else if ((x > 1.0) && (x <= 2.0)){
7172  m = x - 1.0;
7173  m2 = m*m;
7174  return -one3 + 0.5*x + m2*(0.5 - two3*m);
7175  } else if ((x > 2.0) && (x <= 3.0)){
7176  m = x - 2.0;
7177  m2 = m*m;
7178  return 1.5 - 0.5*x + m2*(-1.5 + m);
7179  } else if ((x > 3.0) && (x <= 4.0)){
7180  m = x - 3.0;
7181  m2 = m*m;
7182  return 1.0 - 0.5*x + m2*(1.5 - two3*m);
7183  } else if ((x > 4.0) && (x <= 5.0)){
7184  m = x - 4.0;
7185  m2 = m*m;
7186  return -thirteen6 + 0.5*x + m2*(-0.5 + one6*m);
7187  } else {
7188  return 0.0;
7189  }
7190 }
7191 
7192 VPUBLIC double d2bspline4(double x) {
7193 
7194  double m, m2;
7195 
7196  if ((x > 0.0) && (x <= 1.0)){
7197  return 0.5*x*x;
7198  } else if ((x > 1.0) && (x <= 2.0)){
7199  m = x - 1.0;
7200  m2 = m*m;
7201  return -0.5 + x - 2.0*m2;
7202  } else if ((x > 2.0) && (x <= 3.0)){
7203  m = x - 2.0;
7204  m2 = m*m;
7205  return 5.5 - 3.0*x + 3.0*m2;
7206  } else if ((x > 3.0) && (x <= 4.0)){
7207  m = x - 3.0;
7208  m2 = m*m;
7209  return -9.5 + 3.0*x - 2.0*m2;
7210  } else if ((x > 4.0) && (x <= 5.0)){
7211  m = x - 4.0;
7212  m2 = m*m;
7213  return 4.5 - x + 0.5*m2;
7214  } else {
7215  return 0.0;
7216  }
7217 }
7218 
7219 VPUBLIC double d3bspline4(double x) {
7220 
7221  if ((x > 0.0) && (x <= 1.0)) return x;
7222  else if ((x > 1.0) && (x <= 2.0)) return 5.0 - 4.0 * x;
7223  else if ((x > 2.0) && (x <= 3.0)) return -15.0 + 6.0 * x;
7224  else if ((x > 3.0) && (x <= 4.0)) return 15.0 - 4.0 * x;
7225  else if ((x > 4.0) && (x <= 5.0)) return x - 5.0;
7226  else return 0.0;
7227 
7228 }
7229 
7230 VPUBLIC void fillcoPermanentMultipole(Vpmg *thee) {
7231 
7232  Valist *alist;
7233  Vpbe *pbe;
7234  Vatom *atom;
7235  /* Coversions */
7236  double zmagic, f;
7237  /* Grid */
7238  double xmin, xmax, ymin, ymax, zmin, zmax;
7239  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
7240  double hx, hy, hzed, *apos;
7241  /* Multipole */
7242  double charge, *dipole,*quad;
7243  double c,ux,uy,uz,qxx,qyx,qyy,qzx,qzy,qzz,qave;
7244  /* B-spline weights */
7245  double mx,my,mz,dmx,dmy,dmz,d2mx,d2my,d2mz;
7246  double mi,mj,mk;
7247  /* Loop variables */
7248  int i, ii, jj, kk, nx, ny, nz, iatom;
7249  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
7250 
7251  /* sanity check */
7252  double mir,mjr,mkr,mr2;
7253  double debye,mc,mux,muy,muz,mqxx,mqyx,mqyy,mqzx,mqzy,mqzz;
7254 
7255  VASSERT(thee != VNULL);
7256 
7257  /* Get PBE info */
7258  pbe = thee->pbe;
7259  alist = pbe->alist;
7260  zmagic = Vpbe_getZmagic(pbe);
7261 
7262  /* Mesh info */
7263  nx = thee->pmgp->nx;
7264  ny = thee->pmgp->ny;
7265  nz = thee->pmgp->nz;
7266  hx = thee->pmgp->hx;
7267  hy = thee->pmgp->hy;
7268  hzed = thee->pmgp->hzed;
7269 
7270  /* Conversion */
7271  f = zmagic/(hx*hy*hzed);
7272 
7273  /* Define the total domain size */
7274  xlen = thee->pmgp->xlen;
7275  ylen = thee->pmgp->ylen;
7276  zlen = thee->pmgp->zlen;
7277 
7278  /* Define the min/max dimensions */
7279  xmin = thee->pmgp->xcent - (xlen/2.0);
7280  ymin = thee->pmgp->ycent - (ylen/2.0);
7281  zmin = thee->pmgp->zcent - (zlen/2.0);
7282  xmax = thee->pmgp->xcent + (xlen/2.0);
7283  ymax = thee->pmgp->ycent + (ylen/2.0);
7284  zmax = thee->pmgp->zcent + (zlen/2.0);
7285 
7286  /* Fill in the source term (permanent atomic multipoles) */
7287  Vnm_print(0, "fillcoPermanentMultipole: filling in source term.\n");
7288  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
7289 
7290  atom = Valist_getAtom(alist, iatom);
7291  apos = Vatom_getPosition(atom);
7292 
7293  c = Vatom_getCharge(atom)*f;
7294 
7295 #if defined(WITH_TINKER)
7296  dipole = Vatom_getDipole(atom);
7297  ux = dipole[0]/hx*f;
7298  uy = dipole[1]/hy*f;
7299  uz = dipole[2]/hzed*f;
7300  quad = Vatom_getQuadrupole(atom);
7301  qxx = (1.0/3.0)*quad[0]/(hx*hx)*f;
7302  qyx = (2.0/3.0)*quad[3]/(hx*hy)*f;
7303  qyy = (1.0/3.0)*quad[4]/(hy*hy)*f;
7304  qzx = (2.0/3.0)*quad[6]/(hzed*hx)*f;
7305  qzy = (2.0/3.0)*quad[7]/(hzed*hy)*f;
7306  qzz = (1.0/3.0)*quad[8]/(hzed*hzed)*f;
7307 #else
7308  ux = 0.0;
7309  uy = 0.0;
7310  uz = 0.0;
7311  qxx = 0.0;
7312  qyx = 0.0;
7313  qyy = 0.0;
7314  qzx = 0.0;
7315  qzy = 0.0;
7316  qzz = 0.0;
7317 #endif /* if defined(WITH_TINKER) */
7318 
7319  /* check
7320  mc = 0.0;
7321  mux = 0.0;
7322  muy = 0.0;
7323  muz = 0.0;
7324  mqxx = 0.0;
7325  mqyx = 0.0;
7326  mqyy = 0.0;
7327  mqzx = 0.0;
7328  mqzy = 0.0;
7329  mqzz = 0.0; */
7330 
7331  /* Make sure we're on the grid */
7332  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
7333  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
7334  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
7335  Vnm_print(2, "fillcoPermanentMultipole: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring this atom):\n", iatom, apos[0], apos[1], apos[2]);
7336  Vnm_print(2, "fillcoPermanentMultipole: xmin = %g, xmax = %g\n", xmin, xmax);
7337  Vnm_print(2, "fillcoPermanentMultipole: ymin = %g, ymax = %g\n", ymin, ymax);
7338  Vnm_print(2, "fillcoPermanentMultipole: zmin = %g, zmax = %g\n", zmin, zmax);
7339  fflush(stderr);
7340  } else {
7341 
7342  /* Convert the atom position to grid reference frame */
7343  position[0] = apos[0] - xmin;
7344  position[1] = apos[1] - ymin;
7345  position[2] = apos[2] - zmin;
7346 
7347  /* Figure out which vertices we're next to */
7348  ifloat = position[0]/hx;
7349  jfloat = position[1]/hy;
7350  kfloat = position[2]/hzed;
7351 
7352  ip1 = (int)ceil(ifloat);
7353  ip2 = ip1 + 2;
7354  im1 = (int)floor(ifloat);
7355  im2 = im1 - 2;
7356  jp1 = (int)ceil(jfloat);
7357  jp2 = jp1 + 2;
7358  jm1 = (int)floor(jfloat);
7359  jm2 = jm1 - 2;
7360  kp1 = (int)ceil(kfloat);
7361  kp2 = kp1 + 2;
7362  km1 = (int)floor(kfloat);
7363  km2 = km1 - 2;
7364 
7365  /* This step shouldn't be necessary, but it saves nasty debugging
7366  * later on if something goes wrong */
7367  ip2 = VMIN2(ip2,nx-1);
7368  ip1 = VMIN2(ip1,nx-1);
7369  im1 = VMAX2(im1,0);
7370  im2 = VMAX2(im2,0);
7371  jp2 = VMIN2(jp2,ny-1);
7372  jp1 = VMIN2(jp1,ny-1);
7373  jm1 = VMAX2(jm1,0);
7374  jm2 = VMAX2(jm2,0);
7375  kp2 = VMIN2(kp2,nz-1);
7376  kp1 = VMIN2(kp1,nz-1);
7377  km1 = VMAX2(km1,0);
7378  km2 = VMAX2(km2,0);
7379 
7380  /* Now assign fractions of the charge to the nearby verts */
7381  for (ii=im2; ii<=ip2; ii++) {
7382  mi = VFCHI4(ii,ifloat);
7383  mx = bspline4(mi);
7384  dmx = dbspline4(mi);
7385  d2mx = d2bspline4(mi);
7386  for (jj=jm2; jj<=jp2; jj++) {
7387  mj = VFCHI4(jj,jfloat);
7388  my = bspline4(mj);
7389  dmy = dbspline4(mj);
7390  d2my = d2bspline4(mj);
7391  for (kk=km2; kk<=kp2; kk++) {
7392  mk = VFCHI4(kk,kfloat);
7393  mz = bspline4(mk);
7394  dmz = dbspline4(mk);
7395  d2mz = d2bspline4(mk);
7396  charge = mx*my*mz*c -
7397  dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz +
7398  d2mx*my*mz*qxx +
7399  dmx*dmy*mz*qyx + mx*d2my*mz*qyy +
7400  dmx*my*dmz*qzx + mx*dmy*dmz*qzy + mx*my*d2mz*qzz;
7401  thee->charge[IJK(ii,jj,kk)] += charge;
7402 
7403  /* sanity check - recalculate traceless multipoles
7404  from the grid charge distribution for this
7405  site.
7406 
7407  mir = (mi - 2.5) * hx;
7408  mjr = (mj - 2.5) * hy;
7409  mkr = (mk - 2.5) * hzed;
7410  mr2 = mir*mir+mjr*mjr+mkr*mkr;
7411  mc += charge;
7412  mux += mir * charge;
7413  muy += mjr * charge;
7414  muz += mkr * charge;
7415  mqxx += (1.5*mir*mir - 0.5*mr2) * charge;
7416  mqyx += 1.5*mjr*mir * charge;
7417  mqyy += (1.5*mjr*mjr - 0.5*mr2) * charge;
7418  mqzx += 1.5*mkr*mir * charge;
7419  mqzy += 1.5*mkr*mjr * charge;
7420  mqzz += (1.5*mkr*mkr - 0.5*mr2) * charge;
7421  */
7422  }
7423  }
7424  }
7425  } /* endif (on the mesh) */
7426 
7427  /* print out the Grid vs. Ideal Point Multipole. */
7428 
7429  /*
7430  debye = 4.8033324;
7431  mc = mc/f;
7432  mux = mux/f*debye;
7433  muy = muy/f*debye;
7434  muz = muz/f*debye;
7435  mqxx = mqxx/f*debye;
7436  mqyy = mqyy/f*debye;
7437  mqzz = mqzz/f*debye;
7438  mqyx = mqyx/f*debye;
7439  mqzx = mqzx/f*debye;
7440  mqzy = mqzy/f*debye;
7441 
7442  printf(" Grid v. Actual Permanent Multipole for Site %i\n",iatom);
7443  printf(" G: %10.6f\n",mc);
7444  printf(" A: %10.6f\n\n",c/f);
7445  printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
7446  printf(" A: %10.6f %10.6f %10.6f\n\n",
7447  (ux * hx / f) * debye,
7448  (uy * hy / f) * debye,
7449  (uz * hzed /f) * debye);
7450  printf(" G: %10.6f\n",mqxx);
7451  printf(" A: %10.6f\n",quad[0]*debye);
7452  printf(" G: %10.6f %10.6f\n",mqyx,mqyy);
7453  printf(" A: %10.6f %10.6f\n",quad[3]*debye,quad[4]*debye);
7454  printf(" G: %10.6f %10.6f %10.6f\n",mqzx,mqzy,mqzz);
7455  printf(" A: %10.6f %10.6f %10.6f\n\n",
7456  quad[6]*debye,quad[7]*debye,quad[8]*debye); */
7457 
7458  } /* endfor (each atom) */
7459 }
7460 
7461 #if defined(WITH_TINKER)
7462 
7463 VPUBLIC void fillcoInducedDipole(Vpmg *thee) {
7464 
7465  Valist *alist;
7466  Vpbe *pbe;
7467  Vatom *atom;
7468  /* Conversions */
7469  double zmagic, f;
7470  /* Grid */
7471  double xmin, xmax, ymin, ymax, zmin, zmax;
7472  double xlen, ylen, zlen, ifloat, jfloat, kfloat;
7473  double hx, hy, hzed, *apos, position[3];
7474  /* B-spline weights */
7475  double mx, my, mz, dmx, dmy, dmz;
7476  /* Dipole */
7477  double charge, *dipole, ux,uy,uz;
7478  double mi,mj,mk;
7479  /* Loop indeces */
7480  int i, ii, jj, kk, nx, ny, nz, iatom;
7481  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
7482 
7483  double debye;
7484  double mux,muy,muz;
7485  double mir,mjr,mkr;
7486 
7487  VASSERT(thee != VNULL);
7488 
7489  /* Get PBE info */
7490  pbe = thee->pbe;
7491  alist = pbe->alist;
7492  zmagic = Vpbe_getZmagic(pbe);
7493 
7494  /* Mesh info */
7495  nx = thee->pmgp->nx;
7496  ny = thee->pmgp->ny;
7497  nz = thee->pmgp->nz;
7498  hx = thee->pmgp->hx;
7499  hy = thee->pmgp->hy;
7500  hzed = thee->pmgp->hzed;
7501 
7502  /* Conversion */
7503  f = zmagic/(hx*hy*hzed);
7504 
7505  /* Define the total domain size */
7506  xlen = thee->pmgp->xlen;
7507  ylen = thee->pmgp->ylen;
7508  zlen = thee->pmgp->zlen;
7509 
7510  /* Define the min/max dimensions */
7511  xmin = thee->pmgp->xcent - (xlen/2.0);
7512  ymin = thee->pmgp->ycent - (ylen/2.0);
7513  zmin = thee->pmgp->zcent - (zlen/2.0);
7514  xmax = thee->pmgp->xcent + (xlen/2.0);
7515  ymax = thee->pmgp->ycent + (ylen/2.0);
7516  zmax = thee->pmgp->zcent + (zlen/2.0);
7517 
7518  /* Fill in the source term (induced dipoles) */
7519  Vnm_print(0, "fillcoInducedDipole: filling in the source term.\n");
7520  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
7521 
7522  atom = Valist_getAtom(alist, iatom);
7523  apos = Vatom_getPosition(atom);
7524 
7525  dipole = Vatom_getInducedDipole(atom);
7526  ux = dipole[0]/hx*f;
7527  uy = dipole[1]/hy*f;
7528  uz = dipole[2]/hzed*f;
7529 
7530  mux = 0.0;
7531  muy = 0.0;
7532  muz = 0.0;
7533 
7534  /* Make sure we're on the grid */
7535  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
7536  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
7537  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
7538  Vnm_print(2, "fillcoInducedDipole: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring this atom):\n", iatom, apos[0], apos[1], apos[2]);
7539  Vnm_print(2, "fillcoInducedDipole: xmin = %g, xmax = %g\n", xmin, xmax);
7540  Vnm_print(2, "fillcoInducedDipole: ymin = %g, ymax = %g\n", ymin, ymax);
7541  Vnm_print(2, "fillcoInducedDipole: zmin = %g, zmax = %g\n", zmin, zmax);
7542  fflush(stderr);
7543  } else {
7544 
7545  /* Convert the atom position to grid reference frame */
7546  position[0] = apos[0] - xmin;
7547  position[1] = apos[1] - ymin;
7548  position[2] = apos[2] - zmin;
7549 
7550  /* Figure out which vertices we're next to */
7551  ifloat = position[0]/hx;
7552  jfloat = position[1]/hy;
7553  kfloat = position[2]/hzed;
7554 
7555  ip1 = (int)ceil(ifloat);
7556  ip2 = ip1 + 2;
7557  im1 = (int)floor(ifloat);
7558  im2 = im1 - 2;
7559  jp1 = (int)ceil(jfloat);
7560  jp2 = jp1 + 2;
7561  jm1 = (int)floor(jfloat);
7562  jm2 = jm1 - 2;
7563  kp1 = (int)ceil(kfloat);
7564  kp2 = kp1 + 2;
7565  km1 = (int)floor(kfloat);
7566  km2 = km1 - 2;
7567 
7568  /* This step shouldn't be necessary, but it saves nasty debugging
7569  * later on if something goes wrong */
7570  ip2 = VMIN2(ip2,nx-1);
7571  ip1 = VMIN2(ip1,nx-1);
7572  im1 = VMAX2(im1,0);
7573  im2 = VMAX2(im2,0);
7574  jp2 = VMIN2(jp2,ny-1);
7575  jp1 = VMIN2(jp1,ny-1);
7576  jm1 = VMAX2(jm1,0);
7577  jm2 = VMAX2(jm2,0);
7578  kp2 = VMIN2(kp2,nz-1);
7579  kp1 = VMIN2(kp1,nz-1);
7580  km1 = VMAX2(km1,0);
7581  km2 = VMAX2(km2,0);
7582 
7583  /* Now assign fractions of the dipole to the nearby verts */
7584  for (ii=im2; ii<=ip2; ii++) {
7585  mi = VFCHI4(ii,ifloat);
7586  mx = bspline4(mi);
7587  dmx = dbspline4(mi);
7588  for (jj=jm2; jj<=jp2; jj++) {
7589  mj = VFCHI4(jj,jfloat);
7590  my = bspline4(mj);
7591  dmy = dbspline4(mj);
7592  for (kk=km2; kk<=kp2; kk++) {
7593  mk = VFCHI4(kk,kfloat);
7594  mz = bspline4(mk);
7595  dmz = dbspline4(mk);
7596  charge = -dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz;
7597  thee->charge[IJK(ii,jj,kk)] += charge;
7598 
7599  /*
7600  mir = (mi - 2.5) * hx;
7601  mjr = (mj - 2.5) * hy;
7602  mkr = (mk - 2.5) * hzed;
7603  mux += mir * charge;
7604  muy += mjr * charge;
7605  muz += mkr * charge;
7606  */
7607  }
7608  }
7609  }
7610  } /* endif (on the mesh) */
7611 
7612  /* check
7613  debye = 4.8033324;
7614  mux = mux/f*debye;
7615  muy = muy/f*debye;
7616  muz = muz/f*debye;
7617 
7618  printf(" Grid v. Actual Induced Dipole for Site %i\n",iatom);
7619  printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
7620  printf(" A: %10.6f %10.6f %10.6f\n\n",
7621  (ux * hx / f) * debye,
7622  (uy * hy / f) * debye,
7623  (uz * hzed /f) * debye);
7624  */
7625 
7626  } /* endfor (each atom) */
7627 }
7628 
7629 VPUBLIC void fillcoNLInducedDipole(Vpmg *thee) {
7630 
7631  Valist *alist;
7632  Vpbe *pbe;
7633  Vatom *atom;
7634  /* Conversions */
7635  double zmagic, f;
7636  /* Grid */
7637  double xmin, xmax, ymin, ymax, zmin, zmax;
7638  double xlen, ylen, zlen, ifloat, jfloat, kfloat;
7639  double hx, hy, hzed, *apos, position[3];
7640  /* B-spline weights */
7641  double mx, my, mz, dmx, dmy, dmz;
7642  /* Dipole */
7643  double charge, *dipole, ux,uy,uz;
7644  double mi,mj,mk;
7645  /* Loop indeces */
7646  int i, ii, jj, kk, nx, ny, nz, iatom;
7647  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
7648 
7649  /* sanity check
7650  double debye;
7651  double mux,muy,muz;
7652  double mir,mjr,mkr;
7653  */
7654 
7655  VASSERT(thee != VNULL);
7656 
7657  /* Get PBE info */
7658  pbe = thee->pbe;
7659  alist = pbe->alist;
7660  zmagic = Vpbe_getZmagic(pbe);
7661 
7662  /* Mesh info */
7663  nx = thee->pmgp->nx;
7664  ny = thee->pmgp->ny;
7665  nz = thee->pmgp->nz;
7666  hx = thee->pmgp->hx;
7667  hy = thee->pmgp->hy;
7668  hzed = thee->pmgp->hzed;
7669 
7670  /* Conversion */
7671  f = zmagic/(hx*hy*hzed);
7672 
7673  /* Define the total domain size */
7674  xlen = thee->pmgp->xlen;
7675  ylen = thee->pmgp->ylen;
7676  zlen = thee->pmgp->zlen;
7677 
7678  /* Define the min/max dimensions */
7679  xmin = thee->pmgp->xcent - (xlen/2.0);
7680  ymin = thee->pmgp->ycent - (ylen/2.0);
7681  zmin = thee->pmgp->zcent - (zlen/2.0);
7682  xmax = thee->pmgp->xcent + (xlen/2.0);
7683  ymax = thee->pmgp->ycent + (ylen/2.0);
7684  zmax = thee->pmgp->zcent + (zlen/2.0);
7685 
7686  /* Fill in the source term (non-local induced dipoles) */
7687  Vnm_print(0, "fillcoNLInducedDipole: filling in source term.\n");
7688  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
7689 
7690  atom = Valist_getAtom(alist, iatom);
7691  apos = Vatom_getPosition(atom);
7692 
7693  dipole = Vatom_getNLInducedDipole(atom);
7694  ux = dipole[0]/hx*f;
7695  uy = dipole[1]/hy*f;
7696  uz = dipole[2]/hzed*f;
7697 
7698  /*
7699  mux = 0.0;
7700  muy = 0.0;
7701  muz = 0.0;
7702  */
7703 
7704  /* Make sure we're on the grid */
7705  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
7706  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
7707  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
7708  Vnm_print(2, "fillcoNLInducedDipole: Atom #%d at (%4.3f, %4.3f,%4.3f) is off the mesh (ignoring this atom):\n", iatom, apos[0], apos[1], apos[2]);
7709  Vnm_print(2, "fillcoNLInducedDipole: xmin = %g, xmax = %g\n", xmin, xmax);
7710  Vnm_print(2, "fillcoNLInducedDipole: ymin = %g, ymax = %g\n", ymin, ymax);
7711  Vnm_print(2, "fillcoNLInducedDipole: zmin = %g, zmax = %g\n", zmin, zmax);
7712  fflush(stderr);
7713  } else {
7714 
7715  /* Convert the atom position to grid reference frame */
7716  position[0] = apos[0] - xmin;
7717  position[1] = apos[1] - ymin;
7718  position[2] = apos[2] - zmin;
7719 
7720  /* Figure out which vertices we're next to */
7721  ifloat = position[0]/hx;
7722  jfloat = position[1]/hy;
7723  kfloat = position[2]/hzed;
7724 
7725  ip1 = (int)ceil(ifloat);
7726  ip2 = ip1 + 2;
7727  im1 = (int)floor(ifloat);
7728  im2 = im1 - 2;
7729  jp1 = (int)ceil(jfloat);
7730  jp2 = jp1 + 2;
7731  jm1 = (int)floor(jfloat);
7732  jm2 = jm1 - 2;
7733  kp1 = (int)ceil(kfloat);
7734  kp2 = kp1 + 2;
7735  km1 = (int)floor(kfloat);
7736  km2 = km1 - 2;
7737 
7738  /* This step shouldn't be necessary, but it saves nasty debugging
7739  * later on if something goes wrong */
7740  ip2 = VMIN2(ip2,nx-1);
7741  ip1 = VMIN2(ip1,nx-1);
7742  im1 = VMAX2(im1,0);
7743  im2 = VMAX2(im2,0);
7744  jp2 = VMIN2(jp2,ny-1);
7745  jp1 = VMIN2(jp1,ny-1);
7746  jm1 = VMAX2(jm1,0);
7747  jm2 = VMAX2(jm2,0);
7748  kp2 = VMIN2(kp2,nz-1);
7749  kp1 = VMIN2(kp1,nz-1);
7750  km1 = VMAX2(km1,0);
7751  km2 = VMAX2(km2,0);
7752 
7753  /* Now assign fractions of the non local induced dipole
7754  to the nearby verts */
7755  for (ii=im2; ii<=ip2; ii++) {
7756  mi = VFCHI4(ii,ifloat);
7757  mx = bspline4(mi);
7758  dmx = dbspline4(mi);
7759  for (jj=jm2; jj<=jp2; jj++) {
7760  mj = VFCHI4(jj,jfloat);
7761  my = bspline4(mj);
7762  dmy = dbspline4(mj);
7763  for (kk=km2; kk<=kp2; kk++) {
7764  mk = VFCHI4(kk,kfloat);
7765  mz = bspline4(mk);
7766  dmz = dbspline4(mk);
7767  charge = -dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz;
7768  thee->charge[IJK(ii,jj,kk)] += charge;
7769 
7770  /*
7771  mir = (mi - 2.5) * hx;
7772  mjr = (mj - 2.5) * hy;
7773  mkr = (mk - 2.5) * hzed;
7774  mux += mir * charge;
7775  muy += mjr * charge;
7776  muz += mkr * charge;
7777  */
7778  }
7779  }
7780  }
7781  } /* endif (on the mesh) */
7782 
7783  /*
7784  debye = 4.8033324;
7785  mux = mux/f*debye;
7786  muy = muy/f*debye;
7787  muz = muz/f*debye;
7788 
7789  printf(" Grid v. Actual Non-Local Induced Dipole for Site %i\n",iatom);
7790  printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
7791  printf(" A: %10.6f %10.6f %10.6f\n\n",
7792  (ux * hx / f) * debye,
7793  (uy * hy / f) * debye,
7794  (uz * hzed /f) * debye); */
7795 
7796  } /* endfor (each atom) */
7797 }
7798 
7799 VPUBLIC double Vpmg_qfPermanentMultipoleEnergy(Vpmg *thee, int atomID) {
7800 
7801  double *u;
7802  Vatom *atom;
7803  /* Grid variables */
7804  int nx, ny, nz;
7805  double xmax, xmin, ymax, ymin, zmax, zmin;
7806  double hx, hy, hzed, ifloat, jfloat, kfloat;
7807  double mi, mj, mk;
7808  double *position;
7809  /* B-spline weights */
7810  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz;
7811  /* Loop indeces */
7812  int ip1,ip2,im1,im2,jp1,jp2,jm1,jm2,kp1,kp2,km1,km2;
7813  int i,j,ii,jj,kk;
7814  /* Potential, field, field gradient and multipole components */
7815  double pot, rfe[3], rfde[3][3], energy;
7816  double f, charge, *dipole, *quad;
7817  double qxx, qyx, qyy, qzx, qzy, qzz;
7818 
7819 
7820  VASSERT(thee != VNULL);
7821  VASSERT(thee->filled);
7822 
7823  /* Get the mesh information */
7824  nx = thee->pmgp->nx;
7825  ny = thee->pmgp->ny;
7826  nz = thee->pmgp->nz;
7827  hx = thee->pmgp->hx;
7828  hy = thee->pmgp->hy;
7829  hzed = thee->pmgp->hzed;
7830  xmax = thee->xf[nx-1];
7831  ymax = thee->yf[ny-1];
7832  zmax = thee->zf[nz-1];
7833  xmin = thee->xf[0];
7834  ymin = thee->yf[0];
7835  zmin = thee->zf[0];
7836 
7837  u = thee->u;
7838 
7839  atom = Valist_getAtom(thee->pbe->alist, atomID);
7840 
7841  /* Currently all atoms must be in the same partition. */
7842 
7843  VASSERT(atom->partID != 0);
7844 
7845  /* Convert the atom position to grid coordinates */
7846 
7847  position = Vatom_getPosition(atom);
7848  ifloat = (position[0] - xmin)/hx;
7849  jfloat = (position[1] - ymin)/hy;
7850  kfloat = (position[2] - zmin)/hzed;
7851  ip1 = (int)ceil(ifloat);
7852  ip2 = ip1 + 2;
7853  im1 = (int)floor(ifloat);
7854  im2 = im1 - 2;
7855  jp1 = (int)ceil(jfloat);
7856  jp2 = jp1 + 2;
7857  jm1 = (int)floor(jfloat);
7858  jm2 = jm1 - 2;
7859  kp1 = (int)ceil(kfloat);
7860  kp2 = kp1 + 2;
7861  km1 = (int)floor(kfloat);
7862  km2 = km1 - 2;
7863 
7864  /* This step shouldn't be necessary, but it saves nasty debugging
7865  * later on if something goes wrong */
7866  ip2 = VMIN2(ip2,nx-1);
7867  ip1 = VMIN2(ip1,nx-1);
7868  im1 = VMAX2(im1,0);
7869  im2 = VMAX2(im2,0);
7870  jp2 = VMIN2(jp2,ny-1);
7871  jp1 = VMIN2(jp1,ny-1);
7872  jm1 = VMAX2(jm1,0);
7873  jm2 = VMAX2(jm2,0);
7874  kp2 = VMIN2(kp2,nz-1);
7875  kp1 = VMIN2(kp1,nz-1);
7876  km1 = VMAX2(km1,0);
7877  km2 = VMAX2(km2,0);
7878 
7879  /* Initialize observables to zero */
7880  energy = 0.0;
7881  pot = 0.0;
7882  for (i=0;i<3;i++){
7883  rfe[i] = 0.0;
7884  for (j=0;j<3;j++){
7885  rfde[i][j] = 0.0;
7886  }
7887  }
7888 
7889  for (ii=im2; ii<=ip2; ii++) {
7890  mi = VFCHI4(ii,ifloat);
7891  mx = bspline4(mi);
7892  dmx = dbspline4(mi);
7893  d2mx = d2bspline4(mi);
7894  for (jj=jm2; jj<=jp2; jj++) {
7895  mj = VFCHI4(jj,jfloat);
7896  my = bspline4(mj);
7897  dmy = dbspline4(mj);
7898  d2my = d2bspline4(mj);
7899  for (kk=km2; kk<=kp2; kk++) {
7900  mk = VFCHI4(kk,kfloat);
7901  mz = bspline4(mk);
7902  dmz = dbspline4(mk);
7903  d2mz = d2bspline4(mk);
7904  f = u[IJK(ii,jj,kk)];
7905  /* potential */
7906  pot += f*mx*my*mz;
7907  /* field */
7908  rfe[0] += f*dmx*my*mz/hx;
7909  rfe[1] += f*mx*dmy*mz/hy;
7910  rfe[2] += f*mx*my*dmz/hzed;
7911  /* field gradient */
7912  rfde[0][0] += f*d2mx*my*mz/(hx*hx);
7913  rfde[1][0] += f*dmx*dmy*mz/(hy*hx);
7914  rfde[1][1] += f*mx*d2my*mz/(hy*hy);
7915  rfde[2][0] += f*dmx*my*dmz/(hx*hzed);
7916  rfde[2][1] += f*mx*dmy*dmz/(hy*hzed);
7917  rfde[2][2] += f*mx*my*d2mz/(hzed*hzed);
7918  }
7919  }
7920  }
7921 
7922  charge = Vatom_getCharge(atom);
7923  dipole = Vatom_getDipole(atom);
7924  quad = Vatom_getQuadrupole(atom);
7925  qxx = quad[0]/3.0;
7926  qyx = quad[3]/3.0;
7927  qyy = quad[4]/3.0;
7928  qzx = quad[6]/3.0;
7929  qzy = quad[7]/3.0;
7930  qzz = quad[8]/3.0;
7931 
7932  energy = pot * charge
7933  - rfe[0] * dipole[0]
7934  - rfe[1] * dipole[1]
7935  - rfe[2] * dipole[2]
7936  + rfde[0][0]*qxx
7937  + 2.0*rfde[1][0]*qyx + rfde[1][1]*qyy
7938  + 2.0*rfde[2][0]*qzx + 2.0*rfde[2][1]*qzy + rfde[2][2]*qzz;
7939 
7940  return energy;
7941 }
7942 
7943 VPUBLIC void Vpmg_fieldSpline4(Vpmg *thee, int atomID, double field[3]) {
7944 
7945  Vatom *atom;
7946  double *u, f;
7947  /* Grid variables */
7948  int nx, ny, nz;
7949  double xmax, xmin, ymax, ymin, zmax, zmin;
7950  double hx, hy, hzed, ifloat, jfloat, kfloat;
7951  double *apos, position[3];
7952  /* B-Spline weights */
7953  double mx, my, mz, dmx, dmy, dmz;
7954  double mi, mj, mk;
7955  /* Loop indeces */
7956  int ip1,ip2,im1,im2,jp1,jp2,jm1,jm2,kp1,kp2,km1,km2;
7957  int i,j,ii,jj,kk;
7958 
7959 
7960  VASSERT (thee != VNULL);
7961 
7962  /* Get the mesh information */
7963  nx = thee->pmgp->nx;
7964  ny = thee->pmgp->ny;
7965  nz = thee->pmgp->nz;
7966  hx = thee->pmgp->hx;
7967  hy = thee->pmgp->hy;
7968  hzed = thee->pmgp->hzed;
7969  xmax = thee->xf[nx-1];
7970  ymax = thee->yf[ny-1];
7971  zmax = thee->zf[nz-1];
7972  xmin = thee->xf[0];
7973  ymin = thee->yf[0];
7974  zmin = thee->zf[0];
7975 
7976  u = thee->u;
7977 
7978  atom = Valist_getAtom(thee->pbe->alist, atomID);
7979 
7980  /* Currently all atoms must be in the same partition. */
7981 
7982  VASSERT (atom->partID != 0);
7983 
7984  /* Convert the atom position to grid coordinates */
7985 
7986  apos = Vatom_getPosition(atom);
7987  position[0] = apos[0] - xmin;
7988  position[1] = apos[1] - ymin;
7989  position[2] = apos[2] - zmin;
7990  ifloat = position[0]/hx;
7991  jfloat = position[1]/hy;
7992  kfloat = position[2]/hzed;
7993  ip1 = (int)ceil(ifloat);
7994  ip2 = ip1 + 2;
7995  im1 = (int)floor(ifloat);
7996  im2 = im1 - 2;
7997  jp1 = (int)ceil(jfloat);
7998  jp2 = jp1 + 2;
7999  jm1 = (int)floor(jfloat);
8000  jm2 = jm1 - 2;
8001  kp1 = (int)ceil(kfloat);
8002  kp2 = kp1 + 2;
8003  km1 = (int)floor(kfloat);
8004  km2 = km1 - 2;
8005 
8006  /* This step shouldn't be necessary, but it saves nasty debugging
8007  * later on if something goes wrong */
8008  ip2 = VMIN2(ip2,nx-1);
8009  ip1 = VMIN2(ip1,nx-1);
8010  im1 = VMAX2(im1,0);
8011  im2 = VMAX2(im2,0);
8012  jp2 = VMIN2(jp2,ny-1);
8013  jp1 = VMIN2(jp1,ny-1);
8014  jm1 = VMAX2(jm1,0);
8015  jm2 = VMAX2(jm2,0);
8016  kp2 = VMIN2(kp2,nz-1);
8017  kp1 = VMIN2(kp1,nz-1);
8018  km1 = VMAX2(km1,0);
8019  km2 = VMAX2(km2,0);
8020 
8021  for (i=0;i<3;i++){
8022  field[i] = 0.0;
8023  }
8024 
8025  for (ii=im2; ii<=ip2; ii++) {
8026  mi = VFCHI4(ii,ifloat);
8027  mx = bspline4(mi);
8028  dmx = dbspline4(mi);
8029  for (jj=jm2; jj<=jp2; jj++) {
8030  mj = VFCHI4(jj,jfloat);
8031  my = bspline4(mj);
8032  dmy = dbspline4(mj);
8033  for (kk=km2; kk<=kp2; kk++) {
8034  mk = VFCHI4(kk,kfloat);
8035  mz = bspline4(mk);
8036  dmz = dbspline4(mk);
8037  f = u[IJK(ii,jj,kk)];
8038 
8039  field[0] += f*dmx*my*mz/hx;
8040  field[1] += f*mx*dmy*mz/hy;
8041  field[2] += f*mx*my*dmz/hzed;
8042  }
8043  }
8044  }
8045 }
8046 
8047 VPUBLIC void Vpmg_qfPermanentMultipoleForce(Vpmg *thee, int atomID,
8048  double force[3], double torque[3]) {
8049 
8050  Vatom *atom;
8051  double f, *u, *apos, position[3];
8052 
8053  /* Grid variables */
8054  int nx,ny,nz;
8055  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
8056  double hx, hy, hzed, ifloat, jfloat, kfloat;
8057 
8058  /* B-spline weights */
8059  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, d3mx, d3my, d3mz;
8060  double mi, mj, mk;
8061 
8062  /* Loop indeces */
8063  int i, j, k, ii, jj, kk;
8064  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
8065 
8066  /* Potential, field, field gradient and 2nd field gradient */
8067  double pot, e[3], de[3][3], d2e[3][3][3];
8068 
8069  /* Permanent multipole components */
8070  double *dipole, *quad;
8071  double c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
8072 
8073  VASSERT(thee != VNULL);
8074  VASSERT(thee->filled);
8075 
8076  atom = Valist_getAtom(thee->pbe->alist, atomID);
8077 
8078  /* Currently all atoms must be in the same partition. */
8079 
8080  VASSERT(atom->partID != 0);
8081 
8082  apos = Vatom_getPosition(atom);
8083 
8084  c = Vatom_getCharge(atom);
8085  dipole = Vatom_getDipole(atom);
8086  ux = dipole[0];
8087  uy = dipole[1];
8088  uz = dipole[2];
8089  quad = Vatom_getQuadrupole(atom);
8090  qxx = quad[0]/3.0;
8091  qxy = quad[1]/3.0;
8092  qxz = quad[2]/3.0;
8093  qyx = quad[3]/3.0;
8094  qyy = quad[4]/3.0;
8095  qyz = quad[5]/3.0;
8096  qzx = quad[6]/3.0;
8097  qzy = quad[7]/3.0;
8098  qzz = quad[8]/3.0;
8099 
8100  /* Initialize observables */
8101  pot = 0.0;
8102  for (i=0;i<3;i++){
8103  e[i] = 0.0;
8104  for (j=0;j<3;j++){
8105  de[i][j] = 0.0;
8106  for (k=0;k<3;k++){
8107  d2e[i][j][k] = 0.0;
8108  }
8109  }
8110  }
8111 
8112  /* Mesh info */
8113  nx = thee->pmgp->nx;
8114  ny = thee->pmgp->ny;
8115  nz = thee->pmgp->nz;
8116  hx = thee->pmgp->hx;
8117  hy = thee->pmgp->hy;
8118  hzed = thee->pmgp->hzed;
8119  xlen = thee->pmgp->xlen;
8120  ylen = thee->pmgp->ylen;
8121  zlen = thee->pmgp->zlen;
8122  xmin = thee->pmgp->xmin;
8123  ymin = thee->pmgp->ymin;
8124  zmin = thee->pmgp->zmin;
8125  xmax = thee->pmgp->xmax;
8126  ymax = thee->pmgp->ymax;
8127  zmax = thee->pmgp->zmax;
8128  u = thee->u;
8129 
8130  /* Make sure we're on the grid */
8131  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
8132  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
8133  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
8134  Vnm_print(2, "qfPermanentMultipoleForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
8135  fflush(stderr);
8136  } else {
8137 
8138  /* Convert the atom position to grid coordinates */
8139  position[0] = apos[0] - xmin;
8140  position[1] = apos[1] - ymin;
8141  position[2] = apos[2] - zmin;
8142  ifloat = position[0]/hx;
8143  jfloat = position[1]/hy;
8144  kfloat = position[2]/hzed;
8145  ip1 = (int)ceil(ifloat);
8146  ip2 = ip1 + 2;
8147  im1 = (int)floor(ifloat);
8148  im2 = im1 - 2;
8149  jp1 = (int)ceil(jfloat);
8150  jp2 = jp1 + 2;
8151  jm1 = (int)floor(jfloat);
8152  jm2 = jm1 - 2;
8153  kp1 = (int)ceil(kfloat);
8154  kp2 = kp1 + 2;
8155  km1 = (int)floor(kfloat);
8156  km2 = km1 - 2;
8157 
8158  /* This step shouldn't be necessary, but it saves nasty debugging
8159  * later on if something goes wrong */
8160  ip2 = VMIN2(ip2,nx-1);
8161  ip1 = VMIN2(ip1,nx-1);
8162  im1 = VMAX2(im1,0);
8163  im2 = VMAX2(im2,0);
8164  jp2 = VMIN2(jp2,ny-1);
8165  jp1 = VMIN2(jp1,ny-1);
8166  jm1 = VMAX2(jm1,0);
8167  jm2 = VMAX2(jm2,0);
8168  kp2 = VMIN2(kp2,nz-1);
8169  kp1 = VMIN2(kp1,nz-1);
8170  km1 = VMAX2(km1,0);
8171  km2 = VMAX2(km2,0);
8172 
8173  for (ii=im2; ii<=ip2; ii++) {
8174  mi = VFCHI4(ii,ifloat);
8175  mx = bspline4(mi);
8176  dmx = dbspline4(mi);
8177  d2mx = d2bspline4(mi);
8178  d3mx = d3bspline4(mi);
8179  for (jj=jm2; jj<=jp2; jj++) {
8180  mj = VFCHI4(jj,jfloat);
8181  my = bspline4(mj);
8182  dmy = dbspline4(mj);
8183  d2my = d2bspline4(mj);
8184  d3my = d3bspline4(mj);
8185  for (kk=km2; kk<=kp2; kk++) {
8186  mk = VFCHI4(kk,kfloat);
8187  mz = bspline4(mk);
8188  dmz = dbspline4(mk);
8189  d2mz = d2bspline4(mk);
8190  d3mz = d3bspline4(mk);
8191  f = u[IJK(ii,jj,kk)];
8192  /* Potential */
8193  pot += f*mx*my*mz;
8194  /* Field */
8195  e[0] += f*dmx*my*mz/hx;
8196  e[1] += f*mx*dmy*mz/hy;
8197  e[2] += f*mx*my*dmz/hzed;
8198  /* Field gradient */
8199  de[0][0] += f*d2mx*my*mz/(hx*hx);
8200  de[1][0] += f*dmx*dmy*mz/(hy*hx);
8201  de[1][1] += f*mx*d2my*mz/(hy*hy);
8202  de[2][0] += f*dmx*my*dmz/(hx*hzed);
8203  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
8204  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
8205  /* 2nd Field Gradient
8206  VxVxVa */
8207  d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
8208  d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
8209  d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
8210  /* VyVxVa */
8211  d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
8212  d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
8213  d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
8214  /* VyVyVa */
8215  d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
8216  d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
8217  d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
8218  /* VzVxVa */
8219  d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
8220  d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
8221  d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
8222  /* VzVyVa */
8223  d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
8224  d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
8225  d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8226  /* VzVzVa */
8227  d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
8228  d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8229  d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
8230  }
8231  }
8232  }
8233  }
8234 
8235  /* Monopole Force */
8236  force[0] = e[0]*c;
8237  force[1] = e[1]*c;
8238  force[2] = e[2]*c;
8239 
8240  /* Dipole Force */
8241  force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;
8242  force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;
8243  force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;
8244 
8245  /* Quadrupole Force */
8246  force[0] += d2e[0][0][0]*qxx
8247  + d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
8248  + d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
8249  force[1] += d2e[0][0][1]*qxx
8250  + d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
8251  + d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
8252  force[2] += d2e[0][0][2]*qxx
8253  + d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
8254  + d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
8255 
8256  /* Dipole Torque */
8257  torque[0] = uy * e[2] - uz * e[1];
8258  torque[1] = uz * e[0] - ux * e[2];
8259  torque[2] = ux * e[1] - uy * e[0];
8260  /* Quadrupole Torque */
8261  de[0][1] = de[1][0];
8262  de[0][2] = de[2][0];
8263  de[1][2] = de[2][1];
8264  torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
8265  - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
8266  torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
8267  - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
8268  torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
8269  - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
8270 
8271 
8272  /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
8273  printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
8274 }
8275 
8276 VPUBLIC void Vpmg_ibPermanentMultipoleForce(Vpmg *thee, int atomID,
8277  double force[3]) {
8278 
8279  Valist *alist;
8280  Vacc *acc;
8281  Vpbe *pbe;
8282  Vatom *atom;
8283  Vsurf_Meth srfm;
8284 
8285  /* Grid variables */
8286  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
8287  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
8288  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
8289  double izmagic;
8290  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
8291 
8292  VASSERT(thee != VNULL);
8293 
8294  /* Nonlinear PBE is not implemented for AMOEBA */
8295  VASSERT(!thee->pmgp->nonlin);
8296 
8297  acc = thee->pbe->acc;
8298  srfm = thee->surfMeth;
8299  atom = Valist_getAtom(thee->pbe->alist, atomID);
8300 
8301  /* Currently all atoms must be in the same partition. */
8302 
8303  VASSERT(atom->partID != 0);
8304  apos = Vatom_getPosition(atom);
8305  arad = Vatom_getRadius(atom);
8306 
8307  /* Reset force */
8308  force[0] = 0.0;
8309  force[1] = 0.0;
8310  force[2] = 0.0;
8311 
8312  /* Get PBE info */
8313  pbe = thee->pbe;
8314  acc = pbe->acc;
8315  alist = pbe->alist;
8316  irad = Vpbe_getMaxIonRadius(pbe);
8317  zkappa2 = Vpbe_getZkappa2(pbe);
8318  izmagic = 1.0/Vpbe_getZmagic(pbe);
8319 
8320  /* Should be a check for this further up. */
8321  VASSERT (zkappa2 > VPMGSMALL);
8322 
8323  /* Mesh info */
8324  nx = thee->pmgp->nx;
8325  ny = thee->pmgp->ny;
8326  nz = thee->pmgp->nz;
8327  hx = thee->pmgp->hx;
8328  hy = thee->pmgp->hy;
8329  hzed = thee->pmgp->hzed;
8330  xlen = thee->pmgp->xlen;
8331  ylen = thee->pmgp->ylen;
8332  zlen = thee->pmgp->zlen;
8333  xmin = thee->pmgp->xmin;
8334  ymin = thee->pmgp->ymin;
8335  zmin = thee->pmgp->zmin;
8336  xmax = thee->pmgp->xmax;
8337  ymax = thee->pmgp->ymax;
8338  zmax = thee->pmgp->zmax;
8339 
8340  /* Make sure we're on the grid */
8341  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
8342  (apos[1]<=ymin) || (apos[1]>=ymax) || \
8343  (apos[2]<=zmin) || (apos[2]>=zmax)) {
8344  Vnm_print(2, "ibPermanentMultipoleForce: Atom %d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", atomID, apos[0], apos[1], apos[2]);
8345  Vnm_print(2, "ibPermanentMultipoleForce: xmin = %g, xmax = %g\n", xmin, xmax);
8346  Vnm_print(2, "ibPermanentMultipoleForce: ymin = %g, ymax = %g\n", ymin, ymax);
8347  Vnm_print(2, "ibPermanentMultipoleForce: zmin = %g, zmax = %g\n", zmin, zmax);
8348  fflush(stderr);
8349  } else {
8350 
8351  /* Convert the atom position to grid reference frame */
8352  position[0] = apos[0] - xmin;
8353  position[1] = apos[1] - ymin;
8354  position[2] = apos[2] - zmin;
8355 
8356  /* Integrate over points within this atom's (inflated) radius */
8357  rtot = (irad + arad + thee->splineWin);
8358  rtot2 = VSQR(rtot);
8359  dx = rtot + 0.5*hx;
8360  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
8361  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
8362  for (i=imin; i<=imax; i++) {
8363  dx2 = VSQR(position[0] - hx*i);
8364  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
8365  else dy = 0.5*hy;
8366  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
8367  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
8368  for (j=jmin; j<=jmax; j++) {
8369  dy2 = VSQR(position[1] - hy*j);
8370  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
8371  else dz = 0.5*hzed;
8372  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
8373  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
8374  for (k=kmin; k<=kmax; k++) {
8375  dz2 = VSQR(k*hzed - position[2]);
8376  /* See if grid point is inside ivdw radius and set ccf
8377  * accordingly (do spline assignment here) */
8378  if ((dz2 + dy2 + dx2) <= rtot2) {
8379  gpos[0] = i*hx + xmin;
8380  gpos[1] = j*hy + ymin;
8381  gpos[2] = k*hzed + zmin;
8382  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad, atom, tgrad);
8383  fmag = VSQR(thee->u[IJK(i,j,k)])*thee->kappa[IJK(i,j,k)];
8384  force[0] += (zkappa2*fmag*tgrad[0]);
8385  force[1] += (zkappa2*fmag*tgrad[1]);
8386  force[2] += (zkappa2*fmag*tgrad[2]);
8387  }
8388  } /* k loop */
8389  } /* j loop */
8390  } /* i loop */
8391  }
8392 
8393  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
8394  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
8395  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
8396 
8397 }
8398 
8399 VPUBLIC void Vpmg_dbPermanentMultipoleForce(Vpmg *thee, int atomID,
8400  double force[3]) {
8401 
8402  Vacc *acc;
8403  Vpbe *pbe;
8404  Vatom *atom;
8405  Vsurf_Meth srfm;
8406 
8407  double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
8408  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
8409  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
8410  double *u, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
8411  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
8412  double dHzijkm1[3];
8413  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
8414 
8415  VASSERT(thee != VNULL);
8416 
8417  acc = thee->pbe->acc;
8418  srfm = thee->surfMeth;
8419  atom = Valist_getAtom(thee->pbe->alist, atomID);
8420 
8421  /* Currently all atoms must be in the same partition. */
8422 
8423  VASSERT(atom->partID != 0);
8424  arad = Vatom_getRadius(atom);
8425  apos = Vatom_getPosition(atom);
8426 
8427  /* Reset force */
8428  force[0] = 0.0;
8429  force[1] = 0.0;
8430  force[2] = 0.0;
8431 
8432  /* Get PBE info */
8433  pbe = thee->pbe;
8434  acc = pbe->acc;
8435  epsp = Vpbe_getSoluteDiel(pbe);
8436  epsw = Vpbe_getSolventDiel(pbe);
8437  kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
8438  izmagic = 1.0/Vpbe_getZmagic(pbe);
8439 
8440 
8441  deps = (epsw - epsp);
8442  depsi = 1.0/deps;
8443 
8444  VASSERT(VABS(deps) > VPMGSMALL);
8445 
8446  /* Mesh info */
8447  nx = thee->pmgp->nx;
8448  ny = thee->pmgp->ny;
8449  nz = thee->pmgp->nz;
8450  hx = thee->pmgp->hx;
8451  hy = thee->pmgp->hy;
8452  hzed = thee->pmgp->hzed;
8453  xlen = thee->pmgp->xlen;
8454  ylen = thee->pmgp->ylen;
8455  zlen = thee->pmgp->zlen;
8456  xmin = thee->pmgp->xmin;
8457  ymin = thee->pmgp->ymin;
8458  zmin = thee->pmgp->zmin;
8459  xmax = thee->pmgp->xmax;
8460  ymax = thee->pmgp->ymax;
8461  zmax = thee->pmgp->zmax;
8462  u = thee->u;
8463 
8464  /* Make sure we're on the grid */
8465  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
8466  (apos[1]<=ymin) || (apos[1]>=ymax) || \
8467  (apos[2]<=zmin) || (apos[2]>=zmax)) {
8468  Vnm_print(2, "dbPermanentMultipoleForce: Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", apos[0], apos[1], apos[2]);
8469  Vnm_print(2, "dbPermanentMultipoleForce: xmin = %g, xmax = %g\n", xmin, xmax);
8470  Vnm_print(2, "dbPermanentMultipoleForce: ymin = %g, ymax = %g\n", ymin, ymax);
8471  Vnm_print(2, "dbPermanentMultipoleForce: zmin = %g, zmax = %g\n", zmin, zmax);
8472  fflush(stderr);
8473  } else {
8474 
8475  /* Convert the atom position to grid reference frame */
8476  position[0] = apos[0] - xmin;
8477  position[1] = apos[1] - ymin;
8478  position[2] = apos[2] - zmin;
8479 
8480  /* Integrate over points within this atom's (inflated) radius */
8481  rtot = (arad + thee->splineWin);
8482  rtot2 = VSQR(rtot);
8483  dx = rtot/hx;
8484  imin = (int)floor((position[0]-rtot)/hx);
8485  if (imin < 1) {
8486  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8487  return;
8488  }
8489  imax = (int)ceil((position[0]+rtot)/hx);
8490  if (imax > (nx-2)) {
8491  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8492  return;
8493  }
8494  jmin = (int)floor((position[1]-rtot)/hy);
8495  if (jmin < 1) {
8496  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8497  return;
8498  }
8499  jmax = (int)ceil((position[1]+rtot)/hy);
8500  if (jmax > (ny-2)) {
8501  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8502  return;
8503  }
8504  kmin = (int)floor((position[2]-rtot)/hzed);
8505  if (kmin < 1) {
8506  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8507  return;
8508  }
8509  kmax = (int)ceil((position[2]+rtot)/hzed);
8510  if (kmax > (nz-2)) {
8511  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8512  return;
8513  }
8514  for (i=imin; i<=imax; i++) {
8515  for (j=jmin; j<=jmax; j++) {
8516  for (k=kmin; k<=kmax; k++) {
8517  /* i,j,k */
8518  gpos[0] = (i+0.5)*hx + xmin;
8519  gpos[1] = j*hy + ymin;
8520  gpos[2] = k*hzed + zmin;
8521  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
8522  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8523  atom, dHxijk);
8524  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
8525  gpos[0] = i*hx + xmin;
8526  gpos[1] = (j+0.5)*hy + ymin;
8527  gpos[2] = k*hzed + zmin;
8528  Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
8529  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8530  atom, dHyijk);
8531  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
8532  gpos[0] = i*hx + xmin;
8533  gpos[1] = j*hy + ymin;
8534  gpos[2] = (k+0.5)*hzed + zmin;
8535  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
8536  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8537  atom, dHzijk);
8538  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
8539  /* i-1,j,k */
8540  gpos[0] = (i-0.5)*hx + xmin;
8541  gpos[1] = j*hy + ymin;
8542  gpos[2] = k*hzed + zmin;
8543  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
8544  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8545  atom, dHxim1jk);
8546  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
8547  /* i,j-1,k */
8548  gpos[0] = i*hx + xmin;
8549  gpos[1] = (j-0.5)*hy + ymin;
8550  gpos[2] = k*hzed + zmin;
8551  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
8552  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8553  atom, dHyijm1k);
8554  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
8555  /* i,j,k-1 */
8556  gpos[0] = i*hx + xmin;
8557  gpos[1] = j*hy + ymin;
8558  gpos[2] = (k-0.5)*hzed + zmin;
8559  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
8560  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8561  atom, dHzijkm1);
8562  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
8563  dbFmag = u[IJK(i,j,k)];
8564  tgrad[0] =
8565  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
8566  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
8567  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
8568  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
8569  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
8570  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
8571  tgrad[1] =
8572  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
8573  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
8574  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
8575  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
8576  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
8577  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
8578  tgrad[2] =
8579  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
8580  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
8581  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
8582  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
8583  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
8584  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
8585  force[0] += (dbFmag*tgrad[0]);
8586  force[1] += (dbFmag*tgrad[1]);
8587  force[2] += (dbFmag*tgrad[2]);
8588  } /* k loop */
8589  } /* j loop */
8590  } /* i loop */
8591  force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
8592  force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
8593  force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
8594  }
8595 }
8596 
8597 VPUBLIC void Vpmg_qfDirectPolForce(Vpmg *thee, Vgrid* perm, Vgrid *induced,
8598  int atomID, double force[3], double torque[3]) {
8599 
8600  Vatom *atom;
8601  Vpbe *pbe;
8602  double f, fp, *u, *up, *apos, position[3];
8603 
8604  /* Grid variables */
8605  int nx,ny,nz;
8606  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
8607  double hx, hy, hzed, ifloat, jfloat, kfloat;
8608 
8609  /* B-spline weights */
8610  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, d3mx, d3my, d3mz;
8611  double mi, mj, mk;
8612 
8613  /* Loop indeces */
8614  int i, j, k, ii, jj, kk;
8615  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
8616 
8617  /* Permanent potential, field, field gradient and 2nd field gradient */
8618  double pot, e[3], de[3][3], d2e[3][3][3];
8619  /* Induced dipole field */
8620  double dep[3][3];
8621 
8622  /* Permanent multipole components */
8623  double *dipole, *quad;
8624  double c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
8625  double uix, uiy, uiz;
8626 
8627  VASSERT(thee != VNULL);
8628  VASSERT(induced != VNULL); /* the potential due to permanent multipoles.*/
8629  VASSERT(induced != VNULL); /* the potential due to local induced dipoles.*/
8630  VASSERT(thee->pbe != VNULL);
8631  VASSERT(thee->pbe->alist != VNULL);
8632 
8633  atom = Valist_getAtom(thee->pbe->alist, atomID);
8634  VASSERT(atom->partID != 0); /* all atoms must be in the same partition.*/
8635  apos = Vatom_getPosition(atom);
8636 
8637  c = Vatom_getCharge(atom);
8638  dipole = Vatom_getDipole(atom);
8639  ux = dipole[0];
8640  uy = dipole[1];
8641  uz = dipole[2];
8642  quad = Vatom_getQuadrupole(atom);
8643  qxx = quad[0]/3.0;
8644  qxy = quad[1]/3.0;
8645  qxz = quad[2]/3.0;
8646  qyx = quad[3]/3.0;
8647  qyy = quad[4]/3.0;
8648  qyz = quad[5]/3.0;
8649  qzx = quad[6]/3.0;
8650  qzy = quad[7]/3.0;
8651  qzz = quad[8]/3.0;
8652 
8653  dipole = Vatom_getInducedDipole(atom);
8654  uix = dipole[0];
8655  uiy = dipole[1];
8656  uiz = dipole[2];
8657 
8658  /* Reset Field Gradients */
8659  pot = 0.0;
8660  for (i=0;i<3;i++){
8661  e[i] = 0.0;
8662  for (j=0;j<3;j++){
8663  de[i][j] = 0.0;
8664  dep[i][j] = 0.0;
8665  for (k=0;k<3;k++){
8666  d2e[i][j][k] = 0.0;
8667  }
8668  }
8669  }
8670 
8671  /* Mesh info */
8672  nx = thee->pmgp->nx;
8673  ny = thee->pmgp->ny;
8674  nz = thee->pmgp->nz;
8675  hx = thee->pmgp->hx;
8676  hy = thee->pmgp->hy;
8677  hzed = thee->pmgp->hzed;
8678  xlen = thee->pmgp->xlen;
8679  ylen = thee->pmgp->ylen;
8680  zlen = thee->pmgp->zlen;
8681  xmin = thee->pmgp->xmin;
8682  ymin = thee->pmgp->ymin;
8683  zmin = thee->pmgp->zmin;
8684  xmax = thee->pmgp->xmax;
8685  ymax = thee->pmgp->ymax;
8686  zmax = thee->pmgp->zmax;
8687  u = induced->data;
8688  up = perm->data;
8689 
8690  /* Make sure we're on the grid */
8691  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
8692  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
8693  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
8694  Vnm_print(2, "qfDirectPolForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
8695  fflush(stderr);
8696 
8697  } else {
8698 
8699  /* Convert the atom position to grid coordinates */
8700  position[0] = apos[0] - xmin;
8701  position[1] = apos[1] - ymin;
8702  position[2] = apos[2] - zmin;
8703  ifloat = position[0]/hx;
8704  jfloat = position[1]/hy;
8705  kfloat = position[2]/hzed;
8706  ip1 = (int)ceil(ifloat);
8707  ip2 = ip1 + 2;
8708  im1 = (int)floor(ifloat);
8709  im2 = im1 - 2;
8710  jp1 = (int)ceil(jfloat);
8711  jp2 = jp1 + 2;
8712  jm1 = (int)floor(jfloat);
8713  jm2 = jm1 - 2;
8714  kp1 = (int)ceil(kfloat);
8715  kp2 = kp1 + 2;
8716  km1 = (int)floor(kfloat);
8717  km2 = km1 - 2;
8718 
8719  /* This step shouldn't be necessary, but it saves nasty debugging
8720  * later on if something goes wrong */
8721  ip2 = VMIN2(ip2,nx-1);
8722  ip1 = VMIN2(ip1,nx-1);
8723  im1 = VMAX2(im1,0);
8724  im2 = VMAX2(im2,0);
8725  jp2 = VMIN2(jp2,ny-1);
8726  jp1 = VMIN2(jp1,ny-1);
8727  jm1 = VMAX2(jm1,0);
8728  jm2 = VMAX2(jm2,0);
8729  kp2 = VMIN2(kp2,nz-1);
8730  kp1 = VMIN2(kp1,nz-1);
8731  km1 = VMAX2(km1,0);
8732  km2 = VMAX2(km2,0);
8733 
8734  for (ii=im2; ii<=ip2; ii++) {
8735  mi = VFCHI4(ii,ifloat);
8736  mx = bspline4(mi);
8737  dmx = dbspline4(mi);
8738  d2mx = d2bspline4(mi);
8739  d3mx = d3bspline4(mi);
8740  for (jj=jm2; jj<=jp2; jj++) {
8741  mj = VFCHI4(jj,jfloat);
8742  my = bspline4(mj);
8743  dmy = dbspline4(mj);
8744  d2my = d2bspline4(mj);
8745  d3my = d3bspline4(mj);
8746  for (kk=km2; kk<=kp2; kk++) {
8747  mk = VFCHI4(kk,kfloat);
8748  mz = bspline4(mk);
8749  dmz = dbspline4(mk);
8750  d2mz = d2bspline4(mk);
8751  d3mz = d3bspline4(mk);
8752  f = u[IJK(ii,jj,kk)];
8753  fp = up[IJK(ii,jj,kk)];
8754  /* The potential */
8755  pot += f*mx*my*mz;
8756  /* The field */
8757  e[0] += f*dmx*my*mz/hx;
8758  e[1] += f*mx*dmy*mz/hy;
8759  e[2] += f*mx*my*dmz/hzed;
8760  /* The gradient of the field */
8761  de[0][0] += f*d2mx*my*mz/(hx*hx);
8762  de[1][0] += f*dmx*dmy*mz/(hy*hx);
8763  de[1][1] += f*mx*d2my*mz/(hy*hy);
8764  de[2][0] += f*dmx*my*dmz/(hx*hzed);
8765  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
8766  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
8767  /* The gradient of the (permanent) field */
8768  dep[0][0] += fp*d2mx*my*mz/(hx*hx);
8769  dep[1][0] += fp*dmx*dmy*mz/(hy*hx);
8770  dep[1][1] += fp*mx*d2my*mz/(hy*hy);
8771  dep[2][0] += fp*dmx*my*dmz/(hx*hzed);
8772  dep[2][1] += fp*mx*dmy*dmz/(hy*hzed);
8773  dep[2][2] += fp*mx*my*d2mz/(hzed*hzed);
8774  /* The 2nd gradient of the field
8775  VxVxVa */
8776  d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
8777  d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
8778  d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
8779  /* VyVxVa */
8780  d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
8781  d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
8782  d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
8783  /* VyVyVa */
8784  d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
8785  d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
8786  d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
8787  /* VzVxVa */
8788  d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
8789  d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
8790  d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
8791  /* VzVyVa */
8792  d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
8793  d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
8794  d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8795  /* VzVzVa */
8796  d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
8797  d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8798  d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
8799  }
8800  }
8801  }
8802  }
8803 
8804  /* force on permanent multipole due to induced reaction field */
8805 
8806  /* Monopole Force */
8807  force[0] = e[0]*c;
8808  force[1] = e[1]*c;
8809  force[2] = e[2]*c;
8810 
8811  /* Dipole Force */
8812  force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;
8813  force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;
8814  force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;
8815 
8816  /* Quadrupole Force */
8817  force[0] += d2e[0][0][0]*qxx
8818  + d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
8819  + d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
8820  force[1] += d2e[0][0][1]*qxx
8821  + d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
8822  + d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
8823  force[2] += d2e[0][0][2]*qxx
8824  + d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
8825  + d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
8826 
8827  /* torque on permanent mulitpole due to induced reaction field */
8828 
8829  /* Dipole Torque */
8830  torque[0] = uy * e[2] - uz * e[1];
8831  torque[1] = uz * e[0] - ux * e[2];
8832  torque[2] = ux * e[1] - uy * e[0];
8833 
8834  /* Quadrupole Torque */
8835  /* Tx = -2.0*(Sum_a (Qya*dEaz) + Sum_b (Qzb*dEby))
8836  Ty = -2.0*(Sum_a (Qza*dEax) + Sum_b (Qxb*dEbz))
8837  Tz = -2.0*(Sum_a (Qxa*dEay) + Sum_b (Qyb*dEbx)) */
8838  de[0][1] = de[1][0];
8839  de[0][2] = de[2][0];
8840  de[1][2] = de[2][1];
8841  torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
8842  - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
8843  torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
8844  - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
8845  torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
8846  - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
8847 
8848  /* force on induced dipole due to permanent reaction field */
8849 
8850  force[0] -= dep[0][0]*uix+dep[1][0]*uiy+dep[2][0]*uiz;
8851  force[1] -= dep[1][0]*uix+dep[1][1]*uiy+dep[2][1]*uiz;
8852  force[2] -= dep[2][0]*uix+dep[2][1]*uiy+dep[2][2]*uiz;
8853 
8854  force[0] = 0.5 * force[0];
8855  force[1] = 0.5 * force[1];
8856  force[2] = 0.5 * force[2];
8857  torque[0] = 0.5 * torque[0];
8858  torque[1] = 0.5 * torque[1];
8859  torque[2] = 0.5 * torque[2];
8860 
8861  /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
8862  printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
8863 }
8864 
8865 VPUBLIC void Vpmg_qfNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
8866  int atomID, double force[3], double torque[3]) {
8867 
8868  Vatom *atom;
8869  double *apos, *dipole, *quad, position[3], hx, hy, hzed;
8870  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
8871  double pot, e[3],de[3][3],dep[3][3],d2e[3][3][3];
8872  double mx, my, mz, dmx, dmy, dmz, mi, mj, mk;
8873  double d2mx, d2my, d2mz, d3mx, d3my, d3mz;
8874  double *u, *up, charge, ifloat, jfloat, kfloat;
8875  double f, fp, c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
8876  double uix, uiy, uiz;
8877  int i,j,k,nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1;
8878  int kp1, kp2, ii, jj, kk;
8879 
8880  VASSERT(thee != VNULL);
8881  VASSERT(perm != VNULL); /* potential due to permanent multipoles. */
8882  VASSERT(nlInduced != VNULL); /* potential due to non-local induced dipoles */
8883  VASSERT(!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
8884 
8885  atom = Valist_getAtom(thee->pbe->alist, atomID);
8886  VASSERT(atom->partID != 0); /* Currently all atoms must be in the same partition. */
8887  apos = Vatom_getPosition(atom);
8888 
8889  c = Vatom_getCharge(atom);
8890  dipole = Vatom_getDipole(atom);
8891  ux = dipole[0];
8892  uy = dipole[1];
8893  uz = dipole[2];
8894  quad = Vatom_getQuadrupole(atom);
8895  qxx = quad[0]/3.0;
8896  qxy = quad[1]/3.0;
8897  qxz = quad[2]/3.0;
8898  qyx = quad[3]/3.0;
8899  qyy = quad[4]/3.0;
8900  qyz = quad[5]/3.0;
8901  qzx = quad[6]/3.0;
8902  qzy = quad[7]/3.0;
8903  qzz = quad[8]/3.0;
8904 
8905  dipole = Vatom_getNLInducedDipole(atom);
8906  uix = dipole[0];
8907  uiy = dipole[1];
8908  uiz = dipole[2];
8909 
8910  /* Reset Field Gradients */
8911  pot = 0.0;
8912  for (i=0;i<3;i++){
8913  e[i] = 0.0;
8914  for (j=0;j<3;j++){
8915  de[i][j] = 0.0;
8916  dep[i][j] = 0.0;
8917  for (k=0;k<3;k++){
8918  d2e[i][j][k] = 0.0;
8919  }
8920  }
8921  }
8922 
8923  /* Mesh info */
8924  nx = thee->pmgp->nx;
8925  ny = thee->pmgp->ny;
8926  nz = thee->pmgp->nz;
8927  hx = thee->pmgp->hx;
8928  hy = thee->pmgp->hy;
8929  hzed = thee->pmgp->hzed;
8930  xlen = thee->pmgp->xlen;
8931  ylen = thee->pmgp->ylen;
8932  zlen = thee->pmgp->zlen;
8933  xmin = thee->pmgp->xmin;
8934  ymin = thee->pmgp->ymin;
8935  zmin = thee->pmgp->zmin;
8936  xmax = thee->pmgp->xmax;
8937  ymax = thee->pmgp->ymax;
8938  zmax = thee->pmgp->zmax;
8939  u = nlInduced->data;
8940  up = perm->data;
8941 
8942 
8943  /* Make sure we're on the grid */
8944  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
8945  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
8946  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
8947  Vnm_print(2, "qfNLDirectMultipoleForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
8948  } else {
8949 
8950  /* Convert the atom position to grid coordinates */
8951  position[0] = apos[0] - xmin;
8952  position[1] = apos[1] - ymin;
8953  position[2] = apos[2] - zmin;
8954  ifloat = position[0]/hx;
8955  jfloat = position[1]/hy;
8956  kfloat = position[2]/hzed;
8957  ip1 = (int)ceil(ifloat);
8958  ip2 = ip1 + 2;
8959  im1 = (int)floor(ifloat);
8960  im2 = im1 - 2;
8961  jp1 = (int)ceil(jfloat);
8962  jp2 = jp1 + 2;
8963  jm1 = (int)floor(jfloat);
8964  jm2 = jm1 - 2;
8965  kp1 = (int)ceil(kfloat);
8966  kp2 = kp1 + 2;
8967  km1 = (int)floor(kfloat);
8968  km2 = km1 - 2;
8969 
8970  /* This step shouldn't be necessary, but it saves nasty debugging
8971  * later on if something goes wrong */
8972  ip2 = VMIN2(ip2,nx-1);
8973  ip1 = VMIN2(ip1,nx-1);
8974  im1 = VMAX2(im1,0);
8975  im2 = VMAX2(im2,0);
8976  jp2 = VMIN2(jp2,ny-1);
8977  jp1 = VMIN2(jp1,ny-1);
8978  jm1 = VMAX2(jm1,0);
8979  jm2 = VMAX2(jm2,0);
8980  kp2 = VMIN2(kp2,nz-1);
8981  kp1 = VMIN2(kp1,nz-1);
8982  km1 = VMAX2(km1,0);
8983  km2 = VMAX2(km2,0);
8984 
8985  for (ii=im2; ii<=ip2; ii++) {
8986  mi = VFCHI4(ii,ifloat);
8987  mx = bspline4(mi);
8988  dmx = dbspline4(mi);
8989  d2mx = d2bspline4(mi);
8990  d3mx = d3bspline4(mi);
8991  for (jj=jm2; jj<=jp2; jj++) {
8992  mj = VFCHI4(jj,jfloat);
8993  my = bspline4(mj);
8994  dmy = dbspline4(mj);
8995  d2my = d2bspline4(mj);
8996  d3my = d3bspline4(mj);
8997  for (kk=km2; kk<=kp2; kk++) {
8998  mk = VFCHI4(kk,kfloat);
8999  mz = bspline4(mk);
9000  dmz = dbspline4(mk);
9001  d2mz = d2bspline4(mk);
9002  d3mz = d3bspline4(mk);
9003  f = u[IJK(ii,jj,kk)];
9004  fp = up[IJK(ii,jj,kk)];
9005  /* The potential */
9006  pot += f*mx*my*mz;
9007  /* The field */
9008  e[0] += f*dmx*my*mz/hx;
9009  e[1] += f*mx*dmy*mz/hy;
9010  e[2] += f*mx*my*dmz/hzed;
9011  /* The gradient of the field */
9012  de[0][0] += f*d2mx*my*mz/(hx*hx);
9013  de[1][0] += f*dmx*dmy*mz/(hy*hx);
9014  de[1][1] += f*mx*d2my*mz/(hy*hy);
9015  de[2][0] += f*dmx*my*dmz/(hx*hzed);
9016  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
9017  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
9018  /* The gradient of the (permanent) field */
9019  dep[0][0] += fp*d2mx*my*mz/(hx*hx);
9020  dep[1][0] += fp*dmx*dmy*mz/(hy*hx);
9021  dep[1][1] += fp*mx*d2my*mz/(hy*hy);
9022  dep[2][0] += fp*dmx*my*dmz/(hx*hzed);
9023  dep[2][1] += fp*mx*dmy*dmz/(hy*hzed);
9024  dep[2][2] += fp*mx*my*d2mz/(hzed*hzed);
9025  /* The 2nd gradient of the field */
9026  /* VxVxVa */
9027  d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
9028  d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
9029  d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
9030  /* VyVxVa */
9031  d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
9032  d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
9033  d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
9034  /* VyVyVa */
9035  d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
9036  d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
9037  d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
9038  /* VzVxVa */
9039  d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
9040  d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
9041  d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
9042  /* VzVyVa */
9043  d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
9044  d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
9045  d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
9046  /* VzVzVa */
9047  d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
9048  d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
9049  d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
9050  }
9051  }
9052  }
9053  }
9054 
9055  /* force on permanent multipole due to non-local induced reaction field */
9056 
9057  /* Monopole Force */
9058  force[0] = e[0]*c;
9059  force[1] = e[1]*c;
9060  force[2] = e[2]*c;
9061 
9062  /* Dipole Force */
9063  force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;
9064  force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;
9065  force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;
9066 
9067  /* Quadrupole Force */
9068  force[0] += d2e[0][0][0]*qxx
9069  + d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
9070  + d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
9071  force[1] += d2e[0][0][1]*qxx
9072  + d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
9073  + d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
9074  force[2] += d2e[0][0][2]*qxx
9075  + d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
9076  + d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
9077 
9078  /* torque on permanent mulitpole due to non-local induced reaction field */
9079 
9080  /* Dipole Torque */
9081  torque[0] = uy * e[2] - uz * e[1];
9082  torque[1] = uz * e[0] - ux * e[2];
9083  torque[2] = ux * e[1] - uy * e[0];
9084 
9085  /* Quadrupole Torque */
9086  /* Tx = -2.0*(Sum_a (Qya*dEaz) + Sum_b (Qzb*dEby))
9087  Ty = -2.0*(Sum_a (Qza*dEax) + Sum_b (Qxb*dEbz))
9088  Tz = -2.0*(Sum_a (Qxa*dEay) + Sum_b (Qyb*dEbx)) */
9089  de[0][1] = de[1][0];
9090  de[0][2] = de[2][0];
9091  de[1][2] = de[2][1];
9092  torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
9093  - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
9094  torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
9095  - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
9096  torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
9097  - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
9098 
9099  /* force on non-local induced dipole due to permanent reaction field */
9100 
9101  force[0] -= dep[0][0]*uix+dep[1][0]*uiy+dep[2][0]*uiz;
9102  force[1] -= dep[1][0]*uix+dep[1][1]*uiy+dep[2][1]*uiz;
9103  force[2] -= dep[2][0]*uix+dep[2][1]*uiy+dep[2][2]*uiz;
9104 
9105  force[0] = 0.5 * force[0];
9106  force[1] = 0.5 * force[1];
9107  force[2] = 0.5 * force[2];
9108  torque[0] = 0.5 * torque[0];
9109  torque[1] = 0.5 * torque[1];
9110  torque[2] = 0.5 * torque[2];
9111 
9112  /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
9113  printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
9114 }
9115 
9116 VPUBLIC void Vpmg_ibDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced,
9117  int atomID, double force[3]) {
9118 
9119  Vatom *atom;
9120  Valist *alist;
9121  Vacc *acc;
9122  Vpbe *pbe;
9123  Vsurf_Meth srfm;
9124 
9125  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
9126  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
9127  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
9128  double izmagic;
9129  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9130 
9131  VASSERT(thee != VNULL);
9132  VASSERT(perm != VNULL); /* potential due to permanent multipoles.*/
9133  VASSERT(induced != VNULL); /* potential due to induced dipoles. */
9134  VASSERT (!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
9135 
9136  acc = thee->pbe->acc;
9137  srfm = thee->surfMeth;
9138  atom = Valist_getAtom(thee->pbe->alist, atomID);
9139  VASSERT(atom->partID != 0); /* Currently all atoms must be in the same partition. */
9140  apos = Vatom_getPosition(atom);
9141  arad = Vatom_getRadius(atom);
9142 
9143  /* Reset force */
9144  force[0] = 0.0;
9145  force[1] = 0.0;
9146  force[2] = 0.0;
9147 
9148  /* Get PBE info */
9149  pbe = thee->pbe;
9150  acc = pbe->acc;
9151  alist = pbe->alist;
9152  irad = Vpbe_getMaxIonRadius(pbe);
9153  zkappa2 = Vpbe_getZkappa2(pbe);
9154  izmagic = 1.0/Vpbe_getZmagic(pbe);
9155 
9156  VASSERT (zkappa2 > VPMGSMALL); /* It is ok to run AMOEBA with no ions, but this is checked for higher up in the driver. */
9157 
9158  /* Mesh info */
9159  nx = induced->nx;
9160  ny = induced->ny;
9161  nz = induced->nz;
9162  hx = induced->hx;
9163  hy = induced->hy;
9164  hzed = induced->hzed;
9165  xmin = induced->xmin;
9166  ymin = induced->ymin;
9167  zmin = induced->zmin;
9168  xmax = induced->xmax;
9169  ymax = induced->ymax;
9170  zmax = induced->zmax;
9171  xlen = xmax-xmin;
9172  ylen = ymax-ymin;
9173  zlen = zmax-zmin;
9174 
9175  /* Make sure we're on the grid */
9176  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9177  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9178  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9179  Vnm_print(2, "Vpmg_ibForce: Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
9180  apos[0], apos[1], apos[2]);
9181  Vnm_print(2, "Vpmg_ibForce: xmin = %g, xmax = %g\n", xmin, xmax);
9182  Vnm_print(2, "Vpmg_ibForce: ymin = %g, ymax = %g\n", ymin, ymax);
9183  Vnm_print(2, "Vpmg_ibForce: zmin = %g, zmax = %g\n", zmin, zmax);
9184  fflush(stderr);
9185  } else {
9186 
9187  /* Convert the atom position to grid reference frame */
9188  position[0] = apos[0] - xmin;
9189  position[1] = apos[1] - ymin;
9190  position[2] = apos[2] - zmin;
9191 
9192  /* Integrate over points within this atom's (inflated) radius */
9193  rtot = (irad + arad + thee->splineWin);
9194  rtot2 = VSQR(rtot);
9195  dx = rtot + 0.5*hx;
9196  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
9197  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
9198  for (i=imin; i<=imax; i++) {
9199  dx2 = VSQR(position[0] - hx*i);
9200  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
9201  else dy = 0.5*hy;
9202  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
9203  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
9204  for (j=jmin; j<=jmax; j++) {
9205  dy2 = VSQR(position[1] - hy*j);
9206  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
9207  else dz = 0.5*hzed;
9208  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
9209  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
9210  for (k=kmin; k<=kmax; k++) {
9211  dz2 = VSQR(k*hzed - position[2]);
9212  /* See if grid point is inside ivdw radius and set ccf
9213  * accordingly (do spline assignment here) */
9214  if ((dz2 + dy2 + dx2) <= rtot2) {
9215  gpos[0] = i*hx + xmin;
9216  gpos[1] = j*hy + ymin;
9217  gpos[2] = k*hzed + zmin;
9218  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad,
9219  atom, tgrad);
9220  fmag = induced->data[IJK(i,j,k)];
9221  fmag *= perm->data[IJK(i,j,k)];
9222  fmag *= thee->kappa[IJK(i,j,k)];
9223  force[0] += (zkappa2*fmag*tgrad[0]);
9224  force[1] += (zkappa2*fmag*tgrad[1]);
9225  force[2] += (zkappa2*fmag*tgrad[2]);
9226  }
9227  } /* k loop */
9228  } /* j loop */
9229  } /* i loop */
9230  }
9231 
9232  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
9233  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
9234  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
9235 
9236 }
9237 
9238 VPUBLIC void Vpmg_ibNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
9239  int atomID, double force[3]) {
9240  Vpmg_ibDirectPolForce(thee, perm, nlInduced, atomID, force);
9241 }
9242 
9243 VPUBLIC void Vpmg_dbDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced,
9244  int atomID, double force[3]) {
9245 
9246  Vatom *atom;
9247  Vacc *acc;
9248  Vpbe *pbe;
9249  Vsurf_Meth srfm;
9250 
9251  double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
9252  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
9253  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
9254  double *u, *up, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
9255  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
9256  double dHzijkm1[3];
9257  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9258 
9259  VASSERT(thee != VNULL);
9260  VASSERT(perm != VNULL); /* permanent multipole PMG solution. */
9261  VASSERT(induced != VNULL); /* potential due to induced dipoles. */
9262 
9263  acc = thee->pbe->acc;
9264  atom = Valist_getAtom(thee->pbe->alist, atomID);
9265  VASSERT (atom->partID != 0); /* Currently all atoms must be in the same partition. */
9266  apos = Vatom_getPosition(atom);
9267  arad = Vatom_getRadius(atom);
9268 
9269  /* Reset force */
9270  force[0] = 0.0;
9271  force[1] = 0.0;
9272  force[2] = 0.0;
9273 
9274  /* Get PBE info */
9275  pbe = thee->pbe;
9276  acc = pbe->acc;
9277  srfm = thee->surfMeth;
9278  epsp = Vpbe_getSoluteDiel(pbe);
9279  epsw = Vpbe_getSolventDiel(pbe);
9280  kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
9281  izmagic = 1.0/Vpbe_getZmagic(pbe);
9282 
9283  deps = (epsw - epsp);
9284  depsi = 1.0/deps;
9285  VASSERT(VABS(deps) > VPMGSMALL);
9286 
9287  /* Mesh info */
9288  nx = thee->pmgp->nx;
9289  ny = thee->pmgp->ny;
9290  nz = thee->pmgp->nz;
9291  hx = thee->pmgp->hx;
9292  hy = thee->pmgp->hy;
9293  hzed = thee->pmgp->hzed;
9294  xlen = thee->pmgp->xlen;
9295  ylen = thee->pmgp->ylen;
9296  zlen = thee->pmgp->zlen;
9297  xmin = thee->pmgp->xmin;
9298  ymin = thee->pmgp->ymin;
9299  zmin = thee->pmgp->zmin;
9300  xmax = thee->pmgp->xmax;
9301  ymax = thee->pmgp->ymax;
9302  zmax = thee->pmgp->zmax;
9303  /* If the permanent and induced potentials are flipped the
9304  results are exactly the same. */
9305  u = induced->data;
9306  up = perm->data;
9307 
9308  /* Make sure we're on the grid */
9309  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9310  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9311  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9312  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", apos[0], apos[1], apos[2]);
9313  Vnm_print(2, "Vpmg_dbDirectPolForce: xmin = %g, xmax = %g\n", xmin, xmax);
9314  Vnm_print(2, "Vpmg_dbDirectPolForce: ymin = %g, ymax = %g\n", ymin, ymax);
9315  Vnm_print(2, "Vpmg_dbDirectPolForce: zmin = %g, zmax = %g\n", zmin, zmax);
9316  fflush(stderr);
9317  } else {
9318 
9319  /* Convert the atom position to grid reference frame */
9320  position[0] = apos[0] - xmin;
9321  position[1] = apos[1] - ymin;
9322  position[2] = apos[2] - zmin;
9323 
9324  /* Integrate over points within this atom's (inflated) radius */
9325  rtot = (arad + thee->splineWin);
9326  rtot2 = VSQR(rtot);
9327  dx = rtot/hx;
9328  imin = (int)floor((position[0]-rtot)/hx);
9329  if (imin < 1) {
9330  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9331  return;
9332  }
9333  imax = (int)ceil((position[0]+rtot)/hx);
9334  if (imax > (nx-2)) {
9335  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9336  return;
9337  }
9338  jmin = (int)floor((position[1]-rtot)/hy);
9339  if (jmin < 1) {
9340  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9341  return;
9342  }
9343  jmax = (int)ceil((position[1]+rtot)/hy);
9344  if (jmax > (ny-2)) {
9345  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9346  return;
9347  }
9348  kmin = (int)floor((position[2]-rtot)/hzed);
9349  if (kmin < 1) {
9350  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9351  return;
9352  }
9353  kmax = (int)ceil((position[2]+rtot)/hzed);
9354  if (kmax > (nz-2)) {
9355  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9356  return;
9357  }
9358  for (i=imin; i<=imax; i++) {
9359  for (j=jmin; j<=jmax; j++) {
9360  for (k=kmin; k<=kmax; k++) {
9361  /* i,j,k */
9362  gpos[0] = (i+0.5)*hx + xmin;
9363  gpos[1] = j*hy + ymin;
9364  gpos[2] = k*hzed + zmin;
9365  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
9366  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9367  atom, dHxijk);
9368  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
9369  gpos[0] = i*hx + xmin;
9370  gpos[1] = (j+0.5)*hy + ymin;
9371  gpos[2] = k*hzed + zmin;
9372  Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
9373  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9374  atom, dHyijk);
9375  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
9376  gpos[0] = i*hx + xmin;
9377  gpos[1] = j*hy + ymin;
9378  gpos[2] = (k+0.5)*hzed + zmin;
9379  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
9380  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9381  atom, dHzijk);
9382  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
9383  /* i-1,j,k */
9384  gpos[0] = (i-0.5)*hx + xmin;
9385  gpos[1] = j*hy + ymin;
9386  gpos[2] = k*hzed + zmin;
9387  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
9388  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9389  atom, dHxim1jk);
9390  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
9391  /* i,j-1,k */
9392  gpos[0] = i*hx + xmin;
9393  gpos[1] = (j-0.5)*hy + ymin;
9394  gpos[2] = k*hzed + zmin;
9395  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
9396  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9397  atom, dHyijm1k);
9398  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
9399  /* i,j,k-1 */
9400  gpos[0] = i*hx + xmin;
9401  gpos[1] = j*hy + ymin;
9402  gpos[2] = (k-0.5)*hzed + zmin;
9403  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
9404  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9405  atom, dHzijkm1);
9406  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
9407 
9408  dbFmag = up[IJK(i,j,k)];
9409  tgrad[0] =
9410  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9411  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9412  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9413  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9414  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9415  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9416  tgrad[1] =
9417  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9418  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9419  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9420  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9421  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9422  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9423  tgrad[2] =
9424  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9425  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9426  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9427  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9428  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9429  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9430  force[0] += (dbFmag*tgrad[0]);
9431  force[1] += (dbFmag*tgrad[1]);
9432  force[2] += (dbFmag*tgrad[2]);
9433 
9434  } /* k loop */
9435  } /* j loop */
9436  } /* i loop */
9437 
9438  force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
9439  force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
9440  force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
9441 
9442  }
9443 }
9444 
9445 VPUBLIC void Vpmg_dbNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
9446  int atomID, double force[3]) {
9447  Vpmg_dbDirectPolForce(thee, perm, nlInduced, atomID, force);
9448 }
9449 
9450 VPUBLIC void Vpmg_qfMutualPolForce(Vpmg *thee, Vgrid *induced,
9451  Vgrid *nlinduced, int atomID, double force[3]) {
9452 
9453  Vatom *atom;
9454  double *apos, *dipole, position[3], hx, hy, hzed;
9455  double *u, *unl;
9456  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
9457  double de[3][3], denl[3][3];
9458  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, mi, mj, mk;
9459  double ifloat, jfloat, kfloat;
9460  double f, fnl, uix, uiy, uiz, uixnl, uiynl, uiznl;
9461  int i,j,k,nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1;
9462  int kp1, kp2, ii, jj, kk;
9463 
9464  VASSERT(thee != VNULL); /* PMG object with PBE info. */
9465  VASSERT(induced != VNULL); /* potential due to induced dipoles. */
9466  VASSERT(nlinduced != VNULL); /* potential due to non-local induced dipoles. */
9467  atom = Valist_getAtom(thee->pbe->alist, atomID);
9468  VASSERT(atom->partID != 0); /* all atoms must be in the same partition. */
9469  apos = Vatom_getPosition(atom);
9470  dipole = Vatom_getInducedDipole(atom);
9471  uix = dipole[0];
9472  uiy = dipole[1];
9473  uiz = dipole[2];
9474  dipole = Vatom_getNLInducedDipole(atom);
9475  uixnl = dipole[0];
9476  uiynl = dipole[1];
9477  uiznl = dipole[2];
9478  u = induced->data;
9479  unl = nlinduced->data;
9480 
9481  for (i=0;i<3;i++){
9482  for (j=0;j<3;j++){
9483  de[i][j] = 0.0;
9484  denl[i][j] = 0.0;
9485  }
9486  }
9487 
9488  /* Mesh info */
9489  nx = induced->nx;
9490  ny = induced->ny;
9491  nz = induced->nz;
9492  hx = induced->hx;
9493  hy = induced->hy;
9494  hzed = induced->hzed;
9495  xmin = induced->xmin;
9496  ymin = induced->ymin;
9497  zmin = induced->zmin;
9498  xmax = induced->xmax;
9499  ymax = induced->ymax;
9500  zmax = induced->zmax;
9501  xlen = xmax-xmin;
9502  ylen = ymax-ymin;
9503  zlen = zmax-zmin;
9504 
9505  /* If we aren't in the current position, then we're done */
9506  if (atom->partID == 0) return;
9507 
9508  /* Make sure we're on the grid */
9509  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
9510  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
9511  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
9512  Vnm_print(2, "qfMutualPolForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
9513  fflush(stderr);
9514  } else {
9515 
9516  /* Convert the atom position to grid coordinates */
9517  position[0] = apos[0] - xmin;
9518  position[1] = apos[1] - ymin;
9519  position[2] = apos[2] - zmin;
9520  ifloat = position[0]/hx;
9521  jfloat = position[1]/hy;
9522  kfloat = position[2]/hzed;
9523  ip1 = (int)ceil(ifloat);
9524  ip2 = ip1 + 2;
9525  im1 = (int)floor(ifloat);
9526  im2 = im1 - 2;
9527  jp1 = (int)ceil(jfloat);
9528  jp2 = jp1 + 2;
9529  jm1 = (int)floor(jfloat);
9530  jm2 = jm1 - 2;
9531  kp1 = (int)ceil(kfloat);
9532  kp2 = kp1 + 2;
9533  km1 = (int)floor(kfloat);
9534  km2 = km1 - 2;
9535 
9536  /* This step shouldn't be necessary, but it saves nasty debugging
9537  * later on if something goes wrong */
9538  ip2 = VMIN2(ip2,nx-1);
9539  ip1 = VMIN2(ip1,nx-1);
9540  im1 = VMAX2(im1,0);
9541  im2 = VMAX2(im2,0);
9542  jp2 = VMIN2(jp2,ny-1);
9543  jp1 = VMIN2(jp1,ny-1);
9544  jm1 = VMAX2(jm1,0);
9545  jm2 = VMAX2(jm2,0);
9546  kp2 = VMIN2(kp2,nz-1);
9547  kp1 = VMIN2(kp1,nz-1);
9548  km1 = VMAX2(km1,0);
9549  km2 = VMAX2(km2,0);
9550 
9551  for (ii=im2; ii<=ip2; ii++) {
9552  mi = VFCHI4(ii,ifloat);
9553  mx = bspline4(mi);
9554  dmx = dbspline4(mi);
9555  d2mx = d2bspline4(mi);
9556  for (jj=jm2; jj<=jp2; jj++) {
9557  mj = VFCHI4(jj,jfloat);
9558  my = bspline4(mj);
9559  dmy = dbspline4(mj);
9560  d2my = d2bspline4(mj);
9561  for (kk=km2; kk<=kp2; kk++) {
9562  mk = VFCHI4(kk,kfloat);
9563  mz = bspline4(mk);
9564  dmz = dbspline4(mk);
9565  d2mz = d2bspline4(mk);
9566  f = u[IJK(ii,jj,kk)];
9567  fnl = unl[IJK(ii,jj,kk)];
9568 
9569  /* The gradient of the reaction field
9570  due to induced dipoles */
9571  de[0][0] += f*d2mx*my*mz/(hx*hx);
9572  de[1][0] += f*dmx*dmy*mz/(hy*hx);
9573  de[1][1] += f*mx*d2my*mz/(hy*hy);
9574  de[2][0] += f*dmx*my*dmz/(hx*hzed);
9575  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
9576  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
9577 
9578  /* The gradient of the reaction field
9579  due to non-local induced dipoles */
9580  denl[0][0] += fnl*d2mx*my*mz/(hx*hx);
9581  denl[1][0] += fnl*dmx*dmy*mz/(hy*hx);
9582  denl[1][1] += fnl*mx*d2my*mz/(hy*hy);
9583  denl[2][0] += fnl*dmx*my*dmz/(hx*hzed);
9584  denl[2][1] += fnl*mx*dmy*dmz/(hy*hzed);
9585  denl[2][2] += fnl*mx*my*d2mz/(hzed*hzed);
9586  }
9587  }
9588  }
9589  }
9590 
9591  /* mutual polarization force */
9592  force[0] = -(de[0][0]*uixnl + de[1][0]*uiynl + de[2][0]*uiznl);
9593  force[1] = -(de[1][0]*uixnl + de[1][1]*uiynl + de[2][1]*uiznl);
9594  force[2] = -(de[2][0]*uixnl + de[2][1]*uiynl + de[2][2]*uiznl);
9595  force[0] -= denl[0][0]*uix + denl[1][0]*uiy + denl[2][0]*uiz;
9596  force[1] -= denl[1][0]*uix + denl[1][1]*uiy + denl[2][1]*uiz;
9597  force[2] -= denl[2][0]*uix + denl[2][1]*uiy + denl[2][2]*uiz;
9598 
9599  force[0] = 0.5 * force[0];
9600  force[1] = 0.5 * force[1];
9601  force[2] = 0.5 * force[2];
9602 
9603 }
9604 
9605 VPUBLIC void Vpmg_ibMutualPolForce(Vpmg *thee, Vgrid *induced, Vgrid *nlinduced,
9606  int atomID, double force[3]) {
9607 
9608  Vatom *atom;
9609  Valist *alist;
9610  Vacc *acc;
9611  Vpbe *pbe;
9612  Vsurf_Meth srfm;
9613 
9614  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
9615  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
9616  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
9617  double izmagic;
9618  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9619 
9620  VASSERT(thee != VNULL); /* We need a PMG object with PBE info. */
9621  VASSERT(induced != VNULL); /* We need the potential due to induced dipoles. */
9622  VASSERT(nlinduced != VNULL); /* We need the potential due to non-local induced dipoles. */
9623  VASSERT (!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
9624 
9625  atom = Valist_getAtom(thee->pbe->alist, atomID);
9626  VASSERT (atom->partID != 0); /* Currently all atoms must be in the same partition. */
9627 
9628  acc = thee->pbe->acc;
9629  srfm = thee->surfMeth;
9630  apos = Vatom_getPosition(atom);
9631  arad = Vatom_getRadius(atom);
9632 
9633  /* Reset force */
9634  force[0] = 0.0;
9635  force[1] = 0.0;
9636  force[2] = 0.0;
9637 
9638  /* If we aren't in the current position, then we're done */
9639  if (atom->partID == 0) return;
9640 
9641  /* Get PBE info */
9642  pbe = thee->pbe;
9643  acc = pbe->acc;
9644  alist = pbe->alist;
9645  irad = Vpbe_getMaxIonRadius(pbe);
9646  zkappa2 = Vpbe_getZkappa2(pbe);
9647  izmagic = 1.0/Vpbe_getZmagic(pbe);
9648 
9649  VASSERT (zkappa2 > VPMGSMALL); /* Should be a check for this further up.*/
9650 
9651  /* Mesh info */
9652  nx = induced->nx;
9653  ny = induced->ny;
9654  nz = induced->nz;
9655  hx = induced->hx;
9656  hy = induced->hy;
9657  hzed = induced->hzed;
9658  xmin = induced->xmin;
9659  ymin = induced->ymin;
9660  zmin = induced->zmin;
9661  xmax = induced->xmax;
9662  ymax = induced->ymax;
9663  zmax = induced->zmax;
9664  xlen = xmax-xmin;
9665  ylen = ymax-ymin;
9666  zlen = zmax-zmin;
9667 
9668  /* Make sure we're on the grid */
9669  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9670  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9671  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9672  Vnm_print(2, "Vpmg_ibMutalPolForce: Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", apos[0], apos[1], apos[2]);
9673  Vnm_print(2, "Vpmg_ibMutalPolForce: xmin = %g, xmax = %g\n", xmin, xmax);
9674  Vnm_print(2, "Vpmg_ibMutalPolForce: ymin = %g, ymax = %g\n", ymin, ymax);
9675  Vnm_print(2, "Vpmg_ibMutalPolForce: zmin = %g, zmax = %g\n", zmin, zmax);
9676  fflush(stderr);
9677  } else {
9678 
9679  /* Convert the atom position to grid reference frame */
9680  position[0] = apos[0] - xmin;
9681  position[1] = apos[1] - ymin;
9682  position[2] = apos[2] - zmin;
9683 
9684  /* Integrate over points within this atom's (inflated) radius */
9685  rtot = (irad + arad + thee->splineWin);
9686  rtot2 = VSQR(rtot);
9687  dx = rtot + 0.5*hx;
9688  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
9689  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
9690  for (i=imin; i<=imax; i++) {
9691  dx2 = VSQR(position[0] - hx*i);
9692  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
9693  else dy = 0.5*hy;
9694  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
9695  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
9696  for (j=jmin; j<=jmax; j++) {
9697  dy2 = VSQR(position[1] - hy*j);
9698  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
9699  else dz = 0.5*hzed;
9700  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
9701  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
9702  for (k=kmin; k<=kmax; k++) {
9703  dz2 = VSQR(k*hzed - position[2]);
9704  /* See if grid point is inside ivdw radius and set ccf
9705  * accordingly (do spline assignment here) */
9706  if ((dz2 + dy2 + dx2) <= rtot2) {
9707  gpos[0] = i*hx + xmin;
9708  gpos[1] = j*hy + ymin;
9709  gpos[2] = k*hzed + zmin;
9710  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad,
9711  atom, tgrad);
9712  fmag = induced->data[IJK(i,j,k)];
9713  fmag *= nlinduced->data[IJK(i,j,k)];
9714  fmag *= thee->kappa[IJK(i,j,k)];
9715  force[0] += (zkappa2*fmag*tgrad[0]);
9716  force[1] += (zkappa2*fmag*tgrad[1]);
9717  force[2] += (zkappa2*fmag*tgrad[2]);
9718  }
9719  } /* k loop */
9720  } /* j loop */
9721  } /* i loop */
9722  }
9723 
9724  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
9725  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
9726  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
9727 }
9728 
9729 VPUBLIC void Vpmg_dbMutualPolForce(Vpmg *thee, Vgrid *induced,
9730  Vgrid *nlinduced, int atomID,
9731  double force[3]) {
9732 
9733  Vatom *atom;
9734  Vacc *acc;
9735  Vpbe *pbe;
9736  Vsurf_Meth srfm;
9737 
9738  double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
9739  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
9740  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
9741  double *u, *unl, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
9742  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
9743  double dHzijkm1[3];
9744  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9745 
9746  VASSERT(thee != VNULL); /* PMG object with PBE info. */
9747  VASSERT(induced != VNULL); /* potential due to induced dipoles.*/
9748  VASSERT(nlinduced != VNULL); /* potential due to non-local induced dipoles.*/
9749 
9750  acc = thee->pbe->acc;
9751  srfm = thee->surfMeth;
9752  atom = Valist_getAtom(thee->pbe->alist, atomID);
9753  VASSERT (atom->partID != 0); /* all atoms must be in the same partition.*/
9754  apos = Vatom_getPosition(atom);
9755  arad = Vatom_getRadius(atom);
9756 
9757  /* Reset force */
9758  force[0] = 0.0;
9759  force[1] = 0.0;
9760  force[2] = 0.0;
9761 
9762  /* Get PBE info */
9763  pbe = thee->pbe;
9764  acc = pbe->acc;
9765  epsp = Vpbe_getSoluteDiel(pbe);
9766  epsw = Vpbe_getSolventDiel(pbe);
9767  kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
9768  izmagic = 1.0/Vpbe_getZmagic(pbe);
9769 
9770  deps = (epsw - epsp);
9771  depsi = 1.0/deps;
9772  VASSERT(VABS(deps) > VPMGSMALL);
9773 
9774  /* Mesh info */
9775  nx = thee->pmgp->nx;
9776  ny = thee->pmgp->ny;
9777  nz = thee->pmgp->nz;
9778  hx = thee->pmgp->hx;
9779  hy = thee->pmgp->hy;
9780  hzed = thee->pmgp->hzed;
9781  xlen = thee->pmgp->xlen;
9782  ylen = thee->pmgp->ylen;
9783  zlen = thee->pmgp->zlen;
9784  xmin = thee->pmgp->xmin;
9785  ymin = thee->pmgp->ymin;
9786  zmin = thee->pmgp->zmin;
9787  xmax = thee->pmgp->xmax;
9788  ymax = thee->pmgp->ymax;
9789  zmax = thee->pmgp->zmax;
9790  u = induced->data;
9791  unl = nlinduced->data;
9792 
9793  /* Make sure we're on the grid */
9794  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9795  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9796  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9797  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", apos[0], apos[1], apos[2]);
9798  Vnm_print(2, "Vpmg_dbMutualPolForce: xmin = %g, xmax = %g\n", xmin, xmax);
9799  Vnm_print(2, "Vpmg_dbMutualPolForce: ymin = %g, ymax = %g\n", ymin, ymax);
9800  Vnm_print(2, "Vpmg_dbMutualPolForce: zmin = %g, zmax = %g\n", zmin, zmax);
9801  fflush(stderr);
9802  } else {
9803 
9804  /* Convert the atom position to grid reference frame */
9805  position[0] = apos[0] - xmin;
9806  position[1] = apos[1] - ymin;
9807  position[2] = apos[2] - zmin;
9808 
9809  /* Integrate over points within this atom's (inflated) radius */
9810  rtot = (arad + thee->splineWin);
9811  rtot2 = VSQR(rtot);
9812  dx = rtot/hx;
9813  imin = (int)floor((position[0]-rtot)/hx);
9814  if (imin < 1) {
9815  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9816  return;
9817  }
9818  imax = (int)ceil((position[0]+rtot)/hx);
9819  if (imax > (nx-2)) {
9820  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9821  return;
9822  }
9823  jmin = (int)floor((position[1]-rtot)/hy);
9824  if (jmin < 1) {
9825  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9826  return;
9827  }
9828  jmax = (int)ceil((position[1]+rtot)/hy);
9829  if (jmax > (ny-2)) {
9830  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9831  return;
9832  }
9833  kmin = (int)floor((position[2]-rtot)/hzed);
9834  if (kmin < 1) {
9835  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9836  return;
9837  }
9838  kmax = (int)ceil((position[2]+rtot)/hzed);
9839  if (kmax > (nz-2)) {
9840  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9841  return;
9842  }
9843  for (i=imin; i<=imax; i++) {
9844  for (j=jmin; j<=jmax; j++) {
9845  for (k=kmin; k<=kmax; k++) {
9846  /* i,j,k */
9847  gpos[0] = (i+0.5)*hx + xmin;
9848  gpos[1] = j*hy + ymin;
9849  gpos[2] = k*hzed + zmin;
9850  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
9851  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9852  atom, dHxijk);
9853  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
9854  gpos[0] = i*hx + xmin;
9855  gpos[1] = (j+0.5)*hy + ymin;
9856  gpos[2] = k*hzed + zmin;
9857  Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
9858  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9859  atom, dHyijk);
9860  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
9861  gpos[0] = i*hx + xmin;
9862  gpos[1] = j*hy + ymin;
9863  gpos[2] = (k+0.5)*hzed + zmin;
9864  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
9865  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9866  atom, dHzijk);
9867  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
9868  /* i-1,j,k */
9869  gpos[0] = (i-0.5)*hx + xmin;
9870  gpos[1] = j*hy + ymin;
9871  gpos[2] = k*hzed + zmin;
9872  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
9873  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9874  atom, dHxim1jk);
9875  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
9876  /* i,j-1,k */
9877  gpos[0] = i*hx + xmin;
9878  gpos[1] = (j-0.5)*hy + ymin;
9879  gpos[2] = k*hzed + zmin;
9880  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
9881  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9882  atom, dHyijm1k);
9883  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
9884  /* i,j,k-1 */
9885  gpos[0] = i*hx + xmin;
9886  gpos[1] = j*hy + ymin;
9887  gpos[2] = (k-0.5)*hzed + zmin;
9888  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
9889  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9890  atom, dHzijkm1);
9891  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
9892  dbFmag = unl[IJK(i,j,k)];
9893  tgrad[0] =
9894  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9895  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9896  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9897  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9898  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9899  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9900  tgrad[1] =
9901  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9902  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9903  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9904  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9905  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9906  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9907  tgrad[2] =
9908  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9909  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9910  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9911  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9912  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9913  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9914  force[0] += (dbFmag*tgrad[0]);
9915  force[1] += (dbFmag*tgrad[1]);
9916  force[2] += (dbFmag*tgrad[2]);
9917  } /* k loop */
9918  } /* j loop */
9919  } /* i loop */
9920 
9921  force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
9922  force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
9923  force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
9924  }
9925 }
9926 
9927 #endif /* if defined(WITH_TINKER) */
9928 
9929 VPRIVATE void fillcoCoefSpline4(Vpmg *thee) {
9930 
9931  Valist *alist;
9932  Vpbe *pbe;
9933  Vatom *atom;
9934  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
9935  double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
9936  double irad, dx, dy, dz, epsw, epsp, w2i;
9937  double hx, hy, hzed, *apos, arad, sctot2;
9938  double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin;
9939  double dist, value, denom, sm, sm2, sm3, sm4, sm5, sm6, sm7;
9940  double e, e2, e3, e4, e5, e6, e7;
9941  double b, b2, b3, b4, b5, b6, b7;
9942  double c0, c1, c2, c3, c4, c5, c6, c7;
9943  double ic0, ic1, ic2, ic3, ic4, ic5, ic6, ic7;
9944  int i, j, k, nx, ny, nz, iatom;
9945  int imin, imax, jmin, jmax, kmin, kmax;
9946 
9947  VASSERT(thee != VNULL);
9948  splineWin = thee->splineWin;
9949 
9950  /* Get PBE info */
9951  pbe = thee->pbe;
9952  alist = pbe->alist;
9953  irad = Vpbe_getMaxIonRadius(pbe);
9954  ionstr = Vpbe_getBulkIonicStrength(pbe);
9955  epsw = Vpbe_getSolventDiel(pbe);
9956  epsp = Vpbe_getSoluteDiel(pbe);
9957 
9958  /* Mesh info */
9959  nx = thee->pmgp->nx;
9960  ny = thee->pmgp->ny;
9961  nz = thee->pmgp->nz;
9962  hx = thee->pmgp->hx;
9963  hy = thee->pmgp->hy;
9964  hzed = thee->pmgp->hzed;
9965 
9966  /* Define the total domain size */
9967  xlen = thee->pmgp->xlen;
9968  ylen = thee->pmgp->ylen;
9969  zlen = thee->pmgp->zlen;
9970 
9971  /* Define the min/max dimensions */
9972  xmin = thee->pmgp->xcent - (xlen/2.0);
9973  ymin = thee->pmgp->ycent - (ylen/2.0);
9974  zmin = thee->pmgp->zcent - (zlen/2.0);
9975  xmax = thee->pmgp->xcent + (xlen/2.0);
9976  ymax = thee->pmgp->ycent + (ylen/2.0);
9977  zmax = thee->pmgp->zcent + (zlen/2.0);
9978 
9979  /* This is a floating point parameter related to the non-zero nature of the
9980  * bulk ionic strength. If the ionic strength is greater than zero; this
9981  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
9982  * Otherwise, this parameter is set to 0.0 */
9983  if (ionstr > VPMGSMALL) ionmask = 1.0;
9984  else ionmask = 0.0;
9985 
9986  /* Reset the kappa, epsx, epsy, and epsz arrays */
9987  for (i=0; i<(nx*ny*nz); i++) {
9988  thee->kappa[i] = 1.0;
9989  thee->epsx[i] = 1.0;
9990  thee->epsy[i] = 1.0;
9991  thee->epsz[i] = 1.0;
9992  }
9993 
9994  /* Loop through the atoms and do assign the dielectric */
9995  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
9996 
9997  atom = Valist_getAtom(alist, iatom);
9998  apos = Vatom_getPosition(atom);
9999  arad = Vatom_getRadius(atom);
10000 
10001  b = arad - splineWin;
10002  e = arad + splineWin;
10003  e2 = e * e;
10004  e3 = e2 * e;
10005  e4 = e3 * e;
10006  e5 = e4 * e;
10007  e6 = e5 * e;
10008  e7 = e6 * e;
10009  b2 = b * b;
10010  b3 = b2 * b;
10011  b4 = b3 * b;
10012  b5 = b4 * b;
10013  b6 = b5 * b;
10014  b7 = b6 * b;
10015  denom = e7 - 7.0*b*e6 + 21.0*b2*e5 - 35.0*e4*b3
10016  + 35.0*e3*b4 - 21.0*b5*e2 + 7.0*e*b6 - b7;
10017  c0 = b4*(35.0*e3 - 21.0*b*e2 + 7*e*b2 - b3)/denom;
10018  c1 = -140.0*b3*e3/denom;
10019  c2 = 210.0*e2*b2*(e + b)/denom;
10020  c3 = -140.0*e*b*(e2 + 3.0*b*e + b2)/denom;
10021  c4 = 35.0*(e3 + 9.0*b*e2 + + 9.0*e*b2 + b3)/denom;
10022  c5 = -84.0*(e2 + 3.0*b*e + b2)/denom;
10023  c6 = 70.0*(e + b)/denom;
10024  c7 = -20.0/denom;
10025 
10026  b = irad + arad - splineWin;
10027  e = irad + arad + splineWin;
10028  e2 = e * e;
10029  e3 = e2 * e;
10030  e4 = e3 * e;
10031  e5 = e4 * e;
10032  e6 = e5 * e;
10033  e7 = e6 * e;
10034  b2 = b * b;
10035  b3 = b2 * b;
10036  b4 = b3 * b;
10037  b5 = b4 * b;
10038  b6 = b5 * b;
10039  b7 = b6 * b;
10040  denom = e7 - 7.0*b*e6 + 21.0*b2*e5 - 35.0*e4*b3
10041  + 35.0*e3*b4 - 21.0*b5*e2 + 7.0*e*b6 - b7;
10042  ic0 = b4*(35.0*e3 - 21.0*b*e2 + 7*e*b2 - b3)/denom;
10043  ic1 = -140.0*b3*e3/denom;
10044  ic2 = 210.0*e2*b2*(e + b)/denom;
10045  ic3 = -140.0*e*b*(e2 + 3.0*b*e + b2)/denom;
10046  ic4 = 35.0*(e3 + 9.0*b*e2 + + 9.0*e*b2 + b3)/denom;
10047  ic5 = -84.0*(e2 + 3.0*b*e + b2)/denom;
10048  ic6 = 70.0*(e + b)/denom;
10049  ic7 = -20.0/denom;
10050 
10051  /* Make sure we're on the grid */
10052  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
10053  (apos[1]<=ymin) || (apos[1]>=ymax) || \
10054  (apos[2]<=zmin) || (apos[2]>=zmax)) {
10055  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
10056  (thee->pmgp->bcfl != BCFL_MAP)) {
10057  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
10058  %4.3f) is off the mesh (ignoring):\n",
10059  iatom, apos[0], apos[1], apos[2]);
10060  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
10061  xmin, xmax);
10062  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
10063  ymin, ymax);
10064  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
10065  zmin, zmax);
10066  }
10067  fflush(stderr);
10068 
10069  } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
10070 
10071  /* Convert the atom position to grid reference frame */
10072  position[0] = apos[0] - xmin;
10073  position[1] = apos[1] - ymin;
10074  position[2] = apos[2] - zmin;
10075 
10076  /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
10077  * ASSIGNMENT (Steps #1-3) */
10078  itot = irad + arad + splineWin;
10079  itot2 = VSQR(itot);
10080  ictot = VMAX2(0, (irad + arad - splineWin));
10081  ictot2 = VSQR(ictot);
10082  stot = arad + splineWin;
10083  stot2 = VSQR(stot);
10084  sctot = VMAX2(0, (arad - splineWin));
10085  sctot2 = VSQR(sctot);
10086 
10087  /* We'll search over grid points which are in the greater of
10088  * these two radii */
10089  rtot = VMAX2(itot, stot);
10090  rtot2 = VMAX2(itot2, stot2);
10091  dx = rtot + 0.5*hx;
10092  dy = rtot + 0.5*hy;
10093  dz = rtot + 0.5*hzed;
10094  imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
10095  imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
10096  jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
10097  jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
10098  kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
10099  kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
10100  for (i=imin; i<=imax; i++) {
10101  dx2 = VSQR(position[0] - hx*i);
10102  for (j=jmin; j<=jmax; j++) {
10103  dy2 = VSQR(position[1] - hy*j);
10104  for (k=kmin; k<=kmax; k++) {
10105  dz2 = VSQR(position[2] - k*hzed);
10106 
10107  /* ASSIGN CCF */
10108  if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
10109  dist2 = dz2 + dy2 + dx2;
10110  if (dist2 >= itot2) {
10111  ;
10112  }
10113  if (dist2 <= ictot2) {
10114  thee->kappa[IJK(i,j,k)] = 0.0;
10115  }
10116  if ((dist2 < itot2) && (dist2 > ictot2)) {
10117  dist = VSQRT(dist2);
10118  sm = dist;
10119  sm2 = dist2;
10120  sm3 = sm2 * sm;
10121  sm4 = sm3 * sm;
10122  sm5 = sm4 * sm;
10123  sm6 = sm5 * sm;
10124  sm7 = sm6 * sm;
10125  value = ic0 + ic1*sm + ic2*sm2 + ic3*sm3
10126  + ic4*sm4 + ic5*sm5 + ic6*sm6 + ic7*sm7;
10127  if (value > 1.0) {
10128  value = 1.0;
10129  } else if (value < 0.0){
10130  value = 0.0;
10131  }
10132  thee->kappa[IJK(i,j,k)] *= value;
10133  }
10134  }
10135 
10136  /* ASSIGN A1CF */
10137  if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
10138  dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
10139  if (dist2 >= stot2) {
10140  thee->epsx[IJK(i,j,k)] *= 1.0;
10141  }
10142  if (dist2 <= sctot2) {
10143  thee->epsx[IJK(i,j,k)] = 0.0;
10144  }
10145  if ((dist2 > sctot2) && (dist2 < stot2)) {
10146  dist = VSQRT(dist2);
10147  sm = dist;
10148  sm2 = VSQR(sm);
10149  sm3 = sm2 * sm;
10150  sm4 = sm3 * sm;
10151  sm5 = sm4 * sm;
10152  sm6 = sm5 * sm;
10153  sm7 = sm6 * sm;
10154  value = c0 + c1*sm + c2*sm2 + c3*sm3
10155  + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
10156  if (value > 1.0) {
10157  value = 1.0;
10158  } else if (value < 0.0){
10159  value = 0.0;
10160  }
10161  thee->epsx[IJK(i,j,k)] *= value;
10162  }
10163  }
10164 
10165  /* ASSIGN A2CF */
10166  if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
10167  dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
10168  if (dist2 >= stot2) {
10169  thee->epsy[IJK(i,j,k)] *= 1.0;
10170  }
10171  if (dist2 <= sctot2) {
10172  thee->epsy[IJK(i,j,k)] = 0.0;
10173  }
10174  if ((dist2 > sctot2) && (dist2 < stot2)) {
10175  dist = VSQRT(dist2);
10176  sm = dist;
10177  sm2 = VSQR(sm);
10178  sm3 = sm2 * sm;
10179  sm4 = sm3 * sm;
10180  sm5 = sm4 * sm;
10181  sm6 = sm5 * sm;
10182  sm7 = sm6 * sm;
10183  value = c0 + c1*sm + c2*sm2 + c3*sm3
10184  + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
10185  if (value > 1.0) {
10186  value = 1.0;
10187  } else if (value < 0.0){
10188  value = 0.0;
10189  }
10190  thee->epsy[IJK(i,j,k)] *= value;
10191  }
10192  }
10193 
10194  /* ASSIGN A3CF */
10195  if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
10196  dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
10197  if (dist2 >= stot2) {
10198  thee->epsz[IJK(i,j,k)] *= 1.0;
10199  }
10200  if (dist2 <= sctot2) {
10201  thee->epsz[IJK(i,j,k)] = 0.0;
10202  }
10203  if ((dist2 > sctot2) && (dist2 < stot2)) {
10204  dist = VSQRT(dist2);
10205  sm = dist;
10206  sm2 = dist2;
10207  sm3 = sm2 * sm;
10208  sm4 = sm3 * sm;
10209  sm5 = sm4 * sm;
10210  sm6 = sm5 * sm;
10211  sm7 = sm6 * sm;
10212  value = c0 + c1*sm + c2*sm2 + c3*sm3
10213  + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
10214  if (value > 1.0) {
10215  value = 1.0;
10216  } else if (value < 0.0){
10217  value = 0.0;
10218  }
10219  thee->epsz[IJK(i,j,k)] *= value;
10220  }
10221  }
10222 
10223 
10224  } /* k loop */
10225  } /* j loop */
10226  } /* i loop */
10227  } /* endif (on the mesh) */
10228  } /* endfor (over all atoms) */
10229 
10230  Vnm_print(0, "Vpmg_fillco: filling coefficient arrays\n");
10231  /* Interpret markings and fill the coefficient arrays */
10232  for (k=0; k<nz; k++) {
10233  for (j=0; j<ny; j++) {
10234  for (i=0; i<nx; i++) {
10235 
10236  thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
10237  thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)]
10238  + epsp;
10239  thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)]
10240  + epsp;
10241  thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)]
10242  + epsp;
10243 
10244  } /* i loop */
10245  } /* j loop */
10246  } /* k loop */
10247 
10248 }
10249 
10250 VPUBLIC void fillcoPermanentInduced(Vpmg *thee) {
10251 
10252  Valist *alist;
10253  Vpbe *pbe;
10254  Vatom *atom;
10255  /* Coversions */
10256  double zmagic, f;
10257  /* Grid */
10258  double xmin, xmax, ymin, ymax, zmin, zmax;
10259  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
10260  double hx, hy, hzed, *apos;
10261  /* Multipole */
10262  double charge, *dipole,*quad;
10263  double c,ux,uy,uz,qxx,qyx,qyy,qzx,qzy,qzz,qave;
10264  /* B-spline weights */
10265  double mx,my,mz,dmx,dmy,dmz,d2mx,d2my,d2mz;
10266  double mi,mj,mk;
10267  /* Loop variables */
10268  int i, ii, jj, kk, nx, ny, nz, iatom;
10269  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
10270 
10271  VASSERT(thee != VNULL);
10272 
10273  /* Get PBE info */
10274  pbe = thee->pbe;
10275  alist = pbe->alist;
10276  zmagic = Vpbe_getZmagic(pbe);
10277 
10278  /* Mesh info */
10279  nx = thee->pmgp->nx;
10280  ny = thee->pmgp->ny;
10281  nz = thee->pmgp->nz;
10282  hx = thee->pmgp->hx;
10283  hy = thee->pmgp->hy;
10284  hzed = thee->pmgp->hzed;
10285 
10286  /* Conversion */
10287  f = zmagic/(hx*hy*hzed);
10288 
10289  /* Define the total domain size */
10290  xlen = thee->pmgp->xlen;
10291  ylen = thee->pmgp->ylen;
10292  zlen = thee->pmgp->zlen;
10293 
10294  /* Define the min/max dimensions */
10295  xmin = thee->pmgp->xcent - (xlen/2.0);
10296  ymin = thee->pmgp->ycent - (ylen/2.0);
10297  zmin = thee->pmgp->zcent - (zlen/2.0);
10298  xmax = thee->pmgp->xcent + (xlen/2.0);
10299  ymax = thee->pmgp->ycent + (ylen/2.0);
10300  zmax = thee->pmgp->zcent + (zlen/2.0);
10301 
10302  /* Fill in the source term (permanent atomic multipoles
10303  and induced dipoles) */
10304  Vnm_print(0, "fillcoPermanentInduced: filling in source term.\n");
10305  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
10306 
10307  atom = Valist_getAtom(alist, iatom);
10308  apos = Vatom_getPosition(atom);
10309 
10310  c = Vatom_getCharge(atom)*f;
10311 
10312 #if defined(WITH_TINKER)
10313  dipole = Vatom_getDipole(atom);
10314  ux = dipole[0]/hx*f;
10315  uy = dipole[1]/hy*f;
10316  uz = dipole[2]/hzed*f;
10317  dipole = Vatom_getInducedDipole(atom);
10318  ux = ux + dipole[0]/hx*f;
10319  uy = uy + dipole[1]/hy*f;
10320  uz = uz + dipole[2]/hzed*f;
10321  quad = Vatom_getQuadrupole(atom);
10322  qxx = (1.0/3.0)*quad[0]/(hx*hx)*f;
10323  qyx = (2.0/3.0)*quad[3]/(hx*hy)*f;
10324  qyy = (1.0/3.0)*quad[4]/(hy*hy)*f;
10325  qzx = (2.0/3.0)*quad[6]/(hzed*hx)*f;
10326  qzy = (2.0/3.0)*quad[7]/(hzed*hy)*f;
10327  qzz = (1.0/3.0)*quad[8]/(hzed*hzed)*f;
10328 #else
10329  ux = 0.0;
10330  uy = 0.0;
10331  uz = 0.0;
10332  qxx = 0.0;
10333  qyx = 0.0;
10334  qyy = 0.0;
10335  qzx = 0.0;
10336  qzy = 0.0;
10337  qzz = 0.0;
10338 #endif /* if defined(WITH_TINKER) */
10339 
10340  /* Make sure we're on the grid */
10341  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
10342  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
10343  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
10344  Vnm_print(2, "fillcoPermanentMultipole: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring this atom):\n", iatom, apos[0], apos[1], apos[2]);
10345  Vnm_print(2, "fillcoPermanentMultipole: xmin = %g, xmax = %g\n", xmin, xmax);
10346  Vnm_print(2, "fillcoPermanentMultipole: ymin = %g, ymax = %g\n", ymin, ymax);
10347  Vnm_print(2, "fillcoPermanentMultipole: zmin = %g, zmax = %g\n", zmin, zmax);
10348  fflush(stderr);
10349  } else {
10350 
10351  /* Convert the atom position to grid reference frame */
10352  position[0] = apos[0] - xmin;
10353  position[1] = apos[1] - ymin;
10354  position[2] = apos[2] - zmin;
10355 
10356  /* Figure out which vertices we're next to */
10357  ifloat = position[0]/hx;
10358  jfloat = position[1]/hy;
10359  kfloat = position[2]/hzed;
10360 
10361  ip1 = (int)ceil(ifloat);
10362  ip2 = ip1 + 2;
10363  im1 = (int)floor(ifloat);
10364  im2 = im1 - 2;
10365  jp1 = (int)ceil(jfloat);
10366  jp2 = jp1 + 2;
10367  jm1 = (int)floor(jfloat);
10368  jm2 = jm1 - 2;
10369  kp1 = (int)ceil(kfloat);
10370  kp2 = kp1 + 2;
10371  km1 = (int)floor(kfloat);
10372  km2 = km1 - 2;
10373 
10374  /* This step shouldn't be necessary, but it saves nasty debugging
10375  * later on if something goes wrong */
10376  ip2 = VMIN2(ip2,nx-1);
10377  ip1 = VMIN2(ip1,nx-1);
10378  im1 = VMAX2(im1,0);
10379  im2 = VMAX2(im2,0);
10380  jp2 = VMIN2(jp2,ny-1);
10381  jp1 = VMIN2(jp1,ny-1);
10382  jm1 = VMAX2(jm1,0);
10383  jm2 = VMAX2(jm2,0);
10384  kp2 = VMIN2(kp2,nz-1);
10385  kp1 = VMIN2(kp1,nz-1);
10386  km1 = VMAX2(km1,0);
10387  km2 = VMAX2(km2,0);
10388 
10389  /* Now assign fractions of the charge to the nearby verts */
10390  for (ii=im2; ii<=ip2; ii++) {
10391  mi = VFCHI4(ii,ifloat);
10392  mx = bspline4(mi);
10393  dmx = dbspline4(mi);
10394  d2mx = d2bspline4(mi);
10395  for (jj=jm2; jj<=jp2; jj++) {
10396  mj = VFCHI4(jj,jfloat);
10397  my = bspline4(mj);
10398  dmy = dbspline4(mj);
10399  d2my = d2bspline4(mj);
10400  for (kk=km2; kk<=kp2; kk++) {
10401  mk = VFCHI4(kk,kfloat);
10402  mz = bspline4(mk);
10403  dmz = dbspline4(mk);
10404  d2mz = d2bspline4(mk);
10405  charge = mx*my*mz*c -
10406  dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz +
10407  d2mx*my*mz*qxx +
10408  dmx*dmy*mz*qyx + mx*d2my*mz*qyy +
10409  dmx*my*dmz*qzx + mx*dmy*dmz*qzy + mx*my*d2mz*qzz;
10410  thee->charge[IJK(ii,jj,kk)] += charge;
10411 
10412  }
10413  }
10414  }
10415  } /* endif (on the mesh) */
10416 
10417  } /* endfor (each atom) */
10418 }
10419 
10420 VPRIVATE void fillcoCoefSpline3(Vpmg *thee) {
10421 
10422  Valist *alist;
10423  Vpbe *pbe;
10424  Vatom *atom;
10425  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
10426  double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
10427  double irad, dx, dy, dz, epsw, epsp, w2i;
10428  double hx, hy, hzed, *apos, arad, sctot2;
10429  double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin;
10430  double dist, value, denom, sm, sm2, sm3, sm4, sm5;
10431  double e, e2, e3, e4, e5;
10432  double b, b2, b3, b4, b5;
10433  double c0, c1, c2, c3, c4, c5;
10434  double ic0, ic1, ic2, ic3, ic4, ic5;
10435  int i, j, k, nx, ny, nz, iatom;
10436  int imin, imax, jmin, jmax, kmin, kmax;
10437 
10438  VASSERT(thee != VNULL);
10439  splineWin = thee->splineWin;
10440 
10441  /* Get PBE info */
10442  pbe = thee->pbe;
10443  alist = pbe->alist;
10444  irad = Vpbe_getMaxIonRadius(pbe);
10445  ionstr = Vpbe_getBulkIonicStrength(pbe);
10446  epsw = Vpbe_getSolventDiel(pbe);
10447  epsp = Vpbe_getSoluteDiel(pbe);
10448 
10449  /* Mesh info */
10450  nx = thee->pmgp->nx;
10451  ny = thee->pmgp->ny;
10452  nz = thee->pmgp->nz;
10453  hx = thee->pmgp->hx;
10454  hy = thee->pmgp->hy;
10455  hzed = thee->pmgp->hzed;
10456 
10457  /* Define the total domain size */
10458  xlen = thee->pmgp->xlen;
10459  ylen = thee->pmgp->ylen;
10460  zlen = thee->pmgp->zlen;
10461 
10462  /* Define the min/max dimensions */
10463  xmin = thee->pmgp->xcent - (xlen/2.0);
10464  ymin = thee->pmgp->ycent - (ylen/2.0);
10465  zmin = thee->pmgp->zcent - (zlen/2.0);
10466  xmax = thee->pmgp->xcent + (xlen/2.0);
10467  ymax = thee->pmgp->ycent + (ylen/2.0);
10468  zmax = thee->pmgp->zcent + (zlen/2.0);
10469 
10470  /* This is a floating point parameter related to the non-zero nature of the
10471  * bulk ionic strength. If the ionic strength is greater than zero; this
10472  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
10473  * Otherwise, this parameter is set to 0.0 */
10474  if (ionstr > VPMGSMALL) ionmask = 1.0;
10475  else ionmask = 0.0;
10476 
10477  /* Reset the kappa, epsx, epsy, and epsz arrays */
10478  for (i=0; i<(nx*ny*nz); i++) {
10479  thee->kappa[i] = 1.0;
10480  thee->epsx[i] = 1.0;
10481  thee->epsy[i] = 1.0;
10482  thee->epsz[i] = 1.0;
10483  }
10484 
10485  /* Loop through the atoms and do assign the dielectric */
10486  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
10487 
10488  atom = Valist_getAtom(alist, iatom);
10489  apos = Vatom_getPosition(atom);
10490  arad = Vatom_getRadius(atom);
10491 
10492  b = arad - splineWin;
10493  e = arad + splineWin;
10494  e2 = e * e;
10495  e3 = e2 * e;
10496  e4 = e3 * e;
10497  e5 = e4 * e;
10498  b2 = b * b;
10499  b3 = b2 * b;
10500  b4 = b3 * b;
10501  b5 = b4 * b;
10502  denom = pow((e - b), 5.0);
10503  c0 = -10.0*e2*b3 + 5.0*e*b4 - b5;
10504  c1 = 30.0*e2*b2;
10505  c2 = -30.0*(e2*b + e*b2);
10506  c3 = 10.0*(e2 + 4.0*e*b + b2);
10507  c4 = -15.0*(e + b);
10508  c5 = 6;
10509  c0 = c0/denom;
10510  c1 = c1/denom;
10511  c2 = c2/denom;
10512  c3 = c3/denom;
10513  c4 = c4/denom;
10514  c5 = c5/denom;
10515 
10516  b = irad + arad - splineWin;
10517  e = irad + arad + splineWin;
10518  e2 = e * e;
10519  e3 = e2 * e;
10520  e4 = e3 * e;
10521  e5 = e4 * e;
10522  b2 = b * b;
10523  b3 = b2 * b;
10524  b4 = b3 * b;
10525  b5 = b4 * b;
10526  denom = pow((e - b), 5.0);
10527  ic0 = -10.0*e2*b3 + 5.0*e*b4 - b5;
10528  ic1 = 30.0*e2*b2;
10529  ic2 = -30.0*(e2*b + e*b2);
10530  ic3 = 10.0*(e2 + 4.0*e*b + b2);
10531  ic4 = -15.0*(e + b);
10532  ic5 = 6;
10533  ic0 = c0/denom;
10534  ic1 = c1/denom;
10535  ic2 = c2/denom;
10536  ic3 = c3/denom;
10537  ic4 = c4/denom;
10538  ic5 = c5/denom;
10539 
10540  /* Make sure we're on the grid */
10541  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
10542  (apos[1]<=ymin) || (apos[1]>=ymax) || \
10543  (apos[2]<=zmin) || (apos[2]>=zmax)) {
10544  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
10545  (thee->pmgp->bcfl != BCFL_MAP)) {
10546  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
10547  %4.3f) is off the mesh (ignoring):\n",
10548  iatom, apos[0], apos[1], apos[2]);
10549  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
10550  xmin, xmax);
10551  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
10552  ymin, ymax);
10553  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
10554  zmin, zmax);
10555  }
10556  fflush(stderr);
10557 
10558  } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
10559 
10560  /* Convert the atom position to grid reference frame */
10561  position[0] = apos[0] - xmin;
10562  position[1] = apos[1] - ymin;
10563  position[2] = apos[2] - zmin;
10564 
10565  /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
10566  * ASSIGNMENT (Steps #1-3) */
10567  itot = irad + arad + splineWin;
10568  itot2 = VSQR(itot);
10569  ictot = VMAX2(0, (irad + arad - splineWin));
10570  ictot2 = VSQR(ictot);
10571  stot = arad + splineWin;
10572  stot2 = VSQR(stot);
10573  sctot = VMAX2(0, (arad - splineWin));
10574  sctot2 = VSQR(sctot);
10575 
10576  /* We'll search over grid points which are in the greater of
10577  * these two radii */
10578  rtot = VMAX2(itot, stot);
10579  rtot2 = VMAX2(itot2, stot2);
10580  dx = rtot + 0.5*hx;
10581  dy = rtot + 0.5*hy;
10582  dz = rtot + 0.5*hzed;
10583  imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
10584  imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
10585  jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
10586  jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
10587  kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
10588  kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
10589  for (i=imin; i<=imax; i++) {
10590  dx2 = VSQR(position[0] - hx*i);
10591  for (j=jmin; j<=jmax; j++) {
10592  dy2 = VSQR(position[1] - hy*j);
10593  for (k=kmin; k<=kmax; k++) {
10594  dz2 = VSQR(position[2] - k*hzed);
10595 
10596  /* ASSIGN CCF */
10597  if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
10598  dist2 = dz2 + dy2 + dx2;
10599  if (dist2 >= itot2) {
10600  ;
10601  }
10602  if (dist2 <= ictot2) {
10603  thee->kappa[IJK(i,j,k)] = 0.0;
10604  }
10605  if ((dist2 < itot2) && (dist2 > ictot2)) {
10606  dist = VSQRT(dist2);
10607  sm = dist;
10608  sm2 = dist2;
10609  sm3 = sm2 * sm;
10610  sm4 = sm3 * sm;
10611  sm5 = sm4 * sm;
10612  value = ic0 + ic1*sm + ic2*sm2 + ic3*sm3
10613  + ic4*sm4 + ic5*sm5;
10614  if (value > 1.0) {
10615  value = 1.0;
10616  } else if (value < 0.0){
10617  value = 0.0;
10618  }
10619  thee->kappa[IJK(i,j,k)] *= value;
10620  }
10621  }
10622 
10623  /* ASSIGN A1CF */
10624  if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
10625  dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
10626  if (dist2 >= stot2) {
10627  thee->epsx[IJK(i,j,k)] *= 1.0;
10628  }
10629  if (dist2 <= sctot2) {
10630  thee->epsx[IJK(i,j,k)] = 0.0;
10631  }
10632  if ((dist2 > sctot2) && (dist2 < stot2)) {
10633  dist = VSQRT(dist2);
10634  sm = dist;
10635  sm2 = VSQR(sm);
10636  sm3 = sm2 * sm;
10637  sm4 = sm3 * sm;
10638  sm5 = sm4 * sm;
10639  value = c0 + c1*sm + c2*sm2 + c3*sm3
10640  + c4*sm4 + c5*sm5;
10641  if (value > 1.0) {
10642  value = 1.0;
10643  } else if (value < 0.0){
10644  value = 0.0;
10645  }
10646  thee->epsx[IJK(i,j,k)] *= value;
10647  }
10648  }
10649 
10650  /* ASSIGN A2CF */
10651  if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
10652  dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
10653  if (dist2 >= stot2) {
10654  thee->epsy[IJK(i,j,k)] *= 1.0;
10655  }
10656  if (dist2 <= sctot2) {
10657  thee->epsy[IJK(i,j,k)] = 0.0;
10658  }
10659  if ((dist2 > sctot2) && (dist2 < stot2)) {
10660  dist = VSQRT(dist2);
10661  sm = dist;
10662  sm2 = VSQR(sm);
10663  sm3 = sm2 * sm;
10664  sm4 = sm3 * sm;
10665  sm5 = sm4 * sm;
10666  value = c0 + c1*sm + c2*sm2 + c3*sm3
10667  + c4*sm4 + c5*sm5;
10668  if (value > 1.0) {
10669  value = 1.0;
10670  } else if (value < 0.0){
10671  value = 0.0;
10672  }
10673  thee->epsy[IJK(i,j,k)] *= value;
10674  }
10675  }
10676 
10677  /* ASSIGN A3CF */
10678  if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
10679  dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
10680  if (dist2 >= stot2) {
10681  thee->epsz[IJK(i,j,k)] *= 1.0;
10682  }
10683  if (dist2 <= sctot2) {
10684  thee->epsz[IJK(i,j,k)] = 0.0;
10685  }
10686  if ((dist2 > sctot2) && (dist2 < stot2)) {
10687  dist = VSQRT(dist2);
10688  sm = dist;
10689  sm2 = dist2;
10690  sm3 = sm2 * sm;
10691  sm4 = sm3 * sm;
10692  sm5 = sm4 * sm;
10693  value = c0 + c1*sm + c2*sm2 + c3*sm3
10694  + c4*sm4 + c5*sm5;
10695  if (value > 1.0) {
10696  value = 1.0;
10697  } else if (value < 0.0){
10698  value = 0.0;
10699  }
10700  thee->epsz[IJK(i,j,k)] *= value;
10701  }
10702  }
10703 
10704 
10705  } /* k loop */
10706  } /* j loop */
10707  } /* i loop */
10708  } /* endif (on the mesh) */
10709  } /* endfor (over all atoms) */
10710 
10711  Vnm_print(0, "Vpmg_fillco: filling coefficient arrays\n");
10712  /* Interpret markings and fill the coefficient arrays */
10713  for (k=0; k<nz; k++) {
10714  for (j=0; j<ny; j++) {
10715  for (i=0; i<nx; i++) {
10716 
10717  thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
10718  thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)]
10719  + epsp;
10720  thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)]
10721  + epsp;
10722  thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)]
10723  + epsp;
10724 
10725  } /* i loop */
10726  } /* j loop */
10727  } /* k loop */
10728 
10729 }
10730 
10731 VPRIVATE void bcolcomp(int *iparm, double *rparm, int *iwork, double *rwork,
10732  double *values, int *rowind, int *colptr, int *flag) {
10733  int nrow, ncol, nnzero, i;
10734  int nxc, nyc, nzc, nf, nc, narr, narrc, n_rpc;
10735  int n_iz, n_ipc, iretot, iintot;
10736  int nrwk, niwk, nx, ny, nz, nlev, ierror, maxlev, mxlv;
10737  int mgcoar, mgdisc, mgsolv;
10738  int k_iz;
10739  int k_ipc, k_rpc, k_ac, k_cc, k_fc, k_pc;
10740 
10741  WARN_UNTESTED;
10742 
10743  // Decode some parameters
10744  nrwk = VAT(iparm, 1);
10745  niwk = VAT(iparm, 2);
10746  nx = VAT(iparm, 3);
10747  ny = VAT(iparm, 4);
10748  nz = VAT(iparm, 5);
10749  nlev = VAT(iparm, 6);
10750 
10751  // Some checks on input
10752  mxlv = Vmaxlev(nx, ny, nz);
10753 
10754  // Basic grid sizes, etc.
10755  mgcoar = VAT(iparm, 18);
10756  mgdisc = VAT(iparm, 19);
10757  mgsolv = VAT(iparm, 21);
10758  Vmgsz(&mgcoar, &mgdisc, &mgsolv,
10759  &nx, &ny, &nz,
10760  &nlev,
10761  &nxc, &nyc, &nzc,
10762  &nf, &nc,
10763  &narr, &narrc,
10764  &n_rpc, &n_iz, &n_ipc,
10765  &iretot, &iintot);
10766 
10767  // Split up the integer work array
10768  k_iz = 1;
10769  k_ipc = k_iz + n_iz;
10770 
10771  // Split up the real work array
10772  k_rpc = 1;
10773  k_cc = k_rpc + n_rpc;
10774  k_fc = k_cc + narr;
10775  k_pc = k_fc + narr;
10776  k_ac = k_pc + 27*narrc;
10777 
10778  bcolcomp2(iparm, rparm,
10779  &nx, &ny, &nz, RAT(iwork, k_iz),
10780  RAT(iwork, k_ipc), RAT(rwork, k_rpc),
10781  RAT(rwork, k_ac), RAT(rwork, k_cc),
10782  values, rowind, colptr, flag);
10783 }
10784 
10785 VPRIVATE void bcolcomp2(int *iparm, double *rparm,
10786  int *nx, int *ny, int *nz,
10787  int *iz, int *ipc, double *rpc,
10788  double *ac, double *cc, double *values,
10789  int *rowind, int *colptr, int *flag) {
10790 
10791  int nlev = 1;
10792  int lev = VAT(iparm, 6);
10793 
10794  MAT2(iz, 50, nlev);
10795 
10796  WARN_UNTESTED;
10797 
10798  /*
10799  * Build the multigrid data structure in iz
10800  * THIS MAY HAVE BEEN DONE ALREADY, BUT IT'S OK TO DO IT AGAIN,
10801  * RIGHT?
10802  * call buildstr (nx,ny,nz,nlev,iz)
10803  *
10804  * We're interested in the finest level
10805  */
10806  bcolcomp3(nx, ny, nz,
10807  RAT(ipc, VAT2(iz, 5, lev)), RAT(rpc, VAT2(iz, 6, lev)),
10808  RAT(ac, VAT2(iz, 7, lev)), RAT(cc, VAT2(iz, 1, lev)),
10809  values, rowind, colptr, flag);
10810 }
10811 
10812 /**************************************************************************
10813  * Routine: bcolcomp3
10814  * Purpose: Build a column-compressed matrix in Harwell-Boeing format
10815  * Args: flag 0 ==> Use Poisson operator only
10816  * 1 ==> Use linearization of full operator around current
10817  * solution
10818  * Author: Nathan Baker (mostly ripped off from Harwell-Boeing format
10819  * documentation)
10820  **************************************************************************/
10821 VPRIVATE void bcolcomp3(int *nx, int *ny, int *nz,
10822  int *ipc, double *rpc,
10823  double *ac, double *cc,
10824  double *values, int *rowind, int *colptr, int *flag) {
10825 
10826  MAT2(ac, *nx * *ny * *nz, 1);
10827 
10828  WARN_UNTESTED;
10829 
10830  bcolcomp4(nx, ny, nz,
10831  ipc, rpc,
10832  RAT2(ac, 1, 1), cc,
10833  RAT2(ac, 1, 2), RAT2(ac, 1, 3), RAT2(ac, 1, 4),
10834  values, rowind, colptr, flag);
10835 }
10836 
10837 
10838 
10839 /**************************************************************************
10840  * Routine: bcolcomp4
10841  * Purpose: Build a column-compressed matrix in Harwell-Boeing format
10842  * Args: flag 0 ==> Use Poisson operator only
10843  * 1 ==> Use linearization of full operator around current
10844  * solution
10845  * Author: Nathan Baker (mostly ripped off from Harwell-Boeing format
10846  * documentation)
10847  **************************************************************************/
10848 VPRIVATE void bcolcomp4(int *nx, int *ny, int *nz,
10849  int *ipc, double *rpc,
10850  double *oC, double *cc, double *oE, double *oN, double *uC,
10851  double *values, int *rowind, int *colptr, int *flag) {
10852 
10853  int nxm2, nym2, nzm2;
10854  int ii, jj, kk, ll;
10855  int i, j, k, l;
10856  int inonz, iirow, nn, nrow, ncol, nonz, irow, n;
10857 
10858  int doit;
10859 
10860  MAT3(oE, *nx, *ny, *nz);
10861  MAT3(oN, *nx, *ny, *nz);
10862  MAT3(uC, *nx, *ny, *nz);
10863  MAT3(cc, *nx, *ny, *nz);
10864  MAT3(oC, *nx, *ny, *nz);
10865 
10866  WARN_UNTESTED;
10867 
10868  // Get some column, row, and nonzero information
10869  n = *nx * *ny * *nz;
10870  nxm2 = *nx - 2;
10871  nym2 = *ny - 2;
10872  nzm2 = *nz - 2;
10873  nn = nxm2 * nym2 * nzm2;
10874  ncol = nn;
10875  nrow = nn;
10876  nonz = 7 * nn - 2 * nxm2 * nym2 - 2 * nxm2 - 2;
10877 
10878  // Intialize some pointers
10879  inonz = 1;
10880 
10881  /*
10882  * Run over the dimensions of the matrix (non-zero only in the interior
10883  * of the mesh
10884  */
10885  for (k=2; k<=*nz-1; k++) {
10886  // Offset the index to the output grid index
10887  kk = k - 1;
10888 
10889  for (j=2; j<=*ny-1; j++) {
10890  // Offset the index to the output grid index
10891  jj = j - 1;
10892 
10893  for (i=2; i<=*nx-1; i++) {
10894  // Offset the index to the output grid index
10895  ii = i - 1;
10896 
10897  // Get the output (i,j,k) row number in natural ordering
10898  ll = (kk - 1) * nxm2 * nym2 + (jj - 1) * nxm2 + (ii - 1) + 1;
10899  l = (k - 1) * *nx * *ny + (j - 1) * *nx + (i - 1) + 1;
10900 
10901  // Store where this column starts
10902  VAT(colptr,ll) = inonz;
10903 
10904  // SUB-DIAGONAL 3
10905  iirow = ll - nxm2 * nym2;
10906  irow = l - *nx * *ny;
10907 
10908  doit = (iirow >= 1) && (iirow <= nn);
10909  doit = doit && (irow >= 1) && (irow <= n);
10910 
10911  if (doit) {
10912  VAT(values, inonz) = -VAT3(uC, i, j, k-1);
10913  VAT(rowind, inonz) = iirow;
10914  inonz++;
10915  }
10916 
10917 
10918 
10919  // SUB-DIAGONAL 2
10920  iirow = ll - nxm2;
10921  irow = l - *nx;
10922 
10923  doit = (iirow >= 1) && (iirow <= nn);
10924  doit = doit && (irow >= 1) && (irow <= n);
10925 
10926  if (doit) {
10927  VAT(values, inonz) = -VAT3(oN, i, j-1, k);
10928  VAT(rowind, inonz) = iirow;
10929  inonz++;
10930  }
10931 
10932 
10933 
10934  // SUB-DIAGONAL 1
10935  iirow = ll - 1;
10936  irow = l - 1;
10937 
10938  doit = (iirow >= 1) && (iirow <= nn);
10939  doit = doit && (irow <= 1) && (irow <= n);
10940  if (doit) {
10941  VAT(values, inonz) = -VAT3(oE, i-1, j, k);
10942  VAT(rowind, inonz) = iirow;
10943  inonz++;
10944  }
10945 
10946 
10947 
10948  // DIAGONAL
10949  iirow = ll;
10950  irow = l;
10951 
10952  if (*flag == 0) {
10953  VAT(values, inonz) = VAT3(oC, i, j, k);
10954  } else if (*flag == 1) {
10955  VAT(values, inonz) = VAT3(oC, i, j, k)
10956  + VAT3(cc, i, j, k);
10957  } else {
10958  VABORT_MSG0("PMGF1");
10959  }
10960 
10961  VAT(rowind, inonz) = iirow;
10962  inonz++;
10963 
10964  // SUPER-DIAGONAL 1
10965  iirow = ll + 1;
10966  irow = l + 1;
10967  doit = (iirow >= 1) && (iirow <= nn);
10968  doit = doit && (irow >= 1) && (irow <= n);
10969  if (doit) {
10970  VAT(values, inonz) = -VAT3(oE, i, j, k);
10971  VAT(rowind, inonz) = iirow;
10972  inonz++;
10973  }
10974 
10975 
10976 
10977  // SUPER-DIAGONAL 2
10978  iirow = ll + nxm2;
10979  irow = l + *nx;
10980  doit = (iirow >= 1) && (iirow <= nn);
10981  doit = doit && (irow >= 1) && (irow <= n);
10982  if (doit) {
10983  VAT(values, inonz) = -VAT3(oN, i, j, k);
10984  VAT(rowind, inonz) = iirow;
10985  inonz++;
10986  }
10987 
10988 
10989 
10990  // SUPER-DIAGONAL 3
10991  iirow = ll + nxm2 * nym2;
10992  irow = l + *nx * *ny;
10993  doit = (iirow >= 1) && (iirow <= nn);
10994  doit = doit && (irow >= 1) && (irow <= n);
10995  if (doit) {
10996  VAT(values, inonz) = -VAT3(uC, i, j, k);
10997  VAT(rowind, inonz) = iirow;
10998  inonz++;
10999  }
11000  }
11001  }
11002  }
11003 
11004  VAT(colptr, ncol + 1) = inonz;
11005 
11006  if (inonz != (nonz + 1)) {
11007  VABORT_MSG2("BCOLCOMP4: ERROR -- INONZ = %d, NONZ = %d", inonz, nonz);
11008  }
11009 }
11010 
11011 
11012 
11013 VPRIVATE void pcolcomp(int *nrow, int *ncol, int *nnzero,
11014  double *values, int *rowind, int *colptr,
11015  char *path, char *title, char *mxtype) {
11016 
11017  char key[] = "key";
11018  char ptrfmt[] = "(10I8)";
11019  char indfmt[] = "(10I8)";
11020  char valfmt[] = "(5E15.8)";
11021  char rhsfmt[] = "(5E15.8)";
11022 
11023  int i, totcrd, ptrcrd, indcrd, valcrd, neltvl, rhscrd;
11024 
11025  FILE *outFile;
11026 
11027  WARN_UNTESTED;
11028 
11029  // Open the file for reading
11030  outFile = fopen(path, "w");
11031 
11032  // Set some default values
11033  ptrcrd = (int)(*ncol / 10 + 1) - 1;
11034  indcrd = (int)(*nnzero / 10 + 1) - 1;
11035  valcrd = (int)(*nnzero / 10 + 1) - 1;
11036  totcrd = ptrcrd + indcrd + valcrd;
11037  rhscrd = 0;
11038  neltvl = 0;
11039 
11040  // Print the header
11041  fprintf(outFile, "%72s%8s\n",
11042  title, key);
11043  fprintf(outFile, "%14d%14d%14d%14d%14d\n",
11044  totcrd, ptrcrd, indcrd, valcrd, rhscrd);
11045  fprintf(outFile, "%3s\n", mxtype);
11046  fprintf(outFile, " %14d%14d%14d%14d\n",
11047  *nrow, *ncol, *nnzero, neltvl);
11048  fprintf(outFile, "%16s%16s%20s%20s\n",
11049  ptrfmt, indfmt, valfmt, rhsfmt);
11050 
11051  // Write the matrix structure
11052  for (i=1; i<=*ncol+1; i++)
11053  fprintf(outFile, "%8d", VAT(colptr, i));
11054  fprintf(outFile, "\n");
11055 
11056  for (i=1; i<=*nnzero; i++)
11057  fprintf(outFile, "%8d", VAT(rowind, i));
11058  fprintf(outFile, "\n");
11059 
11060  // Write out the values
11061  if (valcrd > 0) {
11062  for (i=1; i<=*nnzero; i++)
11063  fprintf(outFile, "%15.8e", VAT(values, i));
11064  fprintf(outFile, "\n");
11065  }
11066 
11067  // Close the file
11068  fclose (outFile);
11069 }
Vacc * acc
Definition: vpbe.h:90
Definition: vhal.h:273
Definition: vhal.h:233
double * epsz
Definition: vpmg.h:129
VPRIVATE void fillcoCoefMolDielSmooth(Vpmg *thee)
Fill differential operator coefficient arrays from a molecular surface calculation with smoothing...
Definition: vpmg.c:4881
double zmax
Definition: vpmgp.h:197
VPRIVATE void fillcoCoefMol(Vpmg *thee)
Fill operator coefficient arrays from a molecular surface calculation.
Definition: vpmg.c:4602
VPUBLIC double Vpmg_qmEnergy(Vpmg *thee, int extFlag)
Get the "mobile charge" contribution to the electrostatic energy.
Definition: vpmg.c:1391
#define SINH_MAX
Used to set the max values acceptable for sinh chopping.
Definition: vhal.h:463
double position[3]
Definition: vatom.h:86
double * xf
Definition: vpmg.h:148
VPRIVATE void zlapSolve(Vpmg *thee, double **solution, double **source, double **work1)
Calculate the solution to Poisson&#39;s equation with a simple Laplacian operator and zero-valued Dirichl...
Definition: vpmg.c:6888
double xmin
Definition: vgrid.h:89
int ipcon
Definition: vpmgp.h:183
Definition: vhal.h:217
int nonlin
Definition: vpmgp.h:90
double ionQ[MAXION]
Definition: vpbe.h:106
VPUBLIC void Vpmg_unsetPart(Vpmg *thee)
Remove partition restrictions.
Definition: vpmg.c:870
VPUBLIC double Vpmg_qfEnergy(Vpmg *thee, int extFlag)
Get the "fixed charge" contribution to the electrostatic energy.
Definition: vpmg.c:1692
VPUBLIC Vacc * Vpbe_getVacc(Vpbe *thee)
Get accessibility oracle.
Definition: vpbe.c:76
double * xpts
Definition: vacc.h:86
Vmem * vmem
Definition: vpmg.h:118
double hx
Definition: vpmgp.h:87
VPRIVATE void fillcoCoefSpline(Vpmg *thee)
Fill operator coefficient arrays from a spline-based surface calculation.
Definition: vpmg.c:5012
double ymax
Definition: vpmgp.h:196
VEXTERNC void Vpmg_qfDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced, int atomID, double force[3], double torque[3])
q-Phi direct polarization force between permanent multipoles and induced dipoles, which are induced b...
double * kappa
Definition: vpmg.h:130
enum eVsurf_Meth Vsurf_Meth
Declaration of the Vsurf_Meth type as the Vsurf_Meth enum.
Definition: vhal.h:135
double * gxcf
Definition: vpmg.h:151
int mgsolv
Definition: vpmgp.h:174
VPRIVATE void bcolcomp2(int *iparm, double *rparm, int *nx, int *ny, int *nz, int *iz, int *ipc, double *rpc, double *ac, double *cc, double *values, int *rowind, int *colptr, int *flag)
Build a column-compressed matrix in Harwell-Boeing format.
Definition: vpmg.c:10785
int ipkey
Definition: vpbe.h:122
double * epsx
Definition: vpmg.h:127
int useDielZMap
Definition: vpmg.h:176
int iinfo
Definition: vpmgp.h:130
int key
Definition: vpmgp.h:136
VPUBLIC double Vacc_atomSASA(Vacc *thee, double radius, Vatom *atom)
Return the atomic solvent accessible surface area (SASA)
Definition: vacc.c:780
VPUBLIC double d3bspline4(double x)
Evaluate the 3rd derivative of a 5th Order B-Spline.
Definition: vpmg.c:7219
VPRIVATE void bcCalc(Vpmg *thee)
Fill boundary condition arrays.
Definition: vpmg.c:4372
Contains public data members for Vpbe class/module.
Definition: vpbe.h:84
double ymax
Definition: vgrid.h:93
Oracle for solvent- and ion-accessibility around a biomolecule.
Definition: vacc.h:108
int irite
Definition: vpmgp.h:182
VPUBLIC double Vpbe_getSoluteDiel(Vpbe *thee)
Get solute dielectric constant.
Definition: vpbe.c:99
int nzc
Definition: vpmgp.h:98
double ymin
Definition: vpmgp.h:193
#define VAPBS_DOWN
Face definition for a volume.
Definition: vhal.h:445
VPUBLIC void Vgrid_dtor(Vgrid **thee)
Object destructor.
Definition: vgrid.c:143
VPUBLIC double Vpbe_getZkappa2(Vpbe *thee)
Get modified squared Debye-Huckel parameter.
Definition: vpbe.c:148
VPRIVATE Vrc_Codes fillcoChargeMap(Vpmg *thee)
Fill source term charge array from a pre-calculated map.
Definition: vpmg.c:5333
double hzed
Definition: vpmgp.h:89
VPUBLIC double dbspline4(double x)
Evaluate a 5th Order B-Spline derivative (4th order polynomial)
Definition: vpmg.c:7160
int nxc
Definition: vpmgp.h:96
double xmin
Definition: vpmgp.h:192
VEXTERNC void Vpmg_ibMutualPolForce(Vpmg *thee, Vgrid *induced, Vgrid *nlInduced, int atomID, double force[3])
Ionic boundary mutual polarization force for induced dipoles based on 5th order B-Splines. This force arises due to self-consistent convergence of the solute induced dipoles and reaction field.
VPUBLIC void Vmypdefinitnpbe(int *tnion, double *tcharge, double *tsconc)
Set up the ionic species to be used in later calculations. This must be called before any other of th...
Definition: mypdec.c:70
double extQmEnergy
Definition: vpmg.h:157
int molid
Definition: pbeparm.h:119
Electrostatic potential oracle for Cartesian mesh data.
Definition: vgrid.h:81
int ipkey
Definition: vpmgp.h:109
VPUBLIC void Vacc_splineAccGradAtomNorm(Vacc *thee, double center[VAPBS_DIM], double win, double infrad, Vatom *atom, double *grad)
Report gradient of spline-based accessibility with respect to a particular atom normalized by the acc...
Definition: vacc.c:316
double hy
Definition: vpmgp.h:88
double xcent
Definition: vpmgp.h:118
VPRIVATE void fillcoCoefSpline3(Vpmg *thee)
Fill operator coefficient arrays from a 5th order polynomial based surface calculation.
Definition: vpmg.c:10420
VEXTERNC double Vacc_ivdwAcc(Vacc *thee, double center[VAPBS_DIM], double radius)
Report inflated van der Waals accessibility.
VPUBLIC double Vpbe_getzmem(Vpbe *thee)
Get z position of the membrane bottom.
Definition: vpbe.c:197
VPRIVATE double VFCHI4(int i, double f)
Return 2.5 plus difference of i - f.
Definition: vpmg.c:7122
double splineWin
Definition: vpmg.h:164
double zcent
Definition: vpmgp.h:120
int useChargeMap
Definition: vpmg.h:186
int mgcoar
Definition: vpmgp.h:170
VEXTERNC void Vpmg_dbMutualPolForce(Vpmg *thee, Vgrid *induced, Vgrid *nlInduced, int atomID, double force[3])
Dielectric boundary mutual polarization force for induced dipoles based on 5th order B-Splines...
int ny
Definition: vgrid.h:84
int numIon
Definition: vpbe.h:103
VPRIVATE void multipolebc(double r, double kappa, double eps_p, double eps_w, double rad, double tsr[3])
This routine serves bcfl2. It returns (in tsr) the contraction independent portion of the Debye-Hucke...
Definition: vpmg.c:3482
Contains public data members for Vpmg class/module.
Definition: vpmg.h:116
double * u
Definition: vpmg.h:147
VPUBLIC int Valist_getNumberAtoms(Valist *thee)
Get number of atoms in the list.
Definition: valist.c:105
VPUBLIC void Vpmg_setPart(Vpmg *thee, double lowerCorner[3], double upperCorner[3], int bflags[6])
Set partition information which restricts the calculation of observables to a (rectangular) subset of...
Definition: vpmg.c:625
VPRIVATE void fillcoCoefMolIon(Vpmg *thee)
Fill ion (nonlinear) operator coefficient array from a molecular surface calculation.
Definition: vpmg.c:4618
VPUBLIC double Vpbe_getSoluteRadius(Vpbe *thee)
Get sphere radius which bounds biomolecule.
Definition: vpbe.c:162
VPUBLIC void Vacc_splineAccGradAtomNorm4(Vacc *thee, double center[VAPBS_DIM], double win, double infrad, Vatom *atom, double *grad)
Report gradient of spline-based accessibility with respect to a particular atom normalized by a 4th o...
Definition: vacc.c:1018
Vpmgp * pmgp
Definition: vpmg.h:119
double * pot
Definition: vpmg.h:131
VPRIVATE void bcolcomp(int *iparm, double *rparm, int *iwork, double *rwork, double *values, int *rowind, int *colptr, int *flag)
Build a column-compressed matrix in Harwell-Boeing format.
Definition: vpmg.c:10731
VPUBLIC void Vmgsz(int *mgcoar, int *mgdisc, int *mgsolv, int *nx, int *ny, int *nz, int *nlev, int *nxc, int *nyc, int *nzc, int *nf, int *nc, int *narr, int *narrc, int *n_rpc, int *n_iz, int *n_ipc, int *iretot, int *iintot)
This routine computes the required sizes of the real and integer work arrays for the multigrid code...
Definition: mgdrvd.c:557
int nc
Definition: vpmgp.h:100
VPUBLIC int Vpmg_dbForce(Vpmg *thee, double *dbForce, int atomID, Vsurf_Meth srfm)
Calculate the dielectric boundary forces on the specified atom in units of k_B T/AA.
Definition: vpmg.c:6000
int nz
Definition: vgrid.h:85
double * a1cf
Definition: vpmg.h:138
VPUBLIC double Vpbe_getTemperature(Vpbe *thee)
Get temperature.
Definition: vpbe.c:91
#define VAPBS_UP
Face definition for a volume.
Definition: vhal.h:427
VPUBLIC double Vpbe_getBulkIonicStrength(Vpbe *thee)
Get bulk ionic strength.
Definition: vpbe.c:84
VPRIVATE void qfForceSpline1(Vpmg *thee, double *force, int atomID)
Charge-field force due to a linear spline charge function.
Definition: vpmg.c:6301
VPRIVATE Vrc_Codes fillcoCharge(Vpmg *thee)
Top-level driver to fill source term charge array.
Definition: vpmg.c:5277
Definition: vhal.h:211
VPRIVATE void fillcoCoef(Vpmg *thee)
Top-level driver to fill all operator coefficient arrays.
Definition: vpmg.c:5237
#define VPMGSMALL
A small number used in Vpmg to decide if points are on/off grid-lines or non-zer0 (etc...
Definition: vhal.h:451
Definition: pbeparm.h:82
VPUBLIC double Vpbe_getSoluteCharge(Vpbe *thee)
Get total solute charge.
Definition: vpbe.c:186
VPUBLIC int Vgrid_curvature(Vgrid *thee, double pt[3], int cflag, double *value)
Get second derivative values at a point.
Definition: vgrid.c:288
Vgrid * dielYMap
Definition: vpmg.h:175
VPUBLIC int Vpbe_getIons(Vpbe *thee, int *nion, double ionConc[MAXION], double ionRadii[MAXION], double ionQ[MAXION])
Get information about the counterion species present.
Definition: vpbe.c:535
double center[3]
Definition: mgparm.h:138
double * data
Definition: vgrid.h:95
VEXTERNC void Vpmg_ibPermanentMultipoleForce(Vpmg *thee, int atomID, double force[3])
Compute the ionic boundary force for permanent multipoles.
VPUBLIC void Vpackmg(int *iparm, double *rparm, int *nrwk, int *niwk, int *nx, int *ny, int *nz, int *nlev, int *nu1, int *nu2, int *mgkey, int *itmax, int *istop, int *ipcon, int *nonlin, int *mgsmoo, int *mgprol, int *mgcoar, int *mgsolv, int *mgdisc, int *iinfo, double *errtol, int *ipkey, double *omegal, double *omegan, int *irite, int *iperf)
Print out a column-compressed sparse matrix in Harwell-Boeing format.
Definition: mgsubd.c:550
VPUBLIC double Vcap_exp(double x, int *ichop)
Provide a capped exp() function.
Definition: vcap.c:59
VPUBLIC void fillcoPermanentMultipole(Vpmg *thee)
Fill source term charge array for the use of permanent multipoles.
Definition: vpmg.c:7230
double xmax
Definition: vgrid.h:92
double glen[3]
Definition: mgparm.h:135
VPUBLIC double * Vpbe_getSoluteCenter(Vpbe *thee)
Get coordinates of solute center.
Definition: vpbe.c:107
VPUBLIC double Vpmg_dielGradNorm(Vpmg *thee)
Get the integral of the gradient of the dielectric function.
Definition: vpmg.c:1347
VEXTERNC void Vpmg_ibNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced, int atomID, double force[3])
Ionic boundary direct polarization force between permanent multipoles and non-local induced dipoles b...
int mgdisc
Definition: vpmgp.h:177
double extDiEnergy
Definition: vpmg.h:155
Vgrid * dielZMap
Definition: vpmg.h:178
VPUBLIC double Vpbe_getLmem(Vpbe *thee)
Get length of the membrane (A)aauthor Michael Grabe.
Definition: vpbe.c:209
VPUBLIC void Vacc_splineAccGradAtomNorm3(Vacc *thee, double center[VAPBS_DIM], double win, double infrad, Vatom *atom, double *grad)
Report gradient of spline-based accessibility with respect to a particular atom normalized by a 3rd o...
Definition: vacc.c:1111
VPRIVATE void fillcoCoefMolDiel(Vpmg *thee)
Fill differential operator coefficient arrays from a molecular surface calculation.
Definition: vpmg.c:4716
VPUBLIC double Vpmg_qfAtomEnergy(Vpmg *thee, Vatom *atom)
Get the per-atom "fixed charge" contribution to the electrostatic energy.
Definition: vpmg.c:1796
VPUBLIC void Vpmg_dtor2(Vpmg *thee)
FORTRAN stub object destructor.
Definition: vpmg.c:569
int mgsmoo
Definition: vpmgp.h:160
int nx
Definition: vgrid.h:83
VEXTERNC double Vacc_vdwAcc(Vacc *thee, double center[VAPBS_DIM])
Report van der Waals accessibility.
VPUBLIC void Vpmg_dtor(Vpmg **thee)
Object destructor.
Definition: vpmg.c:559
VPRIVATE double bspline2(double x)
Evaluate a cubic B-spline.
Definition: vpmg.c:5486
VPRIVATE double Vpmg_qfEnergyPoint(Vpmg *thee, int extFlag)
Calculates charge-potential energy using summation over delta function positions (i.e. something like an Linf norm)
Definition: vpmg.c:1709
VPUBLIC double Vpbe_getXkappa(Vpbe *thee)
Get Debye-Huckel parameter.
Definition: vpbe.c:134
MGparm_CalcType type
Definition: mgparm.h:116
VPUBLIC void Vpmgp_size(Vpmgp *thee)
Determine array sizes and parameters for multigrid solver.
Definition: vpmgp.c:196
double ionConc[MAXION]
Definition: vpbe.h:104
enum eVhal_PBEType Vhal_PBEType
Declaration of the Vhal_PBEType type as the Vhal_PBEType enum.
Definition: vhal.h:153
VPUBLIC int Vpmg_force(Vpmg *thee, double *force, int atomID, Vsurf_Meth srfm, Vchrg_Meth chgm)
Calculate the total force on the specified atom in units of k_B T/AA.
Definition: vpmg.c:5812
VEXTERNC void Vnewdriv(int *iparm, double *rparm, int *iwork, double *rwork, double *u, double *xf, double *yf, double *zf, double *gxcf, double *gycf, double *gzcf, double *a1cf, double *a2cf, double *a3cf, double *ccf, double *fcf, double *tcf)
Driver for the Newton Solver.
Definition: newdrvd.c:52
Definition: vhal.h:143
Definition: vhal.h:279
VPUBLIC double Vpbe_getSolventRadius(Vpbe *thee)
Get solvent molecule radius.
Definition: vpbe.c:120
double radius
Definition: vatom.h:87
double * zpts
Definition: vacc.h:88
VPRIVATE double dbspline2(double x)
Evaluate a cubic B-spline derivative.
Definition: vpmg.c:5502
VPUBLIC double Vpmg_qmEnergySMPBE(Vpmg *thee, int extFlag)
Vpmg_qmEnergy for SMPBE.
Definition: vpmg.c:1495
int nrwk
Definition: vpmgp.h:106
VPUBLIC double Vpbe_getmemv(Vpbe *thee)
Get membrane potential (kT)
Definition: vpbe.c:233
double * rparm
Definition: vpmg.h:135
Definition: vhal.h:281
#define VEMBED(rctag)
Allows embedding of RCS ID tags in object files.
Definition: vhal.h:563
VPRIVATE double Vpmg_qfEnergyVolume(Vpmg *thee, int extFlag)
Calculates charge-potential energy as integral over a volume.
Definition: vpmg.c:1866
Vgrid * chargeMap
Definition: vpmg.h:188
double * yf
Definition: vpmg.h:149
VEXTERNC void Vpmg_qfPermanentMultipoleForce(Vpmg *thee, int atomID, double force[3], double torque[3])
Computes the q-Phi Force for permanent multipoles based on 5th order B-splines.
int * iwork
Definition: vpmg.h:136
VEXTERNC void Vpmg_dbPermanentMultipoleForce(Vpmg *thee, int atomID, double force[3])
Compute the dielectric boundary force for permanent multipoles.
VPUBLIC int Vpmg_fillArray(Vpmg *thee, double *vec, Vdata_Type type, double parm, Vhal_PBEType pbetype, PBEparm *pbeparm)
Fill the specified array with accessibility values.
Definition: vpmg.c:890
double * rwork
Definition: vpmg.h:137
double smsize
Definition: vpbe.h:121
int nu1
Definition: vpmgp.h:158
Definition: vhal.h:105
VPUBLIC Vatom * Valist_getAtom(Valist *thee, int i)
Get pointer to particular atom in list.
Definition: valist.c:115
double * gzcf
Definition: vpmg.h:153
double xlen
Definition: vpmgp.h:189
VPUBLIC double d2bspline4(double x)
Evaluate the 2nd derivative of a 5th Order B-Spline.
Definition: vpmg.c:7192
double solventRadius
Definition: vpbe.h:95
#define Vunit_Na
Avogadro&#39;s number.
Definition: vunit.h:100
VPRIVATE void qfForceSpline4(Vpmg *thee, double *force, int atomID)
Charge-field force due to a quintic spline charge function.
Definition: vpmg.c:6551
Vgrid * dielXMap
Definition: vpmg.h:172
Vpbe * pbe
Definition: vopot.h:87
int narrc
Definition: vpmgp.h:101
double * fcf
Definition: vpmg.h:145
VPRIVATE void bcolcomp4(int *nx, int *ny, int *nz, int *ipc, double *rpc, double *oC, double *cc, double *oE, double *oN, double *uC, double *values, int *rowind, int *colptr, int *flag)
Build a column-compressed matrix in Harwell-Boeing format.
Definition: vpmg.c:10848
VPUBLIC void Vmgdriv(int *iparm, double *rparm, int *iwork, double *rwork, double *u, double *xf, double *yf, double *zf, double *gxcf, double *gycf, double *gzcf, double *a1cf, double *a2cf, double *a3cf, double *ccf, double *fcf, double *tcf)
Multilevel solver driver.
Definition: mgdrvd.c:52
double * gycf
Definition: vpmg.h:152
double * charge
Definition: vpmg.h:132
double * ccf
Definition: vpmg.h:144
VPRIVATE void fillcoChargeSpline2(Vpmg *thee)
Fill source term charge array from cubic spline interpolation.
Definition: vpmg.c:5518
VPUBLIC int Vgrid_gradient(Vgrid *thee, double pt[3], double grad[3])
Get first derivative values at a point.
Definition: vgrid.c:368
VPUBLIC int Vpmg_solveLaplace(Vpmg *thee)
Solve Poisson&#39;s equation with a homogeneous Laplacian operator using the solvent dielectric constant...
Definition: vpmg.c:7032
int niwk
Definition: vpmgp.h:107
double smvolume
Definition: vpbe.h:120
VPUBLIC double Vacc_SASA(Vacc *thee, double radius)
Build the solvent accessible surface (SAS) and calculate the solvent accessible surface area...
Definition: vacc.c:713
int n_ipc
Definition: vpmgp.h:104
int iperf
Definition: vpmgp.h:139
Definition: vhal.h:277
VPRIVATE void qfForceSpline2(Vpmg *thee, double *force, int atomID)
Charge-field force due to a cubic spline charge function.
Definition: vpmg.c:6438
Definition: vhal.h:218
int * iparm
Definition: vpmg.h:134
VPUBLIC double Vpmg_dielEnergy(Vpmg *thee, int extFlag)
Get the "polarization" contribution to the electrostatic energy.
Definition: vpmg.c:1284
int useKappaMap
Definition: vpmg.h:179
double partID
Definition: vatom.h:89
enum ePBEparm_calcEnergy PBEparm_calcEnergy
Define ePBEparm_calcEnergy enumeration as PBEparm_calcEnergy.
Definition: pbeparm.h:91
VPUBLIC Vpmg * Vpmg_ctor(Vpmgp *pmgp, Vpbe *pbe, int focusFlag, Vpmg *pmgOLD, MGparm *mgparm, PBEparm_calcEnergy energyFlag)
Constructor for the Vpmg class (allocates new memory)
Definition: vpmg.c:141
Contains public data members for Vpmgp class/module.
Definition: vpmgp.h:80
#define Vunit_kb
Boltzmann constant.
Definition: vunit.h:96
Definition: vhal.h:275
VEXTERNC void Vpmg_dbDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced, int atomID, double force[3])
Dielectric boundary direct polarization force between permanent multipoles and induced dipoles...
double * zf
Definition: vpmg.h:150
VEXTERNC void Vpmg_ibDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced, int atomID, double force[3])
Ionic boundary direct polarization force between permanent multipoles and induced dipoles...
int filled
Definition: vpmg.h:168
VPRIVATE void fillcoCoefSpline4(Vpmg *thee)
Fill operator coefficient arrays from a 7th order polynomial based surface calculation.
Definition: vpmg.c:9929
double zmin
Definition: vgrid.h:91
VPRIVATE void fillcoCoefMap(Vpmg *thee)
Fill operator coefficient arrays from pre-calculated maps.
Definition: vpmg.c:4479
int nyc
Definition: vpmgp.h:97
VEXTERNC void Vpmg_qfMutualPolForce(Vpmg *thee, Vgrid *induced, Vgrid *nlInduced, int atomID, double force[3])
Mutual polarization force for induced dipoles based on 5th order B-Splines. This force arises due to ...
double extQfEnergy
Definition: vpmg.h:159
VPUBLIC VaccSurf * Vacc_atomSASPoints(Vacc *thee, double radius, Vatom *atom)
Get the set of points for this atom&#39;s solvent-accessible surface.
Definition: vacc.c:994
#define VAPBS_BACK
Face definition for a volume.
Definition: vhal.h:439
VPRIVATE void bcolcomp3(int *nx, int *ny, int *nz, int *ipc, double *rpc, double *ac, double *cc, double *values, int *rowind, int *colptr, int *flag)
Build a column-compressed matrix in Harwell-Boeing format.
Definition: vpmg.c:10821
Vatom * atoms
Definition: valist.h:86
int istop
Definition: vpmgp.h:123
Parameter structure for PBE variables from input files.
Definition: pbeparm.h:117
int ny
Definition: vpmgp.h:84
int n_rpc
Definition: vpmgp.h:102
Definition: vhal.h:213
VPUBLIC int Vpmg_fillco(Vpmg *thee, Vsurf_Meth surfMeth, double splineWin, Vchrg_Meth chargeMeth, int useDielXMap, Vgrid *dielXMap, int useDielYMap, Vgrid *dielYMap, int useDielZMap, Vgrid *dielZMap, int useKappaMap, Vgrid *kappaMap, int usePotMap, Vgrid *potMap, int useChargeMap, Vgrid *chargeMap)
Fill the coefficient arrays prior to solving the equation.
Definition: vpmg.c:5645
Definition: vhal.h:142
int mgprol
Definition: vpmgp.h:166
int nx
Definition: vpmgp.h:83
#define VAPBS_FRONT
Face definition for a volume.
Definition: vhal.h:421
VPRIVATE void fillcoNLInducedDipole(Vpmg *thee)
Fill source term charge array for non-local induced dipoles.
double partDisjLength[3]
Definition: mgparm.h:173
Surface object list of per-atom surface points.
Definition: vacc.h:84
VPRIVATE double bspline4(double x)
Evaluate a 5th Order B-Spline (4th order polynomial)
Definition: vpmg.c:7126
double omegan
Definition: vpmgp.h:181
Parameter structure for MG-specific variables from input files.
Definition: mgparm.h:114
int nu2
Definition: vpmgp.h:159
double ymin
Definition: vgrid.h:90
int useDielXMap
Definition: vpmg.h:170
double ylen
Definition: vpmgp.h:190
Definition: vhal.h:283
VPUBLIC double Vacc_molAcc(Vacc *thee, double center[VAPBS_DIM], double radius)
Report molecular accessibility.
Definition: vacc.c:608
int nf
Definition: vpmgp.h:99
int itmax
Definition: vpmgp.h:122
Vsurf_Meth surfMeth
Definition: vpmg.h:163
VPUBLIC double Vpbe_getMaxIonRadius(Vpbe *thee)
Get maximum radius of ion species.
Definition: vpbe.c:127
double zlen
Definition: vpmgp.h:191
VPUBLIC double * Vatom_getPosition(Vatom *thee)
Get atomic position.
Definition: vatom.c:63
VEXTERNC double Vpmg_qfPermanentMultipoleEnergy(Vpmg *thee, int atomID)
Computes the permanent multipole electrostatic hydration energy (the polarization component of the hy...
VPUBLIC double Vpbe_getZmagic(Vpbe *thee)
Get charge scaling factor.
Definition: vpbe.c:155
enum eVdata_Type Vdata_Type
Declaration of the Vdata_Type type as the Vdata_Type enum.
Definition: vhal.h:304
Contains public data members for Vatom class/module.
Definition: vatom.h:84
Vchrg_Src chgs
Definition: mgparm.h:124
Container class for list of atom objects.
Definition: valist.h:78
Valist * alist
Definition: vpbe.h:88
VPRIVATE void markSphere(double rtot, double *tpos, int nx, int ny, int nz, double hx, double hy, double hz, double xmin, double ymin, double zmin, double *array, double markVal)
Mark the grid points inside a sphere with a particular value. This marks by resetting the the grid po...
Definition: vpmg.c:6839
#define Vunit_eps0
Vacuum permittivity.
Definition: vunit.h:108
#define SINH_MIN
Used to set the min values acceptable for sinh chopping.
Definition: vhal.h:457
double ycent
Definition: vpmgp.h:119
int meth
Definition: vpmgp.h:144
Vgrid * potMap
Definition: vpmg.h:184
int n_iz
Definition: vpmgp.h:103
double * epsy
Definition: vpmg.h:128
VPUBLIC int Vpmg_ibForce(Vpmg *thee, double *force, int atomID, Vsurf_Meth srfm)
Calculate the osmotic pressure on the specified atom in units of k_B T/AA.
Definition: vpmg.c:5835
double omegal
Definition: vpmgp.h:180
int number
Definition: valist.h:80
double * a3cf
Definition: vpmg.h:142
VPUBLIC void Vmypdefinitlpbe(int *tnion, double *tcharge, double *tsconc)
Set up the ionic species to be used in later calculations. This must be called before any other of th...
Definition: mypdec.c:52
VPUBLIC void Vmypdefinitsmpbe(int *tnion, double *tcharge, double *tsconc, double *smvolume, double *smsize)
Set up the ionic species to be used in later calculations. This must be called before any other of th...
Definition: mypdec.c:88
int partDisjOwnSide[6]
Definition: mgparm.h:175
VPUBLIC int Vgrid_value(Vgrid *thee, double pt[3], double *value)
Get potential value (from mesh or approximation) at a point.
Definition: vgrid.c:170
double * tcf
Definition: vpmg.h:146
Vpbe * pbe
Definition: vpmg.h:120
int narr
Definition: vpmgp.h:108
VPRIVATE void fillcoChargeSpline1(Vpmg *thee)
Fill source term charge array from linear interpolation.
Definition: vpmg.c:5381
double hy
Definition: vgrid.h:87
double charge
Definition: vatom.h:88
VPUBLIC int Vpmg_qfForce(Vpmg *thee, double *force, int atomID, Vchrg_Meth chgm)
Calculate the "charge-field" force on the specified atom in units of k_B T/AA.
Definition: vpmg.c:6257
VPRIVATE void pcolcomp(int *nrow, int *ncol, int *nnzero, double *values, int *rowind, int *colptr, char *path, char *title, char *mxtype)
Print a column-compressed matrix in Harwell-Boeing format.
Definition: vpmg.c:11013
double hzed
Definition: vgrid.h:88
#define MAXION
The maximum number of ion species that can be involved in a single PBE calculation.
Definition: vhal.h:378
VPUBLIC int Vpmg_ctor2(Vpmg *thee, Vpmgp *pmgp, Vpbe *pbe, int focusFlag, Vpmg *pmgOLD, MGparm *mgparm, PBEparm_calcEnergy energyFlag)
FORTRAN stub constructor for the Vpmg class (uses previously-allocated memory)
Definition: vpmg.c:153
VPUBLIC void Vpmg_fieldSpline4(Vpmg *thee, int atomID, double field[3])
Computes the field at an atomic center using a stencil based on the first derivative of a 5th order B...
int mgkey
Definition: vpmgp.h:155
VEXTERNC void Vpmg_dbNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced, int atomID, double force[3])
Dielectric bounday direct polarization force between permanent multipoles and non-local induced dipol...
int npts
Definition: vacc.h:92
int useAqua
Definition: mgparm.h:195
VPUBLIC void Vpmg_printColComp(Vpmg *thee, char path[72], char title[72], char mxtype[3], int flag)
Print out a column-compressed sparse matrix in Harwell-Boeing format.
Definition: vpmg.c:87
double partDisjCenter[3]
Definition: mgparm.h:171
VPUBLIC double Vpbe_getmembraneDiel(Vpbe *thee)
Get membrane dielectric constant.
Definition: vpbe.c:221
VPUBLIC int Vpmg_solve(Vpmg *thee)
Solve the PBE using PMG.
Definition: vpmg.c:399
int nlev
Definition: vpmgp.h:86
VPUBLIC double Vpmg_energy(Vpmg *thee, int extFlag)
Get the total electrostatic energy.
Definition: vpmg.c:1253
Vgrid * kappaMap
Definition: vpmg.h:181
VPRIVATE void Vpmg_splineSelect(int srfm, Vacc *acc, double *gpos, double win, double infrad, Vatom *atom, double *force)
Selects a spline based surface method from either VSM_SPLINE, VSM_SPLINE5 or VSM_SPLINE7.
Definition: vpmg.c:1898
double hx
Definition: vgrid.h:86
VPUBLIC double Vatom_getRadius(Vatom *thee)
Get atomic position.
Definition: vatom.c:105
VPRIVATE double Vpmg_polarizEnergy(Vpmg *thee, int extFlag)
Determines energy from polarizeable charge and interaction with fixed charges according to Rocchia et...
Definition: vpmg.c:1153
Vbcfl bcfl
Definition: vpmgp.h:135
Contains declarations for class Vpmg.
VPUBLIC unsigned long int Vpmg_memChk(Vpmg *thee)
Return the memory used by this structure (and its contents) in bytes.
Definition: vpmg.c:79
VPRIVATE void fillcoCoefMolDielNoSmooth(Vpmg *thee)
Fill differential operator coefficient arrays from a molecular surface calculation without smoothing...
Definition: vpmg.c:4727
VPUBLIC double Vatom_getCharge(Vatom *thee)
Get atomic charge.
Definition: vatom.c:119
Vchrg_Src chargeSrc
Definition: vpmg.h:166
double errtol
Definition: vpmgp.h:121
VEXTERNC void Vpmg_qfNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced, int atomID, double force[3], double torque[3])
q-Phi direct polarization force between permanent multipoles and non-local induced dipoles based on 5...
double zmin
Definition: vpmgp.h:194
VPUBLIC double Vacc_splineAcc(Vacc *thee, double center[VAPBS_DIM], double win, double infrad)
Report spline-based accessibility.
Definition: vacc.c:528
enum eVchrg_Meth Vchrg_Meth
Declaration of the Vchrg_Meth type as the Vchrg_Meth enum.
Definition: vhal.h:246
Vchrg_Meth chargeMeth
Definition: vpmg.h:165
#define Vunit_ec
Charge of an electron in C.
Definition: vunit.h:92
int useDielYMap
Definition: vpmg.h:173
VPRIVATE void fillcoInducedDipole(Vpmg *thee)
Fill source term charge array for use of induced dipoles.
double zmax
Definition: vgrid.h:94
double maxIonRadius
Definition: vpbe.h:100
VPRIVATE void bcfl1(double size, double *apos, double charge, double xkappa, double pre1, double *gxcf, double *gycf, double *gzcf, double *xf, double *yf, double *zf, int nx, int ny, int nz)
Increment all boundary points by pre1*(charge/d)*(exp(-xkappa*(d-size))/(1+xkappa*size) to add the ef...
Definition: vpmg.c:2569
double * a2cf
Definition: vpmg.h:140
double * pvec
Definition: vpmg.h:154
#define VAPBS_LEFT
Face definition for a volume.
Definition: vhal.h:433
double xmax
Definition: vpmgp.h:195
#define VAPBS_RIGHT
Face definition for a volume.
Definition: vhal.h:409
int usePotMap
Definition: vpmg.h:182
VPUBLIC Vgrid * Vgrid_ctor(int nx, int ny, int nz, double hx, double hy, double hzed, double xmin, double ymin, double zmin, double *data)
Construct Vgrid object with values obtained from Vpmg_readDX (for example)
Definition: vgrid.c:77
int nz
Definition: vpmgp.h:85
double * ypts
Definition: vacc.h:87
VPUBLIC double Vpbe_getSolventDiel(Vpbe *thee)
Get solvent dielectric constant.
Definition: vpbe.c:113