APBS  1.5
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  size_t size;
160 
161  /* Get the parameters */
162  VASSERT(pmgp != VNULL);
163  VASSERT(pbe != VNULL);
164  thee->pmgp = pmgp;
165  thee->pbe = pbe;
166 
167  /* Set up the memory */
168  thee->vmem = Vmem_ctor("APBS:VPMG");
169 
170 
171 
173  /* Initialize ion concentrations and valencies in PMG routines */
174  zkappa2 = Vpbe_getZkappa2(thee->pbe);
175  ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
176  if (ionstr > 0.0) zks2 = 0.5/ionstr;
177  else zks2 = 0.0;
178  Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
179 
180  /* TEMPORARY USEAQUA */
181  /* Calculate storage requirements */
182  if(mgparm->useAqua == 0){
183  Vpmgp_size(thee->pmgp);
184  }else{
185  VABORT_MSG0("Aqua is currently disabled");
186  }
187 
188  /* We need some additional storage if: nonlinear & newton OR cgmg */
189  /* SMPBE Added - nonlin = 2 added since it mimics NPBE */
190  if ( ( ((thee->pmgp->nonlin == NONLIN_NPBE) || (thee->pmgp->nonlin == NONLIN_SMPBE))
191  && (thee->pmgp->meth == VSOL_Newton) ) || (thee->pmgp->meth == VSOL_CGMG) )
192  {
193  thee->pmgp->nrwk += (2*(thee->pmgp->nf));
194  }
195 
196 
197  if (thee->pmgp->iinfo > 1) {
198  Vnm_print(2, "Vpmg_ctor2: PMG chose nx = %d, ny = %d, nz = %d\n",
199  thee->pmgp->nx, thee->pmgp->ny, thee->pmgp->nz);
200  Vnm_print(2, "Vpmg_ctor2: PMG chose nlev = %d\n",
201  thee->pmgp->nlev);
202  Vnm_print(2, "Vpmg_ctor2: PMG chose nxc = %d, nyc = %d, nzc = %d\n",
203  thee->pmgp->nxc, thee->pmgp->nyc, thee->pmgp->nzc);
204  Vnm_print(2, "Vpmg_ctor2: PMG chose nf = %d, nc = %d\n",
205  thee->pmgp->nf, thee->pmgp->nc);
206  Vnm_print(2, "Vpmg_ctor2: PMG chose narr = %d, narrc = %d\n",
207  thee->pmgp->narr, thee->pmgp->narrc);
208  Vnm_print(2, "Vpmg_ctor2: PMG chose n_rpc = %d, n_iz = %d, n_ipc = %d\n",
209  thee->pmgp->n_rpc, thee->pmgp->n_iz, thee->pmgp->n_ipc);
210  Vnm_print(2, "Vpmg_ctor2: PMG chose nrwk = %d, niwk = %d\n",
211  thee->pmgp->nrwk, thee->pmgp->niwk);
212  }
213 
214 
215 
216  /* Allocate boundary storage */
217  thee->gxcf = (double *)Vmem_malloc(
218  thee->vmem,
219  10*(thee->pmgp->ny)*(thee->pmgp->nz),
220  sizeof(double)
221  );
222 
223  thee->gycf = (double *)Vmem_malloc(
224  thee->vmem,
225  10*(thee->pmgp->nx)*(thee->pmgp->nz),
226  sizeof(double)
227  );
228 
229  thee->gzcf = (double *)Vmem_malloc(
230  thee->vmem,
231  10*(thee->pmgp->nx)*(thee->pmgp->ny),
232  sizeof(double)
233  );
234 
235 
236 
237  /* Warn users if they are using BCFL_MAP that
238  we do not include external energies */
239  if (thee->pmgp->bcfl == BCFL_MAP)
240  Vnm_print(2,"Vpmg_ctor2: \nWarning: External energies are not used in BCFL_MAP calculations!\n");
241 
242  if (focusFlag) {
243 
244  /* Overwrite any default or user-specified boundary condition
245  * arguments; we are now committed to a calculation via focusing */
246  if (thee->pmgp->bcfl != BCFL_FOCUS) {
247  Vnm_print(2,
248  "Vpmg_ctor2: reset boundary condition flag to BCFL_FOCUS!\n");
249  thee->pmgp->bcfl = BCFL_FOCUS;
250  }
251 
252  /* Fill boundaries */
253  Vnm_print(0, "Vpmg_ctor2: Filling boundary with old solution!\n");
254  focusFillBound(thee, pmgOLD);
255 
256  /* Calculate energetic contributions from region outside focusing
257  * domain */
258  if (energyFlag != PCE_NO) {
259 
260  if (mgparm->type == MCT_PARALLEL) {
261 
262  for (j=0; j<3; j++) {
263  partMin[j] = mgparm->partDisjCenter[j]
264  - 0.5*mgparm->partDisjLength[j];
265  partMax[j] = mgparm->partDisjCenter[j]
266  + 0.5*mgparm->partDisjLength[j];
267  }
268 
269  } else {
270  for (j=0; j<3; j++) {
271  partMin[j] = mgparm->center[j] - 0.5*mgparm->glen[j];
272  partMax[j] = mgparm->center[j] + 0.5*mgparm->glen[j];
273  }
274  }
275  extEnergy(thee, pmgOLD, energyFlag, partMin, partMax,
276  mgparm->partDisjOwnSide);
277  }
278 
279  } else {
280 
281  /* Ignore external energy contributions */
282  thee->extQmEnergy = 0;
283  thee->extDiEnergy = 0;
284  thee->extQfEnergy = 0;
285  }
286 
287  /* Allocate partition vector storage */
288  size = (thee->pmgp->nx)*(thee->pmgp->ny)*(thee->pmgp->nz);
289  thee->pvec = (double *)Vmem_malloc(
290  thee->vmem,
291  size,
292  sizeof(double)
293  );
294 
295  /* Allocate remaining storage */
296  thee->iparm = ( int *)Vmem_malloc(thee->vmem, 100, sizeof( int));
297  thee->rparm = (double *)Vmem_malloc(thee->vmem, 100, sizeof(double));
298  thee->iwork = ( int *)Vmem_malloc(thee->vmem, thee->pmgp->niwk, sizeof( int));
299  thee->rwork = (double *)Vmem_malloc(thee->vmem, thee->pmgp->nrwk, sizeof(double));
300  thee->charge = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
301  thee->kappa = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
302  thee->pot = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
303  thee->epsx = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
304  thee->epsy = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
305  thee->epsz = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
306  thee->a1cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
307  thee->a2cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
308  thee->a3cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
309  thee->ccf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
310  thee->fcf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
311  thee->tcf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
312  thee->u = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr, sizeof(double));
313  thee->xf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->nx), sizeof(double));
314  thee->yf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->ny), sizeof(double));
315  thee->zf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->nz), sizeof(double));
316 
317 
318 
319  /* Packs parameters into the iparm and rparm arrays */
320  Vpackmg(thee->iparm, thee->rparm, &(thee->pmgp->nrwk), &(thee->pmgp->niwk),
321  &(thee->pmgp->nx), &(thee->pmgp->ny), &(thee->pmgp->nz),
322  &(thee->pmgp->nlev), &(thee->pmgp->nu1), &(thee->pmgp->nu2),
323  &(thee->pmgp->mgkey), &(thee->pmgp->itmax), &(thee->pmgp->istop),
324  &(thee->pmgp->ipcon), &(thee->pmgp->nonlin), &(thee->pmgp->mgsmoo),
325  &(thee->pmgp->mgprol), &(thee->pmgp->mgcoar), &(thee->pmgp->mgsolv),
326  &(thee->pmgp->mgdisc), &(thee->pmgp->iinfo), &(thee->pmgp->errtol),
327  &(thee->pmgp->ipkey), &(thee->pmgp->omegal), &(thee->pmgp->omegan),
328  &(thee->pmgp->irite), &(thee->pmgp->iperf));
329 
330 
331 
332  /* Currently for SMPBE type calculations we do not want to apply a scale
333  factor to the ionConc */
340  switch(pmgp->ipkey){
341 
342  case IPKEY_SMPBE:
343 
344  Vmypdefinitsmpbe(&nion, ionQ, ionConc, &pbe->smvolume, &pbe->smsize);
345  break;
346 
347 
348 
349  case IPKEY_NPBE:
350 
351  /* Else adjust the inoConc by scaling factor zks2 */
352  for (i=0; i<nion; i++)
353  ionConc[i] = zks2 * ionConc[i];
354 
355  Vmypdefinitnpbe(&nion, ionQ, ionConc);
356  break;
357 
358 
359 
360  case IPKEY_LPBE:
361 
362  /* Else adjust the inoConc by scaling factor zks2 */
363  for (i=0; i<nion; i++)
364  ionConc[i] = zks2 * ionConc[i];
365 
366  Vmypdefinitlpbe(&nion, ionQ, ionConc);
367  break;
368 
369 
370 
371  default:
372  Vnm_print(2, "PMG: Warning: PBE structure not initialized!\n");
373  /* Else adjust the inoConc by scaling factor zks2 */
374  for (i=0; i<nion; i++)
375  ionConc[i] = zks2 * ionConc[i];
376  break;
377  }
378 
379  /* Set the default chargeSrc for 5th order splines */
380  thee->chargeSrc = mgparm->chgs;
381 
382  /* Turn off restriction of observable calculations to a specific
383  * partition */
384  Vpmg_unsetPart(thee);
385 
386  /* The coefficient arrays have not been filled */
387  thee->filled = 0;
388 
389 
390  /*
391  * TODO: Move the dtor out of here. The current ctor is done in routines.c,
392  * This was originally moved out to kill a memory leak. The dtor has
393  * has been removed from initMG and placed back here to keep memory
394  * usage low. killMG has been modified accordingly.
395  */
396  Vpmg_dtor(&pmgOLD);
397 
398  return 1;
399 }
400 
401 VPUBLIC int Vpmg_solve(Vpmg *thee) {
402 
403  int i,
404  nx,
405  ny,
406  nz,
407  n;
408  double zkappa2;
409 
410  nx = thee->pmgp->nx;
411  ny = thee->pmgp->ny;
412  nz = thee->pmgp->nz;
413  n = nx*ny*nz;
414 
415  if (!(thee->filled)) {
416  Vnm_print(2, "Vpmg_solve: Need to call Vpmg_fillco()!\n");
417  return 0;
418  }
419 
420  /* Fill the "true solution" array */
421  for (i=0; i<n; i++) {
422  thee->tcf[i] = 0.0;
423  }
424 
425  /* Fill the RHS array */
426  for (i=0; i<n; i++) {
427  thee->fcf[i] = thee->charge[i];
428  }
429 
430  /* Fill the operator coefficient array. */
431  for (i=0; i<n; i++) {
432  thee->a1cf[i] = thee->epsx[i];
433  thee->a2cf[i] = thee->epsy[i];
434  thee->a3cf[i] = thee->epsz[i];
435  }
436 
437  /* Fill the nonlinear coefficient array by multiplying the kappa
438  * accessibility array (containing values between 0 and 1) by zkappa2. */
439  zkappa2 = Vpbe_getZkappa2(thee->pbe);
440  if (zkappa2 > VPMGSMALL) {
441  for (i=0; i<n; i++) {
442  thee->ccf[i] = zkappa2*thee->kappa[i];
443  }
444  } else {
445  for (i=0; i<n; i++) {
446  thee->ccf[i] = 0.0;
447  }
448  }
449 
450  switch(thee->pmgp->meth) {
451  /* CGMG (linear) */
452  case VSOL_CGMG:
453 
454  if (thee->pmgp->iinfo > 1)
455  Vnm_print(2, "Driving with CGMGDRIV\n");
456 
457  VABORT_MSG0("CGMGDRIV is not currently supported");
458  break;
459 
460  /* Newton (nonlinear) */
461  case VSOL_Newton:
462 
463  if (thee->pmgp->iinfo > 1)
464  Vnm_print(2, "Driving with NEWDRIV\n");
465 
466  Vnewdriv
467  (thee->iparm, thee->rparm, thee->iwork, thee->rwork,
468  thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
469  thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
470  thee->fcf, thee->tcf);
471  break;
472 
473  /* MG (linear/nonlinear) */
474  case VSOL_MG:
475 
476  if (thee->pmgp->iinfo > 1)
477  Vnm_print(2, "Driving with MGDRIV\n");
478 
479  Vmgdriv(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
480  thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
481  thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
482  thee->fcf, thee->tcf);
483  break;
484 
485  /* CGHS (linear/nonlinear) */
486  case VSOL_CG:
487 
488  if (thee->pmgp->iinfo > 1)
489  Vnm_print(2, "Driving with NCGHSDRIV\n");
490 
491  VABORT_MSG0("NCGHSDRIV is not currently supported");
492  break;
493 
494  /* SOR (linear/nonlinear) */
495  case VSOL_SOR:
496 
497  if (thee->pmgp->iinfo > 1)
498  Vnm_print(2, "Driving with NSORDRIV\n");
499 
500  VABORT_MSG0("NSORDRIV is not currently supported");
501  break;
502 
503  /* GSRB (linear/nonlinear) */
504  case VSOL_RBGS:
505 
506  if (thee->pmgp->iinfo > 1)
507  Vnm_print(2, "Driving with NGSRBDRIV\n");
508 
509  VABORT_MSG0("NGSRBDRIV is not currently supported");
510  break;
511 
512  /* WJAC (linear/nonlinear) */
513  case VSOL_WJ:
514 
515  if (thee->pmgp->iinfo > 1)
516  Vnm_print(2, "Driving with NWJACDRIV\n");
517 
518  VABORT_MSG0("NWJACDRIV is not currently supported");
519  break;
520 
521  /* RICH (linear/nonlinear) */
522  case VSOL_Richardson:
523 
524  if (thee->pmgp->iinfo > 1)
525  Vnm_print(2, "Driving with NRICHDRIV\n");
526 
527  VABORT_MSG0("NRICHDRIV is not currently supported");
528  break;
529 
530  /* CGMG (linear) TEMPORARY USEAQUA */
531  case VSOL_CGMGAqua:
532 
533  if (thee->pmgp->iinfo > 1)
534  Vnm_print(2, "Driving with CGMGDRIVAQUA\n");
535 
536  VABORT_MSG0("CGMGDRIVAQUA is not currently supported");
537  break;
538 
539  /* Newton (nonlinear) TEMPORARY USEAQUA */
540  case VSOL_NewtonAqua:
541 
542  if (thee->pmgp->iinfo > 1)
543  Vnm_print(2, "Driving with NEWDRIVAQUA\n");
544 
545  VABORT_MSG0("NEWDRIVAQUA is not currently supported");
546  break;
547 
548  /* Error handling */
549  default:
550  Vnm_print(2, "Vpmg_solve: invalid solver method key (%d)\n",
551  thee->pmgp->key);
552  return 0;
553  break;
554  }
555 
556  return 1;
557 
558 }
559 
560 
561 VPUBLIC void Vpmg_dtor(Vpmg **thee) {
562 
563  if ((*thee) != VNULL) {
564  Vpmg_dtor2(*thee);
565  Vmem_free(VNULL, 1, sizeof(Vpmg), (void **)thee);
566  (*thee) = VNULL;
567  }
568 
569 }
570 
571 VPUBLIC void Vpmg_dtor2(Vpmg *thee) {
572 
573  /* Clean up the storage */
574 
575  Vmem_free(thee->vmem, 100, sizeof(int),
576  (void **)&(thee->iparm));
577  Vmem_free(thee->vmem, 100, sizeof(double),
578  (void **)&(thee->rparm));
579  Vmem_free(thee->vmem, thee->pmgp->niwk, sizeof(int),
580  (void **)&(thee->iwork));
581  Vmem_free(thee->vmem, thee->pmgp->nrwk, sizeof(double),
582  (void **)&(thee->rwork));
583  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
584  (void **)&(thee->charge));
585  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
586  (void **)&(thee->kappa));
587  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
588  (void **)&(thee->pot));
589  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
590  (void **)&(thee->epsx));
591  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
592  (void **)&(thee->epsy));
593  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
594  (void **)&(thee->epsz));
595  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
596  (void **)&(thee->a1cf));
597  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
598  (void **)&(thee->a2cf));
599  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
600  (void **)&(thee->a3cf));
601  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
602  (void **)&(thee->ccf));
603  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
604  (void **)&(thee->fcf));
605  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
606  (void **)&(thee->tcf));
607  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
608  (void **)&(thee->u));
609  Vmem_free(thee->vmem, 5*(thee->pmgp->nx), sizeof(double),
610  (void **)&(thee->xf));
611  Vmem_free(thee->vmem, 5*(thee->pmgp->ny), sizeof(double),
612  (void **)&(thee->yf));
613  Vmem_free(thee->vmem, 5*(thee->pmgp->nz), sizeof(double),
614  (void **)&(thee->zf));
615  Vmem_free(thee->vmem, 10*(thee->pmgp->ny)*(thee->pmgp->nz), sizeof(double),
616  (void **)&(thee->gxcf));
617  Vmem_free(thee->vmem, 10*(thee->pmgp->nx)*(thee->pmgp->nz), sizeof(double),
618  (void **)&(thee->gycf));
619  Vmem_free(thee->vmem, 10*(thee->pmgp->nx)*(thee->pmgp->ny), sizeof(double),
620  (void **)&(thee->gzcf));
621  Vmem_free(thee->vmem, (thee->pmgp->nx)*(thee->pmgp->ny)*(thee->pmgp->nz),
622  sizeof(double), (void **)&(thee->pvec));
623 
624  Vmem_dtor(&(thee->vmem));
625 }
626 
627 VPUBLIC void Vpmg_setPart(Vpmg *thee, double lowerCorner[3],
628  double upperCorner[3], int bflags[6]) {
629 
630  Valist *alist;
631  Vatom *atom;
632  int i, j, k, nx, ny, nz;
633  double xmin, ymin, zmin, x, y, z, hx, hy, hzed, xok, yok, zok;
634  double x0,x1,y0,y1,z0,z1;
635 
636  nx = thee->pmgp->nx;
637  ny = thee->pmgp->ny;
638  nz = thee->pmgp->nz;
639  hx = thee->pmgp->hx;
640  hy = thee->pmgp->hy;
641  hzed = thee->pmgp->hzed;
642  xmin = thee->pmgp->xcent - 0.5*hx*(nx-1);
643  ymin = thee->pmgp->ycent - 0.5*hy*(ny-1);
644  zmin = thee->pmgp->zcent - 0.5*hzed*(nz-1);
645 
646  xok = 0;
647  yok = 0;
648  zok = 0;
649 
650  /* We need have called Vpmg_fillco first */
651 
652  alist = thee->pbe->alist;
653 
654  Vnm_print(0, "Vpmg_setPart: lower corner = (%g, %g, %g)\n",
655  lowerCorner[0], lowerCorner[1], lowerCorner[2]);
656  Vnm_print(0, "Vpmg_setPart: upper corner = (%g, %g, %g)\n",
657  upperCorner[0], upperCorner[1], upperCorner[2]);
658  Vnm_print(0, "Vpmg_setPart: actual minima = (%g, %g, %g)\n",
659  xmin, ymin, zmin);
660  Vnm_print(0, "Vpmg_setPart: actual maxima = (%g, %g, %g)\n",
661  xmin+hx*(nx-1), ymin+hy*(ny-1), zmin+hzed*(nz-1));
662  Vnm_print(0, "Vpmg_setPart: bflag[FRONT] = %d\n",
663  bflags[VAPBS_FRONT]);
664  Vnm_print(0, "Vpmg_setPart: bflag[BACK] = %d\n",
665  bflags[VAPBS_BACK]);
666  Vnm_print(0, "Vpmg_setPart: bflag[LEFT] = %d\n",
667  bflags[VAPBS_LEFT]);
668  Vnm_print(0, "Vpmg_setPart: bflag[RIGHT] = %d\n",
669  bflags[VAPBS_RIGHT]);
670  Vnm_print(0, "Vpmg_setPart: bflag[UP] = %d\n",
671  bflags[VAPBS_UP]);
672  Vnm_print(0, "Vpmg_setPart: bflag[DOWN] = %d\n",
673  bflags[VAPBS_DOWN]);
674 
675  /* Identify atoms as inside, outside, or on the border
676  If on the border, use the bflags to determine if there
677  is an adjacent processor - if so, this atom should be equally
678  shared. */
679 
680  for (i=0; i<Valist_getNumberAtoms(alist); i++) {
681  atom = Valist_getAtom(alist, i);
682 
683  if ((atom->position[0] < upperCorner[0]) &&
684  (atom->position[0] > lowerCorner[0])) xok = 1;
685  else {
686  if ((VABS(atom->position[0] - lowerCorner[0]) < VPMGSMALL) &&
687  (bflags[VAPBS_LEFT] == 0)) xok = 1;
688  else if ((VABS(atom->position[0] - lowerCorner[0]) < VPMGSMALL) &&
689  (bflags[VAPBS_LEFT] == 1)) xok = 0.5;
690  else if ((VABS(atom->position[0] - upperCorner[0]) < VPMGSMALL) &&
691  (bflags[VAPBS_RIGHT] == 0)) xok = 1;
692  else if ((VABS(atom->position[0] - upperCorner[0]) < VPMGSMALL) &&
693  (bflags[VAPBS_RIGHT] == 1)) xok = 0.5;
694  else xok = 0;
695  }
696  if ((atom->position[1] < upperCorner[1]) &&
697  (atom->position[1] > lowerCorner[1])) yok = 1;
698  else {
699  if ((VABS(atom->position[1] - lowerCorner[1]) < VPMGSMALL) &&
700  (bflags[VAPBS_BACK] == 0)) yok = 1;
701  else if ((VABS(atom->position[1] - lowerCorner[1]) < VPMGSMALL) &&
702  (bflags[VAPBS_BACK] == 1)) yok = 0.5;
703  else if ((VABS(atom->position[1] - upperCorner[1]) < VPMGSMALL) &&
704  (bflags[VAPBS_FRONT] == 0)) yok = 1;
705  else if ((VABS(atom->position[1] - upperCorner[1]) < VPMGSMALL) &&
706  (bflags[VAPBS_FRONT] == 1)) yok = 0.5;
707  else yok = 0;
708  }
709  if ((atom->position[2] < upperCorner[2]) &&
710  (atom->position[2] > lowerCorner[2])) zok = 1;
711  else {
712  if ((VABS(atom->position[2] - lowerCorner[2]) < VPMGSMALL) &&
713  (bflags[VAPBS_DOWN] == 0)) zok = 1;
714  else if ((VABS(atom->position[2] - lowerCorner[2]) < VPMGSMALL) &&
715  (bflags[VAPBS_DOWN] == 1)) zok = 0.5;
716  else if ((VABS(atom->position[2] - upperCorner[2]) < VPMGSMALL) &&
717  (bflags[VAPBS_UP] == 0)) zok = 1;
718  else if ((VABS(atom->position[2] - upperCorner[2]) < VPMGSMALL) &&
719  (bflags[VAPBS_UP] == 1)) zok = 0.5;
720  else zok = 0;
721  }
722 
723  atom->partID = xok*yok*zok;
724  /*
725  Vnm_print(1, "DEBUG (%s, %d): atom->position[0] - upperCorner[0] = %g\n",
726  __FILE__, __LINE__, atom->position[0] - upperCorner[0]);
727  Vnm_print(1, "DEBUG (%s, %d): atom->position[0] - lowerCorner[0] = %g\n",
728  __FILE__, __LINE__, atom->position[0] - lowerCorner[0]);
729  Vnm_print(1, "DEBUG (%s, %d): atom->position[1] - upperCorner[1] = %g\n",
730  __FILE__, __LINE__, atom->position[1] - upperCorner[1]);
731  Vnm_print(1, "DEBUG (%s, %d): atom->position[1] - lowerCorner[1] = %g\n",
732  __FILE__, __LINE__, atom->position[1] - lowerCorner[1]);
733  Vnm_print(1, "DEBUG (%s, %d): atom->position[2] - upperCorner[2] = %g\n",
734  __FILE__, __LINE__, atom->position[2] - upperCorner[2]);
735  Vnm_print(1, "DEBUG (%s, %d): atom->position[2] - lowerCorner[0] = %g\n",
736  __FILE__, __LINE__, atom->position[2] - lowerCorner[2]);
737  Vnm_print(1, "DEBUG (%s, %d): xok = %g, yok = %g, zok = %g\n",
738  __FILE__, __LINE__, xok, yok, zok);
739  */
740 
741  }
742 
743  /* Load up pvec -
744  For all points within h{axis}/2 of a border - use a gradient
745  to determine the pvec weight.
746  Points on the boundary depend on the presence of an adjacent
747  processor. */
748 
749  for (i=0; i<(nx*ny*nz); i++) thee->pvec[i] = 0.0;
750 
751  for (i=0; i<nx; i++) {
752  xok = 0.0;
753  x = i*hx + xmin;
754  if ( (x < (upperCorner[0]-hx/2)) &&
755  (x > (lowerCorner[0]+hx/2))
756  ) xok = 1.0;
757  else if ( (VABS(x - lowerCorner[0]) < VPMGSMALL) &&
758  (bflags[VAPBS_LEFT] == 0)) xok = 1.0;
759  else if ((VABS(x - lowerCorner[0]) < VPMGSMALL) &&
760  (bflags[VAPBS_LEFT] == 1)) xok = 0.5;
761  else if ((VABS(x - upperCorner[0]) < VPMGSMALL) &&
762  (bflags[VAPBS_RIGHT] == 0)) xok = 1.0;
763  else if ((VABS(x - upperCorner[0]) < VPMGSMALL) &&
764  (bflags[VAPBS_RIGHT] == 1)) xok = 0.5;
765  else if ((x > (upperCorner[0] + hx/2)) || (x < (lowerCorner[0] - hx/2))) xok = 0.0;
766  else if ((x < (upperCorner[0] + hx/2)) || (x > (lowerCorner[0] - hx/2))) {
767  x0 = VMAX2(x - hx/2, lowerCorner[0]);
768  x1 = VMIN2(x + hx/2, upperCorner[0]);
769  xok = VABS(x1-x0)/hx;
770 
771  if (xok < 0.0) {
772  if (VABS(xok) < VPMGSMALL) xok = 0.0;
773  else {
774  Vnm_print(2, "Vpmg_setPart: fell off x-interval (%1.12E)!\n",
775  xok);
776  VASSERT(0);
777  }
778  }
779  if (xok > 1.0) {
780  if (VABS(xok - 1.0) < VPMGSMALL) xok = 1.0;
781  else {
782  Vnm_print(2, "Vpmg_setPart: fell off x-interval (%1.12E)!\n",
783  xok);
784  VASSERT(0);
785  }
786  }
787 
788  } else xok = 0.0;
789 
790  for (j=0; j<ny; j++) {
791  yok = 0.0;
792  y = j*hy + ymin;
793  if ((y < (upperCorner[1]-hy/2)) && (y > (lowerCorner[1]+hy/2))) yok = 1.0;
794  else if ((VABS(y - lowerCorner[1]) < VPMGSMALL) &&
795  (bflags[VAPBS_BACK] == 0)) yok = 1.0;
796  else if ((VABS(y - lowerCorner[1]) < VPMGSMALL) &&
797  (bflags[VAPBS_BACK] == 1)) yok = 0.5;
798  else if ((VABS(y - upperCorner[1]) < VPMGSMALL) &&
799  (bflags[VAPBS_FRONT] == 0)) yok = 1.0;
800  else if ((VABS(y - upperCorner[1]) < VPMGSMALL) &&
801  (bflags[VAPBS_FRONT] == 1)) yok = 0.5;
802  else if ((y > (upperCorner[1] + hy/2)) || (y < (lowerCorner[1] - hy/2))) yok=0.0;
803  else if ((y < (upperCorner[1] + hy/2)) || (y > (lowerCorner[1] - hy/2))){
804  y0 = VMAX2(y - hy/2, lowerCorner[1]);
805  y1 = VMIN2(y + hy/2, upperCorner[1]);
806  yok = VABS(y1-y0)/hy;
807 
808  if (yok < 0.0) {
809  if (VABS(yok) < VPMGSMALL) yok = 0.0;
810  else {
811  Vnm_print(2, "Vpmg_setPart: fell off y-interval (%1.12E)!\n",
812  yok);
813  VASSERT(0);
814  }
815  }
816  if (yok > 1.0) {
817  if (VABS(yok - 1.0) < VPMGSMALL) yok = 1.0;
818  else {
819  Vnm_print(2, "Vpmg_setPart: fell off y-interval (%1.12E)!\n",
820  yok);
821  VASSERT(0);
822  }
823  }
824  }
825  else yok=0.0;
826 
827  for (k=0; k<nz; k++) {
828  zok = 0.0;
829  z = k*hzed + zmin;
830  if ((z < (upperCorner[2]-hzed/2)) && (z > (lowerCorner[2]+hzed/2))) zok = 1.0;
831  else if ((VABS(z - lowerCorner[2]) < VPMGSMALL) &&
832  (bflags[VAPBS_DOWN] == 0)) zok = 1.0;
833  else if ((VABS(z - lowerCorner[2]) < VPMGSMALL) &&
834  (bflags[VAPBS_DOWN] == 1)) zok = 0.5;
835  else if ((VABS(z - upperCorner[2]) < VPMGSMALL) &&
836  (bflags[VAPBS_UP] == 0)) zok = 1.0;
837  else if ((VABS(z - upperCorner[2]) < VPMGSMALL) &&
838  (bflags[VAPBS_UP] == 1)) zok = 0.5;
839  else if ((z > (upperCorner[2] + hzed/2)) || (z < (lowerCorner[2] - hzed/2))) zok=0.0;
840  else if ((z < (upperCorner[2] + hzed/2)) || (z > (lowerCorner[2] - hzed/2))){
841  z0 = VMAX2(z - hzed/2, lowerCorner[2]);
842  z1 = VMIN2(z + hzed/2, upperCorner[2]);
843  zok = VABS(z1-z0)/hzed;
844 
845  if (zok < 0.0) {
846  if (VABS(zok) < VPMGSMALL) zok = 0.0;
847  else {
848  Vnm_print(2, "Vpmg_setPart: fell off z-interval (%1.12E)!\n",
849  zok);
850  VASSERT(0);
851  }
852  }
853  if (zok > 1.0) {
854  if (VABS(zok - 1.0) < VPMGSMALL) zok = 1.0;
855  else {
856  Vnm_print(2, "Vpmg_setPart: fell off z-interval (%1.12E)!\n",
857  zok);
858  VASSERT(0);
859  }
860  }
861  }
862  else zok = 0.0;
863 
864  if (VABS(xok*yok*zok) < VPMGSMALL) thee->pvec[IJK(i,j,k)] = 0.0;
865  else thee->pvec[IJK(i,j,k)] = xok*yok*zok;
866 
867  }
868  }
869  }
870 }
871 
872 VPUBLIC void Vpmg_unsetPart(Vpmg *thee) {
873 
874  int i, nx, ny, nz;
875  Vatom *atom;
876  Valist *alist;
877 
878  VASSERT(thee != VNULL);
879 
880  nx = thee->pmgp->nx;
881  ny = thee->pmgp->ny;
882  nz = thee->pmgp->nz;
883  alist = thee->pbe->alist;
884 
885  for (i=0; i<(nx*ny*nz); i++) thee->pvec[i] = 1;
886  for (i=0; i<Valist_getNumberAtoms(alist); i++) {
887  atom = Valist_getAtom(alist, i);
888  atom->partID = 1;
889  }
890 }
891 
892 VPUBLIC int Vpmg_fillArray(Vpmg *thee, double *vec, Vdata_Type type,
893  double parm, Vhal_PBEType pbetype, PBEparm *pbeparm) {
894 
895  Vacc *acc = VNULL;
896  Vpbe *pbe = VNULL;
897  Vgrid *grid = VNULL;
898  Vatom *atoms = VNULL;
899  Valist *alist = VNULL;
900  double position[3], hx, hy, hzed, xmin, ymin, zmin;
901  double grad[3], eps, epsp, epss, zmagic, u;
902  int i, j, k, l, nx, ny, nz, ichop;
903 
904  pbe = thee->pbe;
905  acc = Vpbe_getVacc(pbe);
906  nx = thee->pmgp->nx;
907  ny = thee->pmgp->ny;
908  nz = thee->pmgp->nz;
909  hx = thee->pmgp->hx;
910  hy = thee->pmgp->hy;
911  hzed = thee->pmgp->hzed;
912  xmin = thee->pmgp->xmin;
913  ymin = thee->pmgp->ymin;
914  zmin = thee->pmgp->zmin;
915  epsp = Vpbe_getSoluteDiel(pbe);
916  epss = Vpbe_getSolventDiel(pbe);
917  zmagic = Vpbe_getZmagic(pbe);
918 
919  if (!(thee->filled)) {
920  Vnm_print(2, "Vpmg_fillArray: need to call Vpmg_fillco first!\n");
921  return 0;
922  }
923 
924  switch (type) {
925 
926  case VDT_CHARGE:
927 
928  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->charge[i]/zmagic;
929  break;
930 
931  case VDT_DIELX:
932 
933  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsx[i];
934  break;
935 
936  case VDT_DIELY:
937 
938  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsy[i];
939  break;
940 
941  case VDT_DIELZ:
942 
943  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsz[i];
944  break;
945 
946  case VDT_KAPPA:
947 
948  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->kappa[i];
949  break;
950 
951  case VDT_POT:
952 
953  for (i=0; i<nx*ny*nz; i++) vec[i] = thee->u[i];
954  break;
955 
956  case VDT_ATOMPOT:
957  alist = thee->pbe->alist;
958  atoms = alist[pbeparm->molid-1].atoms;
959  grid = Vgrid_ctor(nx, ny, nz, hx, hy,
960  hzed, xmin, ymin, zmin,thee->u);
961  for (i=0; i<alist[pbeparm->molid-1].number;i++) {
962  position[0] = atoms[i].position[0];
963  position[1] = atoms[i].position[1];
964  position[2] = atoms[i].position[2];
965 
966  Vgrid_value(grid, position, &vec[i]);
967  }
968  Vgrid_dtor(&grid);
969  break;
970 
971  case VDT_SMOL:
972 
973  for (k=0; k<nz; k++) {
974  for (j=0; j<ny; j++) {
975  for (i=0; i<nx; i++) {
976 
977  position[0] = i*hx + xmin;
978  position[1] = j*hy + ymin;
979  position[2] = k*hzed + zmin;
980 
981  vec[IJK(i,j,k)] = (Vacc_molAcc(acc,position,parm));
982  }
983  }
984  }
985  break;
986 
987  case VDT_SSPL:
988 
989  for (k=0; k<nz; k++) {
990  for (j=0; j<ny; j++) {
991  for (i=0; i<nx; i++) {
992 
993  position[0] = i*hx + xmin;
994  position[1] = j*hy + ymin;
995  position[2] = k*hzed + zmin;
996 
997  vec[IJK(i,j,k)] = Vacc_splineAcc(acc,position,parm,0);
998  }
999  }
1000  }
1001  break;
1002 
1003  case VDT_VDW:
1004 
1005  for (k=0; k<nz; k++) {
1006  for (j=0; j<ny; j++) {
1007  for (i=0; i<nx; i++) {
1008 
1009  position[0] = i*hx + xmin;
1010  position[1] = j*hy + ymin;
1011  position[2] = k*hzed + zmin;
1012 
1013  vec[IJK(i,j,k)] = Vacc_vdwAcc(acc,position);
1014  }
1015  }
1016  }
1017  break;
1018 
1019  case VDT_IVDW:
1020 
1021  for (k=0; k<nz; k++) {
1022  for (j=0; j<ny; j++) {
1023  for (i=0; i<nx; i++) {
1024 
1025  position[0] = i*hx + xmin;
1026  position[1] = j*hy + ymin;
1027  position[2] = k*hzed + zmin;
1028 
1029  vec[IJK(i,j,k)] = Vacc_ivdwAcc(acc,position,parm);
1030  }
1031  }
1032  }
1033  break;
1034 
1035  case VDT_LAP:
1036 
1037  grid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin,
1038  thee->u);
1039  for (k=0; k<nz; k++) {
1040  for (j=0; j<ny; j++) {
1041  for (i=0; i<nx; i++) {
1042 
1043  if ((k==0) || (k==(nz-1)) ||
1044  (j==0) || (j==(ny-1)) ||
1045  (i==0) || (i==(nx-1))) {
1046 
1047  vec[IJK(i,j,k)] = 0;
1048 
1049  } else {
1050  position[0] = i*hx + xmin;
1051  position[1] = j*hy + ymin;
1052  position[2] = k*hzed + zmin;
1053  VASSERT(Vgrid_curvature(grid,position, 1,
1054  &(vec[IJK(i,j,k)])));
1055  }
1056  }
1057  }
1058  }
1059  Vgrid_dtor(&grid);
1060  break;
1061 
1062  case VDT_EDENS:
1063 
1064  grid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin,
1065  thee->u);
1066  for (k=0; k<nz; k++) {
1067  for (j=0; j<ny; j++) {
1068  for (i=0; i<nx; i++) {
1069 
1070  position[0] = i*hx + xmin;
1071  position[1] = j*hy + ymin;
1072  position[2] = k*hzed + zmin;
1073  VASSERT(Vgrid_gradient(grid, position, grad));
1074  eps = epsp + (epss-epsp)*Vacc_molAcc(acc, position,
1075  pbe->solventRadius);
1076  vec[IJK(i,j,k)] = 0.0;
1077  for (l=0; l<3; l++)
1078  vec[IJK(i,j,k)] += eps*VSQR(grad[l]);
1079  }
1080  }
1081  }
1082  Vgrid_dtor(&grid);
1083  break;
1084 
1085  case VDT_NDENS:
1086 
1087  for (k=0; k<nz; k++) {
1088  for (j=0; j<ny; j++) {
1089  for (i=0; i<nx; i++) {
1090 
1091  position[0] = i*hx + xmin;
1092  position[1] = j*hy + ymin;
1093  position[2] = k*hzed + zmin;
1094  vec[IJK(i,j,k)] = 0.0;
1095  u = thee->u[IJK(i,j,k)];
1096  if ( VABS(Vacc_ivdwAcc(acc,
1097  position, pbe->maxIonRadius) - 1.0) < VSMALL) {
1098  for (l=0; l<pbe->numIon; l++) {
1099  double q = pbe->ionQ[l];
1100  if (pbetype == PBE_NPBE || pbetype == PBE_SMPBE /* SMPBE Added */) {
1101  vec[IJK(i,j,k)] += pbe->ionConc[l]*Vcap_exp(-q*u, &ichop);
1102  } else if (pbetype == PBE_LPBE){
1103  vec[IJK(i,j,k)] += pbe->ionConc[l]*(1 - q*u + 0.5*q*q*u*u);
1104  }
1105  }
1106  }
1107  }
1108  }
1109  }
1110  break;
1111 
1112  case VDT_QDENS:
1113 
1114  for (k=0; k<nz; k++) {
1115  for (j=0; j<ny; j++) {
1116  for (i=0; i<nx; i++) {
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  u = thee->u[IJK(i,j,k)];
1122  if ( VABS(Vacc_ivdwAcc(acc,
1123  position, pbe->maxIonRadius) - 1.0) < VSMALL) {
1124  for (l=0; l<pbe->numIon; l++) {
1125  double q = pbe->ionQ[l];
1126  if (pbetype == PBE_NPBE || pbetype == PBE_SMPBE /* SMPBE Added */) {
1127  vec[IJK(i,j,k)] += pbe->ionConc[l]*q*Vcap_exp(-q*u, &ichop);
1128  } else if (pbetype == PBE_LPBE) {
1129  vec[IJK(i,j,k)] += pbe->ionConc[l]*q*(1 - q*u + 0.5*q*q*u*u);
1130  }
1131  }
1132  }
1133  }}}
1134  break;
1135 
1136  default:
1137 
1138  Vnm_print(2, "main: Bogus data type (%d)!\n", type);
1139  return 0;
1140  break;
1141 
1142  }
1143 
1144  return 1;
1145 
1146 }
1147 
1148 VPRIVATE double Vpmg_polarizEnergy(Vpmg *thee,
1149  int extFlag
1150  ) {
1151 
1152  int i,
1153  j,
1154  k,
1155  ijk,
1156  nx,
1157  ny,
1158  nz,
1159  iatom;
1160  double xmin,
1161  ymin,
1162  zmin,
1163  //x, // gcc: not used
1164  //y,
1165  //z,
1166  hx,
1167  hy,
1168  hzed,
1169  epsp,
1170  lap,
1171  pt[3],
1172  T,
1173  pre,
1174  polq,
1175  dist2,
1176  dist,
1177  energy,
1178  q,
1179  *charge,
1180  *pos,
1181  eps_w;
1182  Vgrid *potgrid;
1183  Vpbe *pbe;
1184  Valist *alist;
1185  Vatom *atom;
1186 
1187  xmin = thee->pmgp->xmin;
1188  ymin = thee->pmgp->ymin;
1189  zmin = thee->pmgp->ymin;
1190  hx = thee->pmgp->hx;
1191  hy = thee->pmgp->hy;
1192  hzed = thee->pmgp->hzed;
1193  nx = thee->pmgp->nx;
1194  ny = thee->pmgp->ny;
1195  nz = thee->pmgp->nz;
1196  pbe = thee->pbe;
1197  epsp = Vpbe_getSoluteDiel(pbe);
1198  eps_w = Vpbe_getSolventDiel(pbe);
1199  alist = pbe->alist;
1200  charge = thee->charge;
1201 
1202  /* Calculate the prefactor for Coulombic calculations */
1203  T = Vpbe_getTemperature(pbe);
1204  pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
1205  pre = pre*(1.0e10);
1206 
1207  /* Set up Vgrid object with solution */
1208  potgrid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin, thee->u);
1209 
1210  /* Calculate polarization charge */
1211  energy = 0.0;
1212  for (i=1; i<(nx-1); i++) {
1213  pt[0] = xmin + hx*i;
1214  for (j=1; j<(ny-1); j++) {
1215  pt[1] = ymin + hy*j;
1216  for (k=1; k<(nz-1); k++) {
1217  pt[2] = zmin + hzed*k;
1218 
1219  /* Calculate polarization charge */
1220  VASSERT(Vgrid_curvature(potgrid, pt, 1, &lap));
1221  ijk = IJK(i,j,k);
1222  polq = charge[ijk] + epsp*lap*3.0;
1223 
1224  /* Calculate interaction energy with atoms */
1225  if (VABS(polq) > VSMALL) {
1226  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
1227  atom = Valist_getAtom(alist, iatom);
1228  q = Vatom_getCharge(atom);
1229  pos = Vatom_getPosition(atom);
1230  dist2 = VSQR(pos[0]-pt[0]) + VSQR(pos[1]-pt[1]) \
1231  + VSQR(pos[2]-pt[2]);
1232  dist = VSQRT(dist2);
1233 
1234  if (dist < VSMALL) {
1235  Vnm_print(2, "Vpmg_polarizEnergy: atom on grid point; ignoring!\n");
1236  } else {
1237  energy = energy + polq*q/dist;
1238  }
1239  }
1240  }
1241  }
1242  }
1243  }
1244 
1245  return pre*energy;
1246 }
1247 
1248 VPUBLIC double Vpmg_energy(Vpmg *thee,
1249  int extFlag
1250  ) {
1251 
1252  double totEnergy = 0.0,
1253  dielEnergy = 0.0,
1254  qmEnergy = 0.0,
1255  qfEnergy = 0.0;
1256 
1257  VASSERT(thee != VNULL);
1258 
1259  if ((thee->pmgp->nonlin) && (Vpbe_getBulkIonicStrength(thee->pbe) > 0.)) {
1260  Vnm_print(0, "Vpmg_energy: calculating full PBE energy\n");
1261  qmEnergy = Vpmg_qmEnergy(thee, extFlag);
1262  Vnm_print(0, "Vpmg_energy: qmEnergy = %1.12E kT\n", qmEnergy);
1263  qfEnergy = Vpmg_qfEnergy(thee, extFlag);
1264  Vnm_print(0, "Vpmg_energy: qfEnergy = %1.12E kT\n", qfEnergy);
1265  dielEnergy = Vpmg_dielEnergy(thee, extFlag);
1266  Vnm_print(0, "Vpmg_energy: dielEnergy = %1.12E kT\n", dielEnergy);
1267  totEnergy = qfEnergy - dielEnergy - qmEnergy;
1268  } else {
1269  Vnm_print(0, "Vpmg_energy: calculating only q-phi energy\n");
1270  qfEnergy = Vpmg_qfEnergy(thee, extFlag);
1271  Vnm_print(0, "Vpmg_energy: qfEnergy = %1.12E kT\n", qfEnergy);
1272  totEnergy = 0.5*qfEnergy;
1273  }
1274 
1275  return totEnergy;
1276 
1277 }
1278 
1279 VPUBLIC double Vpmg_dielEnergy(Vpmg *thee,
1280  int extFlag
1281  ) {
1282 
1283  double hx,
1284  hy,
1285  hzed,
1286  energy,
1287  nrgx,
1288  nrgy,
1289  nrgz,
1290  pvecx,
1291  pvecy,
1292  pvecz;
1293  int i,
1294  j,
1295  k,
1296  nx,
1297  ny,
1298  nz;
1299 
1300  VASSERT(thee != VNULL);
1301 
1302  /* Get the mesh information */
1303  nx = thee->pmgp->nx;
1304  ny = thee->pmgp->ny;
1305  nz = thee->pmgp->nz;
1306  hx = thee->pmgp->hx;
1307  hy = thee->pmgp->hy;
1308  hzed = thee->pmgp->hzed;
1309 
1310  energy = 0.0;
1311 
1312  if (!thee->filled) {
1313  Vnm_print(2, "Vpmg_dielEnergy: Need to call Vpmg_fillco!\n");
1314  VASSERT(0);
1315  }
1316 
1317  for (k=0; k<(nz-1); k++) {
1318  for (j=0; j<(ny-1); j++) {
1319  for (i=0; i<(nx-1); i++) {
1320  pvecx = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i+1,j,k)]);
1321  pvecy = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j+1,k)]);
1322  pvecz = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j,k+1)]);
1323  nrgx = thee->epsx[IJK(i,j,k)]*pvecx
1324  * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i+1,j,k)])/hx);
1325  nrgy = thee->epsy[IJK(i,j,k)]*pvecy
1326  * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i,j+1,k)])/hy);
1327  nrgz = thee->epsz[IJK(i,j,k)]*pvecz
1328  * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i,j,k+1)])/hzed);
1329  energy += (nrgx + nrgy + nrgz);
1330  }
1331  }
1332  }
1333 
1334  energy = 0.5*energy*hx*hy*hzed;
1335  energy = energy/Vpbe_getZmagic(thee->pbe);
1336 
1337  if (extFlag == 1) energy += (thee->extDiEnergy);
1338 
1339  return energy;
1340 }
1341 
1342 VPUBLIC double Vpmg_dielGradNorm(Vpmg *thee) {
1343 
1344  double hx, hy, hzed, energy, nrgx, nrgy, nrgz, pvecx, pvecy, pvecz;
1345  int i, j, k, nx, ny, nz;
1346 
1347  VASSERT(thee != VNULL);
1348 
1349  /* Get the mesh information */
1350  nx = thee->pmgp->nx;
1351  ny = thee->pmgp->ny;
1352  nz = thee->pmgp->nz;
1353  hx = thee->pmgp->hx;
1354  hy = thee->pmgp->hy;
1355  hzed = thee->pmgp->hzed;
1356 
1357  energy = 0.0;
1358 
1359  if (!thee->filled) {
1360  Vnm_print(2, "Vpmg_dielGradNorm: Need to call Vpmg_fillco!\n");
1361  VASSERT(0);
1362  }
1363 
1364  for (k=1; k<nz; k++) {
1365  for (j=1; j<ny; j++) {
1366  for (i=1; i<nx; i++) {
1367  pvecx = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i-1,j,k)]);
1368  pvecy = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j-1,k)]);
1369  pvecz = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j,k-1)]);
1370  nrgx = pvecx
1371  * VSQR((thee->epsx[IJK(i,j,k)]-thee->epsx[IJK(i-1,j,k)])/hx);
1372  nrgy = pvecy
1373  * VSQR((thee->epsy[IJK(i,j,k)]-thee->epsy[IJK(i,j-1,k)])/hy);
1374  nrgz = pvecz
1375  * VSQR((thee->epsz[IJK(i,j,k)]-thee->epsz[IJK(i,j,k-1)])/hzed);
1376  energy += VSQRT(nrgx + nrgy + nrgz);
1377  }
1378  }
1379  }
1380 
1381  energy = energy*hx*hy*hzed;
1382 
1383  return energy;
1384 }
1385 
1386 VPUBLIC double Vpmg_qmEnergy(Vpmg *thee,
1387  int extFlag
1388  ) {
1389 
1390  double energy;
1391 
1392  if(thee->pbe->ipkey == IPKEY_SMPBE){
1393  energy = Vpmg_qmEnergySMPBE(thee,extFlag);
1394  }else{
1395  energy = Vpmg_qmEnergyNONLIN(thee,extFlag);
1396  }
1397 
1398  return energy;
1399 }
1400 
1401 VPRIVATE double Vpmg_qmEnergyNONLIN(Vpmg *thee,
1402  int extFlag
1403  ) {
1404 
1405  double hx,
1406  hy,
1407  hzed,
1408  energy,
1409  ionConc[MAXION],
1410  ionRadii[MAXION],
1411  ionQ[MAXION],
1412  zkappa2,
1413  ionstr,
1414  zks2;
1415  int i, /* Loop variable */
1416  j,
1417  nx,
1418  ny,
1419  nz,
1420  nion,
1421  ichop,
1422  nchop,
1423  len; /* Stores number of iterations for loops to avoid multiple recalculations */
1424 
1425  VASSERT(thee != VNULL);
1426 
1427  /* Get the mesh information */
1428  nx = thee->pmgp->nx;
1429  ny = thee->pmgp->ny;
1430  nz = thee->pmgp->nz;
1431  hx = thee->pmgp->hx;
1432  hy = thee->pmgp->hy;
1433  hzed = thee->pmgp->hzed;
1434  zkappa2 = Vpbe_getZkappa2(thee->pbe);
1435  ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
1436 
1437  /* Bail if we're at zero ionic strength */
1438  if (zkappa2 < VSMALL) {
1439 
1440 #ifndef VAPBSQUIET
1441  Vnm_print(0, "Vpmg_qmEnergy: Zero energy for zero ionic strength!\n");
1442 #endif
1443 
1444  return 0.0;
1445  }
1446  zks2 = 0.5*zkappa2/ionstr;
1447 
1448  if (!thee->filled) {
1449  Vnm_print(2, "Vpmg_qmEnergy: Need to call Vpmg_fillco()!\n");
1450  VASSERT(0);
1451  }
1452 
1453  energy = 0.0;
1454  nchop = 0;
1455  Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
1456  if (thee->pmgp->nonlin) {
1457  Vnm_print(0, "Vpmg_qmEnergy: Calculating nonlinear energy\n");
1458  for (i=0, len=nx*ny*nz; i<len; i++) {
1459  if (thee->pvec[i]*thee->kappa[i] > VSMALL) {
1460  for (j=0; j<nion; j++) {
1461  energy += (thee->pvec[i]*thee->kappa[i]*zks2
1462  * ionConc[j]
1463  * (Vcap_exp(-ionQ[j]*thee->u[i], &ichop)-1.0));
1464  nchop += ichop;
1465  }
1466  }
1467  }
1468  if (nchop > 0){
1469  Vnm_print(2, "Vpmg_qmEnergy: Chopped EXP %d times!\n",nchop);
1470  Vnm_print(2, "\nERROR! Detected large potential values in energy evaluation! \nERROR! This calculation failed -- please report to the APBS developers!\n\n");
1471  VASSERT(0);
1472  }
1473  } else {
1474  /* Zkappa2 OK here b/c LPBE approx */
1475  Vnm_print(0, "Vpmg_qmEnergy: Calculating linear energy\n");
1476  for (i=0, len=nx*ny*nz; i<len; i++) {
1477  if (thee->pvec[i]*thee->kappa[i] > VSMALL)
1478  energy += (thee->pvec[i]*zkappa2*thee->kappa[i]*VSQR(thee->u[i]));
1479  }
1480  energy = 0.5*energy;
1481  }
1482  energy = energy*hx*hy*hzed;
1483  energy = energy/Vpbe_getZmagic(thee->pbe);
1484 
1485  if (extFlag == 1) energy += thee->extQmEnergy;
1486 
1487  return energy;
1488 }
1489 
1490 VPUBLIC double Vpmg_qmEnergySMPBE(Vpmg *thee,
1491  int extFlag
1492  ) {
1493 
1494  double hx,
1495  hy,
1496  hzed,
1497  energy,
1498  ionConc[MAXION],
1499  ionRadii[MAXION],
1500  ionQ[MAXION],
1501  zkappa2,
1502  ionstr,
1503  zks2;
1504  int i,
1505  //j, // gcc: not used
1506  nx,
1507  ny,
1508  nz,
1509  nion,
1510  //ichop, // gcc: not used
1511  nchop,
1512  len; /* Loop variable */
1513 
1514  /* SMPB Modification (vchu, 09/21/06)*/
1515  /* variable declarations for SMPB energy terms */
1516  double a,
1517  k,
1518  z1,
1519  z2,
1520  z3,
1521  cb1,
1522  cb2,
1523  cb3,
1524  a1,
1525  a2,
1526  a3,
1527  c1,
1528  c2,
1529  c3,
1530  currEnergy,
1531  fracOccA,
1532  fracOccB,
1533  fracOccC,
1534  phi,
1535  gpark,
1536  denom;
1537  // Na; /**< @todo remove if no conflicts are caused - This constant is already defined in vpde.h. no need to redefine. */
1538  int ichop1,
1539  ichop2,
1540  ichop3;
1541 
1542  VASSERT(thee != VNULL);
1543 
1544  /* Get the mesh information */
1545  nx = thee->pmgp->nx;
1546  ny = thee->pmgp->ny;
1547  nz = thee->pmgp->nz;
1548  hx = thee->pmgp->hx;
1549  hy = thee->pmgp->hy;
1550  hzed = thee->pmgp->hzed;
1551  zkappa2 = Vpbe_getZkappa2(thee->pbe);
1552  ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
1553 
1554  /* Bail if we're at zero ionic strength */
1555  if (zkappa2 < VSMALL) {
1556 
1557 #ifndef VAPBSQUIET
1558  Vnm_print(0, "Vpmg_qmEnergySMPBE: Zero energy for zero ionic strength!\n");
1559 #endif
1560 
1561  return 0.0;
1562  }
1563  zks2 = 0.5*zkappa2/ionstr;
1564 
1565  if (!thee->filled) {
1566  Vnm_print(2, "Vpmg_qmEnergySMPBE: Need to call Vpmg_fillco()!\n");
1567  VASSERT(0);
1568  }
1569 
1570  energy = 0.0;
1571  nchop = 0;
1572  Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
1573 
1574  /* SMPB Modification (vchu, 09/21/06) */
1575  /* Extensive modification to the first part of the if statement
1576  where that handles the thee->pmgp->nonlin part. Basically, I've
1577  deleted all of the original code and written my own code that computes
1578  the electrostatic free energy in the SMPB framework. Definitely really hacky
1579  at this stage of the game, but gets the job done. The second part of the
1580  if statement (the part that handles linear poisson-boltzmann) has been deleted
1581  because there will be no linearized SMPB energy.. */
1582 
1583  z1 = ionQ[0];
1584  z2 = ionQ[1];
1585  z3 = ionQ[2];
1586  cb1 = ionConc[0];
1587  cb2 = ionConc[1];
1588  cb3 = ionConc[2];
1589  a = thee->pbe->smvolume;
1590  k = thee->pbe->smsize;
1591 
1593  // This constant is defined in vpde.h Do not need to redefine
1594  //Na = 6.022045000e-04; /* Converts from Molar to N/A^3 */
1595 
1596  fracOccA = Na*cb1*VCUB(a);
1597  fracOccB = Na*cb2*VCUB(a);
1598  fracOccC = Na*cb3*VCUB(a);
1599 
1600  phi = (fracOccA/k) + fracOccB + fracOccC;
1601 
1602  if (thee->pmgp->nonlin) {
1603  Vnm_print(0, "Vpmg_qmEnergySMPBE: Calculating nonlinear energy using SMPB functional!\n");
1604  for (i=0, len=nx*ny*nz; i<len; i++) {
1605  if (((k-1) > VSMALL) && (thee->pvec[i]*thee->kappa[i] > VSMALL)) {
1606 
1607  a1 = Vcap_exp(-1.0*z1*thee->u[i], &ichop1);
1608  a2 = Vcap_exp(-1.0*z2*thee->u[i], &ichop2);
1609  a3 = Vcap_exp(-1.0*z3*thee->u[i], &ichop3);
1610 
1611  nchop += ichop1 + ichop2 + ichop3;
1612 
1613  gpark = (1 - phi + (fracOccA/k)*a1);
1614  denom = VPOW(gpark, k) + VPOW(1-fracOccB-fracOccC, k-1)*(fracOccB*a2+fracOccC*a3);
1615 
1616  if (cb1 > VSMALL) {
1617  c1 = Na*cb1*VPOW(gpark, k-1)*a1/denom;
1618  if(c1 != c1) c1 = 0.;
1619  } else c1 = 0.;
1620 
1621  if (cb2 > VSMALL) {
1622  c2 = Na*cb2*VPOW(1-fracOccB-fracOccC,k-1)*a2/denom;
1623  if(c2 != c2) c2 = 0.;
1624  } else c2 = 0.;
1625 
1626  if (cb3 > VSMALL) {
1627  c3 = Na*cb3*VPOW(1-fracOccB-fracOccC,k-1)*a3/denom;
1628  if(c3 != c3) c3 = 0.;
1629  } else c3 = 0.;
1630 
1631  currEnergy = k*VLOG((1-(c1*VCUB(a)/k)-c2*VCUB(a)-c3*VCUB(a))/(1-phi))
1632  -(k-1)*VLOG((1-c2*VCUB(a)-c3*VCUB(a))/(1-phi+(fracOccA/k)));
1633 
1634  energy += thee->pvec[i]*thee->kappa[i]*currEnergy;
1635 
1636  } else if (thee->pvec[i]*thee->kappa[i] > VSMALL){
1637 
1638  a1 = Vcap_exp(-1.0*z1*thee->u[i], &ichop1);
1639  a2 = Vcap_exp(-1.0*z2*thee->u[i], &ichop2);
1640  a3 = Vcap_exp(-1.0*z3*thee->u[i], &ichop3);
1641 
1642  nchop += ichop1 + ichop2 + ichop3;
1643 
1644  gpark = (1 - phi + (fracOccA)*a1);
1645  denom = gpark + (fracOccB*a2+fracOccC*a3);
1646 
1647  if (cb1 > VSMALL) {
1648  c1 = Na*cb1*a1/denom;
1649  if(c1 != c1) c1 = 0.;
1650  } else c1 = 0.;
1651 
1652  if (cb2 > VSMALL) {
1653  c2 = Na*cb2*a2/denom;
1654  if(c2 != c2) c2 = 0.;
1655  } else c2 = 0.;
1656 
1657  if (cb3 > VSMALL) {
1658  c3 = Na*cb3*a3/denom;
1659  if(c3 != c3) c3 = 0.;
1660  } else c3 = 0.;
1661 
1662  currEnergy = VLOG((1-c1*VCUB(a)-c2*VCUB(a)-c3*VCUB(a))/(1-fracOccA-fracOccB-fracOccC));
1663 
1664  energy += thee->pvec[i]*thee->kappa[i]*currEnergy;
1665  }
1666  }
1667 
1668  energy = -energy/VCUB(a);
1669 
1670  if (nchop > 0) Vnm_print(2, "Vpmg_qmEnergySMPBE: Chopped EXP %d times!\n",
1671  nchop);
1672 
1673  } else {
1674  /* Zkappa2 OK here b/c LPBE approx */
1675  Vnm_print(0, "Vpmg_qmEnergySMPBE: ERROR: NO LINEAR ENERGY!! Returning 0!\n");
1676 
1677  energy = 0.0;
1678 
1679  }
1680  energy = energy*hx*hy*hzed;
1681 
1682  if (extFlag == 1) energy += thee->extQmEnergy;
1683 
1684  return energy;
1685 }
1686 
1687 VPUBLIC double Vpmg_qfEnergy(Vpmg *thee,
1688  int extFlag
1689  ) {
1690 
1691  double energy = 0.0;
1692 
1693  VASSERT(thee != VNULL);
1694 
1695  if ((thee->useChargeMap) || (thee->chargeMeth == VCM_BSPL2)) {
1696  energy = Vpmg_qfEnergyVolume(thee, extFlag);
1697  } else {
1698  energy = Vpmg_qfEnergyPoint(thee, extFlag);
1699  }
1700 
1701  return energy;
1702 }
1703 
1704 VPRIVATE double Vpmg_qfEnergyPoint(Vpmg *thee,
1705  int extFlag
1706  ) {
1707 
1708  int iatom, nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
1709  double xmax, ymax, zmax, xmin, ymin, zmin, hx, hy, hzed, ifloat, jfloat;
1710  double charge, kfloat, dx, dy, dz, energy, uval, *position;
1711  double *u;
1712  double *pvec;
1713  Valist *alist;
1714  Vatom *atom;
1715  Vpbe *pbe;
1716 
1717  pbe = thee->pbe;
1718  alist = pbe->alist;
1719  VASSERT(alist != VNULL);
1720 
1721  /* Get the mesh information */
1722  nx = thee->pmgp->nx;
1723  ny = thee->pmgp->ny;
1724  nz = thee->pmgp->nz;
1725  hx = thee->pmgp->hx;
1726  hy = thee->pmgp->hy;
1727  hzed = thee->pmgp->hzed;
1728  xmax = thee->pmgp->xmax;
1729  ymax = thee->pmgp->ymax;
1730  zmax = thee->pmgp->zmax;
1731  xmin = thee->pmgp->xmin;
1732  ymin = thee->pmgp->ymin;
1733  zmin = thee->pmgp->zmin;
1734 
1735  u = thee->u;
1736  pvec = thee->pvec;
1737 
1738  energy = 0.0;
1739 
1740  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
1741 
1742  /* Get atomic information */
1743  atom = Valist_getAtom(alist, iatom);
1744 
1745  position = Vatom_getPosition(atom);
1746  charge = Vatom_getCharge(atom);
1747 
1748  /* Figure out which vertices we're next to */
1749  ifloat = (position[0] - xmin)/hx;
1750  jfloat = (position[1] - ymin)/hy;
1751  kfloat = (position[2] - zmin)/hzed;
1752  ihi = (int)ceil(ifloat);
1753  ilo = (int)floor(ifloat);
1754  jhi = (int)ceil(jfloat);
1755  jlo = (int)floor(jfloat);
1756  khi = (int)ceil(kfloat);
1757  klo = (int)floor(kfloat);
1758 
1759  if (atom->partID > 0) {
1760 
1761  if ((ihi<nx) && (jhi<ny) && (khi<nz) &&
1762  (ilo>=0) && (jlo>=0) && (klo>=0)) {
1763 
1764  /* Now get trilinear interpolation constants */
1765  dx = ifloat - (double)(ilo);
1766  dy = jfloat - (double)(jlo);
1767  dz = kfloat - (double)(klo);
1768  uval =
1769  dx*dy*dz*u[IJK(ihi,jhi,khi)]
1770  + dx*(1.0-dy)*dz*u[IJK(ihi,jlo,khi)]
1771  + dx*dy*(1.0-dz)*u[IJK(ihi,jhi,klo)]
1772  + dx*(1.0-dy)*(1.0-dz)*u[IJK(ihi,jlo,klo)]
1773  + (1.0-dx)*dy*dz*u[IJK(ilo,jhi,khi)]
1774  + (1.0-dx)*(1.0-dy)*dz*u[IJK(ilo,jlo,khi)]
1775  + (1.0-dx)*dy*(1.0-dz)*u[IJK(ilo,jhi,klo)]
1776  + (1.0-dx)*(1.0-dy)*(1.0-dz)*u[IJK(ilo,jlo,klo)];
1777  energy += (uval*charge*atom->partID);
1778  } else if (thee->pmgp->bcfl != BCFL_FOCUS) {
1779  Vnm_print(2, "Vpmg_qfEnergy: Atom #%d at (%4.3f, %4.3f, \
1780 %4.3f) is off the mesh (ignoring)!\n",
1781  iatom, position[0], position[1], position[2]);
1782  }
1783  }
1784  }
1785 
1786  if (extFlag) energy += thee->extQfEnergy;
1787 
1788  return energy;
1789 }
1790 
1791 VPUBLIC double Vpmg_qfAtomEnergy(Vpmg *thee, Vatom *atom) {
1792 
1793  int nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
1794  double xmax, xmin, ymax, ymin, zmax, zmin, hx, hy, hzed, ifloat, jfloat;
1795  double charge, kfloat, dx, dy, dz, energy, uval, *position;
1796  double *u;
1797 
1798 
1799  /* Get the mesh information */
1800  nx = thee->pmgp->nx;
1801  ny = thee->pmgp->ny;
1802  nz = thee->pmgp->nz;
1803  hx = thee->pmgp->hx;
1804  hy = thee->pmgp->hy;
1805  hzed = thee->pmgp->hzed;
1806  xmax = thee->xf[nx-1];
1807  ymax = thee->yf[ny-1];
1808  zmax = thee->zf[nz-1];
1809  xmin = thee->xf[0];
1810  ymin = thee->yf[0];
1811  zmin = thee->zf[0];
1812 
1813  u = thee->u;
1814 
1815  energy = 0.0;
1816 
1817 
1818  position = Vatom_getPosition(atom);
1819  charge = Vatom_getCharge(atom);
1820 
1821  /* Figure out which vertices we're next to */
1822  ifloat = (position[0] - xmin)/hx;
1823  jfloat = (position[1] - ymin)/hy;
1824  kfloat = (position[2] - zmin)/hzed;
1825  ihi = (int)ceil(ifloat);
1826  ilo = (int)floor(ifloat);
1827  jhi = (int)ceil(jfloat);
1828  jlo = (int)floor(jfloat);
1829  khi = (int)ceil(kfloat);
1830  klo = (int)floor(kfloat);
1831 
1832  if (atom->partID > 0) {
1833 
1834  if ((ihi<nx) && (jhi<ny) && (khi<nz) &&
1835  (ilo>=0) && (jlo>=0) && (klo>=0)) {
1836 
1837  /* Now get trilinear interpolation constants */
1838  dx = ifloat - (double)(ilo);
1839  dy = jfloat - (double)(jlo);
1840  dz = kfloat - (double)(klo);
1841  uval =
1842  dx*dy*dz*u[IJK(ihi,jhi,khi)]
1843  + dx*(1.0-dy)*dz*u[IJK(ihi,jlo,khi)]
1844  + dx*dy*(1.0-dz)*u[IJK(ihi,jhi,klo)]
1845  + dx*(1.0-dy)*(1.0-dz)*u[IJK(ihi,jlo,klo)]
1846  + (1.0-dx)*dy*dz*u[IJK(ilo,jhi,khi)]
1847  + (1.0-dx)*(1.0-dy)*dz*u[IJK(ilo,jlo,khi)]
1848  + (1.0-dx)*dy*(1.0-dz)*u[IJK(ilo,jhi,klo)]
1849  + (1.0-dx)*(1.0-dy)*(1.0-dz)*u[IJK(ilo,jlo,klo)];
1850  energy += (uval*charge*atom->partID);
1851  } else if (thee->pmgp->bcfl != BCFL_FOCUS) {
1852  Vnm_print(2, "Vpmg_qfAtomEnergy: Atom at (%4.3f, %4.3f, \
1853 %4.3f) is off the mesh (ignoring)!\n",
1854  position[0], position[1], position[2]);
1855  }
1856  }
1857 
1858  return energy;
1859 }
1860 
1861 VPRIVATE double Vpmg_qfEnergyVolume(Vpmg *thee, int extFlag) {
1862 
1863  double hx, hy, hzed, energy;
1864  int i, nx, ny, nz;
1865 
1866  VASSERT(thee != VNULL);
1867 
1868  /* Get the mesh information */
1869  nx = thee->pmgp->nx;
1870  ny = thee->pmgp->ny;
1871  nz = thee->pmgp->nz;
1872  hx = thee->pmgp->hx;
1873  hy = thee->pmgp->hy;
1874  hzed = thee->pmgp->hzed;
1875 
1876  if (!thee->filled) {
1877  Vnm_print(2, "Vpmg_qfEnergyVolume: need to call Vpmg_fillco!\n");
1878  VASSERT(0);
1879  }
1880 
1881  energy = 0.0;
1882  Vnm_print(0, "Vpmg_qfEnergyVolume: Calculating energy\n");
1883  for (i=0; i<(nx*ny*nz); i++) {
1884  energy += (thee->pvec[i]*thee->u[i]*thee->charge[i]);
1885  }
1886  energy = energy*hx*hy*hzed/Vpbe_getZmagic(thee->pbe);
1887 
1888  if (extFlag == 1) energy += thee->extQfEnergy;
1889 
1890  return energy;
1891 }
1892 
1893 VPRIVATE void Vpmg_splineSelect(int srfm,Vacc *acc,double *gpos,double win,
1894  double infrad,Vatom *atom,double *force){
1895 
1896  switch (srfm) {
1897  case VSM_SPLINE :
1898  Vacc_splineAccGradAtomNorm(acc, gpos, win, infrad, atom, force);
1899  break;
1900  case VSM_SPLINE3:
1901  Vacc_splineAccGradAtomNorm3(acc, gpos, win, infrad, atom, force);
1902  break;
1903  case VSM_SPLINE4 :
1904  Vacc_splineAccGradAtomNorm4(acc, gpos, win, infrad, atom, force);
1905  break;
1906  default:
1907  Vnm_print(2, "Vpmg_dbnbForce: Unknown surface method.\n");
1908  return;
1909  }
1910 
1911  return;
1912 }
1913 
1914 VPRIVATE void focusFillBound(Vpmg *thee,
1915  Vpmg *pmgOLD
1916  ) {
1917 
1918  Vpbe *pbe;
1919  double hxOLD,
1920  hyOLD,
1921  hzOLD,
1922  xminOLD,
1923  yminOLD,
1924  zminOLD,
1925  xmaxOLD,
1926  ymaxOLD,
1927  zmaxOLD,
1928  hxNEW,
1929  hyNEW,
1930  hzNEW,
1931  xminNEW,
1932  yminNEW,
1933  zminNEW,
1934  xmaxNEW,
1935  ymaxNEW,
1936  zmaxNEW,
1937  x,
1938  y,
1939  z,
1940  dx,
1941  dy,
1942  dz,
1943  ifloat,
1944  jfloat,
1945  kfloat,
1946  uval,
1947  eps_w,
1948  T,
1949  pre1,
1950  xkappa,
1951  size,
1952  *apos,
1953  charge,
1954  //pos[3], // gcc: not used
1955  uvalMin,
1956  uvalMax,
1957  *data;
1958  int nxOLD,
1959  nyOLD,
1960  nzOLD,
1961  nxNEW,
1962  nyNEW,
1963  nzNEW,
1964  i,
1965  j,
1966  k,
1967  ihi,
1968  ilo,
1969  jhi,
1970  jlo,
1971  khi,
1972  klo,
1973  nx,
1974  ny,
1975  nz;
1976 
1977  /* Calculate new problem dimensions */
1978  hxNEW = thee->pmgp->hx;
1979  hyNEW = thee->pmgp->hy;
1980  hzNEW = thee->pmgp->hzed;
1981  nx = thee->pmgp->nx;
1982  ny = thee->pmgp->ny;
1983  nz = thee->pmgp->nz;
1984  nxNEW = thee->pmgp->nx;
1985  nyNEW = thee->pmgp->ny;
1986  nzNEW = thee->pmgp->nz;
1987  xminNEW = thee->pmgp->xcent - ((double)(nxNEW-1)*hxNEW)/2.0;
1988  xmaxNEW = thee->pmgp->xcent + ((double)(nxNEW-1)*hxNEW)/2.0;
1989  yminNEW = thee->pmgp->ycent - ((double)(nyNEW-1)*hyNEW)/2.0;
1990  ymaxNEW = thee->pmgp->ycent + ((double)(nyNEW-1)*hyNEW)/2.0;
1991  zminNEW = thee->pmgp->zcent - ((double)(nzNEW-1)*hzNEW)/2.0;
1992  zmaxNEW = thee->pmgp->zcent + ((double)(nzNEW-1)*hzNEW)/2.0;
1993 
1994  if(pmgOLD != VNULL){
1995  /* Relevant old problem parameters */
1996  hxOLD = pmgOLD->pmgp->hx;
1997  hyOLD = pmgOLD->pmgp->hy;
1998  hzOLD = pmgOLD->pmgp->hzed;
1999  nxOLD = pmgOLD->pmgp->nx;
2000  nyOLD = pmgOLD->pmgp->ny;
2001  nzOLD = pmgOLD->pmgp->nz;
2002  xminOLD = pmgOLD->pmgp->xcent - ((double)(nxOLD-1)*hxOLD)/2.0;
2003  xmaxOLD = pmgOLD->pmgp->xcent + ((double)(nxOLD-1)*hxOLD)/2.0;
2004  yminOLD = pmgOLD->pmgp->ycent - ((double)(nyOLD-1)*hyOLD)/2.0;
2005  ymaxOLD = pmgOLD->pmgp->ycent + ((double)(nyOLD-1)*hyOLD)/2.0;
2006  zminOLD = pmgOLD->pmgp->zcent - ((double)(nzOLD-1)*hzOLD)/2.0;
2007  zmaxOLD = pmgOLD->pmgp->zcent + ((double)(nzOLD-1)*hzOLD)/2.0;
2008 
2009  data = pmgOLD->u;
2010  }else{
2011  /* Relevant old problem parameters */
2012  hxOLD = thee->potMap->hx;
2013  hyOLD = thee->potMap->hy;
2014  hzOLD = thee->potMap->hzed;
2015  nxOLD = thee->potMap->nx;
2016  nyOLD = thee->potMap->ny;
2017  nzOLD = thee->potMap->nz;
2018  xminOLD = thee->potMap->xmin;
2019  xmaxOLD = thee->potMap->xmax;
2020  yminOLD = thee->potMap->ymin;
2021  ymaxOLD = thee->potMap->ymax;
2022  zminOLD = thee->potMap->zmin;
2023  zmaxOLD = thee->potMap->zmax;
2024 
2025  data = thee->potMap->data;
2026  }
2027  /* BOUNDARY CONDITION SETUP FOR POINTS OFF OLD MESH:
2028  * For each "atom" (only one for bcfl=1), we use the following formula to
2029  * calculate the boundary conditions:
2030  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2031  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2032  * * 1/d
2033  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
2034  * We only need to evaluate some of these prefactors once:
2035  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2036  * which gives the potential as
2037  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2038  */
2039  pbe = thee->pbe;
2040  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
2041  T = Vpbe_getTemperature(pbe); /* K */
2042  pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
2043 
2044  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
2045  * m/A, then we will only need to deal with distances and sizes in
2046  * Angstroms rather than meters. */
2047  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
2048  pre1 = pre1*(1.0e10);
2049  size = Vpbe_getSoluteRadius(pbe);
2050  apos = Vpbe_getSoluteCenter(pbe);
2051  charge = Vunit_ec*Vpbe_getSoluteCharge(pbe);
2052 
2053  /* Check for rounding error */
2054  if (VABS(xminOLD-xminNEW) < VSMALL) xminNEW = xminOLD;
2055  if (VABS(xmaxOLD-xmaxNEW) < VSMALL) xmaxNEW = xmaxOLD;
2056  if (VABS(yminOLD-yminNEW) < VSMALL) yminNEW = yminOLD;
2057  if (VABS(ymaxOLD-ymaxNEW) < VSMALL) ymaxNEW = ymaxOLD;
2058  if (VABS(zminOLD-zminNEW) < VSMALL) zminNEW = zminOLD;
2059  if (VABS(zmaxOLD-zmaxNEW) < VSMALL) zmaxNEW = zmaxOLD;
2060 
2061 
2062  /* Sanity check: make sure we're within the old mesh */
2063  Vnm_print(0, "VPMG::focusFillBound -- New mesh mins = %g, %g, %g\n",
2064  xminNEW, yminNEW, zminNEW);
2065  Vnm_print(0, "VPMG::focusFillBound -- New mesh maxs = %g, %g, %g\n",
2066  xmaxNEW, ymaxNEW, zmaxNEW);
2067  Vnm_print(0, "VPMG::focusFillBound -- Old mesh mins = %g, %g, %g\n",
2068  xminOLD, yminOLD, zminOLD);
2069  Vnm_print(0, "VPMG::focusFillBound -- Old mesh maxs = %g, %g, %g\n",
2070  xmaxOLD, ymaxOLD, zmaxOLD);
2071 
2072  /* The following is obsolete; we'll substitute analytical boundary
2073  * condition values when the new mesh falls outside the old */
2074  if ((xmaxNEW>xmaxOLD) || (ymaxNEW>ymaxOLD) || (zmaxNEW>zmaxOLD) ||
2075  (xminOLD>xminNEW) || (yminOLD>yminNEW) || (zminOLD>zminNEW)) {
2076 
2077  Vnm_print(2, "Vpmg::focusFillBound -- new mesh not contained in old!\n");
2078  Vnm_print(2, "Vpmg::focusFillBound -- old mesh min = (%g, %g, %g)\n",
2079  xminOLD, yminOLD, zminOLD);
2080  Vnm_print(2, "Vpmg::focusFillBound -- old mesh max = (%g, %g, %g)\n",
2081  xmaxOLD, ymaxOLD, zmaxOLD);
2082  Vnm_print(2, "Vpmg::focusFillBound -- new mesh min = (%g, %g, %g)\n",
2083  xminNEW, yminNEW, zminNEW);
2084  Vnm_print(2, "Vpmg::focusFillBound -- new mesh max = (%g, %g, %g)\n",
2085  xmaxNEW, ymaxNEW, zmaxNEW);
2086  fflush(stderr);
2087  VASSERT(0);
2088  }
2089 
2090  uvalMin = VPMGSMALL;
2091  uvalMax = -VPMGSMALL;
2092 
2093  /* Fill the "i" boundaries (dirichlet) */
2094  for (k=0; k<nzNEW; k++) {
2095  for (j=0; j<nyNEW; j++) {
2096  /* Low X face */
2097  x = xminNEW;
2098  y = yminNEW + j*hyNEW;
2099  z = zminNEW + k*hzNEW;
2100  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2101  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2102  ifloat = (x - xminOLD)/hxOLD;
2103  jfloat = (y - yminOLD)/hyOLD;
2104  kfloat = (z - zminOLD)/hzOLD;
2105  ihi = (int)ceil(ifloat);
2106  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2107  ilo = (int)floor(ifloat);
2108  if (ilo < 0) ilo = 0;
2109  jhi = (int)ceil(jfloat);
2110  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2111  jlo = (int)floor(jfloat);
2112  if (jlo < 0) jlo = 0;
2113  khi = (int)ceil(kfloat);
2114  if (khi > (nzOLD-1)) khi = nzOLD-1;
2115  klo = (int)floor(kfloat);
2116  if (klo < 0) klo = 0;
2117  dx = ifloat - (double)(ilo);
2118  dy = jfloat - (double)(jlo);
2119  dz = kfloat - (double)(klo);
2120  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2121  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2122  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2123  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2124  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2125  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2126  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2127  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2128  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2129  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2130  } else {
2131  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2132  %g!\n", __FILE__, __LINE__, x, y, z);
2133  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2134  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2135  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2136  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2137  VASSERT(0);
2138  }
2139  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2140  thee->gxcf[IJKx(j,k,0)] = uval;
2141  if(uval < uvalMin) uvalMin = uval;
2142  if(uval > uvalMax) uvalMax = uval;
2143 
2144  /* High X face */
2145  x = xmaxNEW;
2146  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2147  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2148  ifloat = (x - xminOLD)/hxOLD;
2149  jfloat = (y - yminOLD)/hyOLD;
2150  kfloat = (z - zminOLD)/hzOLD;
2151  ihi = (int)ceil(ifloat);
2152  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2153  ilo = (int)floor(ifloat);
2154  if (ilo < 0) ilo = 0;
2155  jhi = (int)ceil(jfloat);
2156  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2157  jlo = (int)floor(jfloat);
2158  if (jlo < 0) jlo = 0;
2159  khi = (int)ceil(kfloat);
2160  if (khi > (nzOLD-1)) khi = nzOLD-1;
2161  klo = (int)floor(kfloat);
2162  if (klo < 0) klo = 0;
2163  dx = ifloat - (double)(ilo);
2164  dy = jfloat - (double)(jlo);
2165  dz = kfloat - (double)(klo);
2166  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2167  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2168  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2169  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2170  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2171  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2172  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2173  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2174  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2175  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2176  } else {
2177  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2178  %g!\n", __FILE__, __LINE__, x, y, z);
2179  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2180  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2181  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2182  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2183  VASSERT(0);
2184  }
2185  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2186  thee->gxcf[IJKx(j,k,1)] = uval;
2187  if(uval < uvalMin) uvalMin = uval;
2188  if(uval > uvalMax) uvalMax = uval;
2189 
2190  /* Zero Neumann conditions */
2191  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2192  thee->gxcf[IJKx(j,k,2)] = 0.0;
2193  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2194  thee->gxcf[IJKx(j,k,3)] = 0.0;
2195  }
2196  }
2197 
2198  /* Fill the "j" boundaries (dirichlet) */
2199  for (k=0; k<nzNEW; k++) {
2200  for (i=0; i<nxNEW; i++) {
2201  /* Low Y face */
2202  x = xminNEW + i*hxNEW;
2203  y = yminNEW;
2204  z = zminNEW + k*hzNEW;
2205  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2206  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2207  ifloat = (x - xminOLD)/hxOLD;
2208  jfloat = (y - yminOLD)/hyOLD;
2209  kfloat = (z - zminOLD)/hzOLD;
2210  ihi = (int)ceil(ifloat);
2211  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2212  ilo = (int)floor(ifloat);
2213  if (ilo < 0) ilo = 0;
2214  jhi = (int)ceil(jfloat);
2215  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2216  jlo = (int)floor(jfloat);
2217  if (jlo < 0) jlo = 0;
2218  khi = (int)ceil(kfloat);
2219  if (khi > (nzOLD-1)) khi = nzOLD-1;
2220  klo = (int)floor(kfloat);
2221  if (klo < 0) klo = 0;
2222  dx = ifloat - (double)(ilo);
2223  dy = jfloat - (double)(jlo);
2224  dz = kfloat - (double)(klo);
2225  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2226  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2227  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2228  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2229  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2230  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2231  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2232  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2233  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2234  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2235  } else {
2236  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2237  %g!\n", __FILE__, __LINE__, x, y, z);
2238  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2239  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2240  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2241  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2242  VASSERT(0);
2243  }
2244  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2245  thee->gycf[IJKy(i,k,0)] = uval;
2246  if(uval < uvalMin) uvalMin = uval;
2247  if(uval > uvalMax) uvalMax = uval;
2248 
2249  /* High Y face */
2250  y = ymaxNEW;
2251  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2252  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2253  ifloat = (x - xminOLD)/hxOLD;
2254  jfloat = (y - yminOLD)/hyOLD;
2255  kfloat = (z - zminOLD)/hzOLD;
2256  ihi = (int)ceil(ifloat);
2257  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2258  ilo = (int)floor(ifloat);
2259  if (ilo < 0) ilo = 0;
2260  jhi = (int)ceil(jfloat);
2261  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2262  jlo = (int)floor(jfloat);
2263  if (jlo < 0) jlo = 0;
2264  khi = (int)ceil(kfloat);
2265  if (khi > (nzOLD-1)) khi = nzOLD-1;
2266  klo = (int)floor(kfloat);
2267  if (klo < 0) klo = 0;
2268  dx = ifloat - (double)(ilo);
2269  dy = jfloat - (double)(jlo);
2270  dz = kfloat - (double)(klo);
2271  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2272  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2273  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2274  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2275  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2276  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2277  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2278  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2279  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2280  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2281  } else {
2282  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2283  %g!\n", __FILE__, __LINE__, x, y, z);
2284  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2285  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2286  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2287  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2288  VASSERT(0);
2289  }
2290  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2291  thee->gycf[IJKy(i,k,1)] = uval;
2292  if(uval < uvalMin) uvalMin = uval;
2293  if(uval > uvalMax) uvalMax = uval;
2294 
2295  /* Zero Neumann conditions */
2296  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2297  thee->gycf[IJKy(i,k,2)] = 0.0;
2298  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2299  thee->gycf[IJKy(i,k,3)] = 0.0;
2300  }
2301  }
2302 
2303  /* Fill the "k" boundaries (dirichlet) */
2304  for (j=0; j<nyNEW; j++) {
2305  for (i=0; i<nxNEW; i++) {
2306  /* Low Z face */
2307  x = xminNEW + i*hxNEW;
2308  y = yminNEW + j*hyNEW;
2309  z = zminNEW;
2310  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2311  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2312  ifloat = (x - xminOLD)/hxOLD;
2313  jfloat = (y - yminOLD)/hyOLD;
2314  kfloat = (z - zminOLD)/hzOLD;
2315  ihi = (int)ceil(ifloat);
2316  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2317  ilo = (int)floor(ifloat);
2318  if (ilo < 0) ilo = 0;
2319  jhi = (int)ceil(jfloat);
2320  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2321  jlo = (int)floor(jfloat);
2322  if (jlo < 0) jlo = 0;
2323  khi = (int)ceil(kfloat);
2324  if (khi > (nzOLD-1)) khi = nzOLD-1;
2325  klo = (int)floor(kfloat);
2326  if (klo < 0) klo = 0;
2327  dx = ifloat - (double)(ilo);
2328  dy = jfloat - (double)(jlo);
2329  dz = kfloat - (double)(klo);
2330  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2331  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2332  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2333  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2334  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2335  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2336  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2337  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2338  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2339  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2340  } else {
2341  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2342  %g!\n", __FILE__, __LINE__, x, y, z);
2343  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2344  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2345  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2346  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2347  VASSERT(0);
2348  }
2349  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2350  thee->gzcf[IJKz(i,j,0)] = uval;
2351  if(uval < uvalMin) uvalMin = uval;
2352  if(uval > uvalMax) uvalMax = uval;
2353 
2354  /* High Z face */
2355  z = zmaxNEW;
2356  if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
2357  (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
2358  ifloat = (x - xminOLD)/hxOLD;
2359  jfloat = (y - yminOLD)/hyOLD;
2360  kfloat = (z - zminOLD)/hzOLD;
2361  ihi = (int)ceil(ifloat);
2362  if (ihi > (nxOLD-1)) ihi = nxOLD-1;
2363  ilo = (int)floor(ifloat);
2364  if (ilo < 0) ilo = 0;
2365  jhi = (int)ceil(jfloat);
2366  if (jhi > (nyOLD-1)) jhi = nyOLD-1;
2367  jlo = (int)floor(jfloat);
2368  if (jlo < 0) jlo = 0;
2369  khi = (int)ceil(kfloat);
2370  if (khi > (nzOLD-1)) khi = nzOLD-1;
2371  klo = (int)floor(kfloat);
2372  if (klo < 0) klo = 0;
2373  dx = ifloat - (double)(ilo);
2374  dy = jfloat - (double)(jlo);
2375  dz = kfloat - (double)(klo);
2376  nx = nxOLD; ny = nyOLD; nz = nzOLD;
2377  uval = dx*dy*dz*(data[IJK(ihi,jhi,khi)])
2378  + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
2379  + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
2380  + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
2381  + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
2382  + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
2383  + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
2384  + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
2385  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2386  } else {
2387  Vnm_print(2, "focusFillBound (%s, %d): Off old mesh at %g, %g \
2388  %g!\n", __FILE__, __LINE__, x, y, z);
2389  Vnm_print(2, "focusFillBound (%s, %d): old mesh lower corner at \
2390  %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
2391  Vnm_print(2, "focusFillBound (%s, %d): old mesh upper corner at \
2392  %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
2393  VASSERT(0);
2394  }
2395  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2396  thee->gzcf[IJKz(i,j,1)] = uval;
2397  if(uval < uvalMin) uvalMin = uval;
2398  if(uval > uvalMax) uvalMax = uval;
2399 
2400  /* Zero Neumann conditions */
2401  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2402  thee->gzcf[IJKz(i,j,2)] = 0.0;
2403  nx = nxNEW; ny = nyNEW; nz = nzNEW;
2404  thee->gzcf[IJKz(i,j,3)] = 0.0;
2405  }
2406  }
2407 
2408  VWARN_MSG0(
2409  uvalMin >= SINH_MIN && uvalMax <= SINH_MAX,
2410  "Unusually large potential values\n"
2411  " detected on the focusing boundary!\n"
2412  " Convergence not guaranteed for NPBE/NRPBE calculations!"
2413  );
2414 }
2415 
2416 VPRIVATE void extEnergy(Vpmg *thee, Vpmg *pmgOLD, PBEparm_calcEnergy extFlag,
2417  double partMin[3], double partMax[3], int bflags[6]) {
2418 
2419  Vatom *atom;
2420  double hxNEW, hyNEW, hzNEW;
2421  double lowerCorner[3], upperCorner[3];
2422  int nxNEW, nyNEW, nzNEW;
2423  int nxOLD, nyOLD, nzOLD;
2424  int i,j,k;
2425  double xmin, xmax, ymin, ymax, zmin, zmax;
2426  double hxOLD, hyOLD, hzOLD;
2427  double xval, yval, zval;
2428  double x,y,z;
2429  int nx, ny, nz;
2430 
2431  /* Set the new external energy contribution to zero. Any external
2432  * contributions from higher levels will be included in the appropriate
2433  * energy function call. */
2434  thee->extQmEnergy = 0;
2435  thee->extQfEnergy = 0;
2436  thee->extDiEnergy = 0;
2437 
2438  /* New problem dimensions */
2439  hxNEW = thee->pmgp->hx;
2440  hyNEW = thee->pmgp->hy;
2441  hzNEW = thee->pmgp->hzed;
2442  nxNEW = thee->pmgp->nx;
2443  nyNEW = thee->pmgp->ny;
2444  nzNEW = thee->pmgp->nz;
2445  lowerCorner[0] = thee->pmgp->xcent - ((double)(nxNEW-1)*hxNEW)/2.0;
2446  upperCorner[0] = thee->pmgp->xcent + ((double)(nxNEW-1)*hxNEW)/2.0;
2447  lowerCorner[1] = thee->pmgp->ycent - ((double)(nyNEW-1)*hyNEW)/2.0;
2448  upperCorner[1] = thee->pmgp->ycent + ((double)(nyNEW-1)*hyNEW)/2.0;
2449  lowerCorner[2] = thee->pmgp->zcent - ((double)(nzNEW-1)*hzNEW)/2.0;
2450  upperCorner[2] = thee->pmgp->zcent + ((double)(nzNEW-1)*hzNEW)/2.0;
2451 
2452  Vnm_print(0, "VPMG::extEnergy: energy flag = %d\n", extFlag);
2453 
2454  /* Old problem dimensions */
2455  nxOLD = pmgOLD->pmgp->nx;
2456  nyOLD = pmgOLD->pmgp->ny;
2457  nzOLD = pmgOLD->pmgp->nz;
2458 
2459  /* Create a partition based on the new problem dimensions */
2460  /* Vnm_print(1, "DEBUG (%s, %d): extEnergy calling Vpmg_setPart for old PMG.\n",
2461  __FILE__, __LINE__); */
2462  Vpmg_setPart(pmgOLD, lowerCorner, upperCorner, bflags);
2463 
2464 
2465  Vnm_print(0,"VPMG::extEnergy: Finding extEnergy dimensions...\n");
2466  Vnm_print(0,"VPMG::extEnergy Disj part lower corner = (%g, %g, %g)\n",
2467  partMin[0], partMin[1], partMin[2]);
2468  Vnm_print(0,"VPMG::extEnergy Disj part upper corner = (%g, %g, %g)\n",
2469  partMax[0], partMax[1], partMax[2]);
2470 
2471  /* Find the old dimensions */
2472 
2473  hxOLD = pmgOLD->pmgp->hx;
2474  hyOLD = pmgOLD->pmgp->hy;
2475  hzOLD = pmgOLD->pmgp->hzed;
2476  xmin = pmgOLD->pmgp->xcent - 0.5*hxOLD*(nxOLD-1);
2477  ymin = pmgOLD->pmgp->ycent - 0.5*hyOLD*(nyOLD-1);
2478  zmin = pmgOLD->pmgp->zcent - 0.5*hzOLD*(nzOLD-1);
2479  xmax = xmin+hxOLD*(nxOLD-1);
2480  ymax = ymin+hyOLD*(nyOLD-1);
2481  zmax = zmin+hzOLD*(nzOLD-1);
2482 
2483  Vnm_print(0,"VPMG::extEnergy Old lower corner = (%g, %g, %g)\n",
2484  xmin, ymin, zmin);
2485  Vnm_print(0,"VPMG::extEnergy Old upper corner = (%g, %g, %g)\n",
2486  xmax, ymax, zmax);
2487 
2488  /* Flip the partition, but do not include any points that will
2489  be included by another processor */
2490 
2491  nx = nxOLD;
2492  ny = nyOLD;
2493  nz = nzOLD;
2494 
2495  for(i=0; i<nx; i++) {
2496  xval = 1;
2497  x = i*hxOLD + xmin;
2498  if (x < partMin[0] && bflags[VAPBS_LEFT] == 1) xval = 0;
2499  else if (x > partMax[0] && bflags[VAPBS_RIGHT] == 1) xval = 0;
2500 
2501  for(j=0; j<ny; j++) {
2502  yval = 1;
2503  y = j*hyOLD + ymin;
2504  if (y < partMin[1] && bflags[VAPBS_BACK] == 1) yval = 0;
2505  else if (y > partMax[1] && bflags[VAPBS_FRONT] == 1) yval = 0;
2506 
2507  for(k=0; k<nz; k++) {
2508  zval = 1;
2509  z = k*hzOLD + zmin;
2510  if (z < partMin[2] && bflags[VAPBS_DOWN] == 1) zval = 0;
2511  else if (z > partMax[2] && bflags[VAPBS_UP] == 1) zval = 0;
2512 
2513  if (pmgOLD->pvec[IJK(i,j,k)] > VSMALL) pmgOLD->pvec[IJK(i,j,k)] = 1.0;
2514  pmgOLD->pvec[IJK(i,j,k)] = (1 - (pmgOLD->pvec[IJK(i,j,k)])) * (xval*yval*zval);
2515  }
2516  }
2517  }
2518 
2519  for (i=0; i<Valist_getNumberAtoms(thee->pbe->alist); i++) {
2520  xval=1;
2521  yval=1;
2522  zval=1;
2523  atom = Valist_getAtom(thee->pbe->alist, i);
2524  x = atom->position[0];
2525  y = atom->position[1];
2526  z = atom->position[2];
2527  if (x < partMin[0] && bflags[VAPBS_LEFT] == 1) xval = 0;
2528  else if (x > partMax[0] && bflags[VAPBS_RIGHT] == 1) xval = 0;
2529  if (y < partMin[1] && bflags[VAPBS_BACK] == 1) yval = 0;
2530  else if (y > partMax[1] && bflags[VAPBS_FRONT] == 1) yval = 0;
2531  if (z < partMin[2] && bflags[VAPBS_DOWN] == 1) zval = 0;
2532  else if (z > partMax[2] && bflags[VAPBS_UP] == 1) zval = 0;
2533  if (atom->partID > VSMALL) atom->partID = 1.0;
2534  atom->partID = (1 - atom->partID) * (xval*yval*zval);
2535  }
2536 
2537  /* Now calculate the energy on inverted subset of the domain */
2538  thee->extQmEnergy = Vpmg_qmEnergy(pmgOLD, 1);
2539  Vnm_print(0, "VPMG::extEnergy: extQmEnergy = %g kT\n", thee->extQmEnergy);
2540  thee->extQfEnergy = Vpmg_qfEnergy(pmgOLD, 1);
2541  Vnm_print(0, "VPMG::extEnergy: extQfEnergy = %g kT\n", thee->extQfEnergy);
2542  thee->extDiEnergy = Vpmg_dielEnergy(pmgOLD, 1);
2543  Vnm_print(0, "VPMG::extEnergy: extDiEnergy = %g kT\n", thee->extDiEnergy);
2544  Vpmg_unsetPart(pmgOLD);
2545 }
2546 
2547 VPRIVATE double bcfl1sp(double size, double *apos, double charge,
2548  double xkappa, double pre1, double *pos) {
2549 
2550  double dist, val;
2551 
2552  dist = VSQRT(VSQR(pos[0]-apos[0]) + VSQR(pos[1]-apos[1])
2553  + VSQR(pos[2]-apos[2]));
2554  if (xkappa > VSMALL) {
2555  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2556  / (1+xkappa*size);
2557  } else {
2558  val = pre1*(charge/dist);
2559  }
2560 
2561  return val;
2562 }
2563 
2564 VPRIVATE void bcfl1(double size, double *apos, double charge,
2565  double xkappa, double pre1, double *gxcf, double *gycf, double *gzcf,
2566  double *xf, double *yf, double *zf, int nx, int ny, int nz) {
2567 
2568  int i, j, k;
2569  double dist, val;
2570  double gpos[3];
2571 
2572  /* the "i" boundaries (dirichlet) */
2573  for (k=0; k<nz; k++) {
2574  gpos[2] = zf[k];
2575  for (j=0; j<ny; j++) {
2576  gpos[1] = yf[j];
2577  gpos[0] = xf[0];
2578  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2579  + VSQR(gpos[2]-apos[2]));
2580  if (xkappa > VSMALL) {
2581  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2582  / (1+xkappa*size);
2583  } else {
2584  val = pre1*(charge/dist);
2585  }
2586  gxcf[IJKx(j,k,0)] += val;
2587  gpos[0] = xf[nx-1];
2588  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2589  + VSQR(gpos[2]-apos[2]));
2590  if (xkappa > VSMALL) {
2591  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2592  / (1+xkappa*size);
2593  } else {
2594  val = pre1*(charge/dist);
2595  }
2596  gxcf[IJKx(j,k,1)] += val;
2597  }
2598  }
2599 
2600  /* the "j" boundaries (dirichlet) */
2601  for (k=0; k<nz; k++) {
2602  gpos[2] = zf[k];
2603  for (i=0; i<nx; i++) {
2604  gpos[0] = xf[i];
2605  gpos[1] = yf[0];
2606  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2607  + VSQR(gpos[2]-apos[2]));
2608  if (xkappa > VSMALL) {
2609  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2610  / (1+xkappa*size);
2611  } else {
2612  val = pre1*(charge/dist);
2613  }
2614  gycf[IJKy(i,k,0)] += val;
2615  gpos[1] = yf[ny-1];
2616  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2617  + VSQR(gpos[2]-apos[2]));
2618  if (xkappa > VSMALL) {
2619  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2620  / (1+xkappa*size);
2621  } else {
2622  val = pre1*(charge/dist);
2623  }
2624  gycf[IJKy(i,k,1)] += val;
2625  }
2626  }
2627 
2628  /* the "k" boundaries (dirichlet) */
2629  for (j=0; j<ny; j++) {
2630  gpos[1] = yf[j];
2631  for (i=0; i<nx; i++) {
2632  gpos[0] = xf[i];
2633  gpos[2] = zf[0];
2634  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2635  + VSQR(gpos[2]-apos[2]));
2636  if (xkappa > VSMALL) {
2637  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2638  / (1+xkappa*size);
2639  } else {
2640  val = pre1*(charge/dist);
2641  }
2642  gzcf[IJKz(i,j,0)] += val;
2643  gpos[2] = zf[nz-1];
2644  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
2645  + VSQR(gpos[2]-apos[2]));
2646  if (xkappa > VSMALL) {
2647  val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
2648  / (1+xkappa*size);
2649  } else {
2650  val = pre1*(charge/dist);
2651  }
2652  gzcf[IJKz(i,j,1)] += val;
2653  }
2654  }
2655 }
2656 
2657 VPRIVATE void bcfl2(double size, double *apos,
2658  double charge, double *dipole, double *quad,
2659  double xkappa, double eps_p, double eps_w, double T,
2660  double *gxcf, double *gycf, double *gzcf,
2661  double *xf, double *yf, double *zf,
2662  int nx, int ny, int nz) {
2663 
2664  int i, j, k;
2665  double val;
2666  double gpos[3],tensor[3];
2667  double ux,uy,uz,xr,yr,zr;
2668  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
2669  double dist, pre;
2670 
2671  VASSERT(dipole != VNULL);
2672  ux = dipole[0];
2673  uy = dipole[1];
2674  uz = dipole[2];
2675  if (quad != VNULL) {
2676  /* The factor of 1/3 results from using a
2677  traceless quadrupole definition. See, for example,
2678  "The Theory of Intermolecular Forces" by A.J. Stone,
2679  Chapter 3. */
2680  qxx = quad[0] / 3.0;
2681  qxy = quad[1] / 3.0;
2682  qxz = quad[2] / 3.0;
2683  qyx = quad[3] / 3.0;
2684  qyy = quad[4] / 3.0;
2685  qyz = quad[5] / 3.0;
2686  qzx = quad[6] / 3.0;
2687  qzy = quad[7] / 3.0;
2688  qzz = quad[8] / 3.0;
2689  } else {
2690  qxx = 0.0;
2691  qxy = 0.0;
2692  qxz = 0.0;
2693  qyx = 0.0;
2694  qyy = 0.0;
2695  qyz = 0.0;
2696  qzx = 0.0;
2697  qzy = 0.0;
2698  qzz = 0.0;
2699  }
2700 
2701  pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
2702  pre = pre*(1.0e10);
2703 
2704  /* the "i" boundaries (dirichlet) */
2705  for (k=0; k<nz; k++) {
2706  gpos[2] = zf[k];
2707  for (j=0; j<ny; j++) {
2708  gpos[1] = yf[j];
2709  gpos[0] = xf[0];
2710  xr = gpos[0] - apos[0];
2711  yr = gpos[1] - apos[1];
2712  zr = gpos[2] - apos[2];
2713  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2714  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2715  val = pre*charge*tensor[0];
2716  val -= pre*ux*xr*tensor[1];
2717  val -= pre*uy*yr*tensor[1];
2718  val -= pre*uz*zr*tensor[1];
2719  val += pre*qxx*xr*xr*tensor[2];
2720  val += pre*qyy*yr*yr*tensor[2];
2721  val += pre*qzz*zr*zr*tensor[2];
2722  val += pre*2.0*qxy*xr*yr*tensor[2];
2723  val += pre*2.0*qxz*xr*zr*tensor[2];
2724  val += pre*2.0*qyz*yr*zr*tensor[2];
2725  gxcf[IJKx(j,k,0)] += val;
2726 
2727  gpos[0] = xf[nx-1];
2728  xr = gpos[0] - apos[0];
2729  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2730  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2731  val = pre*charge*tensor[0];
2732  val -= pre*ux*xr*tensor[1];
2733  val -= pre*uy*yr*tensor[1];
2734  val -= pre*uz*zr*tensor[1];
2735  val += pre*qxx*xr*xr*tensor[2];
2736  val += pre*qyy*yr*yr*tensor[2];
2737  val += pre*qzz*zr*zr*tensor[2];
2738  val += pre*2.0*qxy*xr*yr*tensor[2];
2739  val += pre*2.0*qxz*xr*zr*tensor[2];
2740  val += pre*2.0*qyz*yr*zr*tensor[2];
2741  gxcf[IJKx(j,k,1)] += val;
2742  }
2743  }
2744 
2745  /* the "j" boundaries (dirichlet) */
2746  for (k=0; k<nz; k++) {
2747  gpos[2] = zf[k];
2748  for (i=0; i<nx; i++) {
2749  gpos[0] = xf[i];
2750  gpos[1] = yf[0];
2751  xr = gpos[0] - apos[0];
2752  yr = gpos[1] - apos[1];
2753  zr = gpos[2] - apos[2];
2754  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2755  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2756  val = pre*charge*tensor[0];
2757  val -= pre*ux*xr*tensor[1];
2758  val -= pre*uy*yr*tensor[1];
2759  val -= pre*uz*zr*tensor[1];
2760  val += pre*qxx*xr*xr*tensor[2];
2761  val += pre*qyy*yr*yr*tensor[2];
2762  val += pre*qzz*zr*zr*tensor[2];
2763  val += pre*2.0*qxy*xr*yr*tensor[2];
2764  val += pre*2.0*qxz*xr*zr*tensor[2];
2765  val += pre*2.0*qyz*yr*zr*tensor[2];
2766  gycf[IJKy(i,k,0)] += val;
2767 
2768  gpos[1] = yf[ny-1];
2769  yr = gpos[1] - apos[1];
2770  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2771  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2772  val = pre*charge*tensor[0];
2773  val -= pre*ux*xr*tensor[1];
2774  val -= pre*uy*yr*tensor[1];
2775  val -= pre*uz*zr*tensor[1];
2776  val += pre*qxx*xr*xr*tensor[2];
2777  val += pre*qyy*yr*yr*tensor[2];
2778  val += pre*qzz*zr*zr*tensor[2];
2779  val += pre*2.0*qxy*xr*yr*tensor[2];
2780  val += pre*2.0*qxz*xr*zr*tensor[2];
2781  val += pre*2.0*qyz*yr*zr*tensor[2];
2782  gycf[IJKy(i,k,1)] += val;
2783  }
2784  }
2785 
2786  /* the "k" boundaries (dirichlet) */
2787  for (j=0; j<ny; j++) {
2788  gpos[1] = yf[j];
2789  for (i=0; i<nx; i++) {
2790  gpos[0] = xf[i];
2791  gpos[2] = zf[0];
2792  xr = gpos[0] - apos[0];
2793  yr = gpos[1] - apos[1];
2794  zr = gpos[2] - apos[2];
2795  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2796  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2797  val = pre*charge*tensor[0];
2798  val -= pre*ux*xr*tensor[1];
2799  val -= pre*uy*yr*tensor[1];
2800  val -= pre*uz*zr*tensor[1];
2801  val += pre*qxx*xr*xr*tensor[2];
2802  val += pre*qyy*yr*yr*tensor[2];
2803  val += pre*qzz*zr*zr*tensor[2];
2804  val += pre*2.0*qxy*xr*yr*tensor[2];
2805  val += pre*2.0*qxz*xr*zr*tensor[2];
2806  val += pre*2.0*qyz*yr*zr*tensor[2];
2807  gzcf[IJKz(i,j,0)] += val;
2808 
2809  gpos[2] = zf[nz-1];
2810  zr = gpos[2] - apos[2];
2811  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
2812  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
2813  val = pre*charge*tensor[0];
2814  val -= pre*ux*xr*tensor[1];
2815  val -= pre*uy*yr*tensor[1];
2816  val -= pre*uz*zr*tensor[1];
2817  val += pre*qxx*xr*xr*tensor[2];
2818  val += pre*qyy*yr*yr*tensor[2];
2819  val += pre*qzz*zr*zr*tensor[2];
2820  val += pre*2.0*qxy*xr*yr*tensor[2];
2821  val += pre*2.0*qxz*xr*zr*tensor[2];
2822  val += pre*2.0*qyz*yr*zr*tensor[2];
2823  gzcf[IJKz(i,j,1)] += val;
2824  }
2825  }
2826 }
2827 
2828 VPRIVATE void bcCalcOrig(Vpmg *thee) {
2829 
2830  int nx, ny, nz;
2831  double size, *position, charge, xkappa, eps_w, T, pre1;
2832  double *dipole, *quadrupole, debye, eps_p;
2833  double xr,yr,zr,qave,*apos;
2834  double sdhcharge, sdhdipole[3], traced[9], sdhquadrupole[9];
2835  int i, j, k, iatom;
2836  Vpbe *pbe;
2837  Vatom *atom;
2838  Valist *alist;
2839 
2840  pbe = thee->pbe;
2841  alist = thee->pbe->alist;
2842  nx = thee->pmgp->nx;
2843  ny = thee->pmgp->ny;
2844  nz = thee->pmgp->nz;
2845 
2846  /* Zero out the boundaries */
2847  /* the "i" boundaries (dirichlet) */
2848  for (k=0; k<nz; k++) {
2849  for (j=0; j<ny; j++) {
2850  thee->gxcf[IJKx(j,k,0)] = 0.0;
2851  thee->gxcf[IJKx(j,k,1)] = 0.0;
2852  thee->gxcf[IJKx(j,k,2)] = 0.0;
2853  thee->gxcf[IJKx(j,k,3)] = 0.0;
2854  }
2855  }
2856 
2857  /* the "j" boundaries (dirichlet) */
2858  for (k=0; k<nz; k++) {
2859  for (i=0; i<nx; i++) {
2860  thee->gycf[IJKy(i,k,0)] = 0.0;
2861  thee->gycf[IJKy(i,k,1)] = 0.0;
2862  thee->gycf[IJKy(i,k,2)] = 0.0;
2863  thee->gycf[IJKy(i,k,3)] = 0.0;
2864  }
2865  }
2866 
2867  /* the "k" boundaries (dirichlet) */
2868  for (j=0; j<ny; j++) {
2869  for (i=0; i<nx; i++) {
2870  thee->gzcf[IJKz(i,j,0)] = 0.0;
2871  thee->gzcf[IJKz(i,j,1)] = 0.0;
2872  thee->gzcf[IJKz(i,j,2)] = 0.0;
2873  thee->gzcf[IJKz(i,j,3)] = 0.0;
2874  }
2875  }
2876 
2877  /* For each "atom" (only one for bcfl=1), we use the following formula to
2878  * calculate the boundary conditions:
2879  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2880  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2881  * * 1/d
2882  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
2883  * We only need to evaluate some of these prefactors once:
2884  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
2885  * which gives the potential as
2886  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
2887  */
2888  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
2889  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
2890  T = Vpbe_getTemperature(pbe); /* K */
2891  pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
2892 
2893  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
2894  * m/A, then we will only need to deal with distances and sizes in
2895  * Angstroms rather than meters. */
2896  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
2897  pre1 = pre1*(1.0e10);
2898 
2899  switch (thee->pmgp->bcfl) {
2900  /* If we have zero boundary conditions, we're done */
2901  case BCFL_ZERO:
2902  return;
2903 
2904  /* For single DH sphere BC's, we only have one "atom" to deal with;
2905  * get its information and */
2906  case BCFL_SDH:
2907  size = Vpbe_getSoluteRadius(pbe);
2908  position = Vpbe_getSoluteCenter(pbe);
2909 
2910  /*
2911  For AMOEBA SDH boundary conditions, we need to find the
2912  total monopole, dipole and traceless quadrupole moments
2913  of either the permanent multipoles, induced dipoles or
2914  non-local induced dipoles.
2915  */
2916 
2917  sdhcharge = 0.0;
2918  for (i=0; i<3; i++) sdhdipole[i] = 0.0;
2919  for (i=0; i<9; i++) sdhquadrupole[i] = 0.0;
2920 
2921  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
2922  atom = Valist_getAtom(alist, iatom);
2923  apos = Vatom_getPosition(atom);
2924  xr = apos[0] - position[0];
2925  yr = apos[1] - position[1];
2926  zr = apos[2] - position[2];
2927  switch (thee->chargeSrc) {
2928  case VCM_CHARGE:
2929  charge = Vatom_getCharge(atom);
2930  sdhcharge += charge;
2931  sdhdipole[0] += xr * charge;
2932  sdhdipole[1] += yr * charge;
2933  sdhdipole[2] += zr * charge;
2934  traced[0] = xr*xr*charge;
2935  traced[1] = xr*yr*charge;
2936  traced[2] = xr*zr*charge;
2937  traced[3] = yr*xr*charge;
2938  traced[4] = yr*yr*charge;
2939  traced[5] = yr*zr*charge;
2940  traced[6] = zr*xr*charge;
2941  traced[7] = zr*yr*charge;
2942  traced[8] = zr*zr*charge;
2943  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
2944  sdhquadrupole[0] += 1.5*(traced[0] - qave);
2945  sdhquadrupole[1] += 1.5*(traced[1]);
2946  sdhquadrupole[2] += 1.5*(traced[2]);
2947  sdhquadrupole[3] += 1.5*(traced[3]);
2948  sdhquadrupole[4] += 1.5*(traced[4] - qave);
2949  sdhquadrupole[5] += 1.5*(traced[5]);
2950  sdhquadrupole[6] += 1.5*(traced[6]);
2951  sdhquadrupole[7] += 1.5*(traced[7]);
2952  sdhquadrupole[8] += 1.5*(traced[8] - qave);
2953 #if defined(WITH_TINKER)
2954  case VCM_PERMANENT:
2955  charge = Vatom_getCharge(atom);
2956  dipole = Vatom_getDipole(atom);
2957  quadrupole = Vatom_getQuadrupole(atom);
2958  sdhcharge += charge;
2959  sdhdipole[0] += xr * charge;
2960  sdhdipole[1] += yr * charge;
2961  sdhdipole[2] += zr * charge;
2962  traced[0] = xr*xr*charge;
2963  traced[1] = xr*yr*charge;
2964  traced[2] = xr*zr*charge;
2965  traced[3] = yr*xr*charge;
2966  traced[4] = yr*yr*charge;
2967  traced[5] = yr*zr*charge;
2968  traced[6] = zr*xr*charge;
2969  traced[7] = zr*yr*charge;
2970  traced[8] = zr*zr*charge;
2971  sdhdipole[0] += dipole[0];
2972  sdhdipole[1] += dipole[1];
2973  sdhdipole[2] += dipole[2];
2974  traced[0] += 2.0*xr*dipole[0];
2975  traced[1] += xr*dipole[1] + yr*dipole[0];
2976  traced[2] += xr*dipole[2] + zr*dipole[0];
2977  traced[3] += yr*dipole[0] + xr*dipole[1];
2978  traced[4] += 2.0*yr*dipole[1];
2979  traced[5] += yr*dipole[2] + zr*dipole[1];
2980  traced[6] += zr*dipole[0] + xr*dipole[2];
2981  traced[7] += zr*dipole[1] + yr*dipole[2];
2982  traced[8] += 2.0*zr*dipole[2];
2983  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
2984  sdhquadrupole[0] += 1.5*(traced[0] - qave);
2985  sdhquadrupole[1] += 1.5*(traced[1]);
2986  sdhquadrupole[2] += 1.5*(traced[2]);
2987  sdhquadrupole[3] += 1.5*(traced[3]);
2988  sdhquadrupole[4] += 1.5*(traced[4] - qave);
2989  sdhquadrupole[5] += 1.5*(traced[5]);
2990  sdhquadrupole[6] += 1.5*(traced[6]);
2991  sdhquadrupole[7] += 1.5*(traced[7]);
2992  sdhquadrupole[8] += 1.5*(traced[8] - qave);
2993  sdhquadrupole[0] += quadrupole[0];
2994  sdhquadrupole[1] += quadrupole[1];
2995  sdhquadrupole[2] += quadrupole[2];
2996  sdhquadrupole[3] += quadrupole[3];
2997  sdhquadrupole[4] += quadrupole[4];
2998  sdhquadrupole[5] += quadrupole[5];
2999  sdhquadrupole[6] += quadrupole[6];
3000  sdhquadrupole[7] += quadrupole[7];
3001  sdhquadrupole[8] += quadrupole[8];
3002  case VCM_INDUCED:
3003  dipole = Vatom_getInducedDipole(atom);
3004  sdhdipole[0] += dipole[0];
3005  sdhdipole[1] += dipole[1];
3006  sdhdipole[2] += dipole[2];
3007  traced[0] = 2.0*xr*dipole[0];
3008  traced[1] = xr*dipole[1] + yr*dipole[0];
3009  traced[2] = xr*dipole[2] + zr*dipole[0];
3010  traced[3] = yr*dipole[0] + xr*dipole[1];
3011  traced[4] = 2.0*yr*dipole[1];
3012  traced[5] = yr*dipole[2] + zr*dipole[1];
3013  traced[6] = zr*dipole[0] + xr*dipole[2];
3014  traced[7] = zr*dipole[1] + yr*dipole[2];
3015  traced[8] = 2.0*zr*dipole[2];
3016  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3017  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3018  sdhquadrupole[1] += 1.5*(traced[1]);
3019  sdhquadrupole[2] += 1.5*(traced[2]);
3020  sdhquadrupole[3] += 1.5*(traced[3]);
3021  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3022  sdhquadrupole[5] += 1.5*(traced[5]);
3023  sdhquadrupole[6] += 1.5*(traced[6]);
3024  sdhquadrupole[7] += 1.5*(traced[7]);
3025  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3026  case VCM_NLINDUCED:
3027  dipole = Vatom_getNLInducedDipole(atom);
3028  sdhdipole[0] += dipole[0];
3029  sdhdipole[1] += dipole[1];
3030  sdhdipole[2] += dipole[2];
3031  traced[0] = 2.0*xr*dipole[0];
3032  traced[1] = xr*dipole[1] + yr*dipole[0];
3033  traced[2] = xr*dipole[2] + zr*dipole[0];
3034  traced[3] = yr*dipole[0] + xr*dipole[1];
3035  traced[4] = 2.0*yr*dipole[1];
3036  traced[5] = yr*dipole[2] + zr*dipole[1];
3037  traced[6] = zr*dipole[0] + xr*dipole[2];
3038  traced[7] = zr*dipole[1] + yr*dipole[2];
3039  traced[8] = 2.0*zr*dipole[2];
3040  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3041  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3042  sdhquadrupole[1] += 1.5*(traced[1]);
3043  sdhquadrupole[2] += 1.5*(traced[2]);
3044  sdhquadrupole[3] += 1.5*(traced[3]);
3045  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3046  sdhquadrupole[5] += 1.5*(traced[5]);
3047  sdhquadrupole[6] += 1.5*(traced[6]);
3048  sdhquadrupole[7] += 1.5*(traced[7]);
3049  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3050  /*Added the else to kill a warning when building with clang*/
3051 #else
3052  case VCM_PERMANENT:;
3053  case VCM_INDUCED:;
3054  case VCM_NLINDUCED:;
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 /*added this to kill a warning when building with clang (by Juan Brandi).*/
3100 #else
3101  case VCM_PERMANENT:;
3102  case VCM_INDUCED:;
3103  case VCM_NLINDUCED:;
3104 #endif
3105  }
3106  bcfl1(size, position, charge, xkappa, pre1,
3107  thee->gxcf, thee->gycf, thee->gzcf,
3108  thee->xf, thee->yf, thee->zf, nx, ny, nz);
3109  }
3110  break;
3111 
3112  case BCFL_UNUSED:
3113  Vnm_print(2, "bcCalc: Invalid bcfl (%d)!\n", thee->pmgp->bcfl);
3114  VASSERT(0);
3115 
3116  case BCFL_FOCUS:
3117  Vnm_print(2, "VPMG::bcCalc -- not appropriate for focusing!\n");
3118  VASSERT(0);
3119 
3120  default:
3121  Vnm_print(2, "VPMG::bcCalc -- invalid boundary condition \
3122 flag (%d)!\n", thee->pmgp->bcfl);
3123  VASSERT(0);
3124  }
3125 }
3126 
3127 /*
3128  Used by bcflnew
3129  */
3130 VPRIVATE int gridPointIsValid(int i, int j, int k, int nx, int ny, int nz){
3131 
3132  int isValid = 0;
3133 
3134  if((k==0) || (k==nz-1)){
3135  isValid = 1;
3136  }else if((j==0) || (j==ny-1)){
3137  isValid = 1;
3138  }else if((i==0) || (i==nx-1)){
3139  isValid = 1;
3140  }
3141 
3142  return isValid;
3143 }
3144 
3145 /*
3146  Used by bcflnew
3147  */
3148 #ifdef DEBUG_MAC_OSX_OCL
3149 #include "mach_chud.h"
3150 VPRIVATE void packAtomsOpenCL(float *ax, float *ay, float *az,
3151  float *charge, float *size, Vpmg *thee){
3152 
3153  int i;
3154  int natoms;
3155 
3156  Vatom *atom = VNULL;
3157  Valist *alist = VNULL;
3158 
3159  alist = thee->pbe->alist;
3160  natoms = Valist_getNumberAtoms(alist);
3161 
3162  for(i=0;i<natoms;i++){
3163  atom = &(alist->atoms[i]);
3164  charge[i] = Vunit_ec*atom->charge;
3165  ax[i] = atom->position[0];
3166  ay[i] = atom->position[1];
3167  az[i] = atom->position[2];
3168  size[i] = atom->radius;
3169  }
3170 }
3171 
3172 /*
3173  Used by bcflnew
3174  */
3175 VPRIVATE void packUnpackOpenCL(int nx, int ny, int nz, int ngrid,
3176  float *gx, float *gy, float *gz, float *value,
3177  Vpmg *thee, int pack){
3178 
3179  int i,j,k,igrid;
3180  int x0,x1,y0,y1,z0,z1;
3181 
3182  float gpos[3];
3183  double *xf, *yf, *zf;
3184  double *gxcf, *gycf, *gzcf;
3185 
3186  xf = thee->xf;
3187  yf = thee->yf;
3188  zf = thee->zf;
3189 
3190  gxcf = thee->gxcf;
3191  gycf = thee->gycf;
3192  gzcf = thee->gzcf;
3193 
3194  igrid = 0;
3195  for(k=0;k<nz;k++){
3196  gpos[2] = zf[k];
3197  for(j=0;j<ny;j++){
3198  gpos[1] = yf[j];
3199  for(i=0;i<nx;i++){
3200  gpos[0] = xf[i];
3201  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3202  if(pack != 0){
3203  gx[igrid] = gpos[0];
3204  gy[igrid] = gpos[1];
3205  gz[igrid] = gpos[2];
3206 
3207  value[igrid] = 0.0;
3208  }else{
3209  x0 = IJKx(j,k,0);
3210  x1 = IJKx(j,k,1);
3211  y0 = IJKy(i,k,0);
3212  y1 = IJKy(i,k,1);
3213  z0 = IJKz(i,j,0);
3214  z1 = IJKz(i,j,1);
3215 
3216  if(i==0){
3217  gxcf[x0] += value[igrid];
3218  }
3219  if(i==nx-1){
3220  gxcf[x1] += value[igrid];
3221  }
3222  if(j==0){
3223  gycf[y0] += value[igrid];
3224  }
3225  if(j==ny-1){
3226  gycf[y1] += value[igrid];
3227  }
3228  if(k==0){
3229  gzcf[z0] += value[igrid];
3230  }
3231  if(k==nz-1){
3232  gzcf[z1] += value[igrid];
3233  }
3234  }
3235 
3236  igrid++;
3237  } //end is valid point
3238  } //end i
3239  } //end j
3240  } //end k
3241 
3242 }
3243 
3244 /*
3245  bcflnew is an optimized replacement for bcfl1. bcfl1 is still used when TINKER
3246  support is compiled in.
3247  bcflnew uses: packUnpack, packAtoms, gridPointIsValid
3248  */
3249 VPRIVATE void bcflnewOpenCL(Vpmg *thee){
3250 
3251  int i,j,k, iatom, igrid;
3252  int x0, x1, y0, y1, z0, z1;
3253 
3254  int nx, ny, nz;
3255  int natoms, ngrid, ngadj;
3256 
3257  float dist, pre1, eps_w, eps_p, T, xkappa;
3258 
3259  float *ax, *ay, *az;
3260  float *charge, *size, *val;
3261 
3262  float *gx, *gy, *gz;
3263 
3264  Vpbe *pbe = thee->pbe;
3265 
3266  nx = thee->pmgp->nx;
3267  ny = thee->pmgp->ny;
3268  nz = thee->pmgp->nz;
3269 
3270  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3271  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3272  T = Vpbe_getTemperature(pbe); /* K */
3273  pre1 = ((Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T))*(1.0e10);
3274  xkappa = Vpbe_getXkappa(pbe);
3275 
3276  natoms = Valist_getNumberAtoms(thee->pbe->alist);
3277  ngrid = 2*((nx*ny) + (ny*nz) + (nx*nz));
3278  ngadj = ngrid + (512 - (ngrid & 511));
3279 
3280  ax = (float*)malloc(natoms * sizeof(float));
3281  ay = (float*)malloc(natoms * sizeof(float));
3282  az = (float*)malloc(natoms * sizeof(float));
3283 
3284  charge = (float*)malloc(natoms * sizeof(float));
3285  size = (float*)malloc(natoms * sizeof(float));
3286 
3287  gx = (float*)malloc(ngrid * sizeof(float));
3288  gy = (float*)malloc(ngrid * sizeof(float));
3289  gz = (float*)malloc(ngrid * sizeof(float));
3290 
3291  val = (float*)malloc(ngrid * sizeof(float));
3292 
3293  packAtomsOpenCL(ax,ay,az,charge,size,thee);
3294  packUnpackOpenCL(nx,ny,nz,ngrid,gx,gy,gz,val,thee,1);
3295 
3296  runMDHCL(ngrid,natoms,ngadj,ax,ay,az,gx,gy,gz,charge,size,xkappa,pre1,val);
3297 
3298  packUnpackOpenCL(nx,ny,nz,ngrid,gx,gy,gz,val,thee,0);
3299 
3300  free(ax);
3301  free(ay);
3302  free(az);
3303  free(charge);
3304  free(size);
3305 
3306  free(gx);
3307  free(gy);
3308  free(gz);
3309  free(val);
3310 }
3311 #endif
3312 
3313 VPRIVATE void packAtoms(double *ax, double *ay, double *az,
3314  double *charge, double *size, Vpmg *thee){
3315 
3316  int i;
3317  int natoms;
3318 
3319  Vatom *atom = VNULL;
3320  Valist *alist = VNULL;
3321 
3322  alist = thee->pbe->alist;
3323  natoms = Valist_getNumberAtoms(alist);
3324 
3325  for(i=0;i<natoms;i++){
3326  atom = &(alist->atoms[i]);
3327  charge[i] = Vunit_ec*atom->charge;
3328  ax[i] = atom->position[0];
3329  ay[i] = atom->position[1];
3330  az[i] = atom->position[2];
3331  size[i] = atom->radius;
3332  }
3333 }
3334 
3335 /*
3336  Used by bcflnew
3337  */
3338 VPRIVATE void packUnpack(int nx, int ny, int nz, int ngrid,
3339  double *gx, double *gy, double *gz, double *value,
3340  Vpmg *thee, int pack){
3341 
3342  int i,j,k,igrid;
3343  int x0,x1,y0,y1,z0,z1;
3344 
3345  double gpos[3];
3346  double *xf, *yf, *zf;
3347  double *gxcf, *gycf, *gzcf;
3348 
3349  xf = thee->xf;
3350  yf = thee->yf;
3351  zf = thee->zf;
3352 
3353  gxcf = thee->gxcf;
3354  gycf = thee->gycf;
3355  gzcf = thee->gzcf;
3356 
3357  igrid = 0;
3358  for(k=0;k<nz;k++){
3359  gpos[2] = zf[k];
3360  for(j=0;j<ny;j++){
3361  gpos[1] = yf[j];
3362  for(i=0;i<nx;i++){
3363  gpos[0] = xf[i];
3364  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3365  if(pack != 0){
3366  gx[igrid] = gpos[0];
3367  gy[igrid] = gpos[1];
3368  gz[igrid] = gpos[2];
3369 
3370  value[igrid] = 0.0;
3371  }else{
3372  x0 = IJKx(j,k,0);
3373  x1 = IJKx(j,k,1);
3374  y0 = IJKy(i,k,0);
3375  y1 = IJKy(i,k,1);
3376  z0 = IJKz(i,j,0);
3377  z1 = IJKz(i,j,1);
3378 
3379  if(i==0){
3380  gxcf[x0] += value[igrid];
3381  }
3382  if(i==nx-1){
3383  gxcf[x1] += value[igrid];
3384  }
3385  if(j==0){
3386  gycf[y0] += value[igrid];
3387  }
3388  if(j==ny-1){
3389  gycf[y1] += value[igrid];
3390  }
3391  if(k==0){
3392  gzcf[z0] += value[igrid];
3393  }
3394  if(k==nz-1){
3395  gzcf[z1] += value[igrid];
3396  }
3397  }
3398 
3399  igrid++;
3400  } //end is valid point
3401  } //end i
3402  } //end j
3403  } //end k
3404 
3405 }
3406 
3407 VPRIVATE void bcflnew(Vpmg *thee){
3408 
3409  int i,j,k, iatom, igrid;
3410  int x0, x1, y0, y1, z0, z1;
3411 
3412  int nx, ny, nz;
3413  int natoms, ngrid;
3414 
3415  double dist, pre1, eps_w, eps_p, T, xkappa;
3416 
3417  double *ax, *ay, *az;
3418  double *charge, *size, *val;
3419 
3420  double *gx, *gy, *gz;
3421 
3422  Vpbe *pbe = thee->pbe;
3423 
3424  nx = thee->pmgp->nx;
3425  ny = thee->pmgp->ny;
3426  nz = thee->pmgp->nz;
3427 
3428  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3429  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3430  T = Vpbe_getTemperature(pbe); /* K */
3431  pre1 = ((Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T))*(1.0e10);
3432  xkappa = Vpbe_getXkappa(pbe);
3433 
3434  natoms = Valist_getNumberAtoms(thee->pbe->alist);
3435  ngrid = 2*((nx*ny) + (ny*nz) + (nx*nz));
3436 
3437  ax = (double*)malloc(natoms * sizeof(double));
3438  ay = (double*)malloc(natoms * sizeof(double));
3439  az = (double*)malloc(natoms * sizeof(double));
3440 
3441  charge = (double*)malloc(natoms * sizeof(double));
3442  size = (double*)malloc(natoms * sizeof(double));
3443 
3444  gx = (double*)malloc(ngrid * sizeof(double));
3445  gy = (double*)malloc(ngrid * sizeof(double));
3446  gz = (double*)malloc(ngrid * sizeof(double));
3447 
3448  val = (double*)malloc(ngrid * sizeof(double));
3449 
3450  packAtoms(ax,ay,az,charge,size,thee);
3451  packUnpack(nx,ny,nz,ngrid,gx,gy,gz,val,thee,1);
3452 
3453  if(xkappa > VSMALL){
3454 #pragma omp parallel for default(shared) private(igrid,iatom,dist)
3455  for(igrid=0;igrid<ngrid;igrid++){
3456  for(iatom=0; iatom<natoms; iatom++){
3457  dist = VSQRT(VSQR(gx[igrid]-ax[iatom]) + VSQR(gy[igrid]-ay[iatom])
3458  + VSQR(gz[igrid]-az[iatom]));
3459  val[igrid] += pre1*(charge[iatom]/dist)*VEXP(-xkappa*(dist-size[iatom]))
3460  / (1+xkappa*size[iatom]);
3461  }
3462  }
3463  }else{
3464 #pragma omp parallel for default(shared) private(igrid,iatom,dist)
3465  for(igrid=0;igrid<ngrid;igrid++){
3466  for(iatom=0; iatom<natoms; iatom++){
3467  dist = VSQRT(VSQR(gx[igrid]-ax[iatom]) + VSQR(gy[igrid]-ay[iatom])
3468  + VSQR(gz[igrid]-az[iatom]));
3469  val[igrid] += pre1*(charge[iatom]/dist);
3470  }
3471  }
3472  }
3473  packUnpack(nx,ny,nz,ngrid,gx,gy,gz,val,thee,0);
3474 
3475  free(ax);
3476  free(ay);
3477  free(az);
3478  free(charge);
3479  free(size);
3480 
3481  free(gx);
3482  free(gy);
3483  free(gz);
3484  free(val);
3485 }
3486 
3487 VPRIVATE void multipolebc(double r, double kappa, double eps_p,
3488  double eps_w, double rad, double tsr[3]) {
3489  double r2,r3,r5;
3490  double eps_r;
3491  double ka,ka2,ka3;
3492  double kr,kr2,kr3;
3493 
3494  /*
3495  Below an attempt is made to explain the potential outside of a
3496  multipole located at the center of spherical cavity of dieletric
3497  eps_p, with dielectric eps_w outside (and possibly kappa > 0).
3498 
3499 
3500  First, eps_p = 1.0
3501  eps_w = 1.0
3502  kappa = 0.0
3503 
3504  The general form for the potential of a traceless multipole tensor
3505  of rank n in vacuum is:
3506 
3507  V(r) = (-1)^n * u . n . Del^n (1/r)
3508 
3509  where
3510  u is a multipole of order n (3^n components)
3511  u . n. Del^n (1/r) is the contraction of u with the nth
3512  derivative of 1/r
3513 
3514  for example, if n = 1, the dipole potential is
3515  V_vac(r) = (-1)*[ux*x + uy*y + uz*z]/r^3
3516 
3517  This function returns the parts of V(r) for multipoles of
3518  order 0, 1 and 2 that are independent of the contraction.
3519 
3520  For the vacuum example, this would be 1/r, -1/r^3 and 3/r^5
3521  respectively.
3522 
3523  *** Note that this requires that the quadrupole is
3524  traceless. If not, the diagonal quadrupole potential changes
3525  from
3526  qaa * 3*a^2/r^5
3527  to
3528  qaa * (3*a^2/r^5 - 1/r^3a )
3529  where we sum over the trace; a = x, y and z.
3530 
3531  (In other words, the -1/r^3 term cancels for a traceless quadrupole.
3532  qxx + qyy + qzz = 0
3533  such that
3534  -(qxx + qyy + qzz)/r^3 = 0
3535 
3536  For quadrupole with trace:
3537  qxx + qyy + qzz != 0
3538  such that
3539  -(qxx + qyy + qzz)/r^3 != 0
3540  )
3541 
3542  ========================================================================
3543 
3544  eps_p != 1 or eps_w != 1
3545  kappa = 0.0
3546 
3547  If the multipole is placed at the center of a sphere with
3548  dieletric eps_p in a solvent of dielectric eps_w, the potential
3549  outside the sphere is the solution to the Laplace equation:
3550 
3551  V(r) = 1/eps_w * (2*n+1)*eps_r/(n+(n+1)*eps_r)
3552  * (-1)^n * u . n . Del^n (1/r)
3553  where
3554  eps_r = eps_w / eps_p
3555  is the ratio of solvent to solute dielectric
3556 
3557  ========================================================================
3558 
3559  kappa > 0
3560 
3561  Finally, if the region outside the sphere is treated by the linearized
3562  PB equation with Debye-Huckel parameter kappa, the solution is:
3563 
3564  V(r) = kappa/eps_w * alpha_n(kappa*a) * K_n(kappa*r) * r^(n+1)/a^n
3565  * (-1)^n * u . n . Del^n (1/r)
3566  where
3567  alpha_n(x) is [(2n + 1) / x] / [(n*K_n(x)/eps_r) - x*K_n'(x)]
3568  K_n(x) are modified spherical Bessel functions of the third kind.
3569  K_n'(x) is the derivative of K_n(x)
3570  */
3571 
3572  eps_r = eps_w/eps_p;
3573  r2 = r*r;
3574  r3 = r2*r;
3575  r5 = r3*r2;
3576  tsr[0] = (1.0/eps_w)/r;
3577  tsr[1] = (1.0/eps_w)*(-1.0)/r3;
3578  tsr[2] = (1.0/eps_w)*(3.0)/r5;
3579  if (kappa < VSMALL) {
3580  tsr[1] = (3.0*eps_r)/(1.0 + 2.0*eps_r)*tsr[1];
3581  tsr[2] = (5.0*eps_r)/(2.0 + 3.0*eps_r)*tsr[2];
3582  } else {
3583  ka = kappa*rad;
3584  ka2 = ka*ka;
3585  ka3 = ka2*ka;
3586  kr = kappa*r;
3587  kr2 = kr*kr;
3588  kr3 = kr2*kr;
3589  tsr[0] = exp(ka-kr) / (1.0 + ka) * tsr[0];
3590  tsr[1] = 3.0*eps_r*exp(ka-kr)*(1.0 + kr) * tsr[1];
3591  tsr[1] = tsr[1] / (1.0 + ka + eps_r*(2.0 + 2.0*ka + ka2));
3592  tsr[2] = 5.0*eps_r*exp(ka-kr)*(3.0 + 3.0*kr + kr2) * tsr[2];
3593  tsr[2] = tsr[2]/(6.0+6.0*ka+2.0*ka2+eps_r*(9.0+9.0*ka+4.0*ka2+ka3));
3594  }
3595 }
3596 
3597 VPRIVATE void bcfl_sdh(Vpmg *thee){
3598 
3599  int i,j,k,iatom;
3600  int nx, ny, nz;
3601 
3602  double size, *position, charge, xkappa, eps_w, eps_p, T, pre, dist;
3603  double sdhcharge, sdhdipole[3], traced[9], sdhquadrupole[9];
3604  double *dipole, *quadrupole;
3605 
3606  double val, *apos, gpos[3], tensor[3], qave;
3607  double ux, uy, uz, xr, yr, zr;
3608  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
3609 
3610  double *xf, *yf, *zf;
3611  double *gxcf, *gycf, *gzcf;
3612 
3613  Vpbe *pbe;
3614  Vatom *atom;
3615  Valist *alist;
3616 
3617  pbe = thee->pbe;
3618  alist = thee->pbe->alist;
3619  nx = thee->pmgp->nx;
3620  ny = thee->pmgp->ny;
3621  nz = thee->pmgp->nz;
3622 
3623  xf = thee->xf;
3624  yf = thee->yf;
3625  zf = thee->zf;
3626 
3627  gxcf = thee->gxcf;
3628  gycf = thee->gycf;
3629  gzcf = thee->gzcf;
3630 
3631  /* For each "atom" (only one for bcfl=1), we use the following formula to
3632  * calculate the boundary conditions:
3633  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3634  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3635  * * 1/d
3636  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
3637  * We only need to evaluate some of these prefactors once:
3638  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3639  * which gives the potential as
3640  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3641  */
3642  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3643  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3644  T = Vpbe_getTemperature(pbe); /* K */
3645 
3646  pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
3647  pre = pre*(1.0e10);
3648 
3649  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
3650  * m/A, then we will only need to deal with distances and sizes in
3651  * Angstroms rather than meters. */
3652  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
3653 
3654  /* Solute size and position */
3655  size = Vpbe_getSoluteRadius(pbe);
3656  position = Vpbe_getSoluteCenter(pbe);
3657 
3658  sdhcharge = 0.0;
3659  for (i=0; i<3; i++) sdhdipole[i] = 0.0;
3660  for (i=0; i<9; i++) sdhquadrupole[i] = 0.0;
3661 
3662  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
3663  atom = Valist_getAtom(alist, iatom);
3664  apos = Vatom_getPosition(atom);
3665  xr = apos[0] - position[0];
3666  yr = apos[1] - position[1];
3667  zr = apos[2] - position[2];
3668  switch (thee->chargeSrc) {
3669  case VCM_CHARGE:
3670  charge = Vatom_getCharge(atom);
3671  sdhcharge += charge;
3672  sdhdipole[0] += xr * charge;
3673  sdhdipole[1] += yr * charge;
3674  sdhdipole[2] += zr * charge;
3675  traced[0] = xr*xr*charge;
3676  traced[1] = xr*yr*charge;
3677  traced[2] = xr*zr*charge;
3678  traced[3] = yr*xr*charge;
3679  traced[4] = yr*yr*charge;
3680  traced[5] = yr*zr*charge;
3681  traced[6] = zr*xr*charge;
3682  traced[7] = zr*yr*charge;
3683  traced[8] = zr*zr*charge;
3684  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3685  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3686  sdhquadrupole[1] += 1.5*(traced[1]);
3687  sdhquadrupole[2] += 1.5*(traced[2]);
3688  sdhquadrupole[3] += 1.5*(traced[3]);
3689  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3690  sdhquadrupole[5] += 1.5*(traced[5]);
3691  sdhquadrupole[6] += 1.5*(traced[6]);
3692  sdhquadrupole[7] += 1.5*(traced[7]);
3693  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3694 #if defined(WITH_TINKER)
3695  case VCM_PERMANENT:
3696  charge = Vatom_getCharge(atom);
3697  dipole = Vatom_getDipole(atom);
3698  quadrupole = Vatom_getQuadrupole(atom);
3699  sdhcharge += charge;
3700  sdhdipole[0] += xr * charge;
3701  sdhdipole[1] += yr * charge;
3702  sdhdipole[2] += zr * charge;
3703  traced[0] = xr*xr*charge;
3704  traced[1] = xr*yr*charge;
3705  traced[2] = xr*zr*charge;
3706  traced[3] = yr*xr*charge;
3707  traced[4] = yr*yr*charge;
3708  traced[5] = yr*zr*charge;
3709  traced[6] = zr*xr*charge;
3710  traced[7] = zr*yr*charge;
3711  traced[8] = zr*zr*charge;
3712  sdhdipole[0] += dipole[0];
3713  sdhdipole[1] += dipole[1];
3714  sdhdipole[2] += dipole[2];
3715  traced[0] += 2.0*xr*dipole[0];
3716  traced[1] += xr*dipole[1] + yr*dipole[0];
3717  traced[2] += xr*dipole[2] + zr*dipole[0];
3718  traced[3] += yr*dipole[0] + xr*dipole[1];
3719  traced[4] += 2.0*yr*dipole[1];
3720  traced[5] += yr*dipole[2] + zr*dipole[1];
3721  traced[6] += zr*dipole[0] + xr*dipole[2];
3722  traced[7] += zr*dipole[1] + yr*dipole[2];
3723  traced[8] += 2.0*zr*dipole[2];
3724  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3725  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3726  sdhquadrupole[1] += 1.5*(traced[1]);
3727  sdhquadrupole[2] += 1.5*(traced[2]);
3728  sdhquadrupole[3] += 1.5*(traced[3]);
3729  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3730  sdhquadrupole[5] += 1.5*(traced[5]);
3731  sdhquadrupole[6] += 1.5*(traced[6]);
3732  sdhquadrupole[7] += 1.5*(traced[7]);
3733  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3734  sdhquadrupole[0] += quadrupole[0];
3735  sdhquadrupole[1] += quadrupole[1];
3736  sdhquadrupole[2] += quadrupole[2];
3737  sdhquadrupole[3] += quadrupole[3];
3738  sdhquadrupole[4] += quadrupole[4];
3739  sdhquadrupole[5] += quadrupole[5];
3740  sdhquadrupole[6] += quadrupole[6];
3741  sdhquadrupole[7] += quadrupole[7];
3742  sdhquadrupole[8] += quadrupole[8];
3743  case VCM_INDUCED:
3744  dipole = Vatom_getInducedDipole(atom);
3745  sdhdipole[0] += dipole[0];
3746  sdhdipole[1] += dipole[1];
3747  sdhdipole[2] += dipole[2];
3748  traced[0] = 2.0*xr*dipole[0];
3749  traced[1] = xr*dipole[1] + yr*dipole[0];
3750  traced[2] = xr*dipole[2] + zr*dipole[0];
3751  traced[3] = yr*dipole[0] + xr*dipole[1];
3752  traced[4] = 2.0*yr*dipole[1];
3753  traced[5] = yr*dipole[2] + zr*dipole[1];
3754  traced[6] = zr*dipole[0] + xr*dipole[2];
3755  traced[7] = zr*dipole[1] + yr*dipole[2];
3756  traced[8] = 2.0*zr*dipole[2];
3757  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3758  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3759  sdhquadrupole[1] += 1.5*(traced[1]);
3760  sdhquadrupole[2] += 1.5*(traced[2]);
3761  sdhquadrupole[3] += 1.5*(traced[3]);
3762  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3763  sdhquadrupole[5] += 1.5*(traced[5]);
3764  sdhquadrupole[6] += 1.5*(traced[6]);
3765  sdhquadrupole[7] += 1.5*(traced[7]);
3766  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3767  case VCM_NLINDUCED:
3768  dipole = Vatom_getNLInducedDipole(atom);
3769  sdhdipole[0] += dipole[0];
3770  sdhdipole[1] += dipole[1];
3771  sdhdipole[2] += dipole[2];
3772  traced[0] = 2.0*xr*dipole[0];
3773  traced[1] = xr*dipole[1] + yr*dipole[0];
3774  traced[2] = xr*dipole[2] + zr*dipole[0];
3775  traced[3] = yr*dipole[0] + xr*dipole[1];
3776  traced[4] = 2.0*yr*dipole[1];
3777  traced[5] = yr*dipole[2] + zr*dipole[1];
3778  traced[6] = zr*dipole[0] + xr*dipole[2];
3779  traced[7] = zr*dipole[1] + yr*dipole[2];
3780  traced[8] = 2.0*zr*dipole[2];
3781  qave = (traced[0] + traced[4] + traced[8]) / 3.0;
3782  sdhquadrupole[0] += 1.5*(traced[0] - qave);
3783  sdhquadrupole[1] += 1.5*(traced[1]);
3784  sdhquadrupole[2] += 1.5*(traced[2]);
3785  sdhquadrupole[3] += 1.5*(traced[3]);
3786  sdhquadrupole[4] += 1.5*(traced[4] - qave);
3787  sdhquadrupole[5] += 1.5*(traced[5]);
3788  sdhquadrupole[6] += 1.5*(traced[6]);
3789  sdhquadrupole[7] += 1.5*(traced[7]);
3790  sdhquadrupole[8] += 1.5*(traced[8] - qave);
3791 /*added this to kill a warning when building with clang (by Juan Brandi)*/
3792 #else
3793  case VCM_PERMANENT:;
3794  case VCM_INDUCED:;
3795  case VCM_NLINDUCED:;
3796 #endif /* if defined(WITH_TINKER) */
3797  }
3798  }
3799 
3800  ux = sdhdipole[0];
3801  uy = sdhdipole[1];
3802  uz = sdhdipole[2];
3803 
3804  /* The factor of 1/3 results from using a
3805  traceless quadrupole definition. See, for example,
3806  "The Theory of Intermolecular Forces" by A.J. Stone,
3807  Chapter 3. */
3808  qxx = sdhquadrupole[0] / 3.0;
3809  qxy = sdhquadrupole[1] / 3.0;
3810  qxz = sdhquadrupole[2] / 3.0;
3811  qyx = sdhquadrupole[3] / 3.0;
3812  qyy = sdhquadrupole[4] / 3.0;
3813  qyz = sdhquadrupole[5] / 3.0;
3814  qzx = sdhquadrupole[6] / 3.0;
3815  qzy = sdhquadrupole[7] / 3.0;
3816  qzz = sdhquadrupole[8] / 3.0;
3817 
3818  for(k=0;k<nz;k++){
3819  gpos[2] = zf[k];
3820  for(j=0;j<ny;j++){
3821  gpos[1] = yf[j];
3822  for(i=0;i<nx;i++){
3823  gpos[0] = xf[i];
3824  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3825  xr = gpos[0] - position[0];
3826  yr = gpos[1] - position[1];
3827  zr = gpos[2] - position[2];
3828 
3829  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
3830  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
3831 
3832  val = pre*sdhcharge*tensor[0];
3833  val -= pre*ux*xr*tensor[1];
3834  val -= pre*uy*yr*tensor[1];
3835  val -= pre*uz*zr*tensor[1];
3836  val += pre*qxx*xr*xr*tensor[2];
3837  val += pre*qyy*yr*yr*tensor[2];
3838  val += pre*qzz*zr*zr*tensor[2];
3839  val += pre*2.0*qxy*xr*yr*tensor[2];
3840  val += pre*2.0*qxz*xr*zr*tensor[2];
3841  val += pre*2.0*qyz*yr*zr*tensor[2];
3842 
3843  if(i==0){
3844  gxcf[IJKx(j,k,0)] = val;
3845  }
3846  if(i==nx-1){
3847  gxcf[IJKx(j,k,1)] = val;
3848  }
3849  if(j==0){
3850  gycf[IJKy(i,k,0)] = val;
3851  }
3852  if(j==ny-1){
3853  gycf[IJKy(i,k,1)] = val;
3854  }
3855  if(k==0){
3856  gzcf[IJKz(i,j,0)] = val;
3857  }
3858  if(k==nz-1){
3859  gzcf[IJKz(i,j,1)] = val;
3860  }
3861  } /* End grid point is valid */
3862  } /* End i loop */
3863  } /* End j loop */
3864  } /* End k loop */
3865 
3866 }
3867 
3868 VPRIVATE void bcfl_mdh(Vpmg *thee){
3869 
3870  int i,j,k,iatom;
3871  int nx, ny, nz;
3872 
3873  double val, *apos, gpos[3];
3874  double *dipole, *quadrupole;
3875  double size, charge, xkappa, eps_w, eps_p, T, pre1, dist;
3876 
3877  double *xf, *yf, *zf;
3878  double *gxcf, *gycf, *gzcf;
3879 
3880  Vpbe *pbe;
3881  Vatom *atom;
3882  Valist *alist;
3883 
3884  pbe = thee->pbe;
3885  alist = thee->pbe->alist;
3886  nx = thee->pmgp->nx;
3887  ny = thee->pmgp->ny;
3888  nz = thee->pmgp->nz;
3889 
3890  xf = thee->xf;
3891  yf = thee->yf;
3892  zf = thee->zf;
3893 
3894  gxcf = thee->gxcf;
3895  gycf = thee->gycf;
3896  gzcf = thee->gzcf;
3897 
3898  /* For each "atom" (only one for bcfl=1), we use the following formula to
3899  * calculate the boundary conditions:
3900  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3901  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3902  * * 1/d
3903  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
3904  * We only need to evaluate some of these prefactors once:
3905  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
3906  * which gives the potential as
3907  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
3908  */
3909  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
3910  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
3911  T = Vpbe_getTemperature(pbe); /* K */
3912  pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
3913 
3914  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
3915  * m/A, then we will only need to deal with distances and sizes in
3916  * Angstroms rather than meters. */
3917  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
3918  pre1 = pre1*(1.0e10);
3919 
3920  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
3921  * m/A, then we will only need to deal with distances and sizes in
3922  * Angstroms rather than meters. */
3923  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
3924 
3925  for(k=0;k<nz;k++){
3926  gpos[2] = zf[k];
3927  for(j=0;j<ny;j++){
3928  gpos[1] = yf[j];
3929  for(i=0;i<nx;i++){
3930  gpos[0] = xf[i];
3931  if(gridPointIsValid(i, j, k, nx, ny, nz)){
3932 
3933  val = 0.0;
3934 
3935  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
3936  atom = Valist_getAtom(alist, iatom);
3937  apos = Vatom_getPosition(atom);
3938  charge = Vunit_ec*Vatom_getCharge(atom);
3939  size = Vatom_getRadius(atom);
3940 
3941  dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
3942  + VSQR(gpos[2]-apos[2]));
3943  if (xkappa > VSMALL) {
3944  val += pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
3945  / (1+xkappa*size);
3946  } else {
3947  val += pre1*(charge/dist);
3948  }
3949 
3950  }
3951 
3952  if(i==0){
3953  gxcf[IJKx(j,k,0)] = val;
3954  }
3955  if(i==nx-1){
3956  gxcf[IJKx(j,k,1)] = val;
3957  }
3958  if(j==0){
3959  gycf[IJKy(i,k,0)] = val;
3960  }
3961  if(j==ny-1){
3962  gycf[IJKy(i,k,1)] = val;
3963  }
3964  if(k==0){
3965  gzcf[IJKz(i,j,0)] = val;
3966  }
3967  if(k==nz-1){
3968  gzcf[IJKz(i,j,1)] = val;
3969  }
3970  } /* End grid point is valid */
3971  } /* End i loop */
3972  } /* End j loop */
3973  } /* End k loop */
3974 
3975 }
3976 
3977 /* ///////////////////////////////////////////////////////////////////////////
3978  // Routine: bcfl_mem
3979  //
3980  // Purpose: Increment all the boundary points by the
3981  // analytic expression for a membrane system in
3982  // the presence of a membrane potential. This
3983  // Boundary flag should only be used for systems
3984  // that explicitly have membranes in the dielectric
3985  // and solvent maps.
3986  //
3987  // There should be several input variables add to this
3988  // function such as membrane potential, membrane thickness
3989  // and height.
3990  //
3991  // Args: apos is a 3-vector
3992  //
3993  // Author: Michael Grabe
3995 VPRIVATE void bcfl_mem(double zmem, double L, double eps_m, double eps_w,
3996  double V, double xkappa, double *gxcf, double *gycf, double *gzcf,
3997  double *xf, double *yf, double *zf, int nx, int ny, int nz) {
3998 
4000  /* some definitions */
4001  /* L = total length of the membrane */
4002  /* xkappa = inverse Debeye length */
4003  /* zmem = z value of membrane bottom (Cytoplasm) */
4004  /* V = electrical potential inside the cell */
4006  int i, j, k;
4007  double dist, val, z_low, z_high, z_shift;
4008  double A, B, C, D, edge_L, l;
4009  double G, z_0, z_rel;
4010  double gpos[3];
4011 
4012  Vnm_print(0, "Here is the value of kappa: %f\n",xkappa);
4013  Vnm_print(0, "Here is the value of L: %f\n",L);
4014  Vnm_print(0, "Here is the value of zmem: %f\n",zmem);
4015  Vnm_print(0, "Here is the value of mdie: %f\n",eps_m);
4016  Vnm_print(0, "Here is the value of memv: %f\n",V);
4017 
4018  /* no salt symmetric BC's at +/- infinity */
4019  // B=V/(edge_L - l*(1-eps_w/eps_m));
4020  // A=V + B*edge_L;
4021  // D=eps_w/eps_m*B;
4022  z_low = zmem; /* this defines the bottom of the membrane */
4023  z_high = zmem + L; /* this is the top of the membrane */
4024 
4025  /******************************************************/
4026  /* proper boundary conditions for V = 0 extracellular */
4027  /* and psi=-V cytoplasm. */
4028  /* Implicit in this formulation is that the membrane */
4029  /* center be at z = 0 */
4030  /******************************************************/
4031 
4032  l=L/2; /* half of the membrane length */
4033  z_0 = z_low + l; /* center of the membrane */
4034  G=l*eps_w/eps_m*xkappa;
4035  A=-V/2*(1/(G+1))*exp(xkappa*l);
4036  B=V/2;
4037  C=-V/2*eps_w/eps_m*xkappa*(1/(G+1));
4038  D=-A;
4039  /* The analytic expression for the boundary conditions */
4040  /* had the cytoplasmic surface of the membrane set to zero. */
4041  /* This requires an off-set of the BC equations. */
4042 
4043  /* the "i" boundaries (dirichlet) */
4044  for (k=0; k<nz; k++) {
4045  gpos[2] = zf[k];
4046  z_rel = gpos[2] - z_0; /* relative position for BCs */
4047 
4048  for (j=0; j<ny; j++) {
4049 
4050  if (gpos[2] <= z_low) { /* cytoplasmic */
4051 
4052  val = A*exp(xkappa*z_rel) + V;
4053  gxcf[IJKx(j,k,0)] += val; /* assign low side BC */
4054  gxcf[IJKx(j,k,1)] += val; /* assign high side BC */
4055 
4056  }
4057 
4058  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4059 
4060  val = B + C*z_rel;
4061  gxcf[IJKx(j,k,0)] += val; /* assign low side BC */
4062  gxcf[IJKx(j,k,1)] += val; /* assign high side BC */
4063 
4064  }
4065 
4066  else if (gpos[2] > z_high) { /* extracellular */
4067 
4068  val = D*exp(-xkappa*z_rel);
4069  gxcf[IJKx(j,k,0)] += val; /* assign low side BC */
4070  gxcf[IJKx(j,k,1)] += val; /* assign high side BC */
4071 
4072  }
4073 
4074  }
4075  }
4076 
4077  /* the "j" boundaries (dirichlet) */
4078  for (k=0; k<nz; k++) {
4079  gpos[2] = zf[k];
4080  z_rel = gpos[2] - z_0;
4081  for (i=0; i<nx; i++) {
4082 
4083  if (gpos[2] <= z_low) { /* cytoplasmic */
4084 
4085  val = A*exp(xkappa*z_rel) + V;
4086  gycf[IJKy(i,k,0)] += val; /* assign low side BC */
4087  gycf[IJKy(i,k,1)] += val; /* assign high side BC */
4088  //printf("%f \n",val);
4089 
4090  }
4091 
4092  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4093 
4094  val = B + C*z_rel;
4095  gycf[IJKy(i,k,0)] += val; /* assign low side BC */
4096  gycf[IJKy(i,k,1)] += val; /* assign high side BC */
4097  //printf("%f \n",val);
4098 
4099  }
4100  else if (gpos[2] > z_high) { /* extracellular */
4101 
4102  val = D*exp(-xkappa*z_rel);
4103  gycf[IJKy(i,k,0)] += val; /* assign low side BC */
4104  gycf[IJKy(i,k,1)] += val; /* assign high side BC */
4105  //printf("%f \n",val);
4106 
4107  }
4108 
4109  }
4110  }
4111 
4112  /* the "k" boundaries (dirichlet) */
4113  for (j=0; j<ny; j++) {
4114  for (i=0; i<nx; i++) {
4115 
4116  /* first assign the bottom boundary */
4117 
4118  gpos[2] = zf[0];
4119  z_rel = gpos[2] - z_0;
4120 
4121  if (gpos[2] <= z_low) { /* cytoplasmic */
4122 
4123  val = A*exp(xkappa*z_rel) + V;
4124  gzcf[IJKz(i,j,0)] += val; /* assign low side BC */
4125  //printf("%f \n",val);
4126 
4127  }
4128 
4129  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4130 
4131  val = B + C*z_rel;
4132  gzcf[IJKz(i,j,0)] += val; /* assign low side BC */
4133 
4134  }
4135 
4136  else if (gpos[2] > z_high) { /* extracellular */
4137 
4138  val = D*exp(-xkappa*z_rel);
4139  gzcf[IJKz(i,j,0)] += val; /* assign low side BC */
4140 
4141  }
4142 
4143  /* now assign the top boundary */
4144 
4145  gpos[2] = zf[nz-1];
4146  z_rel = gpos[2] - z_0;
4147 
4148  if (gpos[2] <= z_low) { /* cytoplasmic */
4149 
4150  val = A*exp(xkappa*z_rel) + V;
4151  gzcf[IJKz(i,j,1)] += val; /* assign high side BC */
4152 
4153  }
4154 
4155  else if (gpos[2] > z_low && gpos[2] <= z_high) { /* in membrane */
4156 
4157  val = B + C*z_rel;
4158  gzcf[IJKz(i,j,1)] += val; /* assign high side BC */
4159 
4160  }
4161 
4162  else if (gpos[2] > z_high) { /* extracellular */
4163 
4164  val = D*exp(-xkappa*z_rel);
4165  gzcf[IJKz(i,j,1)] += val; /* assign high side BC */
4166  //printf("%f \n",val);
4167 
4168  }
4169 
4170  }
4171  }
4172 }
4173 
4174 VPRIVATE void bcfl_map(Vpmg *thee){
4175 
4176  Vpbe *pbe;
4177  double position[3], pot, hx, hy, hzed;
4178  int i, j, k, nx, ny, nz, rc;
4179 
4180 
4181  VASSERT(thee != VNULL);
4182 
4183  /* Mesh info */
4184  nx = thee->pmgp->nx;
4185  ny = thee->pmgp->ny;
4186  nz = thee->pmgp->nz;
4187  hx = thee->pmgp->hx;
4188  hy = thee->pmgp->hy;
4189  hzed = thee->pmgp->hzed;
4190 
4191  /* Reset the potential array */
4192  for (i=0; i<(nx*ny*nz); i++) thee->pot[i] = 0.0;
4193 
4194  /* Fill in the source term (atomic potentials) */
4195  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
4196  for (k=0; k<nz; k++) {
4197  for (j=0; j<ny; j++) {
4198  for (i=0; i<nx; i++) {
4199  position[0] = thee->xf[i];
4200  position[1] = thee->yf[j];
4201  position[2] = thee->zf[k];
4202  rc = Vgrid_value(thee->potMap, position, &pot);
4203  if (!rc) {
4204  Vnm_print(2, "fillcoChargeMap: Error -- fell off of potential map at (%g, %g, %g)!\n",
4205  position[0], position[1], position[2]);
4206  VASSERT(0);
4207  }
4208  thee->pot[IJK(i,j,k)] = pot;
4209  }
4210  }
4211  }
4212 
4213 }
4214 
4215 #if defined(WITH_TINKER)
4216 VPRIVATE void bcfl_mdh_tinker(Vpmg *thee){
4217 
4218  int i,j,k,iatom;
4219  int nx, ny, nz;
4220 
4221  double val, *apos, gpos[3], tensor[9];
4222  double *dipole, *quadrupole;
4223  double size, charge, xkappa, eps_w, eps_p, T, pre1, dist;
4224 
4225  double ux,uy,uz,xr,yr,zr;
4226  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
4227 
4228  double *xf, *yf, *zf;
4229  double *gxcf, *gycf, *gzcf;
4230 
4231  Vpbe *pbe;
4232  Vatom *atom;
4233  Valist *alist;
4234 
4235  pbe = thee->pbe;
4236  alist = thee->pbe->alist;
4237  nx = thee->pmgp->nx;
4238  ny = thee->pmgp->ny;
4239  nz = thee->pmgp->nz;
4240 
4241  xf = thee->xf;
4242  yf = thee->yf;
4243  zf = thee->zf;
4244 
4245  gxcf = thee->gxcf;
4246  gycf = thee->gycf;
4247  gzcf = thee->gzcf;
4248 
4249  /* For each "atom" (only one for bcfl=1), we use the following formula to
4250  * calculate the boundary conditions:
4251  * g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
4252  * * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
4253  * * 1/d
4254  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
4255  * We only need to evaluate some of these prefactors once:
4256  * pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
4257  * which gives the potential as
4258  * g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
4259  */
4260  eps_w = Vpbe_getSolventDiel(pbe); /* Dimensionless */
4261  eps_p = Vpbe_getSoluteDiel(pbe); /* Dimensionless */
4262  T = Vpbe_getTemperature(pbe); /* K */
4263  pre1 = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
4264 
4265  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
4266  * m/A, then we will only need to deal with distances and sizes in
4267  * Angstroms rather than meters. */
4268  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
4269  pre1 = pre1*(1.0e10);
4270 
4271  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
4272  * m/A, then we will only need to deal with distances and sizes in
4273  * Angstroms rather than meters. */
4274  xkappa = Vpbe_getXkappa(pbe); /* A^{-1} */
4275 
4276  for(k=0;k<nz;k++){
4277  gpos[2] = zf[k];
4278  for(j=0;j<ny;j++){
4279  gpos[1] = yf[j];
4280  for(i=0;i<nx;i++){
4281  gpos[0] = xf[i];
4282  if(gridPointIsValid(i, j, k, nx, ny, nz)){
4283 
4284  val = 0.0;
4285 
4286  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4287  atom = Valist_getAtom(alist, iatom);
4288  apos = Vatom_getPosition(atom);
4289  size = Vatom_getRadius(atom);
4290 
4291  charge = 0.0;
4292 
4293  dipole = VNULL;
4294  quadrupole = VNULL;
4295 
4296  if (thee->chargeSrc == VCM_PERMANENT) {
4297  charge = Vatom_getCharge(atom);
4298  dipole = Vatom_getDipole(atom);
4299  quadrupole = Vatom_getQuadrupole(atom);
4300  } else if (thee->chargeSrc == VCM_INDUCED) {
4301  dipole = Vatom_getInducedDipole(atom);
4302  } else {
4303  dipole = Vatom_getNLInducedDipole(atom);
4304  }
4305 
4306  ux = dipole[0];
4307  uy = dipole[1];
4308  uz = dipole[2];
4309 
4310  if (quadrupole != VNULL) {
4311  /* The factor of 1/3 results from using a
4312  traceless quadrupole definition. See, for example,
4313  "The Theory of Intermolecular Forces" by A.J. Stone,
4314  Chapter 3. */
4315  qxx = quadrupole[0] / 3.0;
4316  qxy = quadrupole[1] / 3.0;
4317  qxz = quadrupole[2] / 3.0;
4318  qyx = quadrupole[3] / 3.0;
4319  qyy = quadrupole[4] / 3.0;
4320  qyz = quadrupole[5] / 3.0;
4321  qzx = quadrupole[6] / 3.0;
4322  qzy = quadrupole[7] / 3.0;
4323  qzz = quadrupole[8] / 3.0;
4324  } else {
4325  qxx = 0.0;
4326  qxy = 0.0;
4327  qxz = 0.0;
4328  qyx = 0.0;
4329  qyy = 0.0;
4330  qyz = 0.0;
4331  qzx = 0.0;
4332  qzy = 0.0;
4333  qzz = 0.0;
4334  }
4335 
4336  xr = gpos[0] - apos[0];
4337  yr = gpos[1] - apos[1];
4338  zr = gpos[2] - apos[2];
4339 
4340  dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
4341  multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
4342 
4343  val += pre1*charge*tensor[0];
4344  val -= pre1*ux*xr*tensor[1];
4345  val -= pre1*uy*yr*tensor[1];
4346  val -= pre1*uz*zr*tensor[1];
4347  val += pre1*qxx*xr*xr*tensor[2];
4348  val += pre1*qyy*yr*yr*tensor[2];
4349  val += pre1*qzz*zr*zr*tensor[2];
4350  val += pre1*2.0*qxy*xr*yr*tensor[2];
4351  val += pre1*2.0*qxz*xr*zr*tensor[2];
4352  val += pre1*2.0*qyz*yr*zr*tensor[2];
4353 
4354  }
4355 
4356  if(i==0){
4357  gxcf[IJKx(j,k,0)] = val;
4358  }
4359  if(i==nx-1){
4360  gxcf[IJKx(j,k,1)] = val;
4361  }
4362  if(j==0){
4363  gycf[IJKy(i,k,0)] = val;
4364  }
4365  if(j==ny-1){
4366  gycf[IJKy(i,k,1)] = val;
4367  }
4368  if(k==0){
4369  gzcf[IJKz(i,j,0)] = val;
4370  }
4371  if(k==nz-1){
4372  gzcf[IJKz(i,j,1)] = val;
4373  }
4374  } /* End grid point is valid */
4375  } /* End i loop */
4376  } /* End j loop */
4377  } /* End k loop */
4378 
4379 }
4380 #endif
4381 
4382 VPRIVATE void bcCalc(Vpmg *thee){
4383 
4384  int i, j, k;
4385  int nx, ny, nz;
4386 
4387  double zmem, eps_m, Lmem, memv, eps_w, xkappa;
4388 
4389  nx = thee->pmgp->nx;
4390  ny = thee->pmgp->ny;
4391  nz = thee->pmgp->nz;
4392 
4393  /* Zero out the boundaries */
4394  /* the "i" boundaries (dirichlet) */
4395  for (k=0; k<nz; k++) {
4396  for (j=0; j<ny; j++) {
4397  thee->gxcf[IJKx(j,k,0)] = 0.0;
4398  thee->gxcf[IJKx(j,k,1)] = 0.0;
4399  thee->gxcf[IJKx(j,k,2)] = 0.0;
4400  thee->gxcf[IJKx(j,k,3)] = 0.0;
4401  }
4402  }
4403 
4404  /* the "j" boundaries (dirichlet) */
4405  for (k=0; k<nz; k++) {
4406  for (i=0; i<nx; i++) {
4407  thee->gycf[IJKy(i,k,0)] = 0.0;
4408  thee->gycf[IJKy(i,k,1)] = 0.0;
4409  thee->gycf[IJKy(i,k,2)] = 0.0;
4410  thee->gycf[IJKy(i,k,3)] = 0.0;
4411  }
4412  }
4413 
4414  /* the "k" boundaries (dirichlet) */
4415  for (j=0; j<ny; j++) {
4416  for (i=0; i<nx; i++) {
4417  thee->gzcf[IJKz(i,j,0)] = 0.0;
4418  thee->gzcf[IJKz(i,j,1)] = 0.0;
4419  thee->gzcf[IJKz(i,j,2)] = 0.0;
4420  thee->gzcf[IJKz(i,j,3)] = 0.0;
4421  }
4422  }
4423 
4424  switch (thee->pmgp->bcfl) {
4425  /* If we have zero boundary conditions, we're done */
4426  case BCFL_ZERO:
4427  return;
4428  case BCFL_SDH:
4429  bcfl_sdh(thee);
4430  break;
4431  case BCFL_MDH:
4432 #if defined(WITH_TINKER)
4433  bcfl_mdh_tinker(thee);
4434 #else
4435 
4436 #ifdef DEBUG_MAC_OSX_OCL
4437 #include "mach_chud.h"
4438  uint64_t mbeg = mach_absolute_time();
4439 
4440  /*
4441  * If OpenCL is available we use it, otherwise fall back to
4442  * normal route (CPU multithreaded w/ OpenMP)
4443  */
4444  if (kOpenCLAvailable == 1) bcflnewOpenCL(thee);
4445  else bcflnew(thee);
4446 
4447  mets_(&mbeg, "MDH");
4448 #else
4449  /* bcfl_mdh(thee); */
4450  bcflnew(thee);
4451 #endif /* DEBUG_MAC_OSX_OCL */
4452 
4453 #endif /* WITH_TINKER */
4454  break;
4455  case BCFL_MEM:
4456 
4457  zmem = Vpbe_getzmem(thee->pbe);
4458  Lmem = Vpbe_getLmem(thee->pbe);
4459  eps_m = Vpbe_getmembraneDiel(thee->pbe);
4460  memv = Vpbe_getmemv(thee->pbe);
4461 
4462  eps_w = Vpbe_getSolventDiel(thee->pbe);
4463  xkappa = Vpbe_getXkappa(thee->pbe);
4464 
4465  bcfl_mem(zmem, Lmem, eps_m, eps_w, memv, xkappa,
4466  thee->gxcf, thee->gycf, thee->gzcf,
4467  thee->xf, thee->yf, thee->zf, nx, ny, nz);
4468  break;
4469  case BCFL_UNUSED:
4470  Vnm_print(2, "bcCalc: Invalid bcfl (%d)!\n", thee->pmgp->bcfl);
4471  VASSERT(0);
4472  break;
4473  case BCFL_FOCUS:
4474  Vnm_print(2, "VPMG::bcCalc -- not appropriate for focusing!\n");
4475  VASSERT(0);
4476  break;
4477  case BCFL_MAP:
4478  bcfl_map(thee);
4479  focusFillBound(thee,VNULL);
4480  break;
4481  default:
4482  Vnm_print(2, "VPMG::bcCalc -- invalid boundary condition \
4483  flag (%d)!\n", thee->pmgp->bcfl);
4484  VASSERT(0);
4485  break;
4486  }
4487 }
4488 
4489 VPRIVATE void fillcoCoefMap(Vpmg *thee) {
4490 
4491  Vpbe *pbe;
4492  double ionstr, position[3], tkappa, eps, pot, hx, hy, hzed;
4493  int i, j, k, nx, ny, nz;
4494  double kappamax;
4495  VASSERT(thee != VNULL);
4496 
4497  /* Get PBE info */
4498  pbe = thee->pbe;
4499  ionstr = Vpbe_getBulkIonicStrength(pbe);
4500 
4501  /* Mesh info */
4502  nx = thee->pmgp->nx;
4503  ny = thee->pmgp->ny;
4504  nz = thee->pmgp->nz;
4505  hx = thee->pmgp->hx;
4506  hy = thee->pmgp->hy;
4507  hzed = thee->pmgp->hzed;
4508 
4509  if ((!thee->useDielXMap) || (!thee->useDielYMap)
4510  || (!thee->useDielZMap) || ((!thee->useKappaMap) && (ionstr>VPMGSMALL))) {
4511 
4512  Vnm_print(2, "fillcoCoefMap: You need to use all coefficient maps!\n");
4513  VASSERT(0);
4514 
4515  }
4516 
4517  /* Scale the kappa map to values between 0 and 1
4518  Thus get the maximum value in the map - this
4519  is theoretically unnecessary, but a good check.*/
4520  kappamax = -1.00;
4521  for (k=0; k<nz; k++) {
4522  for (j=0; j<ny; j++) {
4523  for (i=0; i<nx; i++) {
4524  if (ionstr > VPMGSMALL) {
4525  position[0] = thee->xf[i];
4526  position[1] = thee->yf[j];
4527  position[2] = thee->zf[k];
4528  if (!Vgrid_value(thee->kappaMap, position, &tkappa)) {
4529  Vnm_print(2, "Vpmg_fillco: Off kappaMap at:\n");
4530  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4531  position[0], position[1], position[2]);
4532  VASSERT(0);
4533  }
4534  if (tkappa > kappamax) {
4535  kappamax = tkappa;
4536  }
4537  if (tkappa < 0.0){
4538  Vnm_print(2, "Vpmg_fillcoCoefMap: Kappa map less than 0\n");
4539  Vnm_print(2, "Vpmg_fillcoCoefMap: at (x,y,z) = (%g,%g %g)\n",
4540  position[0], position[1], position[2]);
4541  VASSERT(0);
4542  }
4543  }
4544  }
4545  }
4546  }
4547 
4548  if (kappamax > 1.0){
4549  Vnm_print(2, "Vpmg_fillcoCoefMap: Maximum Kappa value\n");
4550  Vnm_print(2, "%g is greater than 1 - will scale appropriately!\n",
4551  kappamax);
4552  }
4553  else {
4554  kappamax = 1.0;
4555  }
4556 
4557  for (k=0; k<nz; k++) {
4558  for (j=0; j<ny; j++) {
4559  for (i=0; i<nx; i++) {
4560 
4561  if (ionstr > VPMGSMALL) {
4562  position[0] = thee->xf[i];
4563  position[1] = thee->yf[j];
4564  position[2] = thee->zf[k];
4565  if (!Vgrid_value(thee->kappaMap, position, &tkappa)) {
4566  Vnm_print(2, "Vpmg_fillco: Off kappaMap at:\n");
4567  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4568  position[0], position[1], position[2]);
4569  VASSERT(0);
4570  }
4571  if (tkappa < VPMGSMALL) tkappa = 0.0;
4572  thee->kappa[IJK(i,j,k)] = (tkappa / kappamax);
4573  }
4574 
4575  position[0] = thee->xf[i] + 0.5*hx;
4576  position[1] = thee->yf[j];
4577  position[2] = thee->zf[k];
4578  if (!Vgrid_value(thee->dielXMap, position, &eps)) {
4579  Vnm_print(2, "Vpmg_fillco: Off dielXMap at:\n");
4580  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4581  position[0], position[1], position[2]);
4582  VASSERT(0);
4583  }
4584  thee->epsx[IJK(i,j,k)] = eps;
4585 
4586  position[0] = thee->xf[i];
4587  position[1] = thee->yf[j] + 0.5*hy;
4588  position[2] = thee->zf[k];
4589  if (!Vgrid_value(thee->dielYMap, position, &eps)) {
4590  Vnm_print(2, "Vpmg_fillco: Off dielYMap at:\n");
4591  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4592  position[0], position[1], position[2]);
4593  VASSERT(0);
4594  }
4595  thee->epsy[IJK(i,j,k)] = eps;
4596 
4597  position[0] = thee->xf[i];
4598  position[1] = thee->yf[j];
4599  position[2] = thee->zf[k] + 0.5*hzed;
4600  if (!Vgrid_value(thee->dielZMap, position, &eps)) {
4601  Vnm_print(2, "Vpmg_fillco: Off dielZMap at:\n");
4602  Vnm_print(2, "Vpmg_fillco: (x,y,z) = (%g,%g %g)\n",
4603  position[0], position[1], position[2]);
4604  VASSERT(0);
4605  }
4606  thee->epsz[IJK(i,j,k)] = eps;
4607  }
4608  }
4609  }
4610 }
4611 
4612 VPRIVATE void fillcoCoefMol(Vpmg *thee) {
4613 
4614  if (thee->useDielXMap || thee->useDielYMap || thee->useDielZMap ||
4615  thee->useKappaMap) {
4616 
4617  fillcoCoefMap(thee);
4618 
4619  } else {
4620 
4621  fillcoCoefMolDiel(thee);
4622  fillcoCoefMolIon(thee);
4623 
4624  }
4625 
4626 }
4627 
4628 VPRIVATE void fillcoCoefMolIon(Vpmg *thee) {
4629 
4630  Vacc *acc;
4631  Valist *alist;
4632  Vpbe *pbe;
4633  Vatom *atom;
4634  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr;
4635  double xlen, ylen, zlen, irad;
4636  double hx, hy, hzed, *apos, arad;
4637  int i, nx, ny, nz, iatom;
4638  Vsurf_Meth surfMeth;
4639 
4640  VASSERT(thee != VNULL);
4641  surfMeth = thee->surfMeth;
4642 
4643  /* Get PBE info */
4644  pbe = thee->pbe;
4645  acc = pbe->acc;
4646  alist = pbe->alist;
4647  irad = Vpbe_getMaxIonRadius(pbe);
4648  ionstr = Vpbe_getBulkIonicStrength(pbe);
4649 
4650  /* Mesh info */
4651  nx = thee->pmgp->nx;
4652  ny = thee->pmgp->ny;
4653  nz = thee->pmgp->nz;
4654  hx = thee->pmgp->hx;
4655  hy = thee->pmgp->hy;
4656  hzed = thee->pmgp->hzed;
4657 
4658  /* Define the total domain size */
4659  xlen = thee->pmgp->xlen;
4660  ylen = thee->pmgp->ylen;
4661  zlen = thee->pmgp->zlen;
4662 
4663  /* Define the min/max dimensions */
4664  xmin = thee->pmgp->xcent - (xlen/2.0);
4665  ymin = thee->pmgp->ycent - (ylen/2.0);
4666  zmin = thee->pmgp->zcent - (zlen/2.0);
4667  xmax = thee->pmgp->xcent + (xlen/2.0);
4668  ymax = thee->pmgp->ycent + (ylen/2.0);
4669  zmax = thee->pmgp->zcent + (zlen/2.0);
4670 
4671  /* This is a floating point parameter related to the non-zero nature of the
4672  * bulk ionic strength. If the ionic strength is greater than zero; this
4673  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
4674  * Otherwise, this parameter is set to 0.0 */
4675  if (ionstr > VPMGSMALL) ionmask = 1.0;
4676  else ionmask = 0.0;
4677 
4678  /* Reset the kappa array, marking everything accessible */
4679  for (i=0; i<(nx*ny*nz); i++) thee->kappa[i] = ionmask;
4680 
4681  if (ionstr < VPMGSMALL) return;
4682 
4683  /* Loop through the atoms and set kappa = 0.0 (inaccessible) if a point
4684  * is inside the ion-inflated van der Waals radii */
4685  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4686 
4687  atom = Valist_getAtom(alist, iatom);
4688  apos = Vatom_getPosition(atom);
4689  arad = Vatom_getRadius(atom);
4690 
4691  if (arad > VSMALL) {
4692 
4693  /* Make sure we're on the grid */
4694  if ((apos[0]<(xmin-irad-arad)) || (apos[0]>(xmax+irad+arad)) || \
4695  (apos[1]<(ymin-irad-arad)) || (apos[1]>(ymax+irad+arad)) || \
4696  (apos[2]<(zmin-irad-arad)) || (apos[2]>(zmax+irad+arad))) {
4697  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
4698  (thee->pmgp->bcfl != BCFL_MAP)) {
4699  Vnm_print(2,
4700  "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
4701  iatom, apos[0], apos[1], apos[2]);
4702  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
4703  xmin, xmax);
4704  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
4705  ymin, ymax);
4706  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
4707  zmin, zmax);
4708  }
4709  fflush(stderr);
4710 
4711  } else { /* if we're on the mesh */
4712 
4713  /* Mark ions */
4714  markSphere((irad+arad), apos,
4715  nx, ny, nz,
4716  hx, hy, hzed,
4717  xmin, ymin, zmin,
4718  thee->kappa, 0.0);
4719 
4720  } /* endif (on the mesh) */
4721  }
4722  } /* endfor (over all atoms) */
4723 
4724 }
4725 
4726 VPRIVATE void fillcoCoefMolDiel(Vpmg *thee) {
4727 
4728  /* Always call NoSmooth to fill the epsilon arrays */
4730 
4731  /* Call the smoothing algorithm as needed */
4732  if (thee->surfMeth == VSM_MOLSMOOTH) {
4734  }
4735 }
4736 
4737 VPRIVATE void fillcoCoefMolDielNoSmooth(Vpmg *thee) {
4738 
4739  Vacc *acc;
4740  VaccSurf *asurf;
4741  Valist *alist;
4742  Vpbe *pbe;
4743  Vatom *atom;
4744  double xmin, xmax, ymin, ymax, zmin, zmax;
4745  double xlen, ylen, zlen, position[3];
4746  double srad, epsw, epsp, deps, area;
4747  double hx, hy, hzed, *apos, arad;
4748  int i, nx, ny, nz, ntot, iatom, ipt;
4749 
4750  /* Get PBE info */
4751  pbe = thee->pbe;
4752  acc = pbe->acc;
4753  alist = pbe->alist;
4754  srad = Vpbe_getSolventRadius(pbe);
4755  epsw = Vpbe_getSolventDiel(pbe);
4756  epsp = Vpbe_getSoluteDiel(pbe);
4757 
4758  /* Mesh info */
4759  nx = thee->pmgp->nx;
4760  ny = thee->pmgp->ny;
4761  nz = thee->pmgp->nz;
4762  hx = thee->pmgp->hx;
4763  hy = thee->pmgp->hy;
4764  hzed = thee->pmgp->hzed;
4765 
4766  /* Define the total domain size */
4767  xlen = thee->pmgp->xlen;
4768  ylen = thee->pmgp->ylen;
4769  zlen = thee->pmgp->zlen;
4770 
4771  /* Define the min/max dimensions */
4772  xmin = thee->pmgp->xcent - (xlen/2.0);
4773  ymin = thee->pmgp->ycent - (ylen/2.0);
4774  zmin = thee->pmgp->zcent - (zlen/2.0);
4775  xmax = thee->pmgp->xcent + (xlen/2.0);
4776  ymax = thee->pmgp->ycent + (ylen/2.0);
4777  zmax = thee->pmgp->zcent + (zlen/2.0);
4778 
4779  /* Reset the arrays */
4780  ntot = nx*ny*nz;
4781  for (i=0; i<ntot; i++) {
4782  thee->epsx[i] = epsw;
4783  thee->epsy[i] = epsw;
4784  thee->epsz[i] = epsw;
4785  }
4786 
4787  /* Loop through the atoms and set a{123}cf = 0.0 (inaccessible)
4788  * if a point is inside the solvent-inflated van der Waals radii */
4789 #pragma omp parallel for default(shared) private(iatom,atom,apos,arad)
4790  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4791 
4792  atom = Valist_getAtom(alist, iatom);
4793  apos = Vatom_getPosition(atom);
4794  arad = Vatom_getRadius(atom);
4795 
4796  /* Make sure we're on the grid */
4797  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
4798  (apos[1]<=ymin) || (apos[1]>=ymax) || \
4799  (apos[2]<=zmin) || (apos[2]>=zmax)) {
4800  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
4801  (thee->pmgp->bcfl != BCFL_MAP)) {
4802  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
4803  %4.3f) is off the mesh (ignoring):\n",
4804  iatom, apos[0], apos[1], apos[2]);
4805  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
4806  xmin, xmax);
4807  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
4808  ymin, ymax);
4809  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
4810  zmin, zmax);
4811  }
4812  fflush(stderr);
4813 
4814  } else { /* if we're on the mesh */
4815 
4816  if (arad > VSMALL) {
4817  /* Mark x-shifted dielectric */
4818  markSphere((arad+srad), apos,
4819  nx, ny, nz,
4820  hx, hy, hzed,
4821  (xmin+0.5*hx), ymin, zmin,
4822  thee->epsx, epsp);
4823 
4824  /* Mark y-shifted dielectric */
4825  markSphere((arad+srad), apos,
4826  nx, ny, nz,
4827  hx, hy, hzed,
4828  xmin, (ymin+0.5*hy), zmin,
4829  thee->epsy, epsp);
4830 
4831  /* Mark z-shifted dielectric */
4832  markSphere((arad+srad), apos,
4833  nx, ny, nz,
4834  hx, hy, hzed,
4835  xmin, ymin, (zmin+0.5*hzed),
4836  thee->epsz, epsp);
4837  }
4838 
4839  } /* endif (on the mesh) */
4840  } /* endfor (over all atoms) */
4841 
4842  area = Vacc_SASA(acc, srad);
4843 
4844  /* We only need to do the next step for non-zero solvent radii */
4845  if (srad > VSMALL) {
4846 
4847  /* Now loop over the solvent accessible surface points */
4848 
4849 #pragma omp parallel for default(shared) private(iatom,atom,area,asurf,ipt,position)
4850  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
4851  atom = Valist_getAtom(alist, iatom);
4852  area = Vacc_atomSASA(acc, srad, atom);
4853  if (area > 0.0 ) {
4854  asurf = Vacc_atomSASPoints(acc, srad, atom);
4855 
4856  /* Use each point on the SAS to reset the solvent accessibility */
4857  /* TODO: Make sure we're not still wasting time here. */
4858  for (ipt=0; ipt<(asurf->npts); ipt++) {
4859 
4860  position[0] = asurf->xpts[ipt];
4861  position[1] = asurf->ypts[ipt];
4862  position[2] = asurf->zpts[ipt];
4863 
4864  /* Mark x-shifted dielectric */
4865  markSphere(srad, position,
4866  nx, ny, nz,
4867  hx, hy, hzed,
4868  (xmin+0.5*hx), ymin, zmin,
4869  thee->epsx, epsw);
4870 
4871  /* Mark y-shifted dielectric */
4872  markSphere(srad, position,
4873  nx, ny, nz,
4874  hx, hy, hzed,
4875  xmin, (ymin+0.5*hy), zmin,
4876  thee->epsy, epsw);
4877 
4878  /* Mark z-shifted dielectric */
4879  markSphere(srad, position,
4880  nx, ny, nz,
4881  hx, hy, hzed,
4882  xmin, ymin, (zmin+0.5*hzed),
4883  thee->epsz, epsw);
4884 
4885  }
4886  }
4887  }
4888  }
4889 }
4890 
4891 VPRIVATE void fillcoCoefMolDielSmooth(Vpmg *thee) {
4892 
4893  /* This function smoothes using a 9 point method based on
4894  Bruccoleri, et al. J Comput Chem 18 268-276 (1997). The nine points
4895  used are the shifted grid point and the 8 points that are 1/sqrt(2)
4896  grid spacings away. The harmonic mean of the 9 points is then used to
4897  find the overall dielectric value for the point in question. The use of
4898  this function assumes that the non-smoothed values were placed in the
4899  dielectric arrays by the fillcoCoefMolDielNoSmooth function.*/
4900 
4901  Vpbe *pbe;
4902  double frac, epsw;
4903  int i, j, k, nx, ny, nz, numpts;
4904 
4905  /* Mesh info */
4906  nx = thee->pmgp->nx;
4907  ny = thee->pmgp->ny;
4908  nz = thee->pmgp->nz;
4909 
4910  pbe = thee->pbe;
4911  epsw = Vpbe_getSolventDiel(pbe);
4912 
4913  /* Copy the existing diel arrays to work arrays */
4914  for (i=0; i<(nx*ny*nz); i++) {
4915  thee->a1cf[i] = thee->epsx[i];
4916  thee->a2cf[i] = thee->epsy[i];
4917  thee->a3cf[i] = thee->epsz[i];
4918  thee->epsx[i] = epsw;
4919  thee->epsy[i] = epsw;
4920  thee->epsz[i] = epsw;
4921  }
4922 
4923  /* Smooth the dielectric values */
4924  for (i=0; i<nx; i++) {
4925  for (j=0; j<ny; j++) {
4926  for (k=0; k<nz; k++) {
4927 
4928  /* Get the 8 points that are 1/sqrt(2) grid spacings away */
4929 
4930  /* Points for the X-shifted array */
4931  frac = 1.0/thee->a1cf[IJK(i,j,k)];
4932  frac += 1.0/thee->a2cf[IJK(i,j,k)];
4933  frac += 1.0/thee->a3cf[IJK(i,j,k)];
4934  numpts = 3;
4935 
4936  if (j > 0) {
4937  frac += 1.0/thee->a2cf[IJK(i,j-1,k)];
4938  numpts += 1;
4939  }
4940  if (k > 0) {
4941  frac += 1.0/thee->a3cf[IJK(i,j,k-1)];
4942  numpts += 1;
4943  }
4944  if (i < (nx-1)){
4945  frac += 1.0/thee->a2cf[IJK(i+1,j,k)];
4946  frac += 1.0/thee->a3cf[IJK(i+1,j,k)];
4947  numpts += 2;
4948  if (j > 0) {
4949  frac += 1.0/thee->a2cf[IJK(i+1,j-1,k)];
4950  numpts += 1;
4951  }
4952  if (k > 0) {
4953  frac += 1.0/thee->a3cf[IJK(i+1,j,k-1)];
4954  numpts += 1;
4955  }
4956  }
4957  thee->epsx[IJK(i,j,k)] = numpts/frac;
4958 
4959  /* Points for the Y-shifted array */
4960  frac = 1.0/thee->a2cf[IJK(i,j,k)];
4961  frac += 1.0/thee->a1cf[IJK(i,j,k)];
4962  frac += 1.0/thee->a3cf[IJK(i,j,k)];
4963  numpts = 3;
4964 
4965  if (i > 0) {
4966  frac += 1.0/thee->a1cf[IJK(i-1,j,k)];
4967  numpts += 1;
4968  }
4969  if (k > 0) {
4970  frac += 1.0/thee->a3cf[IJK(i,j,k-1)];
4971  numpts += 1;
4972  }
4973  if (j < (ny-1)){
4974  frac += 1.0/thee->a1cf[IJK(i,j+1,k)];
4975  frac += 1.0/thee->a3cf[IJK(i,j+1,k)];
4976  numpts += 2;
4977  if (i > 0) {
4978  frac += 1.0/thee->a1cf[IJK(i-1,j+1,k)];
4979  numpts += 1;
4980  }
4981  if (k > 0) {
4982  frac += 1.0/thee->a3cf[IJK(i,j+1,k-1)];
4983  numpts += 1;
4984  }
4985  }
4986  thee->epsy[IJK(i,j,k)] = numpts/frac;
4987 
4988  /* Points for the Z-shifted array */
4989  frac = 1.0/thee->a3cf[IJK(i,j,k)];
4990  frac += 1.0/thee->a1cf[IJK(i,j,k)];
4991  frac += 1.0/thee->a2cf[IJK(i,j,k)];
4992  numpts = 3;
4993 
4994  if (i > 0) {
4995  frac += 1.0/thee->a1cf[IJK(i-1,j,k)];
4996  numpts += 1;
4997  }
4998  if (j > 0) {
4999  frac += 1.0/thee->a2cf[IJK(i,j-1,k)];
5000  numpts += 1;
5001  }
5002  if (k < (nz-1)){
5003  frac += 1.0/thee->a1cf[IJK(i,j,k+1)];
5004  frac += 1.0/thee->a2cf[IJK(i,j,k+1)];
5005  numpts += 2;
5006  if (i > 0) {
5007  frac += 1.0/thee->a1cf[IJK(i-1,j,k+1)];
5008  numpts += 1;
5009  }
5010  if (j > 0) {
5011  frac += 1.0/thee->a2cf[IJK(i,j-1,k+1)];
5012  numpts += 1;
5013  }
5014  }
5015  thee->epsz[IJK(i,j,k)] = numpts/frac;
5016  }
5017  }
5018  }
5019 }
5020 
5021 
5022 VPRIVATE void fillcoCoefSpline(Vpmg *thee) {
5023 
5024  Valist *alist;
5025  Vpbe *pbe;
5026  Vatom *atom;
5027  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
5028  double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
5029  double irad, dx, dy, dz, epsw, epsp, w2i;
5030  double hx, hy, hzed, *apos, arad, sctot2;
5031  double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin, w3i;
5032  double dist, value, sm, sm2;
5033  int i, j, k, nx, ny, nz, iatom;
5034  int imin, imax, jmin, jmax, kmin, kmax;
5035 
5036  VASSERT(thee != VNULL);
5037  splineWin = thee->splineWin;
5038  w2i = 1.0/(splineWin*splineWin);
5039  w3i = 1.0/(splineWin*splineWin*splineWin);
5040 
5041  /* Get PBE info */
5042  pbe = thee->pbe;
5043  alist = pbe->alist;
5044  irad = Vpbe_getMaxIonRadius(pbe);
5045  ionstr = Vpbe_getBulkIonicStrength(pbe);
5046  epsw = Vpbe_getSolventDiel(pbe);
5047  epsp = Vpbe_getSoluteDiel(pbe);
5048 
5049  /* Mesh info */
5050  nx = thee->pmgp->nx;
5051  ny = thee->pmgp->ny;
5052  nz = thee->pmgp->nz;
5053  hx = thee->pmgp->hx;
5054  hy = thee->pmgp->hy;
5055  hzed = thee->pmgp->hzed;
5056 
5057  /* Define the total domain size */
5058  xlen = thee->pmgp->xlen;
5059  ylen = thee->pmgp->ylen;
5060  zlen = thee->pmgp->zlen;
5061 
5062  /* Define the min/max dimensions */
5063  xmin = thee->pmgp->xcent - (xlen/2.0);
5064  ymin = thee->pmgp->ycent - (ylen/2.0);
5065  zmin = thee->pmgp->zcent - (zlen/2.0);
5066  xmax = thee->pmgp->xcent + (xlen/2.0);
5067  ymax = thee->pmgp->ycent + (ylen/2.0);
5068  zmax = thee->pmgp->zcent + (zlen/2.0);
5069 
5070  /* This is a floating point parameter related to the non-zero nature of the
5071  * bulk ionic strength. If the ionic strength is greater than zero; this
5072  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
5073  * Otherwise, this parameter is set to 0.0 */
5074  if (ionstr > VPMGSMALL) ionmask = 1.0;
5075  else ionmask = 0.0;
5076 
5077  /* Reset the kappa, epsx, epsy, and epsz arrays */
5078  for (i=0; i<(nx*ny*nz); i++) {
5079  thee->kappa[i] = 1.0;
5080  thee->epsx[i] = 1.0;
5081  thee->epsy[i] = 1.0;
5082  thee->epsz[i] = 1.0;
5083  }
5084 
5085  /* Loop through the atoms and do assign the dielectric */
5086  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
5087 
5088  atom = Valist_getAtom(alist, iatom);
5089  apos = Vatom_getPosition(atom);
5090  arad = Vatom_getRadius(atom);
5091 
5092  /* Make sure we're on the grid */
5093  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
5094  (apos[1]<=ymin) || (apos[1]>=ymax) || \
5095  (apos[2]<=zmin) || (apos[2]>=zmax)) {
5096  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5097  (thee->pmgp->bcfl != BCFL_MAP)) {
5098  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
5099  %4.3f) is off the mesh (ignoring):\n",
5100  iatom, apos[0], apos[1], apos[2]);
5101  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
5102  xmin, xmax);
5103  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
5104  ymin, ymax);
5105  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
5106  zmin, zmax);
5107  }
5108  fflush(stderr);
5109 
5110  } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
5111 
5112  /* Convert the atom position to grid reference frame */
5113  position[0] = apos[0] - xmin;
5114  position[1] = apos[1] - ymin;
5115  position[2] = apos[2] - zmin;
5116 
5117  /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
5118  * ASSIGNMENT (Steps #1-3) */
5119  itot = irad + arad + splineWin;
5120  itot2 = VSQR(itot);
5121  ictot = VMAX2(0, (irad + arad - splineWin));
5122  ictot2 = VSQR(ictot);
5123  stot = arad + splineWin;
5124  stot2 = VSQR(stot);
5125  sctot = VMAX2(0, (arad - splineWin));
5126  sctot2 = VSQR(sctot);
5127 
5128  /* We'll search over grid points which are in the greater of
5129  * these two radii */
5130  rtot = VMAX2(itot, stot);
5131  rtot2 = VMAX2(itot2, stot2);
5132  dx = rtot + 0.5*hx;
5133  dy = rtot + 0.5*hy;
5134  dz = rtot + 0.5*hzed;
5135  imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
5136  imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
5137  jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
5138  jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
5139  kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
5140  kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
5141  for (i=imin; i<=imax; i++) {
5142  dx2 = VSQR(position[0] - hx*i);
5143  for (j=jmin; j<=jmax; j++) {
5144  dy2 = VSQR(position[1] - hy*j);
5145  for (k=kmin; k<=kmax; k++) {
5146  dz2 = VSQR(position[2] - k*hzed);
5147 
5148  /* ASSIGN CCF */
5149  if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
5150  dist2 = dz2 + dy2 + dx2;
5151  if (dist2 >= itot2) {
5152  ;
5153  }
5154  if (dist2 <= ictot2) {
5155  thee->kappa[IJK(i,j,k)] = 0.0;
5156  }
5157  if ((dist2 < itot2) && (dist2 > ictot2)) {
5158  dist = VSQRT(dist2);
5159  sm = dist - (arad + irad) + splineWin;
5160  sm2 = VSQR(sm);
5161  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5162  thee->kappa[IJK(i,j,k)] *= value;
5163  }
5164  }
5165 
5166  /* ASSIGN A1CF */
5167  if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
5168  dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
5169  if (dist2 >= stot2) {
5170  thee->epsx[IJK(i,j,k)] *= 1.0;
5171  }
5172  if (dist2 <= sctot2) {
5173  thee->epsx[IJK(i,j,k)] = 0.0;
5174  }
5175  if ((dist2 > sctot2) && (dist2 < stot2)) {
5176  dist = VSQRT(dist2);
5177  sm = dist - arad + splineWin;
5178  sm2 = VSQR(sm);
5179  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5180  thee->epsx[IJK(i,j,k)] *= value;
5181  }
5182  }
5183 
5184  /* ASSIGN A2CF */
5185  if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
5186  dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
5187  if (dist2 >= stot2) {
5188  thee->epsy[IJK(i,j,k)] *= 1.0;
5189  }
5190  if (dist2 <= sctot2) {
5191  thee->epsy[IJK(i,j,k)] = 0.0;
5192  }
5193  if ((dist2 > sctot2) && (dist2 < stot2)) {
5194  dist = VSQRT(dist2);
5195  sm = dist - arad + splineWin;
5196  sm2 = VSQR(sm);
5197  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5198  thee->epsy[IJK(i,j,k)] *= value;
5199  }
5200  }
5201 
5202  /* ASSIGN A3CF */
5203  if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
5204  dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
5205  if (dist2 >= stot2) {
5206  thee->epsz[IJK(i,j,k)] *= 1.0;
5207  }
5208  if (dist2 <= sctot2) {
5209  thee->epsz[IJK(i,j,k)] = 0.0;
5210  }
5211  if ((dist2 > sctot2) && (dist2 < stot2)) {
5212  dist = VSQRT(dist2);
5213  sm = dist - arad + splineWin;
5214  sm2 = VSQR(sm);
5215  value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
5216  thee->epsz[IJK(i,j,k)] *= value;
5217  }
5218  }
5219 
5220 
5221  } /* k loop */
5222  } /* j loop */
5223  } /* i loop */
5224  } /* endif (on the mesh) */
5225  } /* endfor (over all atoms) */
5226 
5227  Vnm_print(0, "Vpmg_fillco: filling coefficient arrays\n");
5228  /* Interpret markings and fill the coefficient arrays */
5229  for (k=0; k<nz; k++) {
5230  for (j=0; j<ny; j++) {
5231  for (i=0; i<nx; i++) {
5232 
5233  thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
5234  thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)]
5235  + epsp;
5236  thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)]
5237  + epsp;
5238  thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)]
5239  + epsp;
5240 
5241  } /* i loop */
5242  } /* j loop */
5243  } /* k loop */
5244 
5245 }
5246 
5247 VPRIVATE void fillcoCoef(Vpmg *thee) {
5248 
5249  VASSERT(thee != VNULL);
5250 
5251  if (thee->useDielXMap || thee->useDielYMap ||
5252  thee->useDielZMap || thee->useKappaMap) {
5253  fillcoCoefMap(thee);
5254  return;
5255  }
5256 
5257  switch(thee->surfMeth) {
5258  case VSM_MOL:
5259  Vnm_print(0, "fillcoCoef: Calling fillcoCoefMol...\n");
5260  fillcoCoefMol(thee);
5261  break;
5262  case VSM_MOLSMOOTH:
5263  Vnm_print(0, "fillcoCoef: Calling fillcoCoefMol...\n");
5264  fillcoCoefMol(thee);
5265  break;
5266  case VSM_SPLINE:
5267  Vnm_print(0, "fillcoCoef: Calling fillcoCoefSpline...\n");
5268  fillcoCoefSpline(thee);
5269  break;
5270  case VSM_SPLINE3:
5271  Vnm_print(0, "fillcoCoef: Calling fillcoCoefSpline3...\n");
5272  fillcoCoefSpline3(thee);
5273  break;
5274  case VSM_SPLINE4:
5275  Vnm_print(0, "fillcoCoef: Calling fillcoCoefSpline4...\n");
5276  fillcoCoefSpline4(thee);
5277  break;
5278  default:
5279  Vnm_print(2, "fillcoCoef: Invalid surfMeth (%d)!\n",
5280  thee->surfMeth);
5281  VASSERT(0);
5282  break;
5283  }
5284 }
5285 
5286 
5287 VPRIVATE Vrc_Codes fillcoCharge(Vpmg *thee) {
5288 
5289  Vrc_Codes rc;
5290 
5291  VASSERT(thee != VNULL);
5292 
5293  if (thee->useChargeMap) {
5294  return fillcoChargeMap(thee);
5295  }
5296 
5297  switch(thee->chargeMeth) {
5298  case VCM_TRIL:
5299  Vnm_print(0, "fillcoCharge: Calling fillcoChargeSpline1...\n");
5300  fillcoChargeSpline1(thee);
5301  break;
5302  case VCM_BSPL2:
5303  Vnm_print(0, "fillcoCharge: Calling fillcoChargeSpline2...\n");
5304  fillcoChargeSpline2(thee);
5305  break;
5306  case VCM_BSPL4:
5307  switch (thee->chargeSrc) {
5308  case VCM_CHARGE:
5309  Vnm_print(0, "fillcoCharge: Calling fillcoPermanentMultipole...\n");
5311  break;
5312 #if defined(WITH_TINKER)
5313  case VCM_PERMANENT:
5314  Vnm_print(0, "fillcoCharge: Calling fillcoPermanentMultipole...\n");
5316  break;
5317  case VCM_INDUCED:
5318  Vnm_print(0, "fillcoCharge: Calling fillcoInducedDipole...\n");
5319  fillcoInducedDipole(thee);
5320  break;
5321  case VCM_NLINDUCED:
5322  Vnm_print(0, "fillcoCharge: Calling fillcoNLInducedDipole...\n");
5323  fillcoNLInducedDipole(thee);
5324  break;
5325 #endif /* if defined(WITH_TINKER) */
5326  default:
5327  Vnm_print(2, "fillcoCharge: Invalid chargeSource (%d)!\n",
5328  thee->chargeSrc);
5329  return VRC_FAILURE;
5330  break;
5331  }
5332  break;
5333  default:
5334  Vnm_print(2, "fillcoCharge: Invalid chargeMeth (%d)!\n",
5335  thee->chargeMeth);
5336  return VRC_FAILURE;
5337  break;
5338  }
5339 
5340  return VRC_SUCCESS;
5341 }
5342 
5343 VPRIVATE Vrc_Codes fillcoChargeMap(Vpmg *thee) {
5344 
5345  Vpbe *pbe;
5346  double position[3], charge, zmagic, hx, hy, hzed;
5347  int i, j, k, nx, ny, nz, rc;
5348 
5349 
5350  VASSERT(thee != VNULL);
5351 
5352  /* Get PBE info */
5353  pbe = thee->pbe;
5354  zmagic = Vpbe_getZmagic(pbe);
5355 
5356  /* Mesh info */
5357  nx = thee->pmgp->nx;
5358  ny = thee->pmgp->ny;
5359  nz = thee->pmgp->nz;
5360  hx = thee->pmgp->hx;
5361  hy = thee->pmgp->hy;
5362  hzed = thee->pmgp->hzed;
5363 
5364  /* Reset the charge array */
5365  for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
5366 
5367  /* Fill in the source term (atomic charges) */
5368  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5369  for (k=0; k<nz; k++) {
5370  for (j=0; j<ny; j++) {
5371  for (i=0; i<nx; i++) {
5372  position[0] = thee->xf[i];
5373  position[1] = thee->yf[j];
5374  position[2] = thee->zf[k];
5375  rc = Vgrid_value(thee->chargeMap, position, &charge);
5376  if (!rc) {
5377  Vnm_print(2, "fillcoChargeMap: Error -- fell off of charge map at (%g, %g, %g)!\n",
5378  position[0], position[1], position[2]);
5379  return VRC_FAILURE;
5380  }
5381  /* Scale the charge to internal units */
5382  charge = charge*zmagic;
5383  thee->charge[IJK(i,j,k)] = charge;
5384  }
5385  }
5386  }
5387 
5388  return VRC_SUCCESS;
5389 }
5390 
5391 VPRIVATE void fillcoChargeSpline1(Vpmg *thee) {
5392 
5393  Valist *alist;
5394  Vpbe *pbe;
5395  Vatom *atom;
5396  double xmin, xmax, ymin, ymax, zmin, zmax;
5397  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
5398  double charge, dx, dy, dz, zmagic, hx, hy, hzed, *apos;
5399  int i, nx, ny, nz, iatom, ihi, ilo, jhi, jlo, khi, klo;
5400 
5401 
5402  VASSERT(thee != VNULL);
5403 
5404  /* Get PBE info */
5405  pbe = thee->pbe;
5406  alist = pbe->alist;
5407  zmagic = Vpbe_getZmagic(pbe);
5408 
5409  /* Mesh info */
5410  nx = thee->pmgp->nx;
5411  ny = thee->pmgp->ny;
5412  nz = thee->pmgp->nz;
5413  hx = thee->pmgp->hx;
5414  hy = thee->pmgp->hy;
5415  hzed = thee->pmgp->hzed;
5416 
5417  /* Define the total domain size */
5418  xlen = thee->pmgp->xlen;
5419  ylen = thee->pmgp->ylen;
5420  zlen = thee->pmgp->zlen;
5421 
5422  /* Define the min/max dimensions */
5423  xmin = thee->pmgp->xcent - (xlen/2.0);
5424  ymin = thee->pmgp->ycent - (ylen/2.0);
5425  zmin = thee->pmgp->zcent - (zlen/2.0);
5426  xmax = thee->pmgp->xcent + (xlen/2.0);
5427  ymax = thee->pmgp->ycent + (ylen/2.0);
5428  zmax = thee->pmgp->zcent + (zlen/2.0);
5429 
5430  /* Reset the charge array */
5431  for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
5432 
5433  /* Fill in the source term (atomic charges) */
5434  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5435  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
5436 
5437  atom = Valist_getAtom(alist, iatom);
5438  apos = Vatom_getPosition(atom);
5439  charge = Vatom_getCharge(atom);
5440 
5441  /* Make sure we're on the grid */
5442  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
5443  (apos[1]<=ymin) || (apos[1]>=ymax) || \
5444  (apos[2]<=zmin) || (apos[2]>=zmax)) {
5445  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5446  (thee->pmgp->bcfl != BCFL_MAP)) {
5447  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f, \
5448 %4.3f) is off the mesh (ignoring):\n",
5449  iatom, apos[0], apos[1], apos[2]);
5450  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
5451  xmin, xmax);
5452  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
5453  ymin, ymax);
5454  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
5455  zmin, zmax);
5456  }
5457  fflush(stderr);
5458  } else {
5459 
5460  /* Convert the atom position to grid reference frame */
5461  position[0] = apos[0] - xmin;
5462  position[1] = apos[1] - ymin;
5463  position[2] = apos[2] - zmin;
5464 
5465  /* Scale the charge to be a delta function */
5466  charge = charge*zmagic/(hx*hy*hzed);
5467 
5468  /* Figure out which vertices we're next to */
5469  ifloat = position[0]/hx;
5470  jfloat = position[1]/hy;
5471  kfloat = position[2]/hzed;
5472 
5473  ihi = (int)ceil(ifloat);
5474  ilo = (int)floor(ifloat);
5475  jhi = (int)ceil(jfloat);
5476  jlo = (int)floor(jfloat);
5477  khi = (int)ceil(kfloat);
5478  klo = (int)floor(kfloat);
5479 
5480  /* Now assign fractions of the charge to the nearby verts */
5481  dx = ifloat - (double)(ilo);
5482  dy = jfloat - (double)(jlo);
5483  dz = kfloat - (double)(klo);
5484  thee->charge[IJK(ihi,jhi,khi)] += (dx*dy*dz*charge);
5485  thee->charge[IJK(ihi,jlo,khi)] += (dx*(1.0-dy)*dz*charge);
5486  thee->charge[IJK(ihi,jhi,klo)] += (dx*dy*(1.0-dz)*charge);
5487  thee->charge[IJK(ihi,jlo,klo)] += (dx*(1.0-dy)*(1.0-dz)*charge);
5488  thee->charge[IJK(ilo,jhi,khi)] += ((1.0-dx)*dy*dz *charge);
5489  thee->charge[IJK(ilo,jlo,khi)] += ((1.0-dx)*(1.0-dy)*dz *charge);
5490  thee->charge[IJK(ilo,jhi,klo)] += ((1.0-dx)*dy*(1.0-dz)*charge);
5491  thee->charge[IJK(ilo,jlo,klo)] += ((1.0-dx)*(1.0-dy)*(1.0-dz)*charge);
5492  } /* endif (on the mesh) */
5493  } /* endfor (each atom) */
5494 }
5495 
5496 VPRIVATE double bspline2(double x) {
5497 
5498  double m2m, m2, m3;
5499 
5500  if ((x >= 0.0) && (x <= 2.0)) m2m = 1.0 - VABS(x - 1.0);
5501  else m2m = 0.0;
5502  if ((x >= 1.0) && (x <= 3.0)) m2 = 1.0 - VABS(x - 2.0);
5503  else m2 = 0.0;
5504 
5505  if ((x >= 0.0) && (x <= 3.0)) m3 = 0.5*x*m2m + 0.5*(3.0-x)*m2;
5506  else m3 = 0.0;
5507 
5508  return m3;
5509 
5510 }
5511 
5512 VPRIVATE double dbspline2(double x) {
5513 
5514  double m2m, m2, dm3;
5515 
5516  if ((x >= 0.0) && (x <= 2.0)) m2m = 1.0 - VABS(x - 1.0);
5517  else m2m = 0.0;
5518  if ((x >= 1.0) && (x <= 3.0)) m2 = 1.0 - VABS(x - 2.0);
5519  else m2 = 0.0;
5520 
5521  dm3 = m2m - m2;
5522 
5523  return dm3;
5524 
5525 }
5526 
5527 
5528 VPRIVATE void fillcoChargeSpline2(Vpmg *thee) {
5529 
5530  Valist *alist;
5531  Vpbe *pbe;
5532  Vatom *atom;
5533  double xmin, xmax, ymin, ymax, zmin, zmax, zmagic;
5534  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
5535  double charge, hx, hy, hzed, *apos, mx, my, mz;
5536  int i, ii, jj, kk, nx, ny, nz, iatom;
5537  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
5538 
5539 
5540  VASSERT(thee != VNULL);
5541 
5542  /* Get PBE info */
5543  pbe = thee->pbe;
5544  alist = pbe->alist;
5545  zmagic = Vpbe_getZmagic(pbe);
5546 
5547  /* Mesh info */
5548  nx = thee->pmgp->nx;
5549  ny = thee->pmgp->ny;
5550  nz = thee->pmgp->nz;
5551  hx = thee->pmgp->hx;
5552  hy = thee->pmgp->hy;
5553  hzed = thee->pmgp->hzed;
5554 
5555  /* Define the total domain size */
5556  xlen = thee->pmgp->xlen;
5557  ylen = thee->pmgp->ylen;
5558  zlen = thee->pmgp->zlen;
5559 
5560  /* Define the min/max dimensions */
5561  xmin = thee->pmgp->xcent - (xlen/2.0);
5562  ymin = thee->pmgp->ycent - (ylen/2.0);
5563  zmin = thee->pmgp->zcent - (zlen/2.0);
5564  xmax = thee->pmgp->xcent + (xlen/2.0);
5565  ymax = thee->pmgp->ycent + (ylen/2.0);
5566  zmax = thee->pmgp->zcent + (zlen/2.0);
5567 
5568  /* Reset the charge array */
5569  for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
5570 
5571  /* Fill in the source term (atomic charges) */
5572  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5573  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
5574 
5575  atom = Valist_getAtom(alist, iatom);
5576  apos = Vatom_getPosition(atom);
5577  charge = Vatom_getCharge(atom);
5578 
5579  /* Make sure we're on the grid */
5580  if ((apos[0]<=(xmin-hx)) || (apos[0]>=(xmax+hx)) || \
5581  (apos[1]<=(ymin-hy)) || (apos[1]>=(ymax+hy)) || \
5582  (apos[2]<=(zmin-hzed)) || (apos[2]>=(zmax+hzed))) {
5583  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5584  (thee->pmgp->bcfl != BCFL_MAP)) {
5585  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f, \
5586 %4.3f) is off the mesh (for cubic splines!!) (ignoring this atom):\n",
5587  iatom, apos[0], apos[1], apos[2]);
5588  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
5589  xmin, xmax);
5590  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
5591  ymin, ymax);
5592  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
5593  zmin, zmax);
5594  }
5595  fflush(stderr);
5596  } else {
5597 
5598  /* Convert the atom position to grid reference frame */
5599  position[0] = apos[0] - xmin;
5600  position[1] = apos[1] - ymin;
5601  position[2] = apos[2] - zmin;
5602 
5603  /* Scale the charge to be a delta function */
5604  charge = charge*zmagic/(hx*hy*hzed);
5605 
5606  /* Figure out which vertices we're next to */
5607  ifloat = position[0]/hx;
5608  jfloat = position[1]/hy;
5609  kfloat = position[2]/hzed;
5610 
5611  ip1 = (int)ceil(ifloat);
5612  ip2 = ip1 + 1;
5613  im1 = (int)floor(ifloat);
5614  im2 = im1 - 1;
5615  jp1 = (int)ceil(jfloat);
5616  jp2 = jp1 + 1;
5617  jm1 = (int)floor(jfloat);
5618  jm2 = jm1 - 1;
5619  kp1 = (int)ceil(kfloat);
5620  kp2 = kp1 + 1;
5621  km1 = (int)floor(kfloat);
5622  km2 = km1 - 1;
5623 
5624  /* This step shouldn't be necessary, but it saves nasty debugging
5625  * later on if something goes wrong */
5626  ip2 = VMIN2(ip2,nx-1);
5627  ip1 = VMIN2(ip1,nx-1);
5628  im1 = VMAX2(im1,0);
5629  im2 = VMAX2(im2,0);
5630  jp2 = VMIN2(jp2,ny-1);
5631  jp1 = VMIN2(jp1,ny-1);
5632  jm1 = VMAX2(jm1,0);
5633  jm2 = VMAX2(jm2,0);
5634  kp2 = VMIN2(kp2,nz-1);
5635  kp1 = VMIN2(kp1,nz-1);
5636  km1 = VMAX2(km1,0);
5637  km2 = VMAX2(km2,0);
5638 
5639  /* Now assign fractions of the charge to the nearby verts */
5640  for (ii=im2; ii<=ip2; ii++) {
5641  mx = bspline2(VFCHI(ii,ifloat));
5642  for (jj=jm2; jj<=jp2; jj++) {
5643  my = bspline2(VFCHI(jj,jfloat));
5644  for (kk=km2; kk<=kp2; kk++) {
5645  mz = bspline2(VFCHI(kk,kfloat));
5646  thee->charge[IJK(ii,jj,kk)] += (charge*mx*my*mz);
5647  }
5648  }
5649  }
5650 
5651  } /* endif (on the mesh) */
5652  } /* endfor (each atom) */
5653 }
5654 
5655 VPUBLIC int Vpmg_fillco(Vpmg *thee,
5656  Vsurf_Meth surfMeth,
5657  double splineWin,
5658  Vchrg_Meth chargeMeth,
5659  int useDielXMap,
5660  Vgrid *dielXMap,
5661  int useDielYMap,
5662  Vgrid *dielYMap,
5663  int useDielZMap,
5664  Vgrid *dielZMap,
5665  int useKappaMap,
5666  Vgrid *kappaMap,
5667  int usePotMap,
5668  Vgrid *potMap,
5669  int useChargeMap,
5670  Vgrid *chargeMap
5671  ) {
5672 
5673  Vpbe *pbe;
5674  double xmin,
5675  xmax,
5676  ymin,
5677  ymax,
5678  zmin,
5679  zmax,
5680  xlen,
5681  ylen,
5682  zlen,
5683  hx,
5684  hy,
5685  hzed,
5686  epsw,
5687  epsp,
5688  ionstr;
5689  int i,
5690  nx,
5691  ny,
5692  nz,
5693  islap;
5694  Vrc_Codes rc;
5695 
5696  if (thee == VNULL) {
5697  Vnm_print(2, "Vpmg_fillco: got NULL thee!\n");
5698  return 0;
5699  }
5700 
5701  thee->surfMeth = surfMeth;
5702  thee->splineWin = splineWin;
5703  thee->chargeMeth = chargeMeth;
5704  thee->useDielXMap = useDielXMap;
5705  if (thee->useDielXMap) thee->dielXMap = dielXMap;
5706  thee->useDielYMap = useDielYMap;
5707  if (thee->useDielYMap) thee->dielYMap = dielYMap;
5708  thee->useDielZMap = useDielZMap;
5709  if (thee->useDielZMap) thee->dielZMap = dielZMap;
5710  thee->useKappaMap = useKappaMap;
5711  if (thee->useKappaMap) thee->kappaMap = kappaMap;
5712  thee->usePotMap = usePotMap;
5713  if (thee->usePotMap) thee->potMap = potMap;
5714  thee->useChargeMap = useChargeMap;
5715  if (thee->useChargeMap) thee->chargeMap = chargeMap;
5716 
5717  /* Get PBE info */
5718  pbe = thee->pbe;
5719  ionstr = Vpbe_getBulkIonicStrength(pbe);
5720  epsw = Vpbe_getSolventDiel(pbe);
5721  epsp = Vpbe_getSoluteDiel(pbe);
5722 
5723  /* Mesh info */
5724  nx = thee->pmgp->nx;
5725  ny = thee->pmgp->ny;
5726  nz = thee->pmgp->nz;
5727  hx = thee->pmgp->hx;
5728  hy = thee->pmgp->hy;
5729  hzed = thee->pmgp->hzed;
5730 
5731  /* Define the total domain size */
5732  xlen = thee->pmgp->xlen;
5733  ylen = thee->pmgp->ylen;
5734  zlen = thee->pmgp->zlen;
5735 
5736  /* Define the min/max dimensions */
5737  xmin = thee->pmgp->xcent - (xlen/2.0);
5738  thee->pmgp->xmin = xmin;
5739  ymin = thee->pmgp->ycent - (ylen/2.0);
5740  thee->pmgp->ymin = ymin;
5741  zmin = thee->pmgp->zcent - (zlen/2.0);
5742  thee->pmgp->zmin = zmin;
5743  xmax = thee->pmgp->xcent + (xlen/2.0);
5744  thee->pmgp->xmax = xmax;
5745  ymax = thee->pmgp->ycent + (ylen/2.0);
5746  thee->pmgp->ymax = ymax;
5747  zmax = thee->pmgp->zcent + (zlen/2.0);
5748  thee->pmgp->zmax = zmax;
5749  thee->rparm[2] = xmin;
5750  thee->rparm[3] = xmax;
5751  thee->rparm[4] = ymin;
5752  thee->rparm[5] = ymax;
5753  thee->rparm[6] = zmin;
5754  thee->rparm[7] = zmax;
5755 
5756  /* This is a flag that gets set if the operator is a simple Laplacian;
5757  * i.e., in the case of a homogenous dielectric and zero ionic strength
5758  * The operator cannot be a simple Laplacian if maps are read in. */
5759  if(thee->useDielXMap || thee->useDielYMap || thee->useDielZMap ||
5760  thee->useKappaMap || thee->usePotMap){
5761  islap = 0;
5762  }else if ( (ionstr < VPMGSMALL) && (VABS(epsp-epsw) < VPMGSMALL) ){
5763  islap = 1;
5764  }else{
5765  islap = 0;
5766  }
5767 
5768  /* Fill the mesh point coordinate arrays */
5769  for (i=0; i<nx; i++) thee->xf[i] = xmin + i*hx;
5770  for (i=0; i<ny; i++) thee->yf[i] = ymin + i*hy;
5771  for (i=0; i<nz; i++) thee->zf[i] = zmin + i*hzed;
5772 
5773  /* Reset the tcf array */
5774  for (i=0; i<(nx*ny*nz); i++) thee->tcf[i] = 0.0;
5775 
5776  /* Fill in the source term (atomic charges) */
5777  Vnm_print(0, "Vpmg_fillco: filling in source term.\n");
5778  rc = fillcoCharge(thee);
5779  switch(rc) {
5780  case VRC_SUCCESS:
5781  break;
5782  case VRC_WARNING:
5783  Vnm_print(2, "Vpmg_fillco: non-fatal errors while filling charge map!\n");
5784  break;
5785  case VRC_FAILURE:
5786  Vnm_print(2, "Vpmg_fillco: fatal errors while filling charge map!\n");
5787  return 0;
5788  break;
5789  }
5790 
5791  /* THE FOLLOWING NEEDS TO BE DONE IF WE'RE NOT USING A SIMPLE LAPLACIAN
5792  * OPERATOR */
5793  if (!islap) {
5794  Vnm_print(0, "Vpmg_fillco: marking ion and solvent accessibility.\n");
5795  fillcoCoef(thee);
5796  Vnm_print(0, "Vpmg_fillco: done filling coefficient arrays\n");
5797 
5798  } else { /* else (!islap) ==> It's a Laplacian operator! */
5799 
5800  for (i=0; i<(nx*ny*nz); i++) {
5801  thee->kappa[i] = 0.0;
5802  thee->epsx[i] = epsp;
5803  thee->epsy[i] = epsp;
5804  thee->epsz[i] = epsp;
5805  }
5806 
5807  } /* endif (!islap) */
5808 
5809  /* Fill the boundary arrays (except when focusing, bcfl = 4) */
5810  if (thee->pmgp->bcfl != BCFL_FOCUS) {
5811  Vnm_print(0, "Vpmg_fillco: filling boundary arrays\n");
5812  bcCalc(thee);
5813  Vnm_print(0, "Vpmg_fillco: done filling boundary arrays\n");
5814  }
5815 
5816  thee->filled = 1;
5817 
5818  return 1;
5819 }
5820 
5821 
5822 VPUBLIC int Vpmg_force(Vpmg *thee, double *force, int atomID,
5823  Vsurf_Meth srfm, Vchrg_Meth chgm) {
5824 
5825  int rc = 1;
5826  double qfF[3]; /* Charge-field force */
5827  double dbF[3]; /* Dielectric boundary force */
5828  double ibF[3]; /* Ion boundary force */
5829  double npF[3]; /* Non-polar boundary force */
5830 
5831  VASSERT(thee != VNULL);
5832 
5833  rc = rc && Vpmg_dbForce(thee, qfF, atomID, srfm);
5834  rc = rc && Vpmg_ibForce(thee, dbF, atomID, srfm);
5835  rc = rc && Vpmg_qfForce(thee, ibF, atomID, chgm);
5836 
5837  force[0] = qfF[0] + dbF[0] + ibF[0];
5838  force[1] = qfF[1] + dbF[1] + ibF[1];
5839  force[2] = qfF[2] + dbF[2] + ibF[2];
5840 
5841  return rc;
5842 
5843 }
5844 
5845 VPUBLIC int Vpmg_ibForce(Vpmg *thee, double *force, int atomID,
5846  Vsurf_Meth srfm) {
5847 
5848  Valist *alist;
5849  Vacc *acc;
5850  Vpbe *pbe;
5851  Vatom *atom;
5852 
5853  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
5854  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
5855  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
5856  double izmagic;
5857  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
5858 
5859  /* For nonlinear forces */
5860  int ichop, nchop, nion, m;
5861  double ionConc[MAXION], ionRadii[MAXION], ionQ[MAXION], ionstr;
5862 
5863  VASSERT(thee != VNULL);
5864 
5865  acc = thee->pbe->acc;
5866  atom = Valist_getAtom(thee->pbe->alist, atomID);
5867  apos = Vatom_getPosition(atom);
5868  arad = Vatom_getRadius(atom);
5869 
5870  /* Reset force */
5871  force[0] = 0.0;
5872  force[1] = 0.0;
5873  force[2] = 0.0;
5874 
5875  /* Check surface definition */
5876  if ((srfm != VSM_SPLINE) && (srfm!=VSM_SPLINE3) && (srfm!=VSM_SPLINE4)) {
5877  Vnm_print(2, "Vpmg_ibForce: Forces *must* be calculated with \
5878 spline-based surfaces!\n");
5879  Vnm_print(2, "Vpmg_ibForce: Skipping ionic boundary force \
5880 calculation!\n");
5881  return 0;
5882  }
5883 
5884  /* If we aren't in the current position, then we're done */
5885  if (atom->partID == 0) return 1;
5886 
5887  /* Get PBE info */
5888  pbe = thee->pbe;
5889  acc = pbe->acc;
5890  alist = pbe->alist;
5891  irad = Vpbe_getMaxIonRadius(pbe);
5892  zkappa2 = Vpbe_getZkappa2(pbe);
5893  izmagic = 1.0/Vpbe_getZmagic(pbe);
5894 
5895  ionstr = Vpbe_getBulkIonicStrength(pbe);
5896  Vpbe_getIons(pbe, &nion, ionConc, ionRadii, ionQ);
5897 
5898  /* Mesh info */
5899  nx = thee->pmgp->nx;
5900  ny = thee->pmgp->ny;
5901  nz = thee->pmgp->nz;
5902  hx = thee->pmgp->hx;
5903  hy = thee->pmgp->hy;
5904  hzed = thee->pmgp->hzed;
5905  xlen = thee->pmgp->xlen;
5906  ylen = thee->pmgp->ylen;
5907  zlen = thee->pmgp->zlen;
5908  xmin = thee->pmgp->xmin;
5909  ymin = thee->pmgp->ymin;
5910  zmin = thee->pmgp->zmin;
5911  xmax = thee->pmgp->xmax;
5912  ymax = thee->pmgp->ymax;
5913  zmax = thee->pmgp->zmax;
5914 
5915  /* Sanity check: there is no force if there is zero ionic strength */
5916  if (zkappa2 < VPMGSMALL) {
5917 #ifndef VAPBSQUIET
5918  Vnm_print(2, "Vpmg_ibForce: No force for zero ionic strength!\n");
5919 #endif
5920  return 1;
5921  }
5922 
5923  /* Make sure we're on the grid */
5924  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
5925  (apos[1]<=ymin) || (apos[1]>=ymax) || \
5926  (apos[2]<=zmin) || (apos[2]>=zmax)) {
5927  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
5928  (thee->pmgp->bcfl != BCFL_MAP)) {
5929  Vnm_print(2, "Vpmg_ibForce: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
5930  atom, apos[0], apos[1], apos[2]);
5931  Vnm_print(2, "Vpmg_ibForce: xmin = %g, xmax = %g\n",
5932  xmin, xmax);
5933  Vnm_print(2, "Vpmg_ibForce: ymin = %g, ymax = %g\n",
5934  ymin, ymax);
5935  Vnm_print(2, "Vpmg_ibForce: zmin = %g, zmax = %g\n",
5936  zmin, zmax);
5937  }
5938  fflush(stderr);
5939  } else {
5940 
5941  /* Convert the atom position to grid reference frame */
5942  position[0] = apos[0] - xmin;
5943  position[1] = apos[1] - ymin;
5944  position[2] = apos[2] - zmin;
5945 
5946  /* Integrate over points within this atom's (inflated) radius */
5947  rtot = (irad + arad + thee->splineWin);
5948  rtot2 = VSQR(rtot);
5949  dx = rtot + 0.5*hx;
5950  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
5951  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
5952  for (i=imin; i<=imax; i++) {
5953  dx2 = VSQR(position[0] - hx*i);
5954  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
5955  else dy = 0.5*hy;
5956  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
5957  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
5958  for (j=jmin; j<=jmax; j++) {
5959  dy2 = VSQR(position[1] - hy*j);
5960  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
5961  else dz = 0.5*hzed;
5962  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
5963  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
5964  for (k=kmin; k<=kmax; k++) {
5965  dz2 = VSQR(k*hzed - position[2]);
5966  /* See if grid point is inside ivdw radius and set kappa
5967  * accordingly (do spline assignment here) */
5968  if ((dz2 + dy2 + dx2) <= rtot2) {
5969  gpos[0] = i*hx + xmin;
5970  gpos[1] = j*hy + ymin;
5971  gpos[2] = k*hzed + zmin;
5972 
5973  /* Select the correct function based on the surface definition
5974  * (now including the 7th order polynomial) */
5975  Vpmg_splineSelect(srfm,acc, gpos,thee->splineWin, irad, atom, tgrad);
5976 
5977  if (thee->pmgp->nonlin) {
5978  /* Nonlinear forces */
5979  fmag = 0.0;
5980  nchop = 0;
5981  for (m=0; m<nion; m++) {
5982  fmag += (thee->kappa[IJK(i,j,k)])*ionConc[m]*(Vcap_exp(-ionQ[m]*thee->u[IJK(i,j,k)], &ichop)-1.0)/ionstr;
5983  nchop += ichop;
5984  }
5985  /* if (nchop > 0) Vnm_print(2, "Vpmg_ibForece: Chopped EXP %d times!\n", nchop);*/
5986  force[0] += (zkappa2*fmag*tgrad[0]);
5987  force[1] += (zkappa2*fmag*tgrad[1]);
5988  force[2] += (zkappa2*fmag*tgrad[2]);
5989  } else {
5990  /* Use of bulk factor (zkappa2) OK here becuase
5991  * LPBE force approximation */
5992  /* NAB -- did we forget a kappa factor here??? */
5993  fmag = VSQR(thee->u[IJK(i,j,k)])*(thee->kappa[IJK(i,j,k)]);
5994  force[0] += (zkappa2*fmag*tgrad[0]);
5995  force[1] += (zkappa2*fmag*tgrad[1]);
5996  force[2] += (zkappa2*fmag*tgrad[2]);
5997  }
5998  }
5999  } /* k loop */
6000  } /* j loop */
6001  } /* i loop */
6002  }
6003  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
6004  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
6005  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
6006 
6007  return 1;
6008 }
6009 
6010 VPUBLIC int Vpmg_dbForce(Vpmg *thee, double *dbForce, int atomID,
6011  Vsurf_Meth srfm) {
6012 
6013  Vacc *acc;
6014  Vpbe *pbe;
6015  Vatom *atom;
6016 
6017  double *apos, position[3], arad, srad, hx, hy, hzed, izmagic, deps, depsi;
6018  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
6019  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
6020  double *u, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
6021  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
6022  double dHzijkm1[3];
6023  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
6024 
6025  VASSERT(thee != VNULL);
6026  if (!thee->filled) {
6027  Vnm_print(2, "Vpmg_dbForce: Need to callVpmg_fillco!\n");
6028  return 0;
6029  }
6030 
6031  acc = thee->pbe->acc;
6032  atom = Valist_getAtom(thee->pbe->alist, atomID);
6033  apos = Vatom_getPosition(atom);
6034  arad = Vatom_getRadius(atom);
6035  srad = Vpbe_getSolventRadius(thee->pbe);
6036 
6037  /* Reset force */
6038  dbForce[0] = 0.0;
6039  dbForce[1] = 0.0;
6040  dbForce[2] = 0.0;
6041 
6042  /* Check surface definition */
6043  if ((srfm != VSM_SPLINE) && (srfm!=VSM_SPLINE3) && (srfm!=VSM_SPLINE4)) {
6044  Vnm_print(2, "Vpmg_dbForce: Forces *must* be calculated with \
6045 spline-based surfaces!\n");
6046  Vnm_print(2, "Vpmg_dbForce: Skipping dielectric/apolar boundary \
6047 force calculation!\n");
6048  return 0;
6049  }
6050 
6051 
6052  /* If we aren't in the current position, then we're done */
6053  if (atom->partID == 0) return 1;
6054 
6055  /* Get PBE info */
6056  pbe = thee->pbe;
6057  acc = pbe->acc;
6058  epsp = Vpbe_getSoluteDiel(pbe);
6059  epsw = Vpbe_getSolventDiel(pbe);
6061  izmagic = 1.0/Vpbe_getZmagic(pbe);
6062 
6063  /* Mesh info */
6064  nx = thee->pmgp->nx;
6065  ny = thee->pmgp->ny;
6066  nz = thee->pmgp->nz;
6067  hx = thee->pmgp->hx;
6068  hy = thee->pmgp->hy;
6069  hzed = thee->pmgp->hzed;
6070  xlen = thee->pmgp->xlen;
6071  ylen = thee->pmgp->ylen;
6072  zlen = thee->pmgp->zlen;
6073  xmin = thee->pmgp->xmin;
6074  ymin = thee->pmgp->ymin;
6075  zmin = thee->pmgp->zmin;
6076  xmax = thee->pmgp->xmax;
6077  ymax = thee->pmgp->ymax;
6078  zmax = thee->pmgp->zmax;
6079  u = thee->u;
6080 
6081  /* Sanity check: there is no force if there is zero ionic strength */
6082  if (VABS(epsp-epsw) < VPMGSMALL) {
6083  Vnm_print(0, "Vpmg_dbForce: No force for uniform dielectric!\n");
6084  return 1;
6085  }
6086  deps = (epsw - epsp);
6087  depsi = 1.0/deps;
6088  rtot = (arad + thee->splineWin + srad);
6089 
6090  /* Make sure we're on the grid */
6091  /* Grid checking modified by Matteo Rotter */
6092  if ((apos[0]<=xmin + rtot) || (apos[0]>=xmax - rtot) || \
6093  (apos[1]<=ymin + rtot) || (apos[1]>=ymax - rtot) || \
6094  (apos[2]<=zmin + rtot) || (apos[2]>=zmax - rtot)) {
6095  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
6096  (thee->pmgp->bcfl != BCFL_MAP)) {
6097  Vnm_print(2, "Vpmg_dbForce: Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
6098  atomID, apos[0], apos[1], apos[2]);
6099  Vnm_print(2, "Vpmg_dbForce: xmin = %g, xmax = %g\n",
6100  xmin, xmax);
6101  Vnm_print(2, "Vpmg_dbForce: ymin = %g, ymax = %g\n",
6102  ymin, ymax);
6103  Vnm_print(2, "Vpmg_dbForce: zmin = %g, zmax = %g\n",
6104  zmin, zmax);
6105  }
6106  fflush(stderr);
6107  } else {
6108 
6109  /* Convert the atom position to grid reference frame */
6110  position[0] = apos[0] - xmin;
6111  position[1] = apos[1] - ymin;
6112  position[2] = apos[2] - zmin;
6113 
6114  /* Integrate over points within this atom's (inflated) radius */
6115  rtot2 = VSQR(rtot);
6116  dx = rtot/hx;
6117  imin = (int)floor((position[0]-rtot)/hx);
6118  if (imin < 1) {
6119  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6120  return 0;
6121  }
6122  imax = (int)ceil((position[0]+rtot)/hx);
6123  if (imax > (nx-2)) {
6124  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6125  return 0;
6126  }
6127  jmin = (int)floor((position[1]-rtot)/hy);
6128  if (jmin < 1) {
6129  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6130  return 0;
6131  }
6132  jmax = (int)ceil((position[1]+rtot)/hy);
6133  if (jmax > (ny-2)) {
6134  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6135  return 0;
6136  }
6137  kmin = (int)floor((position[2]-rtot)/hzed);
6138  if (kmin < 1) {
6139  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6140  return 0;
6141  }
6142  kmax = (int)ceil((position[2]+rtot)/hzed);
6143  if (kmax > (nz-2)) {
6144  Vnm_print(2, "Vpmg_dbForce: Atom %d off grid!\n", atomID);
6145  return 0;
6146  }
6147  for (i=imin; i<=imax; i++) {
6148  for (j=jmin; j<=jmax; j++) {
6149  for (k=kmin; k<=kmax; k++) {
6150  /* i,j,k */
6151  gpos[0] = (i+0.5)*hx + xmin;
6152  gpos[1] = j*hy + ymin;
6153  gpos[2] = k*hzed + zmin;
6154  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
6155 
6156  /* Select the correct function based on the surface definition
6157  * (now including the 7th order polynomial) */
6158  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHxijk);
6159  /*
6160  switch (srfm) {
6161  case VSM_SPLINE :
6162  Vacc_splineAccGradAtomNorm(acc, gpos, thee->splineWin, 0.,
6163  atom, dHxijk);
6164  break;
6165  case VSM_SPLINE4 :
6166  Vacc_splineAccGradAtomNorm4(acc, gpos, thee->splineWin, 0.,
6167  atom, dHxijk);
6168  break;
6169  default:
6170  Vnm_print(2, "Vpmg_dbnbForce: Unknown surface method.\n");
6171  return;
6172  }
6173  */
6174  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
6175  gpos[0] = i*hx + xmin;
6176  gpos[1] = (j+0.5)*hy + ymin;
6177  gpos[2] = k*hzed + zmin;
6178  Hyijk = (thee->epsy[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, dHyijk);
6183 
6184  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
6185  gpos[0] = i*hx + xmin;
6186  gpos[1] = j*hy + ymin;
6187  gpos[2] = (k+0.5)*hzed + zmin;
6188  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
6189 
6190  /* Select the correct function based on the surface definition
6191  * (now including the 7th order polynomial) */
6192  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHzijk);
6193 
6194  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
6195  /* i-1,j,k */
6196  gpos[0] = (i-0.5)*hx + xmin;
6197  gpos[1] = j*hy + ymin;
6198  gpos[2] = k*hzed + zmin;
6199  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
6200 
6201  /* Select the correct function based on the surface definition
6202  * (now including the 7th order polynomial) */
6203  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHxim1jk);
6204 
6205  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
6206  /* i,j-1,k */
6207  gpos[0] = i*hx + xmin;
6208  gpos[1] = (j-0.5)*hy + ymin;
6209  gpos[2] = k*hzed + zmin;
6210  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
6211 
6212  /* Select the correct function based on the surface definition
6213  * (now including the 7th order polynomial) */
6214  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHyijm1k);
6215 
6216  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
6217  /* i,j,k-1 */
6218  gpos[0] = i*hx + xmin;
6219  gpos[1] = j*hy + ymin;
6220  gpos[2] = (k-0.5)*hzed + zmin;
6221  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
6222 
6223  /* Select the correct function based on the surface definition
6224  * (now including the 7th order polynomial) */
6225  Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHzijkm1);
6226 
6227  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
6228  /* *** CALCULATE DIELECTRIC BOUNDARY FORCES *** */
6229  dbFmag = u[IJK(i,j,k)];
6230  tgrad[0] =
6231  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
6232  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
6233  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
6234  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
6235  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
6236  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
6237  tgrad[1] =
6238  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
6239  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
6240  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
6241  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
6242  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
6243  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
6244  tgrad[2] =
6245  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
6246  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
6247  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
6248  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
6249  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
6250  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
6251  dbForce[0] += (dbFmag*tgrad[0]);
6252  dbForce[1] += (dbFmag*tgrad[1]);
6253  dbForce[2] += (dbFmag*tgrad[2]);
6254 
6255  } /* k loop */
6256  } /* j loop */
6257  } /* i loop */
6258 
6259  dbForce[0] = -dbForce[0]*hx*hy*hzed*deps*0.5*izmagic;
6260  dbForce[1] = -dbForce[1]*hx*hy*hzed*deps*0.5*izmagic;
6261  dbForce[2] = -dbForce[2]*hx*hy*hzed*deps*0.5*izmagic;
6262  }
6263 
6264  return 1;
6265 }
6266 
6267 VPUBLIC int Vpmg_qfForce(Vpmg *thee, double *force, int atomID,
6268  Vchrg_Meth chgm) {
6269 
6270  double tforce[3];
6271 
6272  /* Reset force */
6273  force[0] = 0.0;
6274  force[1] = 0.0;
6275  force[2] = 0.0;
6276 
6277  /* Check surface definition */
6278  if (chgm != VCM_BSPL2) {
6279  Vnm_print(2, "Vpmg_qfForce: It is recommended that forces be \
6280 calculated with the\n");
6281  Vnm_print(2, "Vpmg_qfForce: cubic spline charge discretization \
6282 scheme\n");
6283  }
6284 
6285  switch (chgm) {
6286  case VCM_TRIL:
6287  qfForceSpline1(thee, tforce, atomID);
6288  break;
6289  case VCM_BSPL2:
6290  qfForceSpline2(thee, tforce, atomID);
6291  break;
6292  case VCM_BSPL4:
6293  qfForceSpline4(thee, tforce, atomID);
6294  break;
6295  default:
6296  Vnm_print(2, "Vpmg_qfForce: Undefined charge discretization \
6297 method (%d)!\n", chgm);
6298  Vnm_print(2, "Vpmg_qfForce: Forces not calculated!\n");
6299  return 0;
6300  }
6301 
6302  /* Assign forces */
6303  force[0] = tforce[0];
6304  force[1] = tforce[1];
6305  force[2] = tforce[2];
6306 
6307  return 1;
6308 }
6309 
6310 
6311 VPRIVATE void qfForceSpline1(Vpmg *thee, double *force, int atomID) {
6312 
6313  Vatom *atom;
6314 
6315  double *apos, position[3], hx, hy, hzed;
6316  double xmin, ymin, zmin, xmax, ymax, zmax;
6317  double dx, dy, dz;
6318  double *u, charge, ifloat, jfloat, kfloat;
6319  int nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
6320 
6321  VASSERT(thee != VNULL);
6322 
6323  atom = Valist_getAtom(thee->pbe->alist, atomID);
6324  apos = Vatom_getPosition(atom);
6325  charge = Vatom_getCharge(atom);
6326 
6327  /* Reset force */
6328  force[0] = 0.0;
6329  force[1] = 0.0;
6330  force[2] = 0.0;
6331 
6332  /* If we aren't in the current position, then we're done */
6333  if (atom->partID == 0) return;
6334 
6335  /* Mesh info */
6336  nx = thee->pmgp->nx;
6337  ny = thee->pmgp->ny;
6338  nz = thee->pmgp->nz;
6339  hx = thee->pmgp->hx;
6340  hy = thee->pmgp->hy;
6341  hzed = thee->pmgp->hzed;
6342  xmin = thee->pmgp->xmin;
6343  ymin = thee->pmgp->ymin;
6344  zmin = thee->pmgp->zmin;
6345  xmax = thee->pmgp->xmax;
6346  ymax = thee->pmgp->ymax;
6347  zmax = thee->pmgp->zmax;
6348  u = thee->u;
6349 
6350  /* Make sure we're on the grid */
6351  if ((apos[0]<=xmin) || (apos[0]>=xmax) || (apos[1]<=ymin) || \
6352  (apos[1]>=ymax) || (apos[2]<=zmin) || (apos[2]>=zmax)) {
6353  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
6354  (thee->pmgp->bcfl != BCFL_MAP)) {
6355  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]);
6356  Vnm_print(2, "Vpmg_qfForce: xmin = %g, xmax = %g\n", xmin, xmax);
6357  Vnm_print(2, "Vpmg_qfForce: ymin = %g, ymax = %g\n", ymin, ymax);
6358  Vnm_print(2, "Vpmg_qfForce: zmin = %g, zmax = %g\n", zmin, zmax);
6359  }
6360  fflush(stderr);
6361  } else {
6362 
6363  /* Convert the atom position to grid coordinates */
6364  position[0] = apos[0] - xmin;
6365  position[1] = apos[1] - ymin;
6366  position[2] = apos[2] - zmin;
6367  ifloat = position[0]/hx;
6368  jfloat = position[1]/hy;
6369  kfloat = position[2]/hzed;
6370  ihi = (int)ceil(ifloat);
6371  ilo = (int)floor(ifloat);
6372  jhi = (int)ceil(jfloat);
6373  jlo = (int)floor(jfloat);
6374  khi = (int)ceil(kfloat);
6375  klo = (int)floor(kfloat);
6376  VASSERT((ihi < nx) && (ihi >=0));
6377  VASSERT((ilo < nx) && (ilo >=0));
6378  VASSERT((jhi < ny) && (jhi >=0));
6379  VASSERT((jlo < ny) && (jlo >=0));
6380  VASSERT((khi < nz) && (khi >=0));
6381  VASSERT((klo < nz) && (klo >=0));
6382  dx = ifloat - (double)(ilo);
6383  dy = jfloat - (double)(jlo);
6384  dz = kfloat - (double)(klo);
6385 
6386 
6387 #if 0
6388  Vnm_print(1, "Vpmg_qfForce: (DEBUG) u ~ %g\n",
6389  dx *dy *dz *u[IJK(ihi,jhi,khi)]
6390  +dx *dy *(1-dz)*u[IJK(ihi,jhi,klo)]
6391  +dx *(1-dy)*dz *u[IJK(ihi,jlo,khi)]
6392  +dx *(1-dy)*(1-dz)*u[IJK(ihi,jlo,klo)]
6393  +(1-dx)*dy *dz *u[IJK(ilo,jhi,khi)]
6394  +(1-dx)*dy *(1-dz)*u[IJK(ilo,jhi,klo)]
6395  +(1-dx)*(1-dy)*dz *u[IJK(ilo,jlo,khi)]
6396  +(1-dx)*(1-dy)*(1-dz)*u[IJK(ilo,jlo,klo)]);
6397 #endif
6398 
6399 
6400  if ((dx > VPMGSMALL) && (VABS(1.0-dx) > VPMGSMALL)) {
6401  force[0] =
6402  -charge*(dy *dz *u[IJK(ihi,jhi,khi)]
6403  + dy *(1-dz)*u[IJK(ihi,jhi,klo)]
6404  + (1-dy)*dz *u[IJK(ihi,jlo,khi)]
6405  + (1-dy)*(1-dz)*u[IJK(ihi,jlo,klo)]
6406  - dy *dz *u[IJK(ilo,jhi,khi)]
6407  - dy *(1-dz)*u[IJK(ilo,jhi,klo)]
6408  - (1-dy)*dz *u[IJK(ilo,jlo,khi)]
6409  - (1-dy)*(1-dz)*u[IJK(ilo,jlo,klo)])/hx;
6410  } else {
6411  force[0] = 0;
6412  Vnm_print(0,
6413  "Vpmg_qfForce: Atom %d on x gridline; zero x-force\n", atomID);
6414  }
6415  if ((dy > VPMGSMALL) && (VABS(1.0-dy) > VPMGSMALL)) {
6416  force[1] =
6417  -charge*(dx *dz *u[IJK(ihi,jhi,khi)]
6418  + dx *(1-dz)*u[IJK(ihi,jhi,klo)]
6419  - dx *dz *u[IJK(ihi,jlo,khi)]
6420  - dx *(1-dz)*u[IJK(ihi,jlo,klo)]
6421  + (1-dx)*dz *u[IJK(ilo,jhi,khi)]
6422  + (1-dx)*(1-dz)*u[IJK(ilo,jhi,klo)]
6423  - (1-dx)*dz *u[IJK(ilo,jlo,khi)]
6424  - (1-dx)*(1-dz)*u[IJK(ilo,jlo,klo)])/hy;
6425  } else {
6426  force[1] = 0;
6427  Vnm_print(0,
6428  "Vpmg_qfForce: Atom %d on y gridline; zero y-force\n", atomID);
6429  }
6430  if ((dz > VPMGSMALL) && (VABS(1.0-dz) > VPMGSMALL)) {
6431  force[2] =
6432  -charge*(dy *dx *u[IJK(ihi,jhi,khi)]
6433  - dy *dx *u[IJK(ihi,jhi,klo)]
6434  + (1-dy)*dx *u[IJK(ihi,jlo,khi)]
6435  - (1-dy)*dx *u[IJK(ihi,jlo,klo)]
6436  + dy *(1-dx)*u[IJK(ilo,jhi,khi)]
6437  - dy *(1-dx)*u[IJK(ilo,jhi,klo)]
6438  + (1-dy)*(1-dx)*u[IJK(ilo,jlo,khi)]
6439  - (1-dy)*(1-dx)*u[IJK(ilo,jlo,klo)])/hzed;
6440  } else {
6441  force[2] = 0;
6442  Vnm_print(0,
6443  "Vpmg_qfForce: Atom %d on z gridline; zero z-force\n", atomID);
6444  }
6445  }
6446 }
6447 
6448 VPRIVATE void qfForceSpline2(Vpmg *thee, double *force, int atomID) {
6449 
6450  Vatom *atom;
6451 
6452  double *apos, position[3], hx, hy, hzed;
6453  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
6454  double mx, my, mz, dmx, dmy, dmz;
6455  double *u, charge, ifloat, jfloat, kfloat;
6456  int nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1;
6457  int kp1, kp2, ii, jj, kk;
6458 
6459  VASSERT(thee != VNULL);
6460 
6461  atom = Valist_getAtom(thee->pbe->alist, atomID);
6462  apos = Vatom_getPosition(atom);
6463  charge = Vatom_getCharge(atom);
6464 
6465  /* Reset force */
6466  force[0] = 0.0;
6467  force[1] = 0.0;
6468  force[2] = 0.0;
6469 
6470  /* If we aren't in the current position, then we're done */
6471  if (atom->partID == 0) return;
6472 
6473  /* Mesh info */
6474  nx = thee->pmgp->nx;
6475  ny = thee->pmgp->ny;
6476  nz = thee->pmgp->nz;
6477  hx = thee->pmgp->hx;
6478  hy = thee->pmgp->hy;
6479  hzed = thee->pmgp->hzed;
6480  xlen = thee->pmgp->xlen;
6481  ylen = thee->pmgp->ylen;
6482  zlen = thee->pmgp->zlen;
6483  xmin = thee->pmgp->xmin;
6484  ymin = thee->pmgp->ymin;
6485  zmin = thee->pmgp->zmin;
6486  xmax = thee->pmgp->xmax;
6487  ymax = thee->pmgp->ymax;
6488  zmax = thee->pmgp->zmax;
6489  u = thee->u;
6490 
6491  /* Make sure we're on the grid */
6492  if ((apos[0]<=(xmin+hx)) || (apos[0]>=(xmax-hx)) \
6493  || (apos[1]<=(ymin+hy)) || (apos[1]>=(ymax-hy)) \
6494  || (apos[2]<=(zmin+hzed)) || (apos[2]>=(zmax-hzed))) {
6495  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
6496  (thee->pmgp->bcfl != BCFL_MAP)) {
6497  Vnm_print(2, "qfForceSpline2: Atom #%d off the mesh \
6498  (ignoring)\n", atomID);
6499  }
6500  fflush(stderr);
6501 
6502  } else {
6503 
6504  /* Convert the atom position to grid coordinates */
6505  position[0] = apos[0] - xmin;
6506  position[1] = apos[1] - ymin;
6507  position[2] = apos[2] - zmin;
6508  ifloat = position[0]/hx;
6509  jfloat = position[1]/hy;
6510  kfloat = position[2]/hzed;
6511  ip1 = (int)ceil(ifloat);
6512  ip2 = ip1 + 1;
6513  im1 = (int)floor(ifloat);
6514  im2 = im1 - 1;
6515  jp1 = (int)ceil(jfloat);
6516  jp2 = jp1 + 1;
6517  jm1 = (int)floor(jfloat);
6518  jm2 = jm1 - 1;
6519  kp1 = (int)ceil(kfloat);
6520  kp2 = kp1 + 1;
6521  km1 = (int)floor(kfloat);
6522  km2 = km1 - 1;
6523 
6524  /* This step shouldn't be necessary, but it saves nasty debugging
6525  * later on if something goes wrong */
6526  ip2 = VMIN2(ip2,nx-1);
6527  ip1 = VMIN2(ip1,nx-1);
6528  im1 = VMAX2(im1,0);
6529  im2 = VMAX2(im2,0);
6530  jp2 = VMIN2(jp2,ny-1);
6531  jp1 = VMIN2(jp1,ny-1);
6532  jm1 = VMAX2(jm1,0);
6533  jm2 = VMAX2(jm2,0);
6534  kp2 = VMIN2(kp2,nz-1);
6535  kp1 = VMIN2(kp1,nz-1);
6536  km1 = VMAX2(km1,0);
6537  km2 = VMAX2(km2,0);
6538 
6539 
6540  for (ii=im2; ii<=ip2; ii++) {
6541  mx = bspline2(VFCHI(ii,ifloat));
6542  dmx = dbspline2(VFCHI(ii,ifloat));
6543  for (jj=jm2; jj<=jp2; jj++) {
6544  my = bspline2(VFCHI(jj,jfloat));
6545  dmy = dbspline2(VFCHI(jj,jfloat));
6546  for (kk=km2; kk<=kp2; kk++) {
6547  mz = bspline2(VFCHI(kk,kfloat));
6548  dmz = dbspline2(VFCHI(kk,kfloat));
6549 
6550  force[0] += (charge*dmx*my*mz*u[IJK(ii,jj,kk)])/hx;
6551  force[1] += (charge*mx*dmy*mz*u[IJK(ii,jj,kk)])/hy;
6552  force[2] += (charge*mx*my*dmz*u[IJK(ii,jj,kk)])/hzed;
6553 
6554  }
6555  }
6556  }
6557 
6558  }
6559 }
6560 
6561 VPRIVATE void qfForceSpline4(Vpmg *thee, double *force, int atomID) {
6562 
6563  Vatom *atom;
6564  double f, c, *u, *apos, position[3];
6565 
6566  /* Grid variables */
6567  int nx,ny,nz;
6568  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
6569  double hx, hy, hzed, ifloat, jfloat, kfloat;
6570 
6571  /* B-spline weights */
6572  double mx, my, mz, dmx, dmy, dmz;
6573  double mi, mj, mk;
6574 
6575  /* Loop indeces */
6576  int i, j, k, ii, jj, kk;
6577  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
6578 
6579  /* field */
6580  double e[3];
6581 
6582  VASSERT(thee != VNULL);
6583  VASSERT(thee->filled);
6584 
6585  atom = Valist_getAtom(thee->pbe->alist, atomID);
6586  apos = Vatom_getPosition(atom);
6587  c = Vatom_getCharge(atom);
6588 
6589  for (i=0;i<3;i++){
6590  e[i] = 0.0;
6591  }
6592 
6593  /* Mesh info */
6594  nx = thee->pmgp->nx;
6595  ny = thee->pmgp->ny;
6596  nz = thee->pmgp->nz;
6597  hx = thee->pmgp->hx;
6598  hy = thee->pmgp->hy;
6599  hzed = thee->pmgp->hzed;
6600  xlen = thee->pmgp->xlen;
6601  ylen = thee->pmgp->ylen;
6602  zlen = thee->pmgp->zlen;
6603  xmin = thee->pmgp->xmin;
6604  ymin = thee->pmgp->ymin;
6605  zmin = thee->pmgp->zmin;
6606  xmax = thee->pmgp->xmax;
6607  ymax = thee->pmgp->ymax;
6608  zmax = thee->pmgp->zmax;
6609  u = thee->u;
6610 
6611  /* Make sure we're on the grid */
6612  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
6613  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
6614  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
6615  Vnm_print(2, "qfForceSpline4: Atom off the mesh \
6616  (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
6617  fflush(stderr);
6618  } else {
6619 
6620  /* Convert the atom position to grid coordinates */
6621  position[0] = apos[0] - xmin;
6622  position[1] = apos[1] - ymin;
6623  position[2] = apos[2] - zmin;
6624  ifloat = position[0]/hx;
6625  jfloat = position[1]/hy;
6626  kfloat = position[2]/hzed;
6627  ip1 = (int)ceil(ifloat);
6628  ip2 = ip1 + 2;
6629  im1 = (int)floor(ifloat);
6630  im2 = im1 - 2;
6631  jp1 = (int)ceil(jfloat);
6632  jp2 = jp1 + 2;
6633  jm1 = (int)floor(jfloat);
6634  jm2 = jm1 - 2;
6635  kp1 = (int)ceil(kfloat);
6636  kp2 = kp1 + 2;
6637  km1 = (int)floor(kfloat);
6638  km2 = km1 - 2;
6639 
6640  /* This step shouldn't be necessary, but it saves nasty debugging
6641  * later on if something goes wrong */
6642  ip2 = VMIN2(ip2,nx-1);
6643  ip1 = VMIN2(ip1,nx-1);
6644  im1 = VMAX2(im1,0);
6645  im2 = VMAX2(im2,0);
6646  jp2 = VMIN2(jp2,ny-1);
6647  jp1 = VMIN2(jp1,ny-1);
6648  jm1 = VMAX2(jm1,0);
6649  jm2 = VMAX2(jm2,0);
6650  kp2 = VMIN2(kp2,nz-1);
6651  kp1 = VMIN2(kp1,nz-1);
6652  km1 = VMAX2(km1,0);
6653  km2 = VMAX2(km2,0);
6654 
6655  for (ii=im2; ii<=ip2; ii++) {
6656  mi = VFCHI4(ii,ifloat);
6657  mx = bspline4(mi);
6658  dmx = dbspline4(mi);
6659  for (jj=jm2; jj<=jp2; jj++) {
6660  mj = VFCHI4(jj,jfloat);
6661  my = bspline4(mj);
6662  dmy = dbspline4(mj);
6663  for (kk=km2; kk<=kp2; kk++) {
6664  mk = VFCHI4(kk,kfloat);
6665  mz = bspline4(mk);
6666  dmz = dbspline4(mk);
6667  f = u[IJK(ii,jj,kk)];
6668  /* Field */
6669  e[0] += f*dmx*my*mz/hx;
6670  e[1] += f*mx*dmy*mz/hy;
6671  e[2] += f*mx*my*dmz/hzed;
6672  }
6673  }
6674  }
6675  }
6676 
6677  /* Monopole Force */
6678  force[0] = e[0]*c;
6679  force[1] = e[1]*c;
6680  force[2] = e[2]*c;
6681 
6682 }
6683 
6684 VPRIVATE void markFrac(
6685  double rtot, double *tpos,
6686  int nx, int ny, int nz,
6687  double hx, double hy, double hzed,
6688  double xmin, double ymin, double zmin,
6689  double *xarray, double *yarray, double *zarray) {
6690 
6691  int i, j, k, imin, imax, jmin, jmax, kmin, kmax;
6692  double dx, dx2, dy, dy2, dz, dz2, a000, a001, a010, a100, r2;
6693  double x, xp, xm, y, yp, ym, zp, z, zm, xspan, yspan, zspan;
6694  double rtot2, pos[3];
6695 
6696  /* Convert to grid reference frame */
6697  pos[0] = tpos[0] - xmin;
6698  pos[1] = tpos[1] - ymin;
6699  pos[2] = tpos[2] - zmin;
6700 
6701  rtot2 = VSQR(rtot);
6702 
6703  xspan = rtot + 2*hx;
6704  imin = VMAX2(0, (int)ceil((pos[0] - xspan)/hx));
6705  imax = VMIN2(nx-1, (int)floor((pos[0] + xspan)/hx));
6706  for (i=imin; i<=imax; i++) {
6707  x = hx*i;
6708  dx2 = VSQR(pos[0] - x);
6709  if (rtot2 > dx2) {
6710  yspan = VSQRT(rtot2 - dx2) + 2*hy;
6711  } else {
6712  yspan = 2*hy;
6713  }
6714  jmin = VMAX2(0,(int)ceil((pos[1] - yspan)/hy));
6715  jmax = VMIN2(ny-1,(int)floor((pos[1] + yspan)/hy));
6716  for (j=jmin; j<=jmax; j++) {
6717  y = hy*j;
6718  dy2 = VSQR(pos[1] - y);
6719  if (rtot2 > (dx2+dy2)) {
6720  zspan = VSQRT(rtot2-dx2-dy2) + 2*hzed;
6721  } else {
6722  zspan = 2*hzed;
6723  }
6724  kmin = VMAX2(0,(int)ceil((pos[2] - zspan)/hzed));
6725  kmax = VMIN2(nz-1,(int)floor((pos[2] + zspan)/hzed));
6726  for (k=kmin; k<=kmax; k++) {
6727  z = hzed*k;
6728  dz2 = VSQR(pos[2] - z);
6729 
6730  r2 = dx2 + dy2 + dz2;
6731 
6732  /* We need to determine the inclusion value a000 at (i,j,k) */
6733  if (r2 < rtot2) a000 = 1.0;
6734  else a000 = 0.0;
6735 
6736  /* We need to evaluate the values of x which intersect the
6737  * sphere and determine if these are in the interval
6738  * [(i,j,k), (i+1,j,k)] */
6739  if (r2 < (rtot2 - hx*hx)) a100 = 1.0;
6740  else if (r2 > (rtot2 + hx*hx)) a100 = 0.0;
6741  else if (rtot2 > (dy2 + dz2)) {
6742  dx = VSQRT(rtot2 - dy2 - dz2);
6743  xm = pos[0] - dx;
6744  xp = pos[0] + dx;
6745  if ((xm < x+hx) && (xm > x)) {
6746  a100 = (xm - x)/hx;
6747  } else if ((xp < x+hx) && (xp > x)) {
6748  a100 = (xp - x)/hx;
6749  }
6750  } else a100 = 0.0;
6751 
6752  /* We need to evaluate the values of y which intersect the
6753  * sphere and determine if these are in the interval
6754  * [(i,j,k), (i,j+1,k)] */
6755  if (r2 < (rtot2 - hy*hy)) a010 = 1.0;
6756  else if (r2 > (rtot2 + hy*hy)) a010 = 0.0;
6757  else if (rtot2 > (dx2 + dz2)) {
6758  dy = VSQRT(rtot2 - dx2 - dz2);
6759  ym = pos[1] - dy;
6760  yp = pos[1] + dy;
6761  if ((ym < y+hy) && (ym > y)) {
6762  a010 = (ym - y)/hy;
6763  } else if ((yp < y+hy) && (yp > y)) {
6764  a010 = (yp - y)/hy;
6765  }
6766  } else a010 = 0.0;
6767 
6768  /* We need to evaluate the values of y which intersect the
6769  * sphere and determine if these are in the interval
6770  * [(i,j,k), (i,j,k+1)] */
6771  if (r2 < (rtot2 - hzed*hzed)) a001 = 1.0;
6772  else if (r2 > (rtot2 + hzed*hzed)) a001 = 0.0;
6773  else if (rtot2 > (dx2 + dy2)) {
6774  dz = VSQRT(rtot2 - dx2 - dy2);
6775  zm = pos[2] - dz;
6776  zp = pos[2] + dz;
6777  if ((zm < z+hzed) && (zm > z)) {
6778  a001 = (zm - z)/hzed;
6779  } else if ((zp < z+hzed) && (zp > z)) {
6780  a001 = (zp - z)/hzed;
6781  }
6782  } else a001 = 0.0;
6783 
6784  if (a100 < xarray[IJK(i,j,k)]) xarray[IJK(i,j,k)] = a100;
6785  if (a010 < yarray[IJK(i,j,k)]) yarray[IJK(i,j,k)] = a010;
6786  if (a001 < zarray[IJK(i,j,k)]) zarray[IJK(i,j,k)] = a001;
6787 
6788  } /* k loop */
6789  } /* j loop */
6790  } /* i loop */
6791 }
6792 
6793 /*
6794 
6795  NOTE: This is the original version of the markSphere function. It's in here
6796  for reference and in case a reversion to the original code is needed.
6797  D. Gohara (2/14/08)
6798  */
6799 /*
6800 VPRIVATE void markSphere(
6801  double rtot, double *tpos,
6802  int nx, int ny, int nz,
6803  double hx, double hy, double hzed,
6804  double xmin, double ymin, double zmin,
6805  double *array, double markVal) {
6806 
6807  int i, j, k, imin, imax, jmin, jmax, kmin, kmax;
6808  double dx, dx2, dy, dy2, dz, dz2;
6809  double rtot2, pos[3];
6810 
6811  // Convert to grid reference frame
6812  pos[0] = tpos[0] - xmin;
6813  pos[1] = tpos[1] - ymin;
6814  pos[2] = tpos[2] - zmin;
6815 
6816  rtot2 = VSQR(rtot);
6817 
6818  dx = rtot + 0.5*hx;
6819  imin = VMAX2(0,(int)ceil((pos[0] - dx)/hx));
6820  imax = VMIN2(nx-1,(int)floor((pos[0] + dx)/hx));
6821  for (i=imin; i<=imax; i++) {
6822  dx2 = VSQR(pos[0] - hx*i);
6823  if (rtot2 > dx2) {
6824  dy = VSQRT(rtot2 - dx2) + 0.5*hy;
6825  } else {
6826  dy = 0.5*hy;
6827  }
6828  jmin = VMAX2(0,(int)ceil((pos[1] - dy)/hy));
6829  jmax = VMIN2(ny-1,(int)floor((pos[1] + dy)/hy));
6830  for (j=jmin; j<=jmax; j++) {
6831  dy2 = VSQR(pos[1] - hy*j);
6832  if (rtot2 > (dx2+dy2)) {
6833  dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
6834  } else {
6835  dz = 0.5*hzed;
6836  }
6837  kmin = VMAX2(0,(int)ceil((pos[2] - dz)/hzed));
6838  kmax = VMIN2(nz-1,(int)floor((pos[2] + dz)/hzed));
6839  for (k=kmin; k<=kmax; k++) {
6840  dz2 = VSQR(k*hzed - pos[2]);
6841  if ((dz2 + dy2 + dx2) <= rtot2) {
6842  array[IJK(i,j,k)] = markVal;
6843  }
6844  } // k loop
6845  } // j loop
6846  } // i loop
6847 }
6848 */
6849 VPRIVATE void markSphere(double rtot, double *tpos,
6850  int nx, int ny, int nz,
6851  double hx, double hy, double hz,
6852  double xmin, double ymin, double zmin,
6853  double *array, double markVal) {
6854 
6855  int i, j, k;
6856  double fi,fj,fk;
6857  int imin, imax;
6858  int jmin, jmax;
6859  int kmin, kmax;
6860  double dx2, dy2, dz2;
6861  double xrange, yrange, zrange;
6862  double rtot2, posx, posy, posz;
6863 
6864  /* Convert to grid reference frame */
6865  posx = tpos[0] - xmin;
6866  posy = tpos[1] - ymin;
6867  posz = tpos[2] - zmin;
6868 
6869  rtot2 = VSQR(rtot);
6870 
6871  xrange = rtot + 0.5 * hx;
6872  yrange = rtot + 0.5 * hy;
6873  zrange = rtot + 0.5 * hz;
6874 
6875  imin = VMAX2(0, (int)ceil((posx - xrange)/hx));
6876  jmin = VMAX2(0, (int)ceil((posy - yrange)/hy));
6877  kmin = VMAX2(0, (int)ceil((posz - zrange)/hz));
6878 
6879  imax = VMIN2(nx-1, (int)floor((posx + xrange)/hx));
6880  jmax = VMIN2(ny-1, (int)floor((posy + yrange)/hy));
6881  kmax = VMIN2(nz-1, (int)floor((posz + zrange)/hz));
6882 
6883  for (i=imin,fi=imin; i<=imax; i++, fi+=1.) {
6884  dx2 = VSQR(posx - hx*fi);
6885  for (j=jmin,fj=jmin; j<=jmax; j++, fj+=1.) {
6886  dy2 = VSQR(posy - hy*fj);
6887  if((dx2 + dy2) > rtot2) continue;
6888  for (k=kmin, fk=kmin; k<=kmax; k++, fk+=1.) {
6889  dz2 = VSQR(posz - hz*fk);
6890  if ((dz2 + dy2 + dx2) <= rtot2) {
6891  array[IJK(i,j,k)] = markVal;
6892  }
6893  }
6894  }
6895  }
6896 }
6897 
6898 VPRIVATE void zlapSolve(
6899  Vpmg *thee,
6900  double **solution,
6901  double **source,
6902  double **work1
6903  ) {
6904 
6905  /* NOTE: this is an incredibly inefficient algorithm. The next
6906  * improvement is to focus on only non-zero entries in the source term.
6907  * The best improvement is to use a fast sine transform */
6908 
6909  int n, nx, ny, nz, i, j, k, kx, ky, kz;
6910  double hx, hy, hzed, wx, wy, wz, xlen, ylen, zlen;
6911  double phix, phixp1, phixm1, phiy, phiym1, phiyp1, phiz, phizm1, phizp1;
6912  double norm, coef, proj, eigx, eigy, eigz;
6913  double ihx2, ihy2, ihzed2;
6914  double *u, *f, *phi;
6915 
6916  /* Snarf grid parameters */
6917  nx = thee->pmgp->nx;
6918  ny = thee->pmgp->ny;
6919  nz = thee->pmgp->nz;
6920  n = nx*ny*nz;
6921  hx = thee->pmgp->hx;
6922  ihx2 = 1.0/hx/hx;
6923  hy = thee->pmgp->hy;
6924  ihy2 = 1.0/hy/hy;
6925  hzed = thee->pmgp->hzed;
6926  ihzed2 = 1.0/hzed/hzed;
6927  xlen = thee->pmgp->xlen;
6928  ylen = thee->pmgp->ylen;
6929  zlen = thee->pmgp->zlen;
6930 
6931  /* Set solution and source array pointers */
6932  u = *solution;
6933  f = *source;
6934  phi = *work1;
6935 
6936  /* Zero out the solution vector */
6937  for (i=0; i<n; i++) thee->u[i] = 0.0;
6938 
6939  /* Iterate through the wavenumbers */
6940  for (kx=1; kx<(nx-1); kx++) {
6941 
6942  wx = (VPI*(double)kx)/((double)nx - 1.0);
6943  eigx = 2.0*ihx2*(1.0 - cos(wx));
6944 
6945  for (ky=1; ky<(ny-1); ky++) {
6946 
6947  wy = (VPI*(double)ky)/((double)ny - 1.0);
6948  eigy = 2.0*ihy2*(1.0 - cos(wy));
6949 
6950  for (kz=1; kz<(nz-1); kz++) {
6951 
6952  wz = (VPI*(double)kz)/((double)nz - 1.0);
6953  eigz = 2.0*ihzed2*(1.0 - cos(wz));
6954 
6955  /* Calculate the basis function.
6956  * We could calculate each basis function as
6957  * phix(i) = sin(wx*i)
6958  * phiy(j) = sin(wy*j)
6959  * phiz(k) = sin(wz*k)
6960  * However, this is likely to be very expensive.
6961  * Therefore, we can use the fact that
6962  * phix(i+1) = (2-hx*hx*eigx)*phix(i) - phix(i-1)
6963  * */
6964  for (i=1; i<(nx-1); i++) {
6965  if (i == 1) {
6966  phix = sin(wx*(double)i);
6967  phixm1 = 0.0;
6968  } else {
6969  phixp1 = (2.0-hx*hx*eigx)*phix - phixm1;
6970  phixm1 = phix;
6971  phix = phixp1;
6972  }
6973  /* phix = sin(wx*(double)i); */
6974  for (j=1; j<(ny-1); j++) {
6975  if (j == 1) {
6976  phiy = sin(wy*(double)j);
6977  phiym1 = 0.0;
6978  } else {
6979  phiyp1 = (2.0-hy*hy*eigy)*phiy - phiym1;
6980  phiym1 = phiy;
6981  phiy = phiyp1;
6982  }
6983  /* phiy = sin(wy*(double)j); */
6984  for (k=1; k<(nz-1); k++) {
6985  if (k == 1) {
6986  phiz = sin(wz*(double)k);
6987  phizm1 = 0.0;
6988  } else {
6989  phizp1 = (2.0-hzed*hzed*eigz)*phiz - phizm1;
6990  phizm1 = phiz;
6991  phiz = phizp1;
6992  }
6993  /* phiz = sin(wz*(double)k); */
6994 
6995  phi[IJK(i,j,k)] = phix*phiy*phiz;
6996 
6997  }
6998  }
6999  }
7000 
7001  /* Calculate the projection of the source function on this
7002  * basis function */
7003  proj = 0.0;
7004  for (i=1; i<(nx-1); i++) {
7005  for (j=1; j<(ny-1); j++) {
7006  for (k=1; k<(nz-1); k++) {
7007 
7008  proj += f[IJK(i,j,k)]*phi[IJK(i,j,k)];
7009 
7010  } /* k loop */
7011  } /* j loop */
7012  } /* i loop */
7013 
7014  /* Assemble the coefficient to weight the contribution of this
7015  * basis function to the solution */
7016  /* The first contribution is the projection */
7017  coef = proj;
7018  /* The second contribution is the eigenvalue */
7019  coef = coef/(eigx + eigy + eigz);
7020  /* The third contribution is the normalization factor */
7021  coef = (8.0/xlen/ylen/zlen)*coef;
7022  /* The fourth contribution is from scaling the diagonal */
7023  /* coef = hx*hy*hzed*coef; */
7024 
7025  /* Evaluate the basis function at each grid point */
7026  for (i=1; i<(nx-1); i++) {
7027  for (j=1; j<(ny-1); j++) {
7028  for (k=1; k<(nz-1); k++) {
7029 
7030  u[IJK(i,j,k)] += coef*phi[IJK(i,j,k)];
7031 
7032  } /* k loop */
7033  } /* j loop */
7034  } /* i loop */
7035 
7036  } /* kz loop */
7037  } /* ky loop */
7038  } /* kx loop */
7039 
7040 }
7041 
7042 VPUBLIC int Vpmg_solveLaplace(Vpmg *thee) {
7043 
7044  int i, j, k, ijk, nx, ny, nz, n, dilo, dihi, djlo, djhi, dklo, dkhi;
7045  double hx, hy, hzed, epsw, iepsw, scal, scalx, scaly, scalz;
7046 
7047  nx = thee->pmgp->nx;
7048  ny = thee->pmgp->ny;
7049  nz = thee->pmgp->nz;
7050  n = nx*ny*nz;
7051  hx = thee->pmgp->hx;
7052  hy = thee->pmgp->hy;
7053  hzed = thee->pmgp->hzed;
7054  epsw = Vpbe_getSolventDiel(thee->pbe);
7055  iepsw = 1.0/epsw;
7056  scal = hx*hy*hzed;
7057  scalx = hx*hy/hzed;
7058  scaly = hx*hzed/hy;
7059  scalz = hx*hy/hzed;
7060 
7061  if (!(thee->filled)) {
7062  Vnm_print(2, "Vpmg_solve: Need to call Vpmg_fillco()!\n");
7063  return 0;
7064  }
7065 
7066  /* Load boundary conditions into the RHS array */
7067  for (i=1; i<(nx-1); i++) {
7068 
7069  if (i == 1) dilo = 1;
7070  else dilo = 0;
7071  if (i == nx-2) dihi = 1;
7072  else dihi = 0;
7073 
7074  for (j=1; j<(ny-1); j++) {
7075 
7076  if (j == 1) djlo = 1;
7077  else djlo = 0;
7078  if (j == ny-2) djhi = 1;
7079  else djhi = 0;
7080 
7081  for (k=1; k<(nz-1); k++) {
7082 
7083  if (k == 1) dklo = 1;
7084  else dklo = 0;
7085  if (k == nz-2) dkhi = 1;
7086  else dkhi = 0;
7087 
7089  thee->fcf[IJK(i,j,k)] = \
7090  iepsw*scal*thee->charge[IJK(i,j,k)] \
7091  + dilo*scalx*thee->gxcf[IJKx(j,k,0)] \
7092  + dihi*scalx*thee->gxcf[IJKx(j,k,1)] \
7093  + djlo*scaly*thee->gycf[IJKy(i,k,0)] \
7094  + djhi*scaly*thee->gycf[IJKy(i,k,1)] \
7095  + dklo*scalz*thee->gzcf[IJKz(i,j,0)] \
7096  + dkhi*scalz*thee->gzcf[IJKz(i,j,1)] ;
7097 
7098  }
7099  }
7100  }
7101 
7102  /* Solve */
7103  zlapSolve( thee, &(thee->u), &(thee->fcf), &(thee->tcf) );
7104 
7105  /* Add boundary conditions to solution */
7106  /* i faces */
7107  for (j=0; j<ny; j++) {
7108  for (k=0; k<nz; k++) {
7109  thee->u[IJK(0,j,k)] = thee->gxcf[IJKx(j,k,0)];
7110  thee->u[IJK(nx-1,j,k)] = thee->gycf[IJKx(j,k,1)];
7111  }
7112  }
7113  /* j faces */
7114  for (i=0; i<nx; i++) {
7115  for (k=0; k<nz; k++) {
7116  thee->u[IJK(i,0,k)] = thee->gycf[IJKy(i,k,0)];
7117  thee->u[IJK(i,ny-1,k)] = thee->gycf[IJKy(i,k,1)];
7118  }
7119  }
7120  /* k faces */
7121  for (i=0; i<nx; i++) {
7122  for (j=0; j<ny; j++) {
7123  thee->u[IJK(i,j,0)] = thee->gzcf[IJKz(i,j,0)];
7124  thee->u[IJK(i,j,nz-1)] = thee->gzcf[IJKz(i,j,1)];
7125  }
7126  }
7127 
7128  return 1;
7129 
7130 }
7131 
7132 VPRIVATE double VFCHI4(int i, double f) {
7133  return (2.5+((double)(i)-(f)));
7134 }
7135 
7136 VPRIVATE double bspline4(double x) {
7137 
7138  double m, m2;
7139  static double one6 = 1.0/6.0;
7140  static double one8 = 1.0/8.0;
7141  static double one24 = 1.0/24.0;
7142  static double thirteen24 = 13.0/24.0;
7143  static double fourtyseven24 = 47.0/24.0;
7144  static double seventeen24 = 17.0/24.0;
7145 
7146  if ((x > 0.0) && (x <= 1.0)){
7147  m = x*x;
7148  return one24*m*m;
7149  } else if ((x > 1.0) && (x <= 2.0)){
7150  m = x - 1.0;
7151  m2 = m*m;
7152  return -one8 + one6*x + m2*(0.25 + one6*m - one6*m2);
7153  } else if ((x > 2.0) && (x <= 3.0)){
7154  m = x - 2.0;
7155  m2 = m*m;
7156  return -thirteen24 + 0.5*x + m2*(-0.25 - 0.5*m + 0.25*m2);
7157  } else if ((x > 3.0) && (x <= 4.0)){
7158  m = x - 3.0;
7159  m2 = m*m;
7160  return fourtyseven24 - 0.5*x + m2*(-0.25 + 0.5*m - one6*m2);
7161  } else if ((x > 4.0) && (x <= 5.0)){
7162  m = x - 4.0;
7163  m2 = m*m;
7164  return seventeen24 - one6*x + m2*(0.25 - one6*m + one24*m2);
7165  } else {
7166  return 0.0;
7167  }
7168 }
7169 
7170 VPUBLIC double dbspline4(double x) {
7171 
7172  double m, m2;
7173  static double one6 = 1.0/6.0;
7174  static double one3 = 1.0/3.0;
7175  static double two3 = 2.0/3.0;
7176  static double thirteen6 = 13.0/6.0;
7177 
7178  if ((x > 0.0) && (x <= 1.0)){
7179  m2 = x*x;
7180  return one6*x*m2;
7181  } else if ((x > 1.0) && (x <= 2.0)){
7182  m = x - 1.0;
7183  m2 = m*m;
7184  return -one3 + 0.5*x + m2*(0.5 - two3*m);
7185  } else if ((x > 2.0) && (x <= 3.0)){
7186  m = x - 2.0;
7187  m2 = m*m;
7188  return 1.5 - 0.5*x + m2*(-1.5 + m);
7189  } else if ((x > 3.0) && (x <= 4.0)){
7190  m = x - 3.0;
7191  m2 = m*m;
7192  return 1.0 - 0.5*x + m2*(1.5 - two3*m);
7193  } else if ((x > 4.0) && (x <= 5.0)){
7194  m = x - 4.0;
7195  m2 = m*m;
7196  return -thirteen6 + 0.5*x + m2*(-0.5 + one6*m);
7197  } else {
7198  return 0.0;
7199  }
7200 }
7201 
7202 VPUBLIC double d2bspline4(double x) {
7203 
7204  double m, m2;
7205 
7206  if ((x > 0.0) && (x <= 1.0)){
7207  return 0.5*x*x;
7208  } else if ((x > 1.0) && (x <= 2.0)){
7209  m = x - 1.0;
7210  m2 = m*m;
7211  return -0.5 + x - 2.0*m2;
7212  } else if ((x > 2.0) && (x <= 3.0)){
7213  m = x - 2.0;
7214  m2 = m*m;
7215  return 5.5 - 3.0*x + 3.0*m2;
7216  } else if ((x > 3.0) && (x <= 4.0)){
7217  m = x - 3.0;
7218  m2 = m*m;
7219  return -9.5 + 3.0*x - 2.0*m2;
7220  } else if ((x > 4.0) && (x <= 5.0)){
7221  m = x - 4.0;
7222  m2 = m*m;
7223  return 4.5 - x + 0.5*m2;
7224  } else {
7225  return 0.0;
7226  }
7227 }
7228 
7229 VPUBLIC double d3bspline4(double x) {
7230 
7231  if ((x > 0.0) && (x <= 1.0)) return x;
7232  else if ((x > 1.0) && (x <= 2.0)) return 5.0 - 4.0 * x;
7233  else if ((x > 2.0) && (x <= 3.0)) return -15.0 + 6.0 * x;
7234  else if ((x > 3.0) && (x <= 4.0)) return 15.0 - 4.0 * x;
7235  else if ((x > 4.0) && (x <= 5.0)) return x - 5.0;
7236  else return 0.0;
7237 
7238 }
7239 
7240 VPUBLIC void fillcoPermanentMultipole(Vpmg *thee) {
7241 
7242  Valist *alist;
7243  Vpbe *pbe;
7244  Vatom *atom;
7245  /* Coversions */
7246  double zmagic, f;
7247  /* Grid */
7248  double xmin, xmax, ymin, ymax, zmin, zmax;
7249  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
7250  double hx, hy, hzed, *apos;
7251  /* Multipole */
7252  double charge, *dipole,*quad;
7253  double c,ux,uy,uz,qxx,qyx,qyy,qzx,qzy,qzz,qave;
7254  /* B-spline weights */
7255  double mx,my,mz,dmx,dmy,dmz,d2mx,d2my,d2mz;
7256  double mi,mj,mk;
7257  /* Loop variables */
7258  int i, ii, jj, kk, nx, ny, nz, iatom;
7259  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
7260 
7261  /* sanity check */
7262  double mir,mjr,mkr,mr2;
7263  double debye,mc,mux,muy,muz,mqxx,mqyx,mqyy,mqzx,mqzy,mqzz;
7264 
7265  VASSERT(thee != VNULL);
7266 
7267  /* Get PBE info */
7268  pbe = thee->pbe;
7269  alist = pbe->alist;
7270  zmagic = Vpbe_getZmagic(pbe);
7271 
7272  /* Mesh info */
7273  nx = thee->pmgp->nx;
7274  ny = thee->pmgp->ny;
7275  nz = thee->pmgp->nz;
7276  hx = thee->pmgp->hx;
7277  hy = thee->pmgp->hy;
7278  hzed = thee->pmgp->hzed;
7279 
7280  /* Conversion */
7281  f = zmagic/(hx*hy*hzed);
7282 
7283  /* Define the total domain size */
7284  xlen = thee->pmgp->xlen;
7285  ylen = thee->pmgp->ylen;
7286  zlen = thee->pmgp->zlen;
7287 
7288  /* Define the min/max dimensions */
7289  xmin = thee->pmgp->xcent - (xlen/2.0);
7290  ymin = thee->pmgp->ycent - (ylen/2.0);
7291  zmin = thee->pmgp->zcent - (zlen/2.0);
7292  xmax = thee->pmgp->xcent + (xlen/2.0);
7293  ymax = thee->pmgp->ycent + (ylen/2.0);
7294  zmax = thee->pmgp->zcent + (zlen/2.0);
7295 
7296  /* Fill in the source term (permanent atomic multipoles) */
7297  Vnm_print(0, "fillcoPermanentMultipole: filling in source term.\n");
7298  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
7299 
7300  atom = Valist_getAtom(alist, iatom);
7301  apos = Vatom_getPosition(atom);
7302 
7303  c = Vatom_getCharge(atom)*f;
7304 
7305 #if defined(WITH_TINKER)
7306  dipole = Vatom_getDipole(atom);
7307  ux = dipole[0]/hx*f;
7308  uy = dipole[1]/hy*f;
7309  uz = dipole[2]/hzed*f;
7310  quad = Vatom_getQuadrupole(atom);
7311  qxx = (1.0/3.0)*quad[0]/(hx*hx)*f;
7312  qyx = (2.0/3.0)*quad[3]/(hx*hy)*f;
7313  qyy = (1.0/3.0)*quad[4]/(hy*hy)*f;
7314  qzx = (2.0/3.0)*quad[6]/(hzed*hx)*f;
7315  qzy = (2.0/3.0)*quad[7]/(hzed*hy)*f;
7316  qzz = (1.0/3.0)*quad[8]/(hzed*hzed)*f;
7317 #else
7318  ux = 0.0;
7319  uy = 0.0;
7320  uz = 0.0;
7321  qxx = 0.0;
7322  qyx = 0.0;
7323  qyy = 0.0;
7324  qzx = 0.0;
7325  qzy = 0.0;
7326  qzz = 0.0;
7327 #endif /* if defined(WITH_TINKER) */
7328 
7329  /* check
7330  mc = 0.0;
7331  mux = 0.0;
7332  muy = 0.0;
7333  muz = 0.0;
7334  mqxx = 0.0;
7335  mqyx = 0.0;
7336  mqyy = 0.0;
7337  mqzx = 0.0;
7338  mqzy = 0.0;
7339  mqzz = 0.0; */
7340 
7341  /* Make sure we're on the grid */
7342  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
7343  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
7344  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
7345  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]);
7346  Vnm_print(2, "fillcoPermanentMultipole: xmin = %g, xmax = %g\n", xmin, xmax);
7347  Vnm_print(2, "fillcoPermanentMultipole: ymin = %g, ymax = %g\n", ymin, ymax);
7348  Vnm_print(2, "fillcoPermanentMultipole: zmin = %g, zmax = %g\n", zmin, zmax);
7349  fflush(stderr);
7350  } else {
7351 
7352  /* Convert the atom position to grid reference frame */
7353  position[0] = apos[0] - xmin;
7354  position[1] = apos[1] - ymin;
7355  position[2] = apos[2] - zmin;
7356 
7357  /* Figure out which vertices we're next to */
7358  ifloat = position[0]/hx;
7359  jfloat = position[1]/hy;
7360  kfloat = position[2]/hzed;
7361 
7362  ip1 = (int)ceil(ifloat);
7363  ip2 = ip1 + 2;
7364  im1 = (int)floor(ifloat);
7365  im2 = im1 - 2;
7366  jp1 = (int)ceil(jfloat);
7367  jp2 = jp1 + 2;
7368  jm1 = (int)floor(jfloat);
7369  jm2 = jm1 - 2;
7370  kp1 = (int)ceil(kfloat);
7371  kp2 = kp1 + 2;
7372  km1 = (int)floor(kfloat);
7373  km2 = km1 - 2;
7374 
7375  /* This step shouldn't be necessary, but it saves nasty debugging
7376  * later on if something goes wrong */
7377  ip2 = VMIN2(ip2,nx-1);
7378  ip1 = VMIN2(ip1,nx-1);
7379  im1 = VMAX2(im1,0);
7380  im2 = VMAX2(im2,0);
7381  jp2 = VMIN2(jp2,ny-1);
7382  jp1 = VMIN2(jp1,ny-1);
7383  jm1 = VMAX2(jm1,0);
7384  jm2 = VMAX2(jm2,0);
7385  kp2 = VMIN2(kp2,nz-1);
7386  kp1 = VMIN2(kp1,nz-1);
7387  km1 = VMAX2(km1,0);
7388  km2 = VMAX2(km2,0);
7389 
7390  /* Now assign fractions of the charge to the nearby verts */
7391  for (ii=im2; ii<=ip2; ii++) {
7392  mi = VFCHI4(ii,ifloat);
7393  mx = bspline4(mi);
7394  dmx = dbspline4(mi);
7395  d2mx = d2bspline4(mi);
7396  for (jj=jm2; jj<=jp2; jj++) {
7397  mj = VFCHI4(jj,jfloat);
7398  my = bspline4(mj);
7399  dmy = dbspline4(mj);
7400  d2my = d2bspline4(mj);
7401  for (kk=km2; kk<=kp2; kk++) {
7402  mk = VFCHI4(kk,kfloat);
7403  mz = bspline4(mk);
7404  dmz = dbspline4(mk);
7405  d2mz = d2bspline4(mk);
7406  charge = mx*my*mz*c -
7407  dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz +
7408  d2mx*my*mz*qxx +
7409  dmx*dmy*mz*qyx + mx*d2my*mz*qyy +
7410  dmx*my*dmz*qzx + mx*dmy*dmz*qzy + mx*my*d2mz*qzz;
7411  thee->charge[IJK(ii,jj,kk)] += charge;
7412 
7413  /* sanity check - recalculate traceless multipoles
7414  from the grid charge distribution for this
7415  site.
7416 
7417  mir = (mi - 2.5) * hx;
7418  mjr = (mj - 2.5) * hy;
7419  mkr = (mk - 2.5) * hzed;
7420  mr2 = mir*mir+mjr*mjr+mkr*mkr;
7421  mc += charge;
7422  mux += mir * charge;
7423  muy += mjr * charge;
7424  muz += mkr * charge;
7425  mqxx += (1.5*mir*mir - 0.5*mr2) * charge;
7426  mqyx += 1.5*mjr*mir * charge;
7427  mqyy += (1.5*mjr*mjr - 0.5*mr2) * charge;
7428  mqzx += 1.5*mkr*mir * charge;
7429  mqzy += 1.5*mkr*mjr * charge;
7430  mqzz += (1.5*mkr*mkr - 0.5*mr2) * charge;
7431  */
7432  }
7433  }
7434  }
7435  } /* endif (on the mesh) */
7436 
7437  /* print out the Grid vs. Ideal Point Multipole. */
7438 
7439  /*
7440  debye = 4.8033324;
7441  mc = mc/f;
7442  mux = mux/f*debye;
7443  muy = muy/f*debye;
7444  muz = muz/f*debye;
7445  mqxx = mqxx/f*debye;
7446  mqyy = mqyy/f*debye;
7447  mqzz = mqzz/f*debye;
7448  mqyx = mqyx/f*debye;
7449  mqzx = mqzx/f*debye;
7450  mqzy = mqzy/f*debye;
7451 
7452  printf(" Grid v. Actual Permanent Multipole for Site %i\n",iatom);
7453  printf(" G: %10.6f\n",mc);
7454  printf(" A: %10.6f\n\n",c/f);
7455  printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
7456  printf(" A: %10.6f %10.6f %10.6f\n\n",
7457  (ux * hx / f) * debye,
7458  (uy * hy / f) * debye,
7459  (uz * hzed /f) * debye);
7460  printf(" G: %10.6f\n",mqxx);
7461  printf(" A: %10.6f\n",quad[0]*debye);
7462  printf(" G: %10.6f %10.6f\n",mqyx,mqyy);
7463  printf(" A: %10.6f %10.6f\n",quad[3]*debye,quad[4]*debye);
7464  printf(" G: %10.6f %10.6f %10.6f\n",mqzx,mqzy,mqzz);
7465  printf(" A: %10.6f %10.6f %10.6f\n\n",
7466  quad[6]*debye,quad[7]*debye,quad[8]*debye); */
7467 
7468  } /* endfor (each atom) */
7469 }
7470 
7471 #if defined(WITH_TINKER)
7472 
7473 VPUBLIC void fillcoInducedDipole(Vpmg *thee) {
7474 
7475  Valist *alist;
7476  Vpbe *pbe;
7477  Vatom *atom;
7478  /* Conversions */
7479  double zmagic, f;
7480  /* Grid */
7481  double xmin, xmax, ymin, ymax, zmin, zmax;
7482  double xlen, ylen, zlen, ifloat, jfloat, kfloat;
7483  double hx, hy, hzed, *apos, position[3];
7484  /* B-spline weights */
7485  double mx, my, mz, dmx, dmy, dmz;
7486  /* Dipole */
7487  double charge, *dipole, ux,uy,uz;
7488  double mi,mj,mk;
7489  /* Loop indeces */
7490  int i, ii, jj, kk, nx, ny, nz, iatom;
7491  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
7492 
7493  double debye;
7494  double mux,muy,muz;
7495  double mir,mjr,mkr;
7496 
7497  VASSERT(thee != VNULL);
7498 
7499  /* Get PBE info */
7500  pbe = thee->pbe;
7501  alist = pbe->alist;
7502  zmagic = Vpbe_getZmagic(pbe);
7503 
7504  /* Mesh info */
7505  nx = thee->pmgp->nx;
7506  ny = thee->pmgp->ny;
7507  nz = thee->pmgp->nz;
7508  hx = thee->pmgp->hx;
7509  hy = thee->pmgp->hy;
7510  hzed = thee->pmgp->hzed;
7511 
7512  /* Conversion */
7513  f = zmagic/(hx*hy*hzed);
7514 
7515  /* Define the total domain size */
7516  xlen = thee->pmgp->xlen;
7517  ylen = thee->pmgp->ylen;
7518  zlen = thee->pmgp->zlen;
7519 
7520  /* Define the min/max dimensions */
7521  xmin = thee->pmgp->xcent - (xlen/2.0);
7522  ymin = thee->pmgp->ycent - (ylen/2.0);
7523  zmin = thee->pmgp->zcent - (zlen/2.0);
7524  xmax = thee->pmgp->xcent + (xlen/2.0);
7525  ymax = thee->pmgp->ycent + (ylen/2.0);
7526  zmax = thee->pmgp->zcent + (zlen/2.0);
7527 
7528  /* Fill in the source term (induced dipoles) */
7529  Vnm_print(0, "fillcoInducedDipole: filling in the source term.\n");
7530  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
7531 
7532  atom = Valist_getAtom(alist, iatom);
7533  apos = Vatom_getPosition(atom);
7534 
7535  dipole = Vatom_getInducedDipole(atom);
7536  ux = dipole[0]/hx*f;
7537  uy = dipole[1]/hy*f;
7538  uz = dipole[2]/hzed*f;
7539 
7540  mux = 0.0;
7541  muy = 0.0;
7542  muz = 0.0;
7543 
7544  /* Make sure we're on the grid */
7545  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
7546  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
7547  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
7548  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]);
7549  Vnm_print(2, "fillcoInducedDipole: xmin = %g, xmax = %g\n", xmin, xmax);
7550  Vnm_print(2, "fillcoInducedDipole: ymin = %g, ymax = %g\n", ymin, ymax);
7551  Vnm_print(2, "fillcoInducedDipole: zmin = %g, zmax = %g\n", zmin, zmax);
7552  fflush(stderr);
7553  } else {
7554 
7555  /* Convert the atom position to grid reference frame */
7556  position[0] = apos[0] - xmin;
7557  position[1] = apos[1] - ymin;
7558  position[2] = apos[2] - zmin;
7559 
7560  /* Figure out which vertices we're next to */
7561  ifloat = position[0]/hx;
7562  jfloat = position[1]/hy;
7563  kfloat = position[2]/hzed;
7564 
7565  ip1 = (int)ceil(ifloat);
7566  ip2 = ip1 + 2;
7567  im1 = (int)floor(ifloat);
7568  im2 = im1 - 2;
7569  jp1 = (int)ceil(jfloat);
7570  jp2 = jp1 + 2;
7571  jm1 = (int)floor(jfloat);
7572  jm2 = jm1 - 2;
7573  kp1 = (int)ceil(kfloat);
7574  kp2 = kp1 + 2;
7575  km1 = (int)floor(kfloat);
7576  km2 = km1 - 2;
7577 
7578  /* This step shouldn't be necessary, but it saves nasty debugging
7579  * later on if something goes wrong */
7580  ip2 = VMIN2(ip2,nx-1);
7581  ip1 = VMIN2(ip1,nx-1);
7582  im1 = VMAX2(im1,0);
7583  im2 = VMAX2(im2,0);
7584  jp2 = VMIN2(jp2,ny-1);
7585  jp1 = VMIN2(jp1,ny-1);
7586  jm1 = VMAX2(jm1,0);
7587  jm2 = VMAX2(jm2,0);
7588  kp2 = VMIN2(kp2,nz-1);
7589  kp1 = VMIN2(kp1,nz-1);
7590  km1 = VMAX2(km1,0);
7591  km2 = VMAX2(km2,0);
7592 
7593  /* Now assign fractions of the dipole to the nearby verts */
7594  for (ii=im2; ii<=ip2; ii++) {
7595  mi = VFCHI4(ii,ifloat);
7596  mx = bspline4(mi);
7597  dmx = dbspline4(mi);
7598  for (jj=jm2; jj<=jp2; jj++) {
7599  mj = VFCHI4(jj,jfloat);
7600  my = bspline4(mj);
7601  dmy = dbspline4(mj);
7602  for (kk=km2; kk<=kp2; kk++) {
7603  mk = VFCHI4(kk,kfloat);
7604  mz = bspline4(mk);
7605  dmz = dbspline4(mk);
7606  charge = -dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz;
7607  thee->charge[IJK(ii,jj,kk)] += charge;
7608 
7609  /*
7610  mir = (mi - 2.5) * hx;
7611  mjr = (mj - 2.5) * hy;
7612  mkr = (mk - 2.5) * hzed;
7613  mux += mir * charge;
7614  muy += mjr * charge;
7615  muz += mkr * charge;
7616  */
7617  }
7618  }
7619  }
7620  } /* endif (on the mesh) */
7621 
7622  /* check
7623  debye = 4.8033324;
7624  mux = mux/f*debye;
7625  muy = muy/f*debye;
7626  muz = muz/f*debye;
7627 
7628  printf(" Grid v. Actual Induced Dipole for Site %i\n",iatom);
7629  printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
7630  printf(" A: %10.6f %10.6f %10.6f\n\n",
7631  (ux * hx / f) * debye,
7632  (uy * hy / f) * debye,
7633  (uz * hzed /f) * debye);
7634  */
7635 
7636  } /* endfor (each atom) */
7637 }
7638 
7639 VPUBLIC void fillcoNLInducedDipole(Vpmg *thee) {
7640 
7641  Valist *alist;
7642  Vpbe *pbe;
7643  Vatom *atom;
7644  /* Conversions */
7645  double zmagic, f;
7646  /* Grid */
7647  double xmin, xmax, ymin, ymax, zmin, zmax;
7648  double xlen, ylen, zlen, ifloat, jfloat, kfloat;
7649  double hx, hy, hzed, *apos, position[3];
7650  /* B-spline weights */
7651  double mx, my, mz, dmx, dmy, dmz;
7652  /* Dipole */
7653  double charge, *dipole, ux,uy,uz;
7654  double mi,mj,mk;
7655  /* Loop indeces */
7656  int i, ii, jj, kk, nx, ny, nz, iatom;
7657  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
7658 
7659  /* sanity check
7660  double debye;
7661  double mux,muy,muz;
7662  double mir,mjr,mkr;
7663  */
7664 
7665  VASSERT(thee != VNULL);
7666 
7667  /* Get PBE info */
7668  pbe = thee->pbe;
7669  alist = pbe->alist;
7670  zmagic = Vpbe_getZmagic(pbe);
7671 
7672  /* Mesh info */
7673  nx = thee->pmgp->nx;
7674  ny = thee->pmgp->ny;
7675  nz = thee->pmgp->nz;
7676  hx = thee->pmgp->hx;
7677  hy = thee->pmgp->hy;
7678  hzed = thee->pmgp->hzed;
7679 
7680  /* Conversion */
7681  f = zmagic/(hx*hy*hzed);
7682 
7683  /* Define the total domain size */
7684  xlen = thee->pmgp->xlen;
7685  ylen = thee->pmgp->ylen;
7686  zlen = thee->pmgp->zlen;
7687 
7688  /* Define the min/max dimensions */
7689  xmin = thee->pmgp->xcent - (xlen/2.0);
7690  ymin = thee->pmgp->ycent - (ylen/2.0);
7691  zmin = thee->pmgp->zcent - (zlen/2.0);
7692  xmax = thee->pmgp->xcent + (xlen/2.0);
7693  ymax = thee->pmgp->ycent + (ylen/2.0);
7694  zmax = thee->pmgp->zcent + (zlen/2.0);
7695 
7696  /* Fill in the source term (non-local induced dipoles) */
7697  Vnm_print(0, "fillcoNLInducedDipole: filling in source term.\n");
7698  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
7699 
7700  atom = Valist_getAtom(alist, iatom);
7701  apos = Vatom_getPosition(atom);
7702 
7703  dipole = Vatom_getNLInducedDipole(atom);
7704  ux = dipole[0]/hx*f;
7705  uy = dipole[1]/hy*f;
7706  uz = dipole[2]/hzed*f;
7707 
7708  /*
7709  mux = 0.0;
7710  muy = 0.0;
7711  muz = 0.0;
7712  */
7713 
7714  /* Make sure we're on the grid */
7715  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
7716  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
7717  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
7718  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]);
7719  Vnm_print(2, "fillcoNLInducedDipole: xmin = %g, xmax = %g\n", xmin, xmax);
7720  Vnm_print(2, "fillcoNLInducedDipole: ymin = %g, ymax = %g\n", ymin, ymax);
7721  Vnm_print(2, "fillcoNLInducedDipole: zmin = %g, zmax = %g\n", zmin, zmax);
7722  fflush(stderr);
7723  } else {
7724 
7725  /* Convert the atom position to grid reference frame */
7726  position[0] = apos[0] - xmin;
7727  position[1] = apos[1] - ymin;
7728  position[2] = apos[2] - zmin;
7729 
7730  /* Figure out which vertices we're next to */
7731  ifloat = position[0]/hx;
7732  jfloat = position[1]/hy;
7733  kfloat = position[2]/hzed;
7734 
7735  ip1 = (int)ceil(ifloat);
7736  ip2 = ip1 + 2;
7737  im1 = (int)floor(ifloat);
7738  im2 = im1 - 2;
7739  jp1 = (int)ceil(jfloat);
7740  jp2 = jp1 + 2;
7741  jm1 = (int)floor(jfloat);
7742  jm2 = jm1 - 2;
7743  kp1 = (int)ceil(kfloat);
7744  kp2 = kp1 + 2;
7745  km1 = (int)floor(kfloat);
7746  km2 = km1 - 2;
7747 
7748  /* This step shouldn't be necessary, but it saves nasty debugging
7749  * later on if something goes wrong */
7750  ip2 = VMIN2(ip2,nx-1);
7751  ip1 = VMIN2(ip1,nx-1);
7752  im1 = VMAX2(im1,0);
7753  im2 = VMAX2(im2,0);
7754  jp2 = VMIN2(jp2,ny-1);
7755  jp1 = VMIN2(jp1,ny-1);
7756  jm1 = VMAX2(jm1,0);
7757  jm2 = VMAX2(jm2,0);
7758  kp2 = VMIN2(kp2,nz-1);
7759  kp1 = VMIN2(kp1,nz-1);
7760  km1 = VMAX2(km1,0);
7761  km2 = VMAX2(km2,0);
7762 
7763  /* Now assign fractions of the non local induced dipole
7764  to the nearby verts */
7765  for (ii=im2; ii<=ip2; ii++) {
7766  mi = VFCHI4(ii,ifloat);
7767  mx = bspline4(mi);
7768  dmx = dbspline4(mi);
7769  for (jj=jm2; jj<=jp2; jj++) {
7770  mj = VFCHI4(jj,jfloat);
7771  my = bspline4(mj);
7772  dmy = dbspline4(mj);
7773  for (kk=km2; kk<=kp2; kk++) {
7774  mk = VFCHI4(kk,kfloat);
7775  mz = bspline4(mk);
7776  dmz = dbspline4(mk);
7777  charge = -dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz;
7778  thee->charge[IJK(ii,jj,kk)] += charge;
7779 
7780  /*
7781  mir = (mi - 2.5) * hx;
7782  mjr = (mj - 2.5) * hy;
7783  mkr = (mk - 2.5) * hzed;
7784  mux += mir * charge;
7785  muy += mjr * charge;
7786  muz += mkr * charge;
7787  */
7788  }
7789  }
7790  }
7791  } /* endif (on the mesh) */
7792 
7793  /*
7794  debye = 4.8033324;
7795  mux = mux/f*debye;
7796  muy = muy/f*debye;
7797  muz = muz/f*debye;
7798 
7799  printf(" Grid v. Actual Non-Local Induced Dipole for Site %i\n",iatom);
7800  printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
7801  printf(" A: %10.6f %10.6f %10.6f\n\n",
7802  (ux * hx / f) * debye,
7803  (uy * hy / f) * debye,
7804  (uz * hzed /f) * debye); */
7805 
7806  } /* endfor (each atom) */
7807 }
7808 
7809 VPUBLIC double Vpmg_qfPermanentMultipoleEnergy(Vpmg *thee, int atomID) {
7810 
7811  double *u;
7812  Vatom *atom;
7813  /* Grid variables */
7814  int nx, ny, nz;
7815  double xmax, xmin, ymax, ymin, zmax, zmin;
7816  double hx, hy, hzed, ifloat, jfloat, kfloat;
7817  double mi, mj, mk;
7818  double *position;
7819  /* B-spline weights */
7820  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz;
7821  /* Loop indeces */
7822  int ip1,ip2,im1,im2,jp1,jp2,jm1,jm2,kp1,kp2,km1,km2;
7823  int i,j,ii,jj,kk;
7824  /* Potential, field, field gradient and multipole components */
7825  double pot, rfe[3], rfde[3][3], energy;
7826  double f, charge, *dipole, *quad;
7827  double qxx, qyx, qyy, qzx, qzy, qzz;
7828 
7829 
7830  VASSERT(thee != VNULL);
7831  VASSERT(thee->filled);
7832 
7833  /* Get the mesh information */
7834  nx = thee->pmgp->nx;
7835  ny = thee->pmgp->ny;
7836  nz = thee->pmgp->nz;
7837  hx = thee->pmgp->hx;
7838  hy = thee->pmgp->hy;
7839  hzed = thee->pmgp->hzed;
7840  xmax = thee->xf[nx-1];
7841  ymax = thee->yf[ny-1];
7842  zmax = thee->zf[nz-1];
7843  xmin = thee->xf[0];
7844  ymin = thee->yf[0];
7845  zmin = thee->zf[0];
7846 
7847  u = thee->u;
7848 
7849  atom = Valist_getAtom(thee->pbe->alist, atomID);
7850 
7851  /* Currently all atoms must be in the same partition. */
7852 
7853  VASSERT(atom->partID != 0);
7854 
7855  /* Convert the atom position to grid coordinates */
7856 
7857  position = Vatom_getPosition(atom);
7858  ifloat = (position[0] - xmin)/hx;
7859  jfloat = (position[1] - ymin)/hy;
7860  kfloat = (position[2] - zmin)/hzed;
7861  ip1 = (int)ceil(ifloat);
7862  ip2 = ip1 + 2;
7863  im1 = (int)floor(ifloat);
7864  im2 = im1 - 2;
7865  jp1 = (int)ceil(jfloat);
7866  jp2 = jp1 + 2;
7867  jm1 = (int)floor(jfloat);
7868  jm2 = jm1 - 2;
7869  kp1 = (int)ceil(kfloat);
7870  kp2 = kp1 + 2;
7871  km1 = (int)floor(kfloat);
7872  km2 = km1 - 2;
7873 
7874  /* This step shouldn't be necessary, but it saves nasty debugging
7875  * later on if something goes wrong */
7876  ip2 = VMIN2(ip2,nx-1);
7877  ip1 = VMIN2(ip1,nx-1);
7878  im1 = VMAX2(im1,0);
7879  im2 = VMAX2(im2,0);
7880  jp2 = VMIN2(jp2,ny-1);
7881  jp1 = VMIN2(jp1,ny-1);
7882  jm1 = VMAX2(jm1,0);
7883  jm2 = VMAX2(jm2,0);
7884  kp2 = VMIN2(kp2,nz-1);
7885  kp1 = VMIN2(kp1,nz-1);
7886  km1 = VMAX2(km1,0);
7887  km2 = VMAX2(km2,0);
7888 
7889  /* Initialize observables to zero */
7890  energy = 0.0;
7891  pot = 0.0;
7892  for (i=0;i<3;i++){
7893  rfe[i] = 0.0;
7894  for (j=0;j<3;j++){
7895  rfde[i][j] = 0.0;
7896  }
7897  }
7898 
7899  for (ii=im2; ii<=ip2; ii++) {
7900  mi = VFCHI4(ii,ifloat);
7901  mx = bspline4(mi);
7902  dmx = dbspline4(mi);
7903  d2mx = d2bspline4(mi);
7904  for (jj=jm2; jj<=jp2; jj++) {
7905  mj = VFCHI4(jj,jfloat);
7906  my = bspline4(mj);
7907  dmy = dbspline4(mj);
7908  d2my = d2bspline4(mj);
7909  for (kk=km2; kk<=kp2; kk++) {
7910  mk = VFCHI4(kk,kfloat);
7911  mz = bspline4(mk);
7912  dmz = dbspline4(mk);
7913  d2mz = d2bspline4(mk);
7914  f = u[IJK(ii,jj,kk)];
7915  /* potential */
7916  pot += f*mx*my*mz;
7917  /* field */
7918  rfe[0] += f*dmx*my*mz/hx;
7919  rfe[1] += f*mx*dmy*mz/hy;
7920  rfe[2] += f*mx*my*dmz/hzed;
7921  /* field gradient */
7922  rfde[0][0] += f*d2mx*my*mz/(hx*hx);
7923  rfde[1][0] += f*dmx*dmy*mz/(hy*hx);
7924  rfde[1][1] += f*mx*d2my*mz/(hy*hy);
7925  rfde[2][0] += f*dmx*my*dmz/(hx*hzed);
7926  rfde[2][1] += f*mx*dmy*dmz/(hy*hzed);
7927  rfde[2][2] += f*mx*my*d2mz/(hzed*hzed);
7928  }
7929  }
7930  }
7931 
7932  charge = Vatom_getCharge(atom);
7933  dipole = Vatom_getDipole(atom);
7934  quad = Vatom_getQuadrupole(atom);
7935  qxx = quad[0]/3.0;
7936  qyx = quad[3]/3.0;
7937  qyy = quad[4]/3.0;
7938  qzx = quad[6]/3.0;
7939  qzy = quad[7]/3.0;
7940  qzz = quad[8]/3.0;
7941 
7942  energy = pot * charge
7943  - rfe[0] * dipole[0]
7944  - rfe[1] * dipole[1]
7945  - rfe[2] * dipole[2]
7946  + rfde[0][0]*qxx
7947  + 2.0*rfde[1][0]*qyx + rfde[1][1]*qyy
7948  + 2.0*rfde[2][0]*qzx + 2.0*rfde[2][1]*qzy + rfde[2][2]*qzz;
7949 
7950  return energy;
7951 }
7952 
7953 VPUBLIC void Vpmg_fieldSpline4(Vpmg *thee, int atomID, double field[3]) {
7954 
7955  Vatom *atom;
7956  double *u, f;
7957  /* Grid variables */
7958  int nx, ny, nz;
7959  double xmax, xmin, ymax, ymin, zmax, zmin;
7960  double hx, hy, hzed, ifloat, jfloat, kfloat;
7961  double *apos, position[3];
7962  /* B-Spline weights */
7963  double mx, my, mz, dmx, dmy, dmz;
7964  double mi, mj, mk;
7965  /* Loop indeces */
7966  int ip1,ip2,im1,im2,jp1,jp2,jm1,jm2,kp1,kp2,km1,km2;
7967  int i,j,ii,jj,kk;
7968 
7969 
7970  VASSERT (thee != VNULL);
7971 
7972  /* Get the mesh information */
7973  nx = thee->pmgp->nx;
7974  ny = thee->pmgp->ny;
7975  nz = thee->pmgp->nz;
7976  hx = thee->pmgp->hx;
7977  hy = thee->pmgp->hy;
7978  hzed = thee->pmgp->hzed;
7979  xmax = thee->xf[nx-1];
7980  ymax = thee->yf[ny-1];
7981  zmax = thee->zf[nz-1];
7982  xmin = thee->xf[0];
7983  ymin = thee->yf[0];
7984  zmin = thee->zf[0];
7985 
7986  u = thee->u;
7987 
7988  atom = Valist_getAtom(thee->pbe->alist, atomID);
7989 
7990  /* Currently all atoms must be in the same partition. */
7991 
7992  VASSERT (atom->partID != 0);
7993 
7994  /* Convert the atom position to grid coordinates */
7995 
7996  apos = Vatom_getPosition(atom);
7997  position[0] = apos[0] - xmin;
7998  position[1] = apos[1] - ymin;
7999  position[2] = apos[2] - zmin;
8000  ifloat = position[0]/hx;
8001  jfloat = position[1]/hy;
8002  kfloat = position[2]/hzed;
8003  ip1 = (int)ceil(ifloat);
8004  ip2 = ip1 + 2;
8005  im1 = (int)floor(ifloat);
8006  im2 = im1 - 2;
8007  jp1 = (int)ceil(jfloat);
8008  jp2 = jp1 + 2;
8009  jm1 = (int)floor(jfloat);
8010  jm2 = jm1 - 2;
8011  kp1 = (int)ceil(kfloat);
8012  kp2 = kp1 + 2;
8013  km1 = (int)floor(kfloat);
8014  km2 = km1 - 2;
8015 
8016  /* This step shouldn't be necessary, but it saves nasty debugging
8017  * later on if something goes wrong */
8018  ip2 = VMIN2(ip2,nx-1);
8019  ip1 = VMIN2(ip1,nx-1);
8020  im1 = VMAX2(im1,0);
8021  im2 = VMAX2(im2,0);
8022  jp2 = VMIN2(jp2,ny-1);
8023  jp1 = VMIN2(jp1,ny-1);
8024  jm1 = VMAX2(jm1,0);
8025  jm2 = VMAX2(jm2,0);
8026  kp2 = VMIN2(kp2,nz-1);
8027  kp1 = VMIN2(kp1,nz-1);
8028  km1 = VMAX2(km1,0);
8029  km2 = VMAX2(km2,0);
8030 
8031  for (i=0;i<3;i++){
8032  field[i] = 0.0;
8033  }
8034 
8035  for (ii=im2; ii<=ip2; ii++) {
8036  mi = VFCHI4(ii,ifloat);
8037  mx = bspline4(mi);
8038  dmx = dbspline4(mi);
8039  for (jj=jm2; jj<=jp2; jj++) {
8040  mj = VFCHI4(jj,jfloat);
8041  my = bspline4(mj);
8042  dmy = dbspline4(mj);
8043  for (kk=km2; kk<=kp2; kk++) {
8044  mk = VFCHI4(kk,kfloat);
8045  mz = bspline4(mk);
8046  dmz = dbspline4(mk);
8047  f = u[IJK(ii,jj,kk)];
8048 
8049  field[0] += f*dmx*my*mz/hx;
8050  field[1] += f*mx*dmy*mz/hy;
8051  field[2] += f*mx*my*dmz/hzed;
8052  }
8053  }
8054  }
8055 }
8056 
8057 VPUBLIC void Vpmg_qfPermanentMultipoleForce(Vpmg *thee, int atomID,
8058  double force[3], double torque[3]) {
8059 
8060  Vatom *atom;
8061  double f, *u, *apos, position[3];
8062 
8063  /* Grid variables */
8064  int nx,ny,nz;
8065  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
8066  double hx, hy, hzed, ifloat, jfloat, kfloat;
8067 
8068  /* B-spline weights */
8069  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, d3mx, d3my, d3mz;
8070  double mi, mj, mk;
8071 
8072  /* Loop indeces */
8073  int i, j, k, ii, jj, kk;
8074  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
8075 
8076  /* Potential, field, field gradient and 2nd field gradient */
8077  double pot, e[3], de[3][3], d2e[3][3][3];
8078 
8079  /* Permanent multipole components */
8080  double *dipole, *quad;
8081  double c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
8082 
8083  VASSERT(thee != VNULL);
8084  VASSERT(thee->filled);
8085 
8086  atom = Valist_getAtom(thee->pbe->alist, atomID);
8087 
8088  /* Currently all atoms must be in the same partition. */
8089 
8090  VASSERT(atom->partID != 0);
8091 
8092  apos = Vatom_getPosition(atom);
8093 
8094  c = Vatom_getCharge(atom);
8095  dipole = Vatom_getDipole(atom);
8096  ux = dipole[0];
8097  uy = dipole[1];
8098  uz = dipole[2];
8099  quad = Vatom_getQuadrupole(atom);
8100  qxx = quad[0]/3.0;
8101  qxy = quad[1]/3.0;
8102  qxz = quad[2]/3.0;
8103  qyx = quad[3]/3.0;
8104  qyy = quad[4]/3.0;
8105  qyz = quad[5]/3.0;
8106  qzx = quad[6]/3.0;
8107  qzy = quad[7]/3.0;
8108  qzz = quad[8]/3.0;
8109 
8110  /* Initialize observables */
8111  pot = 0.0;
8112  for (i=0;i<3;i++){
8113  e[i] = 0.0;
8114  for (j=0;j<3;j++){
8115  de[i][j] = 0.0;
8116  for (k=0;k<3;k++){
8117  d2e[i][j][k] = 0.0;
8118  }
8119  }
8120  }
8121 
8122  /* Mesh info */
8123  nx = thee->pmgp->nx;
8124  ny = thee->pmgp->ny;
8125  nz = thee->pmgp->nz;
8126  hx = thee->pmgp->hx;
8127  hy = thee->pmgp->hy;
8128  hzed = thee->pmgp->hzed;
8129  xlen = thee->pmgp->xlen;
8130  ylen = thee->pmgp->ylen;
8131  zlen = thee->pmgp->zlen;
8132  xmin = thee->pmgp->xmin;
8133  ymin = thee->pmgp->ymin;
8134  zmin = thee->pmgp->zmin;
8135  xmax = thee->pmgp->xmax;
8136  ymax = thee->pmgp->ymax;
8137  zmax = thee->pmgp->zmax;
8138  u = thee->u;
8139 
8140  /* Make sure we're on the grid */
8141  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
8142  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
8143  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
8144  Vnm_print(2, "qfPermanentMultipoleForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
8145  fflush(stderr);
8146  } else {
8147 
8148  /* Convert the atom position to grid coordinates */
8149  position[0] = apos[0] - xmin;
8150  position[1] = apos[1] - ymin;
8151  position[2] = apos[2] - zmin;
8152  ifloat = position[0]/hx;
8153  jfloat = position[1]/hy;
8154  kfloat = position[2]/hzed;
8155  ip1 = (int)ceil(ifloat);
8156  ip2 = ip1 + 2;
8157  im1 = (int)floor(ifloat);
8158  im2 = im1 - 2;
8159  jp1 = (int)ceil(jfloat);
8160  jp2 = jp1 + 2;
8161  jm1 = (int)floor(jfloat);
8162  jm2 = jm1 - 2;
8163  kp1 = (int)ceil(kfloat);
8164  kp2 = kp1 + 2;
8165  km1 = (int)floor(kfloat);
8166  km2 = km1 - 2;
8167 
8168  /* This step shouldn't be necessary, but it saves nasty debugging
8169  * later on if something goes wrong */
8170  ip2 = VMIN2(ip2,nx-1);
8171  ip1 = VMIN2(ip1,nx-1);
8172  im1 = VMAX2(im1,0);
8173  im2 = VMAX2(im2,0);
8174  jp2 = VMIN2(jp2,ny-1);
8175  jp1 = VMIN2(jp1,ny-1);
8176  jm1 = VMAX2(jm1,0);
8177  jm2 = VMAX2(jm2,0);
8178  kp2 = VMIN2(kp2,nz-1);
8179  kp1 = VMIN2(kp1,nz-1);
8180  km1 = VMAX2(km1,0);
8181  km2 = VMAX2(km2,0);
8182 
8183  for (ii=im2; ii<=ip2; ii++) {
8184  mi = VFCHI4(ii,ifloat);
8185  mx = bspline4(mi);
8186  dmx = dbspline4(mi);
8187  d2mx = d2bspline4(mi);
8188  d3mx = d3bspline4(mi);
8189  for (jj=jm2; jj<=jp2; jj++) {
8190  mj = VFCHI4(jj,jfloat);
8191  my = bspline4(mj);
8192  dmy = dbspline4(mj);
8193  d2my = d2bspline4(mj);
8194  d3my = d3bspline4(mj);
8195  for (kk=km2; kk<=kp2; kk++) {
8196  mk = VFCHI4(kk,kfloat);
8197  mz = bspline4(mk);
8198  dmz = dbspline4(mk);
8199  d2mz = d2bspline4(mk);
8200  d3mz = d3bspline4(mk);
8201  f = u[IJK(ii,jj,kk)];
8202  /* Potential */
8203  pot += f*mx*my*mz;
8204  /* Field */
8205  e[0] += f*dmx*my*mz/hx;
8206  e[1] += f*mx*dmy*mz/hy;
8207  e[2] += f*mx*my*dmz/hzed;
8208  /* Field gradient */
8209  de[0][0] += f*d2mx*my*mz/(hx*hx);
8210  de[1][0] += f*dmx*dmy*mz/(hy*hx);
8211  de[1][1] += f*mx*d2my*mz/(hy*hy);
8212  de[2][0] += f*dmx*my*dmz/(hx*hzed);
8213  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
8214  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
8215  /* 2nd Field Gradient
8216  VxVxVa */
8217  d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
8218  d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
8219  d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
8220  /* VyVxVa */
8221  d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
8222  d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
8223  d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
8224  /* VyVyVa */
8225  d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
8226  d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
8227  d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
8228  /* VzVxVa */
8229  d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
8230  d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
8231  d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
8232  /* VzVyVa */
8233  d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
8234  d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
8235  d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8236  /* VzVzVa */
8237  d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
8238  d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8239  d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
8240  }
8241  }
8242  }
8243  }
8244 
8245  /* Monopole Force */
8246  force[0] = e[0]*c;
8247  force[1] = e[1]*c;
8248  force[2] = e[2]*c;
8249 
8250  /* Dipole Force */
8251  force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;
8252  force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;
8253  force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;
8254 
8255  /* Quadrupole Force */
8256  force[0] += d2e[0][0][0]*qxx
8257  + d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
8258  + d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
8259  force[1] += d2e[0][0][1]*qxx
8260  + d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
8261  + d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
8262  force[2] += d2e[0][0][2]*qxx
8263  + d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
8264  + d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
8265 
8266  /* Dipole Torque */
8267  torque[0] = uy * e[2] - uz * e[1];
8268  torque[1] = uz * e[0] - ux * e[2];
8269  torque[2] = ux * e[1] - uy * e[0];
8270  /* Quadrupole Torque */
8271  de[0][1] = de[1][0];
8272  de[0][2] = de[2][0];
8273  de[1][2] = de[2][1];
8274  torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
8275  - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
8276  torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
8277  - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
8278  torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
8279  - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
8280 
8281 
8282  /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
8283  printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
8284 }
8285 
8286 VPUBLIC void Vpmg_ibPermanentMultipoleForce(Vpmg *thee, int atomID,
8287  double force[3]) {
8288 
8289  Valist *alist;
8290  Vacc *acc;
8291  Vpbe *pbe;
8292  Vatom *atom;
8293  Vsurf_Meth srfm;
8294 
8295  /* Grid variables */
8296  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
8297  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
8298  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
8299  double izmagic;
8300  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
8301 
8302  VASSERT(thee != VNULL);
8303 
8304  /* Nonlinear PBE is not implemented for AMOEBA */
8305  VASSERT(!thee->pmgp->nonlin);
8306 
8307  acc = thee->pbe->acc;
8308  srfm = thee->surfMeth;
8309  atom = Valist_getAtom(thee->pbe->alist, atomID);
8310 
8311  /* Currently all atoms must be in the same partition. */
8312 
8313  VASSERT(atom->partID != 0);
8314  apos = Vatom_getPosition(atom);
8315  arad = Vatom_getRadius(atom);
8316 
8317  /* Reset force */
8318  force[0] = 0.0;
8319  force[1] = 0.0;
8320  force[2] = 0.0;
8321 
8322  /* Get PBE info */
8323  pbe = thee->pbe;
8324  acc = pbe->acc;
8325  alist = pbe->alist;
8326  irad = Vpbe_getMaxIonRadius(pbe);
8327  zkappa2 = Vpbe_getZkappa2(pbe);
8328  izmagic = 1.0/Vpbe_getZmagic(pbe);
8329 
8330  /* Should be a check for this further up. */
8331  VASSERT (zkappa2 > VPMGSMALL);
8332 
8333  /* Mesh info */
8334  nx = thee->pmgp->nx;
8335  ny = thee->pmgp->ny;
8336  nz = thee->pmgp->nz;
8337  hx = thee->pmgp->hx;
8338  hy = thee->pmgp->hy;
8339  hzed = thee->pmgp->hzed;
8340  xlen = thee->pmgp->xlen;
8341  ylen = thee->pmgp->ylen;
8342  zlen = thee->pmgp->zlen;
8343  xmin = thee->pmgp->xmin;
8344  ymin = thee->pmgp->ymin;
8345  zmin = thee->pmgp->zmin;
8346  xmax = thee->pmgp->xmax;
8347  ymax = thee->pmgp->ymax;
8348  zmax = thee->pmgp->zmax;
8349 
8350  /* Make sure we're on the grid */
8351  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
8352  (apos[1]<=ymin) || (apos[1]>=ymax) || \
8353  (apos[2]<=zmin) || (apos[2]>=zmax)) {
8354  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]);
8355  Vnm_print(2, "ibPermanentMultipoleForce: xmin = %g, xmax = %g\n", xmin, xmax);
8356  Vnm_print(2, "ibPermanentMultipoleForce: ymin = %g, ymax = %g\n", ymin, ymax);
8357  Vnm_print(2, "ibPermanentMultipoleForce: zmin = %g, zmax = %g\n", zmin, zmax);
8358  fflush(stderr);
8359  } else {
8360 
8361  /* Convert the atom position to grid reference frame */
8362  position[0] = apos[0] - xmin;
8363  position[1] = apos[1] - ymin;
8364  position[2] = apos[2] - zmin;
8365 
8366  /* Integrate over points within this atom's (inflated) radius */
8367  rtot = (irad + arad + thee->splineWin);
8368  rtot2 = VSQR(rtot);
8369  dx = rtot + 0.5*hx;
8370  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
8371  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
8372  for (i=imin; i<=imax; i++) {
8373  dx2 = VSQR(position[0] - hx*i);
8374  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
8375  else dy = 0.5*hy;
8376  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
8377  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
8378  for (j=jmin; j<=jmax; j++) {
8379  dy2 = VSQR(position[1] - hy*j);
8380  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
8381  else dz = 0.5*hzed;
8382  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
8383  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
8384  for (k=kmin; k<=kmax; k++) {
8385  dz2 = VSQR(k*hzed - position[2]);
8386  /* See if grid point is inside ivdw radius and set ccf
8387  * accordingly (do spline assignment here) */
8388  if ((dz2 + dy2 + dx2) <= rtot2) {
8389  gpos[0] = i*hx + xmin;
8390  gpos[1] = j*hy + ymin;
8391  gpos[2] = k*hzed + zmin;
8392  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad, atom, tgrad);
8393  fmag = VSQR(thee->u[IJK(i,j,k)])*thee->kappa[IJK(i,j,k)];
8394  force[0] += (zkappa2*fmag*tgrad[0]);
8395  force[1] += (zkappa2*fmag*tgrad[1]);
8396  force[2] += (zkappa2*fmag*tgrad[2]);
8397  }
8398  } /* k loop */
8399  } /* j loop */
8400  } /* i loop */
8401  }
8402 
8403  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
8404  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
8405  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
8406 
8407 }
8408 
8409 VPUBLIC void Vpmg_dbPermanentMultipoleForce(Vpmg *thee, int atomID,
8410  double force[3]) {
8411 
8412  Vacc *acc;
8413  Vpbe *pbe;
8414  Vatom *atom;
8415  Vsurf_Meth srfm;
8416 
8417  double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
8418  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
8419  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
8420  double *u, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
8421  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
8422  double dHzijkm1[3];
8423  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
8424 
8425  VASSERT(thee != VNULL);
8426 
8427  acc = thee->pbe->acc;
8428  srfm = thee->surfMeth;
8429  atom = Valist_getAtom(thee->pbe->alist, atomID);
8430 
8431  /* Currently all atoms must be in the same partition. */
8432 
8433  VASSERT(atom->partID != 0);
8434  arad = Vatom_getRadius(atom);
8435  apos = Vatom_getPosition(atom);
8436 
8437  /* Reset force */
8438  force[0] = 0.0;
8439  force[1] = 0.0;
8440  force[2] = 0.0;
8441 
8442  /* Get PBE info */
8443  pbe = thee->pbe;
8444  acc = pbe->acc;
8445  epsp = Vpbe_getSoluteDiel(pbe);
8446  epsw = Vpbe_getSolventDiel(pbe);
8448  izmagic = 1.0/Vpbe_getZmagic(pbe);
8449 
8450 
8451  deps = (epsw - epsp);
8452  depsi = 1.0/deps;
8453 
8454  VASSERT(VABS(deps) > VPMGSMALL);
8455 
8456  /* Mesh info */
8457  nx = thee->pmgp->nx;
8458  ny = thee->pmgp->ny;
8459  nz = thee->pmgp->nz;
8460  hx = thee->pmgp->hx;
8461  hy = thee->pmgp->hy;
8462  hzed = thee->pmgp->hzed;
8463  xlen = thee->pmgp->xlen;
8464  ylen = thee->pmgp->ylen;
8465  zlen = thee->pmgp->zlen;
8466  xmin = thee->pmgp->xmin;
8467  ymin = thee->pmgp->ymin;
8468  zmin = thee->pmgp->zmin;
8469  xmax = thee->pmgp->xmax;
8470  ymax = thee->pmgp->ymax;
8471  zmax = thee->pmgp->zmax;
8472  u = thee->u;
8473 
8474  /* Make sure we're on the grid */
8475  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
8476  (apos[1]<=ymin) || (apos[1]>=ymax) || \
8477  (apos[2]<=zmin) || (apos[2]>=zmax)) {
8478  Vnm_print(2, "dbPermanentMultipoleForce: Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", apos[0], apos[1], apos[2]);
8479  Vnm_print(2, "dbPermanentMultipoleForce: xmin = %g, xmax = %g\n", xmin, xmax);
8480  Vnm_print(2, "dbPermanentMultipoleForce: ymin = %g, ymax = %g\n", ymin, ymax);
8481  Vnm_print(2, "dbPermanentMultipoleForce: zmin = %g, zmax = %g\n", zmin, zmax);
8482  fflush(stderr);
8483  } else {
8484 
8485  /* Convert the atom position to grid reference frame */
8486  position[0] = apos[0] - xmin;
8487  position[1] = apos[1] - ymin;
8488  position[2] = apos[2] - zmin;
8489 
8490  /* Integrate over points within this atom's (inflated) radius */
8491  rtot = (arad + thee->splineWin);
8492  rtot2 = VSQR(rtot);
8493  dx = rtot/hx;
8494  imin = (int)floor((position[0]-rtot)/hx);
8495  if (imin < 1) {
8496  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8497  return;
8498  }
8499  imax = (int)ceil((position[0]+rtot)/hx);
8500  if (imax > (nx-2)) {
8501  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8502  return;
8503  }
8504  jmin = (int)floor((position[1]-rtot)/hy);
8505  if (jmin < 1) {
8506  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8507  return;
8508  }
8509  jmax = (int)ceil((position[1]+rtot)/hy);
8510  if (jmax > (ny-2)) {
8511  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8512  return;
8513  }
8514  kmin = (int)floor((position[2]-rtot)/hzed);
8515  if (kmin < 1) {
8516  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8517  return;
8518  }
8519  kmax = (int)ceil((position[2]+rtot)/hzed);
8520  if (kmax > (nz-2)) {
8521  Vnm_print(2, "dbPermanentMultipoleForce: Atom off grid!\n");
8522  return;
8523  }
8524  for (i=imin; i<=imax; i++) {
8525  for (j=jmin; j<=jmax; j++) {
8526  for (k=kmin; k<=kmax; k++) {
8527  /* i,j,k */
8528  gpos[0] = (i+0.5)*hx + xmin;
8529  gpos[1] = j*hy + ymin;
8530  gpos[2] = k*hzed + zmin;
8531  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
8532  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8533  atom, dHxijk);
8534  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
8535  gpos[0] = i*hx + xmin;
8536  gpos[1] = (j+0.5)*hy + ymin;
8537  gpos[2] = k*hzed + zmin;
8538  Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
8539  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8540  atom, dHyijk);
8541  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
8542  gpos[0] = i*hx + xmin;
8543  gpos[1] = j*hy + ymin;
8544  gpos[2] = (k+0.5)*hzed + zmin;
8545  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
8546  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8547  atom, dHzijk);
8548  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
8549  /* i-1,j,k */
8550  gpos[0] = (i-0.5)*hx + xmin;
8551  gpos[1] = j*hy + ymin;
8552  gpos[2] = k*hzed + zmin;
8553  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
8554  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8555  atom, dHxim1jk);
8556  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
8557  /* i,j-1,k */
8558  gpos[0] = i*hx + xmin;
8559  gpos[1] = (j-0.5)*hy + ymin;
8560  gpos[2] = k*hzed + zmin;
8561  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
8562  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8563  atom, dHyijm1k);
8564  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
8565  /* i,j,k-1 */
8566  gpos[0] = i*hx + xmin;
8567  gpos[1] = j*hy + ymin;
8568  gpos[2] = (k-0.5)*hzed + zmin;
8569  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
8570  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
8571  atom, dHzijkm1);
8572  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
8573  dbFmag = u[IJK(i,j,k)];
8574  tgrad[0] =
8575  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
8576  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
8577  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
8578  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
8579  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
8580  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
8581  tgrad[1] =
8582  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
8583  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
8584  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
8585  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
8586  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
8587  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
8588  tgrad[2] =
8589  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
8590  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
8591  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
8592  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
8593  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
8594  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
8595  force[0] += (dbFmag*tgrad[0]);
8596  force[1] += (dbFmag*tgrad[1]);
8597  force[2] += (dbFmag*tgrad[2]);
8598  } /* k loop */
8599  } /* j loop */
8600  } /* i loop */
8601  force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
8602  force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
8603  force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
8604  }
8605 }
8606 
8607 VPUBLIC void Vpmg_qfDirectPolForce(Vpmg *thee, Vgrid* perm, Vgrid *induced,
8608  int atomID, double force[3], double torque[3]) {
8609 
8610  Vatom *atom;
8611  Vpbe *pbe;
8612  double f, fp, *u, *up, *apos, position[3];
8613 
8614  /* Grid variables */
8615  int nx,ny,nz;
8616  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
8617  double hx, hy, hzed, ifloat, jfloat, kfloat;
8618 
8619  /* B-spline weights */
8620  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, d3mx, d3my, d3mz;
8621  double mi, mj, mk;
8622 
8623  /* Loop indeces */
8624  int i, j, k, ii, jj, kk;
8625  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
8626 
8627  /* Permanent potential, field, field gradient and 2nd field gradient */
8628  double pot, e[3], de[3][3], d2e[3][3][3];
8629  /* Induced dipole field */
8630  double dep[3][3];
8631 
8632  /* Permanent multipole components */
8633  double *dipole, *quad;
8634  double c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
8635  double uix, uiy, uiz;
8636 
8637  VASSERT(thee != VNULL);
8638  VASSERT(induced != VNULL); /* the potential due to permanent multipoles.*/
8639  VASSERT(induced != VNULL); /* the potential due to local induced dipoles.*/
8640  VASSERT(thee->pbe != VNULL);
8641  VASSERT(thee->pbe->alist != VNULL);
8642 
8643  atom = Valist_getAtom(thee->pbe->alist, atomID);
8644  VASSERT(atom->partID != 0); /* all atoms must be in the same partition.*/
8645  apos = Vatom_getPosition(atom);
8646 
8647  c = Vatom_getCharge(atom);
8648  dipole = Vatom_getDipole(atom);
8649  ux = dipole[0];
8650  uy = dipole[1];
8651  uz = dipole[2];
8652  quad = Vatom_getQuadrupole(atom);
8653  qxx = quad[0]/3.0;
8654  qxy = quad[1]/3.0;
8655  qxz = quad[2]/3.0;
8656  qyx = quad[3]/3.0;
8657  qyy = quad[4]/3.0;
8658  qyz = quad[5]/3.0;
8659  qzx = quad[6]/3.0;
8660  qzy = quad[7]/3.0;
8661  qzz = quad[8]/3.0;
8662 
8663  dipole = Vatom_getInducedDipole(atom);
8664  uix = dipole[0];
8665  uiy = dipole[1];
8666  uiz = dipole[2];
8667 
8668  /* Reset Field Gradients */
8669  pot = 0.0;
8670  for (i=0;i<3;i++){
8671  e[i] = 0.0;
8672  for (j=0;j<3;j++){
8673  de[i][j] = 0.0;
8674  dep[i][j] = 0.0;
8675  for (k=0;k<3;k++){
8676  d2e[i][j][k] = 0.0;
8677  }
8678  }
8679  }
8680 
8681  /* Mesh info */
8682  nx = thee->pmgp->nx;
8683  ny = thee->pmgp->ny;
8684  nz = thee->pmgp->nz;
8685  hx = thee->pmgp->hx;
8686  hy = thee->pmgp->hy;
8687  hzed = thee->pmgp->hzed;
8688  xlen = thee->pmgp->xlen;
8689  ylen = thee->pmgp->ylen;
8690  zlen = thee->pmgp->zlen;
8691  xmin = thee->pmgp->xmin;
8692  ymin = thee->pmgp->ymin;
8693  zmin = thee->pmgp->zmin;
8694  xmax = thee->pmgp->xmax;
8695  ymax = thee->pmgp->ymax;
8696  zmax = thee->pmgp->zmax;
8697  u = induced->data;
8698  up = perm->data;
8699 
8700  /* Make sure we're on the grid */
8701  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
8702  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
8703  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
8704  Vnm_print(2, "qfDirectPolForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
8705  fflush(stderr);
8706 
8707  } else {
8708 
8709  /* Convert the atom position to grid coordinates */
8710  position[0] = apos[0] - xmin;
8711  position[1] = apos[1] - ymin;
8712  position[2] = apos[2] - zmin;
8713  ifloat = position[0]/hx;
8714  jfloat = position[1]/hy;
8715  kfloat = position[2]/hzed;
8716  ip1 = (int)ceil(ifloat);
8717  ip2 = ip1 + 2;
8718  im1 = (int)floor(ifloat);
8719  im2 = im1 - 2;
8720  jp1 = (int)ceil(jfloat);
8721  jp2 = jp1 + 2;
8722  jm1 = (int)floor(jfloat);
8723  jm2 = jm1 - 2;
8724  kp1 = (int)ceil(kfloat);
8725  kp2 = kp1 + 2;
8726  km1 = (int)floor(kfloat);
8727  km2 = km1 - 2;
8728 
8729  /* This step shouldn't be necessary, but it saves nasty debugging
8730  * later on if something goes wrong */
8731  ip2 = VMIN2(ip2,nx-1);
8732  ip1 = VMIN2(ip1,nx-1);
8733  im1 = VMAX2(im1,0);
8734  im2 = VMAX2(im2,0);
8735  jp2 = VMIN2(jp2,ny-1);
8736  jp1 = VMIN2(jp1,ny-1);
8737  jm1 = VMAX2(jm1,0);
8738  jm2 = VMAX2(jm2,0);
8739  kp2 = VMIN2(kp2,nz-1);
8740  kp1 = VMIN2(kp1,nz-1);
8741  km1 = VMAX2(km1,0);
8742  km2 = VMAX2(km2,0);
8743 
8744  for (ii=im2; ii<=ip2; ii++) {
8745  mi = VFCHI4(ii,ifloat);
8746  mx = bspline4(mi);
8747  dmx = dbspline4(mi);
8748  d2mx = d2bspline4(mi);
8749  d3mx = d3bspline4(mi);
8750  for (jj=jm2; jj<=jp2; jj++) {
8751  mj = VFCHI4(jj,jfloat);
8752  my = bspline4(mj);
8753  dmy = dbspline4(mj);
8754  d2my = d2bspline4(mj);
8755  d3my = d3bspline4(mj);
8756  for (kk=km2; kk<=kp2; kk++) {
8757  mk = VFCHI4(kk,kfloat);
8758  mz = bspline4(mk);
8759  dmz = dbspline4(mk);
8760  d2mz = d2bspline4(mk);
8761  d3mz = d3bspline4(mk);
8762  f = u[IJK(ii,jj,kk)];
8763  fp = up[IJK(ii,jj,kk)];
8764  /* The potential */
8765  pot += f*mx*my*mz;
8766  /* The field */
8767  e[0] += f*dmx*my*mz/hx;
8768  e[1] += f*mx*dmy*mz/hy;
8769  e[2] += f*mx*my*dmz/hzed;
8770  /* The gradient of the field */
8771  de[0][0] += f*d2mx*my*mz/(hx*hx);
8772  de[1][0] += f*dmx*dmy*mz/(hy*hx);
8773  de[1][1] += f*mx*d2my*mz/(hy*hy);
8774  de[2][0] += f*dmx*my*dmz/(hx*hzed);
8775  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
8776  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
8777  /* The gradient of the (permanent) field */
8778  dep[0][0] += fp*d2mx*my*mz/(hx*hx);
8779  dep[1][0] += fp*dmx*dmy*mz/(hy*hx);
8780  dep[1][1] += fp*mx*d2my*mz/(hy*hy);
8781  dep[2][0] += fp*dmx*my*dmz/(hx*hzed);
8782  dep[2][1] += fp*mx*dmy*dmz/(hy*hzed);
8783  dep[2][2] += fp*mx*my*d2mz/(hzed*hzed);
8784  /* The 2nd gradient of the field
8785  VxVxVa */
8786  d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
8787  d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
8788  d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
8789  /* VyVxVa */
8790  d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
8791  d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
8792  d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
8793  /* VyVyVa */
8794  d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
8795  d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
8796  d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
8797  /* VzVxVa */
8798  d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
8799  d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
8800  d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
8801  /* VzVyVa */
8802  d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
8803  d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
8804  d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8805  /* VzVzVa */
8806  d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
8807  d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
8808  d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
8809  }
8810  }
8811  }
8812  }
8813 
8814  /* force on permanent multipole due to induced reaction field */
8815 
8816  /* Monopole Force */
8817  force[0] = e[0]*c;
8818  force[1] = e[1]*c;
8819  force[2] = e[2]*c;
8820 
8821  /* Dipole Force */
8822  force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;
8823  force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;
8824  force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;
8825 
8826  /* Quadrupole Force */
8827  force[0] += d2e[0][0][0]*qxx
8828  + d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
8829  + d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
8830  force[1] += d2e[0][0][1]*qxx
8831  + d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
8832  + d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
8833  force[2] += d2e[0][0][2]*qxx
8834  + d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
8835  + d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
8836 
8837  /* torque on permanent mulitpole due to induced reaction field */
8838 
8839  /* Dipole Torque */
8840  torque[0] = uy * e[2] - uz * e[1];
8841  torque[1] = uz * e[0] - ux * e[2];
8842  torque[2] = ux * e[1] - uy * e[0];
8843 
8844  /* Quadrupole Torque */
8845  /* Tx = -2.0*(Sum_a (Qya*dEaz) + Sum_b (Qzb*dEby))
8846  Ty = -2.0*(Sum_a (Qza*dEax) + Sum_b (Qxb*dEbz))
8847  Tz = -2.0*(Sum_a (Qxa*dEay) + Sum_b (Qyb*dEbx)) */
8848  de[0][1] = de[1][0];
8849  de[0][2] = de[2][0];
8850  de[1][2] = de[2][1];
8851  torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
8852  - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
8853  torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
8854  - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
8855  torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
8856  - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
8857 
8858  /* force on induced dipole due to permanent reaction field */
8859 
8860  force[0] -= dep[0][0]*uix+dep[1][0]*uiy+dep[2][0]*uiz;
8861  force[1] -= dep[1][0]*uix+dep[1][1]*uiy+dep[2][1]*uiz;
8862  force[2] -= dep[2][0]*uix+dep[2][1]*uiy+dep[2][2]*uiz;
8863 
8864  force[0] = 0.5 * force[0];
8865  force[1] = 0.5 * force[1];
8866  force[2] = 0.5 * force[2];
8867  torque[0] = 0.5 * torque[0];
8868  torque[1] = 0.5 * torque[1];
8869  torque[2] = 0.5 * torque[2];
8870 
8871  /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
8872  printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
8873 }
8874 
8875 VPUBLIC void Vpmg_qfNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
8876  int atomID, double force[3], double torque[3]) {
8877 
8878  Vatom *atom;
8879  double *apos, *dipole, *quad, position[3], hx, hy, hzed;
8880  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
8881  double pot, e[3],de[3][3],dep[3][3],d2e[3][3][3];
8882  double mx, my, mz, dmx, dmy, dmz, mi, mj, mk;
8883  double d2mx, d2my, d2mz, d3mx, d3my, d3mz;
8884  double *u, *up, charge, ifloat, jfloat, kfloat;
8885  double f, fp, c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
8886  double uix, uiy, uiz;
8887  int i,j,k,nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1;
8888  int kp1, kp2, ii, jj, kk;
8889 
8890  VASSERT(thee != VNULL);
8891  VASSERT(perm != VNULL); /* potential due to permanent multipoles. */
8892  VASSERT(nlInduced != VNULL); /* potential due to non-local induced dipoles */
8893  VASSERT(!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
8894 
8895  atom = Valist_getAtom(thee->pbe->alist, atomID);
8896  VASSERT(atom->partID != 0); /* Currently all atoms must be in the same partition. */
8897  apos = Vatom_getPosition(atom);
8898 
8899  c = Vatom_getCharge(atom);
8900  dipole = Vatom_getDipole(atom);
8901  ux = dipole[0];
8902  uy = dipole[1];
8903  uz = dipole[2];
8904  quad = Vatom_getQuadrupole(atom);
8905  qxx = quad[0]/3.0;
8906  qxy = quad[1]/3.0;
8907  qxz = quad[2]/3.0;
8908  qyx = quad[3]/3.0;
8909  qyy = quad[4]/3.0;
8910  qyz = quad[5]/3.0;
8911  qzx = quad[6]/3.0;
8912  qzy = quad[7]/3.0;
8913  qzz = quad[8]/3.0;
8914 
8915  dipole = Vatom_getNLInducedDipole(atom);
8916  uix = dipole[0];
8917  uiy = dipole[1];
8918  uiz = dipole[2];
8919 
8920  /* Reset Field Gradients */
8921  pot = 0.0;
8922  for (i=0;i<3;i++){
8923  e[i] = 0.0;
8924  for (j=0;j<3;j++){
8925  de[i][j] = 0.0;
8926  dep[i][j] = 0.0;
8927  for (k=0;k<3;k++){
8928  d2e[i][j][k] = 0.0;
8929  }
8930  }
8931  }
8932 
8933  /* Mesh info */
8934  nx = thee->pmgp->nx;
8935  ny = thee->pmgp->ny;
8936  nz = thee->pmgp->nz;
8937  hx = thee->pmgp->hx;
8938  hy = thee->pmgp->hy;
8939  hzed = thee->pmgp->hzed;
8940  xlen = thee->pmgp->xlen;
8941  ylen = thee->pmgp->ylen;
8942  zlen = thee->pmgp->zlen;
8943  xmin = thee->pmgp->xmin;
8944  ymin = thee->pmgp->ymin;
8945  zmin = thee->pmgp->zmin;
8946  xmax = thee->pmgp->xmax;
8947  ymax = thee->pmgp->ymax;
8948  zmax = thee->pmgp->zmax;
8949  u = nlInduced->data;
8950  up = perm->data;
8951 
8952 
8953  /* Make sure we're on the grid */
8954  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
8955  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
8956  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
8957  Vnm_print(2, "qfNLDirectMultipoleForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
8958  } else {
8959 
8960  /* Convert the atom position to grid coordinates */
8961  position[0] = apos[0] - xmin;
8962  position[1] = apos[1] - ymin;
8963  position[2] = apos[2] - zmin;
8964  ifloat = position[0]/hx;
8965  jfloat = position[1]/hy;
8966  kfloat = position[2]/hzed;
8967  ip1 = (int)ceil(ifloat);
8968  ip2 = ip1 + 2;
8969  im1 = (int)floor(ifloat);
8970  im2 = im1 - 2;
8971  jp1 = (int)ceil(jfloat);
8972  jp2 = jp1 + 2;
8973  jm1 = (int)floor(jfloat);
8974  jm2 = jm1 - 2;
8975  kp1 = (int)ceil(kfloat);
8976  kp2 = kp1 + 2;
8977  km1 = (int)floor(kfloat);
8978  km2 = km1 - 2;
8979 
8980  /* This step shouldn't be necessary, but it saves nasty debugging
8981  * later on if something goes wrong */
8982  ip2 = VMIN2(ip2,nx-1);
8983  ip1 = VMIN2(ip1,nx-1);
8984  im1 = VMAX2(im1,0);
8985  im2 = VMAX2(im2,0);
8986  jp2 = VMIN2(jp2,ny-1);
8987  jp1 = VMIN2(jp1,ny-1);
8988  jm1 = VMAX2(jm1,0);
8989  jm2 = VMAX2(jm2,0);
8990  kp2 = VMIN2(kp2,nz-1);
8991  kp1 = VMIN2(kp1,nz-1);
8992  km1 = VMAX2(km1,0);
8993  km2 = VMAX2(km2,0);
8994 
8995  for (ii=im2; ii<=ip2; ii++) {
8996  mi = VFCHI4(ii,ifloat);
8997  mx = bspline4(mi);
8998  dmx = dbspline4(mi);
8999  d2mx = d2bspline4(mi);
9000  d3mx = d3bspline4(mi);
9001  for (jj=jm2; jj<=jp2; jj++) {
9002  mj = VFCHI4(jj,jfloat);
9003  my = bspline4(mj);
9004  dmy = dbspline4(mj);
9005  d2my = d2bspline4(mj);
9006  d3my = d3bspline4(mj);
9007  for (kk=km2; kk<=kp2; kk++) {
9008  mk = VFCHI4(kk,kfloat);
9009  mz = bspline4(mk);
9010  dmz = dbspline4(mk);
9011  d2mz = d2bspline4(mk);
9012  d3mz = d3bspline4(mk);
9013  f = u[IJK(ii,jj,kk)];
9014  fp = up[IJK(ii,jj,kk)];
9015  /* The potential */
9016  pot += f*mx*my*mz;
9017  /* The field */
9018  e[0] += f*dmx*my*mz/hx;
9019  e[1] += f*mx*dmy*mz/hy;
9020  e[2] += f*mx*my*dmz/hzed;
9021  /* The gradient of the field */
9022  de[0][0] += f*d2mx*my*mz/(hx*hx);
9023  de[1][0] += f*dmx*dmy*mz/(hy*hx);
9024  de[1][1] += f*mx*d2my*mz/(hy*hy);
9025  de[2][0] += f*dmx*my*dmz/(hx*hzed);
9026  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
9027  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
9028  /* The gradient of the (permanent) field */
9029  dep[0][0] += fp*d2mx*my*mz/(hx*hx);
9030  dep[1][0] += fp*dmx*dmy*mz/(hy*hx);
9031  dep[1][1] += fp*mx*d2my*mz/(hy*hy);
9032  dep[2][0] += fp*dmx*my*dmz/(hx*hzed);
9033  dep[2][1] += fp*mx*dmy*dmz/(hy*hzed);
9034  dep[2][2] += fp*mx*my*d2mz/(hzed*hzed);
9035  /* The 2nd gradient of the field */
9036  /* VxVxVa */
9037  d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
9038  d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
9039  d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
9040  /* VyVxVa */
9041  d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
9042  d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
9043  d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
9044  /* VyVyVa */
9045  d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
9046  d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
9047  d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
9048  /* VzVxVa */
9049  d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
9050  d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
9051  d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
9052  /* VzVyVa */
9053  d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
9054  d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
9055  d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
9056  /* VzVzVa */
9057  d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
9058  d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
9059  d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
9060  }
9061  }
9062  }
9063  }
9064 
9065  /* force on permanent multipole due to non-local induced reaction field */
9066 
9067  /* Monopole Force */
9068  force[0] = e[0]*c;
9069  force[1] = e[1]*c;
9070  force[2] = e[2]*c;
9071 
9072  /* Dipole Force */
9073  force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;
9074  force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;
9075  force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;
9076 
9077  /* Quadrupole Force */
9078  force[0] += d2e[0][0][0]*qxx
9079  + d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
9080  + d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
9081  force[1] += d2e[0][0][1]*qxx
9082  + d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
9083  + d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
9084  force[2] += d2e[0][0][2]*qxx
9085  + d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
9086  + d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
9087 
9088  /* torque on permanent mulitpole due to non-local induced reaction field */
9089 
9090  /* Dipole Torque */
9091  torque[0] = uy * e[2] - uz * e[1];
9092  torque[1] = uz * e[0] - ux * e[2];
9093  torque[2] = ux * e[1] - uy * e[0];
9094 
9095  /* Quadrupole Torque */
9096  /* Tx = -2.0*(Sum_a (Qya*dEaz) + Sum_b (Qzb*dEby))
9097  Ty = -2.0*(Sum_a (Qza*dEax) + Sum_b (Qxb*dEbz))
9098  Tz = -2.0*(Sum_a (Qxa*dEay) + Sum_b (Qyb*dEbx)) */
9099  de[0][1] = de[1][0];
9100  de[0][2] = de[2][0];
9101  de[1][2] = de[2][1];
9102  torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
9103  - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
9104  torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
9105  - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
9106  torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
9107  - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
9108 
9109  /* force on non-local induced dipole due to permanent reaction field */
9110 
9111  force[0] -= dep[0][0]*uix+dep[1][0]*uiy+dep[2][0]*uiz;
9112  force[1] -= dep[1][0]*uix+dep[1][1]*uiy+dep[2][1]*uiz;
9113  force[2] -= dep[2][0]*uix+dep[2][1]*uiy+dep[2][2]*uiz;
9114 
9115  force[0] = 0.5 * force[0];
9116  force[1] = 0.5 * force[1];
9117  force[2] = 0.5 * force[2];
9118  torque[0] = 0.5 * torque[0];
9119  torque[1] = 0.5 * torque[1];
9120  torque[2] = 0.5 * torque[2];
9121 
9122  /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
9123  printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
9124 }
9125 
9126 VPUBLIC void Vpmg_ibDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced,
9127  int atomID, double force[3]) {
9128 
9129  Vatom *atom;
9130  Valist *alist;
9131  Vacc *acc;
9132  Vpbe *pbe;
9133  Vsurf_Meth srfm;
9134 
9135  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
9136  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
9137  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
9138  double izmagic;
9139  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9140 
9141  VASSERT(thee != VNULL);
9142  VASSERT(perm != VNULL); /* potential due to permanent multipoles.*/
9143  VASSERT(induced != VNULL); /* potential due to induced dipoles. */
9144  VASSERT (!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
9145 
9146  acc = thee->pbe->acc;
9147  srfm = thee->surfMeth;
9148  atom = Valist_getAtom(thee->pbe->alist, atomID);
9149  VASSERT(atom->partID != 0); /* Currently all atoms must be in the same partition. */
9150  apos = Vatom_getPosition(atom);
9151  arad = Vatom_getRadius(atom);
9152 
9153  /* Reset force */
9154  force[0] = 0.0;
9155  force[1] = 0.0;
9156  force[2] = 0.0;
9157 
9158  /* Get PBE info */
9159  pbe = thee->pbe;
9160  acc = pbe->acc;
9161  alist = pbe->alist;
9162  irad = Vpbe_getMaxIonRadius(pbe);
9163  zkappa2 = Vpbe_getZkappa2(pbe);
9164  izmagic = 1.0/Vpbe_getZmagic(pbe);
9165 
9166  VASSERT (zkappa2 > VPMGSMALL); /* It is ok to run AMOEBA with no ions, but this is checked for higher up in the driver. */
9167 
9168  /* Mesh info */
9169  nx = induced->nx;
9170  ny = induced->ny;
9171  nz = induced->nz;
9172  hx = induced->hx;
9173  hy = induced->hy;
9174  hzed = induced->hzed;
9175  xmin = induced->xmin;
9176  ymin = induced->ymin;
9177  zmin = induced->zmin;
9178  xmax = induced->xmax;
9179  ymax = induced->ymax;
9180  zmax = induced->zmax;
9181  xlen = xmax-xmin;
9182  ylen = ymax-ymin;
9183  zlen = zmax-zmin;
9184 
9185  /* Make sure we're on the grid */
9186  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9187  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9188  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9189  Vnm_print(2, "Vpmg_ibForce: Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
9190  apos[0], apos[1], apos[2]);
9191  Vnm_print(2, "Vpmg_ibForce: xmin = %g, xmax = %g\n", xmin, xmax);
9192  Vnm_print(2, "Vpmg_ibForce: ymin = %g, ymax = %g\n", ymin, ymax);
9193  Vnm_print(2, "Vpmg_ibForce: zmin = %g, zmax = %g\n", zmin, zmax);
9194  fflush(stderr);
9195  } else {
9196 
9197  /* Convert the atom position to grid reference frame */
9198  position[0] = apos[0] - xmin;
9199  position[1] = apos[1] - ymin;
9200  position[2] = apos[2] - zmin;
9201 
9202  /* Integrate over points within this atom's (inflated) radius */
9203  rtot = (irad + arad + thee->splineWin);
9204  rtot2 = VSQR(rtot);
9205  dx = rtot + 0.5*hx;
9206  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
9207  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
9208  for (i=imin; i<=imax; i++) {
9209  dx2 = VSQR(position[0] - hx*i);
9210  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
9211  else dy = 0.5*hy;
9212  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
9213  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
9214  for (j=jmin; j<=jmax; j++) {
9215  dy2 = VSQR(position[1] - hy*j);
9216  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
9217  else dz = 0.5*hzed;
9218  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
9219  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
9220  for (k=kmin; k<=kmax; k++) {
9221  dz2 = VSQR(k*hzed - position[2]);
9222  /* See if grid point is inside ivdw radius and set ccf
9223  * accordingly (do spline assignment here) */
9224  if ((dz2 + dy2 + dx2) <= rtot2) {
9225  gpos[0] = i*hx + xmin;
9226  gpos[1] = j*hy + ymin;
9227  gpos[2] = k*hzed + zmin;
9228  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad,
9229  atom, tgrad);
9230  fmag = induced->data[IJK(i,j,k)];
9231  fmag *= perm->data[IJK(i,j,k)];
9232  fmag *= thee->kappa[IJK(i,j,k)];
9233  force[0] += (zkappa2*fmag*tgrad[0]);
9234  force[1] += (zkappa2*fmag*tgrad[1]);
9235  force[2] += (zkappa2*fmag*tgrad[2]);
9236  }
9237  } /* k loop */
9238  } /* j loop */
9239  } /* i loop */
9240  }
9241 
9242  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
9243  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
9244  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
9245 
9246 }
9247 
9248 VPUBLIC void Vpmg_ibNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
9249  int atomID, double force[3]) {
9250  Vpmg_ibDirectPolForce(thee, perm, nlInduced, atomID, force);
9251 }
9252 
9253 VPUBLIC void Vpmg_dbDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced,
9254  int atomID, double force[3]) {
9255 
9256  Vatom *atom;
9257  Vacc *acc;
9258  Vpbe *pbe;
9259  Vsurf_Meth srfm;
9260 
9261  double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
9262  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
9263  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
9264  double *u, *up, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
9265  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
9266  double dHzijkm1[3];
9267  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9268 
9269  VASSERT(thee != VNULL);
9270  VASSERT(perm != VNULL); /* permanent multipole PMG solution. */
9271  VASSERT(induced != VNULL); /* potential due to induced dipoles. */
9272 
9273  acc = thee->pbe->acc;
9274  atom = Valist_getAtom(thee->pbe->alist, atomID);
9275  VASSERT (atom->partID != 0); /* Currently all atoms must be in the same partition. */
9276  apos = Vatom_getPosition(atom);
9277  arad = Vatom_getRadius(atom);
9278 
9279  /* Reset force */
9280  force[0] = 0.0;
9281  force[1] = 0.0;
9282  force[2] = 0.0;
9283 
9284  /* Get PBE info */
9285  pbe = thee->pbe;
9286  acc = pbe->acc;
9287  srfm = thee->surfMeth;
9288  epsp = Vpbe_getSoluteDiel(pbe);
9289  epsw = Vpbe_getSolventDiel(pbe);
9291  izmagic = 1.0/Vpbe_getZmagic(pbe);
9292 
9293  deps = (epsw - epsp);
9294  depsi = 1.0/deps;
9295  VASSERT(VABS(deps) > VPMGSMALL);
9296 
9297  /* Mesh info */
9298  nx = thee->pmgp->nx;
9299  ny = thee->pmgp->ny;
9300  nz = thee->pmgp->nz;
9301  hx = thee->pmgp->hx;
9302  hy = thee->pmgp->hy;
9303  hzed = thee->pmgp->hzed;
9304  xlen = thee->pmgp->xlen;
9305  ylen = thee->pmgp->ylen;
9306  zlen = thee->pmgp->zlen;
9307  xmin = thee->pmgp->xmin;
9308  ymin = thee->pmgp->ymin;
9309  zmin = thee->pmgp->zmin;
9310  xmax = thee->pmgp->xmax;
9311  ymax = thee->pmgp->ymax;
9312  zmax = thee->pmgp->zmax;
9313  /* If the permanent and induced potentials are flipped the
9314  results are exactly the same. */
9315  u = induced->data;
9316  up = perm->data;
9317 
9318  /* Make sure we're on the grid */
9319  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9320  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9321  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9322  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]);
9323  Vnm_print(2, "Vpmg_dbDirectPolForce: xmin = %g, xmax = %g\n", xmin, xmax);
9324  Vnm_print(2, "Vpmg_dbDirectPolForce: ymin = %g, ymax = %g\n", ymin, ymax);
9325  Vnm_print(2, "Vpmg_dbDirectPolForce: zmin = %g, zmax = %g\n", zmin, zmax);
9326  fflush(stderr);
9327  } else {
9328 
9329  /* Convert the atom position to grid reference frame */
9330  position[0] = apos[0] - xmin;
9331  position[1] = apos[1] - ymin;
9332  position[2] = apos[2] - zmin;
9333 
9334  /* Integrate over points within this atom's (inflated) radius */
9335  rtot = (arad + thee->splineWin);
9336  rtot2 = VSQR(rtot);
9337  dx = rtot/hx;
9338  imin = (int)floor((position[0]-rtot)/hx);
9339  if (imin < 1) {
9340  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9341  return;
9342  }
9343  imax = (int)ceil((position[0]+rtot)/hx);
9344  if (imax > (nx-2)) {
9345  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9346  return;
9347  }
9348  jmin = (int)floor((position[1]-rtot)/hy);
9349  if (jmin < 1) {
9350  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9351  return;
9352  }
9353  jmax = (int)ceil((position[1]+rtot)/hy);
9354  if (jmax > (ny-2)) {
9355  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9356  return;
9357  }
9358  kmin = (int)floor((position[2]-rtot)/hzed);
9359  if (kmin < 1) {
9360  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9361  return;
9362  }
9363  kmax = (int)ceil((position[2]+rtot)/hzed);
9364  if (kmax > (nz-2)) {
9365  Vnm_print(2, "Vpmg_dbDirectPolForce: Atom %d off grid!\n", atomID);
9366  return;
9367  }
9368  for (i=imin; i<=imax; i++) {
9369  for (j=jmin; j<=jmax; j++) {
9370  for (k=kmin; k<=kmax; k++) {
9371  /* i,j,k */
9372  gpos[0] = (i+0.5)*hx + xmin;
9373  gpos[1] = j*hy + ymin;
9374  gpos[2] = k*hzed + zmin;
9375  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
9376  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9377  atom, dHxijk);
9378  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
9379  gpos[0] = i*hx + xmin;
9380  gpos[1] = (j+0.5)*hy + ymin;
9381  gpos[2] = k*hzed + zmin;
9382  Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
9383  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9384  atom, dHyijk);
9385  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
9386  gpos[0] = i*hx + xmin;
9387  gpos[1] = j*hy + ymin;
9388  gpos[2] = (k+0.5)*hzed + zmin;
9389  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
9390  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9391  atom, dHzijk);
9392  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
9393  /* i-1,j,k */
9394  gpos[0] = (i-0.5)*hx + xmin;
9395  gpos[1] = j*hy + ymin;
9396  gpos[2] = k*hzed + zmin;
9397  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
9398  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9399  atom, dHxim1jk);
9400  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
9401  /* i,j-1,k */
9402  gpos[0] = i*hx + xmin;
9403  gpos[1] = (j-0.5)*hy + ymin;
9404  gpos[2] = k*hzed + zmin;
9405  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
9406  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9407  atom, dHyijm1k);
9408  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
9409  /* i,j,k-1 */
9410  gpos[0] = i*hx + xmin;
9411  gpos[1] = j*hy + ymin;
9412  gpos[2] = (k-0.5)*hzed + zmin;
9413  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
9414  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9415  atom, dHzijkm1);
9416  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
9417 
9418  dbFmag = up[IJK(i,j,k)];
9419  tgrad[0] =
9420  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9421  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9422  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9423  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9424  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9425  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9426  tgrad[1] =
9427  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9428  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9429  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9430  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9431  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9432  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9433  tgrad[2] =
9434  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9435  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9436  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9437  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9438  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9439  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9440  force[0] += (dbFmag*tgrad[0]);
9441  force[1] += (dbFmag*tgrad[1]);
9442  force[2] += (dbFmag*tgrad[2]);
9443 
9444  } /* k loop */
9445  } /* j loop */
9446  } /* i loop */
9447 
9448  force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
9449  force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
9450  force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
9451 
9452  }
9453 }
9454 
9455 VPUBLIC void Vpmg_dbNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
9456  int atomID, double force[3]) {
9457  Vpmg_dbDirectPolForce(thee, perm, nlInduced, atomID, force);
9458 }
9459 
9460 VPUBLIC void Vpmg_qfMutualPolForce(Vpmg *thee, Vgrid *induced,
9461  Vgrid *nlinduced, int atomID, double force[3]) {
9462 
9463  Vatom *atom;
9464  double *apos, *dipole, position[3], hx, hy, hzed;
9465  double *u, *unl;
9466  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
9467  double de[3][3], denl[3][3];
9468  double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, mi, mj, mk;
9469  double ifloat, jfloat, kfloat;
9470  double f, fnl, uix, uiy, uiz, uixnl, uiynl, uiznl;
9471  int i,j,k,nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1;
9472  int kp1, kp2, ii, jj, kk;
9473 
9474  VASSERT(thee != VNULL); /* PMG object with PBE info. */
9475  VASSERT(induced != VNULL); /* potential due to induced dipoles. */
9476  VASSERT(nlinduced != VNULL); /* potential due to non-local induced dipoles. */
9477  atom = Valist_getAtom(thee->pbe->alist, atomID);
9478  VASSERT(atom->partID != 0); /* all atoms must be in the same partition. */
9479  apos = Vatom_getPosition(atom);
9480  dipole = Vatom_getInducedDipole(atom);
9481  uix = dipole[0];
9482  uiy = dipole[1];
9483  uiz = dipole[2];
9484  dipole = Vatom_getNLInducedDipole(atom);
9485  uixnl = dipole[0];
9486  uiynl = dipole[1];
9487  uiznl = dipole[2];
9488  u = induced->data;
9489  unl = nlinduced->data;
9490 
9491  for (i=0;i<3;i++){
9492  for (j=0;j<3;j++){
9493  de[i][j] = 0.0;
9494  denl[i][j] = 0.0;
9495  }
9496  }
9497 
9498  /* Mesh info */
9499  nx = induced->nx;
9500  ny = induced->ny;
9501  nz = induced->nz;
9502  hx = induced->hx;
9503  hy = induced->hy;
9504  hzed = induced->hzed;
9505  xmin = induced->xmin;
9506  ymin = induced->ymin;
9507  zmin = induced->zmin;
9508  xmax = induced->xmax;
9509  ymax = induced->ymax;
9510  zmax = induced->zmax;
9511  xlen = xmax-xmin;
9512  ylen = ymax-ymin;
9513  zlen = zmax-zmin;
9514 
9515  /* If we aren't in the current position, then we're done */
9516  if (atom->partID == 0) return;
9517 
9518  /* Make sure we're on the grid */
9519  if ((apos[0]<=(xmin+2*hx)) || (apos[0]>=(xmax-2*hx)) \
9520  || (apos[1]<=(ymin+2*hy)) || (apos[1]>=(ymax-2*hy)) \
9521  || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
9522  Vnm_print(2, "qfMutualPolForce: Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
9523  fflush(stderr);
9524  } else {
9525 
9526  /* Convert the atom position to grid coordinates */
9527  position[0] = apos[0] - xmin;
9528  position[1] = apos[1] - ymin;
9529  position[2] = apos[2] - zmin;
9530  ifloat = position[0]/hx;
9531  jfloat = position[1]/hy;
9532  kfloat = position[2]/hzed;
9533  ip1 = (int)ceil(ifloat);
9534  ip2 = ip1 + 2;
9535  im1 = (int)floor(ifloat);
9536  im2 = im1 - 2;
9537  jp1 = (int)ceil(jfloat);
9538  jp2 = jp1 + 2;
9539  jm1 = (int)floor(jfloat);
9540  jm2 = jm1 - 2;
9541  kp1 = (int)ceil(kfloat);
9542  kp2 = kp1 + 2;
9543  km1 = (int)floor(kfloat);
9544  km2 = km1 - 2;
9545 
9546  /* This step shouldn't be necessary, but it saves nasty debugging
9547  * later on if something goes wrong */
9548  ip2 = VMIN2(ip2,nx-1);
9549  ip1 = VMIN2(ip1,nx-1);
9550  im1 = VMAX2(im1,0);
9551  im2 = VMAX2(im2,0);
9552  jp2 = VMIN2(jp2,ny-1);
9553  jp1 = VMIN2(jp1,ny-1);
9554  jm1 = VMAX2(jm1,0);
9555  jm2 = VMAX2(jm2,0);
9556  kp2 = VMIN2(kp2,nz-1);
9557  kp1 = VMIN2(kp1,nz-1);
9558  km1 = VMAX2(km1,0);
9559  km2 = VMAX2(km2,0);
9560 
9561  for (ii=im2; ii<=ip2; ii++) {
9562  mi = VFCHI4(ii,ifloat);
9563  mx = bspline4(mi);
9564  dmx = dbspline4(mi);
9565  d2mx = d2bspline4(mi);
9566  for (jj=jm2; jj<=jp2; jj++) {
9567  mj = VFCHI4(jj,jfloat);
9568  my = bspline4(mj);
9569  dmy = dbspline4(mj);
9570  d2my = d2bspline4(mj);
9571  for (kk=km2; kk<=kp2; kk++) {
9572  mk = VFCHI4(kk,kfloat);
9573  mz = bspline4(mk);
9574  dmz = dbspline4(mk);
9575  d2mz = d2bspline4(mk);
9576  f = u[IJK(ii,jj,kk)];
9577  fnl = unl[IJK(ii,jj,kk)];
9578 
9579  /* The gradient of the reaction field
9580  due to induced dipoles */
9581  de[0][0] += f*d2mx*my*mz/(hx*hx);
9582  de[1][0] += f*dmx*dmy*mz/(hy*hx);
9583  de[1][1] += f*mx*d2my*mz/(hy*hy);
9584  de[2][0] += f*dmx*my*dmz/(hx*hzed);
9585  de[2][1] += f*mx*dmy*dmz/(hy*hzed);
9586  de[2][2] += f*mx*my*d2mz/(hzed*hzed);
9587 
9588  /* The gradient of the reaction field
9589  due to non-local induced dipoles */
9590  denl[0][0] += fnl*d2mx*my*mz/(hx*hx);
9591  denl[1][0] += fnl*dmx*dmy*mz/(hy*hx);
9592  denl[1][1] += fnl*mx*d2my*mz/(hy*hy);
9593  denl[2][0] += fnl*dmx*my*dmz/(hx*hzed);
9594  denl[2][1] += fnl*mx*dmy*dmz/(hy*hzed);
9595  denl[2][2] += fnl*mx*my*d2mz/(hzed*hzed);
9596  }
9597  }
9598  }
9599  }
9600 
9601  /* mutual polarization force */
9602  force[0] = -(de[0][0]*uixnl + de[1][0]*uiynl + de[2][0]*uiznl);
9603  force[1] = -(de[1][0]*uixnl + de[1][1]*uiynl + de[2][1]*uiznl);
9604  force[2] = -(de[2][0]*uixnl + de[2][1]*uiynl + de[2][2]*uiznl);
9605  force[0] -= denl[0][0]*uix + denl[1][0]*uiy + denl[2][0]*uiz;
9606  force[1] -= denl[1][0]*uix + denl[1][1]*uiy + denl[2][1]*uiz;
9607  force[2] -= denl[2][0]*uix + denl[2][1]*uiy + denl[2][2]*uiz;
9608 
9609  force[0] = 0.5 * force[0];
9610  force[1] = 0.5 * force[1];
9611  force[2] = 0.5 * force[2];
9612 
9613 }
9614 
9615 VPUBLIC void Vpmg_ibMutualPolForce(Vpmg *thee, Vgrid *induced, Vgrid *nlinduced,
9616  int atomID, double force[3]) {
9617 
9618  Vatom *atom;
9619  Valist *alist;
9620  Vacc *acc;
9621  Vpbe *pbe;
9622  Vsurf_Meth srfm;
9623 
9624  double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
9625  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
9626  double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
9627  double izmagic;
9628  int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9629 
9630  VASSERT(thee != VNULL); /* We need a PMG object with PBE info. */
9631  VASSERT(induced != VNULL); /* We need the potential due to induced dipoles. */
9632  VASSERT(nlinduced != VNULL); /* We need the potential due to non-local induced dipoles. */
9633  VASSERT (!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
9634 
9635  atom = Valist_getAtom(thee->pbe->alist, atomID);
9636  VASSERT (atom->partID != 0); /* Currently all atoms must be in the same partition. */
9637 
9638  acc = thee->pbe->acc;
9639  srfm = thee->surfMeth;
9640  apos = Vatom_getPosition(atom);
9641  arad = Vatom_getRadius(atom);
9642 
9643  /* Reset force */
9644  force[0] = 0.0;
9645  force[1] = 0.0;
9646  force[2] = 0.0;
9647 
9648  /* If we aren't in the current position, then we're done */
9649  if (atom->partID == 0) return;
9650 
9651  /* Get PBE info */
9652  pbe = thee->pbe;
9653  acc = pbe->acc;
9654  alist = pbe->alist;
9655  irad = Vpbe_getMaxIonRadius(pbe);
9656  zkappa2 = Vpbe_getZkappa2(pbe);
9657  izmagic = 1.0/Vpbe_getZmagic(pbe);
9658 
9659  VASSERT (zkappa2 > VPMGSMALL); /* Should be a check for this further up.*/
9660 
9661  /* Mesh info */
9662  nx = induced->nx;
9663  ny = induced->ny;
9664  nz = induced->nz;
9665  hx = induced->hx;
9666  hy = induced->hy;
9667  hzed = induced->hzed;
9668  xmin = induced->xmin;
9669  ymin = induced->ymin;
9670  zmin = induced->zmin;
9671  xmax = induced->xmax;
9672  ymax = induced->ymax;
9673  zmax = induced->zmax;
9674  xlen = xmax-xmin;
9675  ylen = ymax-ymin;
9676  zlen = zmax-zmin;
9677 
9678  /* Make sure we're on the grid */
9679  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9680  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9681  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9682  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]);
9683  Vnm_print(2, "Vpmg_ibMutalPolForce: xmin = %g, xmax = %g\n", xmin, xmax);
9684  Vnm_print(2, "Vpmg_ibMutalPolForce: ymin = %g, ymax = %g\n", ymin, ymax);
9685  Vnm_print(2, "Vpmg_ibMutalPolForce: zmin = %g, zmax = %g\n", zmin, zmax);
9686  fflush(stderr);
9687  } else {
9688 
9689  /* Convert the atom position to grid reference frame */
9690  position[0] = apos[0] - xmin;
9691  position[1] = apos[1] - ymin;
9692  position[2] = apos[2] - zmin;
9693 
9694  /* Integrate over points within this atom's (inflated) radius */
9695  rtot = (irad + arad + thee->splineWin);
9696  rtot2 = VSQR(rtot);
9697  dx = rtot + 0.5*hx;
9698  imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
9699  imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
9700  for (i=imin; i<=imax; i++) {
9701  dx2 = VSQR(position[0] - hx*i);
9702  if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
9703  else dy = 0.5*hy;
9704  jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
9705  jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
9706  for (j=jmin; j<=jmax; j++) {
9707  dy2 = VSQR(position[1] - hy*j);
9708  if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
9709  else dz = 0.5*hzed;
9710  kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
9711  kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
9712  for (k=kmin; k<=kmax; k++) {
9713  dz2 = VSQR(k*hzed - position[2]);
9714  /* See if grid point is inside ivdw radius and set ccf
9715  * accordingly (do spline assignment here) */
9716  if ((dz2 + dy2 + dx2) <= rtot2) {
9717  gpos[0] = i*hx + xmin;
9718  gpos[1] = j*hy + ymin;
9719  gpos[2] = k*hzed + zmin;
9720  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad,
9721  atom, tgrad);
9722  fmag = induced->data[IJK(i,j,k)];
9723  fmag *= nlinduced->data[IJK(i,j,k)];
9724  fmag *= thee->kappa[IJK(i,j,k)];
9725  force[0] += (zkappa2*fmag*tgrad[0]);
9726  force[1] += (zkappa2*fmag*tgrad[1]);
9727  force[2] += (zkappa2*fmag*tgrad[2]);
9728  }
9729  } /* k loop */
9730  } /* j loop */
9731  } /* i loop */
9732  }
9733 
9734  force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
9735  force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
9736  force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
9737 }
9738 
9739 VPUBLIC void Vpmg_dbMutualPolForce(Vpmg *thee, Vgrid *induced,
9740  Vgrid *nlinduced, int atomID,
9741  double force[3]) {
9742 
9743  Vatom *atom;
9744  Vacc *acc;
9745  Vpbe *pbe;
9746  Vsurf_Meth srfm;
9747 
9748  double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
9749  double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
9750  double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
9751  double *u, *unl, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
9752  double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3];
9753  double dHzijkm1[3];
9754  int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
9755 
9756  VASSERT(thee != VNULL); /* PMG object with PBE info. */
9757  VASSERT(induced != VNULL); /* potential due to induced dipoles.*/
9758  VASSERT(nlinduced != VNULL); /* potential due to non-local induced dipoles.*/
9759 
9760  acc = thee->pbe->acc;
9761  srfm = thee->surfMeth;
9762  atom = Valist_getAtom(thee->pbe->alist, atomID);
9763  VASSERT (atom->partID != 0); /* all atoms must be in the same partition.*/
9764  apos = Vatom_getPosition(atom);
9765  arad = Vatom_getRadius(atom);
9766 
9767  /* Reset force */
9768  force[0] = 0.0;
9769  force[1] = 0.0;
9770  force[2] = 0.0;
9771 
9772  /* Get PBE info */
9773  pbe = thee->pbe;
9774  acc = pbe->acc;
9775  epsp = Vpbe_getSoluteDiel(pbe);
9776  epsw = Vpbe_getSolventDiel(pbe);
9778  izmagic = 1.0/Vpbe_getZmagic(pbe);
9779 
9780  deps = (epsw - epsp);
9781  depsi = 1.0/deps;
9782  VASSERT(VABS(deps) > VPMGSMALL);
9783 
9784  /* Mesh info */
9785  nx = thee->pmgp->nx;
9786  ny = thee->pmgp->ny;
9787  nz = thee->pmgp->nz;
9788  hx = thee->pmgp->hx;
9789  hy = thee->pmgp->hy;
9790  hzed = thee->pmgp->hzed;
9791  xlen = thee->pmgp->xlen;
9792  ylen = thee->pmgp->ylen;
9793  zlen = thee->pmgp->zlen;
9794  xmin = thee->pmgp->xmin;
9795  ymin = thee->pmgp->ymin;
9796  zmin = thee->pmgp->zmin;
9797  xmax = thee->pmgp->xmax;
9798  ymax = thee->pmgp->ymax;
9799  zmax = thee->pmgp->zmax;
9800  u = induced->data;
9801  unl = nlinduced->data;
9802 
9803  /* Make sure we're on the grid */
9804  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
9805  (apos[1]<=ymin) || (apos[1]>=ymax) || \
9806  (apos[2]<=zmin) || (apos[2]>=zmax)) {
9807  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]);
9808  Vnm_print(2, "Vpmg_dbMutualPolForce: xmin = %g, xmax = %g\n", xmin, xmax);
9809  Vnm_print(2, "Vpmg_dbMutualPolForce: ymin = %g, ymax = %g\n", ymin, ymax);
9810  Vnm_print(2, "Vpmg_dbMutualPolForce: zmin = %g, zmax = %g\n", zmin, zmax);
9811  fflush(stderr);
9812  } else {
9813 
9814  /* Convert the atom position to grid reference frame */
9815  position[0] = apos[0] - xmin;
9816  position[1] = apos[1] - ymin;
9817  position[2] = apos[2] - zmin;
9818 
9819  /* Integrate over points within this atom's (inflated) radius */
9820  rtot = (arad + thee->splineWin);
9821  rtot2 = VSQR(rtot);
9822  dx = rtot/hx;
9823  imin = (int)floor((position[0]-rtot)/hx);
9824  if (imin < 1) {
9825  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9826  return;
9827  }
9828  imax = (int)ceil((position[0]+rtot)/hx);
9829  if (imax > (nx-2)) {
9830  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9831  return;
9832  }
9833  jmin = (int)floor((position[1]-rtot)/hy);
9834  if (jmin < 1) {
9835  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9836  return;
9837  }
9838  jmax = (int)ceil((position[1]+rtot)/hy);
9839  if (jmax > (ny-2)) {
9840  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9841  return;
9842  }
9843  kmin = (int)floor((position[2]-rtot)/hzed);
9844  if (kmin < 1) {
9845  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9846  return;
9847  }
9848  kmax = (int)ceil((position[2]+rtot)/hzed);
9849  if (kmax > (nz-2)) {
9850  Vnm_print(2, "Vpmg_dbMutualPolForce: Atom %d off grid!\n", atomID);
9851  return;
9852  }
9853  for (i=imin; i<=imax; i++) {
9854  for (j=jmin; j<=jmax; j++) {
9855  for (k=kmin; k<=kmax; k++) {
9856  /* i,j,k */
9857  gpos[0] = (i+0.5)*hx + xmin;
9858  gpos[1] = j*hy + ymin;
9859  gpos[2] = k*hzed + zmin;
9860  Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
9861  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9862  atom, dHxijk);
9863  for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
9864  gpos[0] = i*hx + xmin;
9865  gpos[1] = (j+0.5)*hy + ymin;
9866  gpos[2] = k*hzed + zmin;
9867  Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
9868  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9869  atom, dHyijk);
9870  for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
9871  gpos[0] = i*hx + xmin;
9872  gpos[1] = j*hy + ymin;
9873  gpos[2] = (k+0.5)*hzed + zmin;
9874  Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
9875  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9876  atom, dHzijk);
9877  for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
9878  /* i-1,j,k */
9879  gpos[0] = (i-0.5)*hx + xmin;
9880  gpos[1] = j*hy + ymin;
9881  gpos[2] = k*hzed + zmin;
9882  Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
9883  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9884  atom, dHxim1jk);
9885  for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
9886  /* i,j-1,k */
9887  gpos[0] = i*hx + xmin;
9888  gpos[1] = (j-0.5)*hy + ymin;
9889  gpos[2] = k*hzed + zmin;
9890  Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
9891  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9892  atom, dHyijm1k);
9893  for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
9894  /* i,j,k-1 */
9895  gpos[0] = i*hx + xmin;
9896  gpos[1] = j*hy + ymin;
9897  gpos[2] = (k-0.5)*hzed + zmin;
9898  Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
9899  Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
9900  atom, dHzijkm1);
9901  for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
9902  dbFmag = unl[IJK(i,j,k)];
9903  tgrad[0] =
9904  (dHxijk[0] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9905  + dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9906  + (dHyijk[0] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9907  + dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9908  + (dHzijk[0] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9909  + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9910  tgrad[1] =
9911  (dHxijk[1] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9912  + dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9913  + (dHyijk[1] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9914  + dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9915  + (dHzijk[1] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9916  + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9917  tgrad[2] =
9918  (dHxijk[2] *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
9919  + dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
9920  + (dHyijk[2] *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
9921  + dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
9922  + (dHzijk[2] *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
9923  + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
9924  force[0] += (dbFmag*tgrad[0]);
9925  force[1] += (dbFmag*tgrad[1]);
9926  force[2] += (dbFmag*tgrad[2]);
9927  } /* k loop */
9928  } /* j loop */
9929  } /* i loop */
9930 
9931  force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
9932  force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
9933  force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
9934  }
9935 }
9936 
9937 #endif /* if defined(WITH_TINKER) */
9938 
9939 VPRIVATE void fillcoCoefSpline4(Vpmg *thee) {
9940 
9941  Valist *alist;
9942  Vpbe *pbe;
9943  Vatom *atom;
9944  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
9945  double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
9946  double irad, dx, dy, dz, epsw, epsp, w2i;
9947  double hx, hy, hzed, *apos, arad, sctot2;
9948  double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin;
9949  double dist, value, denom, sm, sm2, sm3, sm4, sm5, sm6, sm7;
9950  double e, e2, e3, e4, e5, e6, e7;
9951  double b, b2, b3, b4, b5, b6, b7;
9952  double c0, c1, c2, c3, c4, c5, c6, c7;
9953  double ic0, ic1, ic2, ic3, ic4, ic5, ic6, ic7;
9954  int i, j, k, nx, ny, nz, iatom;
9955  int imin, imax, jmin, jmax, kmin, kmax;
9956 
9957  VASSERT(thee != VNULL);
9958  splineWin = thee->splineWin;
9959 
9960  /* Get PBE info */
9961  pbe = thee->pbe;
9962  alist = pbe->alist;
9963  irad = Vpbe_getMaxIonRadius(pbe);
9964  ionstr = Vpbe_getBulkIonicStrength(pbe);
9965  epsw = Vpbe_getSolventDiel(pbe);
9966  epsp = Vpbe_getSoluteDiel(pbe);
9967 
9968  /* Mesh info */
9969  nx = thee->pmgp->nx;
9970  ny = thee->pmgp->ny;
9971  nz = thee->pmgp->nz;
9972  hx = thee->pmgp->hx;
9973  hy = thee->pmgp->hy;
9974  hzed = thee->pmgp->hzed;
9975 
9976  /* Define the total domain size */
9977  xlen = thee->pmgp->xlen;
9978  ylen = thee->pmgp->ylen;
9979  zlen = thee->pmgp->zlen;
9980 
9981  /* Define the min/max dimensions */
9982  xmin = thee->pmgp->xcent - (xlen/2.0);
9983  ymin = thee->pmgp->ycent - (ylen/2.0);
9984  zmin = thee->pmgp->zcent - (zlen/2.0);
9985  xmax = thee->pmgp->xcent + (xlen/2.0);
9986  ymax = thee->pmgp->ycent + (ylen/2.0);
9987  zmax = thee->pmgp->zcent + (zlen/2.0);
9988 
9989  /* This is a floating point parameter related to the non-zero nature of the
9990  * bulk ionic strength. If the ionic strength is greater than zero; this
9991  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
9992  * Otherwise, this parameter is set to 0.0 */
9993  if (ionstr > VPMGSMALL) ionmask = 1.0;
9994  else ionmask = 0.0;
9995 
9996  /* Reset the kappa, epsx, epsy, and epsz arrays */
9997  for (i=0; i<(nx*ny*nz); i++) {
9998  thee->kappa[i] = 1.0;
9999  thee->epsx[i] = 1.0;
10000  thee->epsy[i] = 1.0;
10001  thee->epsz[i] = 1.0;
10002  }
10003 
10004  /* Loop through the atoms and do assign the dielectric */
10005  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
10006 
10007  atom = Valist_getAtom(alist, iatom);
10008  apos = Vatom_getPosition(atom);
10009  arad = Vatom_getRadius(atom);
10010 
10011  b = arad - splineWin;
10012  e = arad + splineWin;
10013  e2 = e * e;
10014  e3 = e2 * e;
10015  e4 = e3 * e;
10016  e5 = e4 * e;
10017  e6 = e5 * e;
10018  e7 = e6 * e;
10019  b2 = b * b;
10020  b3 = b2 * b;
10021  b4 = b3 * b;
10022  b5 = b4 * b;
10023  b6 = b5 * b;
10024  b7 = b6 * b;
10025  denom = e7 - 7.0*b*e6 + 21.0*b2*e5 - 35.0*e4*b3
10026  + 35.0*e3*b4 - 21.0*b5*e2 + 7.0*e*b6 - b7;
10027  c0 = b4*(35.0*e3 - 21.0*b*e2 + 7*e*b2 - b3)/denom;
10028  c1 = -140.0*b3*e3/denom;
10029  c2 = 210.0*e2*b2*(e + b)/denom;
10030  c3 = -140.0*e*b*(e2 + 3.0*b*e + b2)/denom;
10031  c4 = 35.0*(e3 + 9.0*b*e2 + + 9.0*e*b2 + b3)/denom;
10032  c5 = -84.0*(e2 + 3.0*b*e + b2)/denom;
10033  c6 = 70.0*(e + b)/denom;
10034  c7 = -20.0/denom;
10035 
10036  b = irad + arad - splineWin;
10037  e = irad + arad + splineWin;
10038  e2 = e * e;
10039  e3 = e2 * e;
10040  e4 = e3 * e;
10041  e5 = e4 * e;
10042  e6 = e5 * e;
10043  e7 = e6 * e;
10044  b2 = b * b;
10045  b3 = b2 * b;
10046  b4 = b3 * b;
10047  b5 = b4 * b;
10048  b6 = b5 * b;
10049  b7 = b6 * b;
10050  denom = e7 - 7.0*b*e6 + 21.0*b2*e5 - 35.0*e4*b3
10051  + 35.0*e3*b4 - 21.0*b5*e2 + 7.0*e*b6 - b7;
10052  ic0 = b4*(35.0*e3 - 21.0*b*e2 + 7*e*b2 - b3)/denom;
10053  ic1 = -140.0*b3*e3/denom;
10054  ic2 = 210.0*e2*b2*(e + b)/denom;
10055  ic3 = -140.0*e*b*(e2 + 3.0*b*e + b2)/denom;
10056  ic4 = 35.0*(e3 + 9.0*b*e2 + + 9.0*e*b2 + b3)/denom;
10057  ic5 = -84.0*(e2 + 3.0*b*e + b2)/denom;
10058  ic6 = 70.0*(e + b)/denom;
10059  ic7 = -20.0/denom;
10060 
10061  /* Make sure we're on the grid */
10062  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
10063  (apos[1]<=ymin) || (apos[1]>=ymax) || \
10064  (apos[2]<=zmin) || (apos[2]>=zmax)) {
10065  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
10066  (thee->pmgp->bcfl != BCFL_MAP)) {
10067  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
10068  %4.3f) is off the mesh (ignoring):\n",
10069  iatom, apos[0], apos[1], apos[2]);
10070  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
10071  xmin, xmax);
10072  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
10073  ymin, ymax);
10074  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
10075  zmin, zmax);
10076  }
10077  fflush(stderr);
10078 
10079  } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
10080 
10081  /* Convert the atom position to grid reference frame */
10082  position[0] = apos[0] - xmin;
10083  position[1] = apos[1] - ymin;
10084  position[2] = apos[2] - zmin;
10085 
10086  /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
10087  * ASSIGNMENT (Steps #1-3) */
10088  itot = irad + arad + splineWin;
10089  itot2 = VSQR(itot);
10090  ictot = VMAX2(0, (irad + arad - splineWin));
10091  ictot2 = VSQR(ictot);
10092  stot = arad + splineWin;
10093  stot2 = VSQR(stot);
10094  sctot = VMAX2(0, (arad - splineWin));
10095  sctot2 = VSQR(sctot);
10096 
10097  /* We'll search over grid points which are in the greater of
10098  * these two radii */
10099  rtot = VMAX2(itot, stot);
10100  rtot2 = VMAX2(itot2, stot2);
10101  dx = rtot + 0.5*hx;
10102  dy = rtot + 0.5*hy;
10103  dz = rtot + 0.5*hzed;
10104  imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
10105  imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
10106  jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
10107  jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
10108  kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
10109  kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
10110  for (i=imin; i<=imax; i++) {
10111  dx2 = VSQR(position[0] - hx*i);
10112  for (j=jmin; j<=jmax; j++) {
10113  dy2 = VSQR(position[1] - hy*j);
10114  for (k=kmin; k<=kmax; k++) {
10115  dz2 = VSQR(position[2] - k*hzed);
10116 
10117  /* ASSIGN CCF */
10118  if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
10119  dist2 = dz2 + dy2 + dx2;
10120  if (dist2 >= itot2) {
10121  ;
10122  }
10123  if (dist2 <= ictot2) {
10124  thee->kappa[IJK(i,j,k)] = 0.0;
10125  }
10126  if ((dist2 < itot2) && (dist2 > ictot2)) {
10127  dist = VSQRT(dist2);
10128  sm = dist;
10129  sm2 = dist2;
10130  sm3 = sm2 * sm;
10131  sm4 = sm3 * sm;
10132  sm5 = sm4 * sm;
10133  sm6 = sm5 * sm;
10134  sm7 = sm6 * sm;
10135  value = ic0 + ic1*sm + ic2*sm2 + ic3*sm3
10136  + ic4*sm4 + ic5*sm5 + ic6*sm6 + ic7*sm7;
10137  if (value > 1.0) {
10138  value = 1.0;
10139  } else if (value < 0.0){
10140  value = 0.0;
10141  }
10142  thee->kappa[IJK(i,j,k)] *= value;
10143  }
10144  }
10145 
10146  /* ASSIGN A1CF */
10147  if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
10148  dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
10149  if (dist2 >= stot2) {
10150  thee->epsx[IJK(i,j,k)] *= 1.0;
10151  }
10152  if (dist2 <= sctot2) {
10153  thee->epsx[IJK(i,j,k)] = 0.0;
10154  }
10155  if ((dist2 > sctot2) && (dist2 < stot2)) {
10156  dist = VSQRT(dist2);
10157  sm = dist;
10158  sm2 = VSQR(sm);
10159  sm3 = sm2 * sm;
10160  sm4 = sm3 * sm;
10161  sm5 = sm4 * sm;
10162  sm6 = sm5 * sm;
10163  sm7 = sm6 * sm;
10164  value = c0 + c1*sm + c2*sm2 + c3*sm3
10165  + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
10166  if (value > 1.0) {
10167  value = 1.0;
10168  } else if (value < 0.0){
10169  value = 0.0;
10170  }
10171  thee->epsx[IJK(i,j,k)] *= value;
10172  }
10173  }
10174 
10175  /* ASSIGN A2CF */
10176  if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
10177  dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
10178  if (dist2 >= stot2) {
10179  thee->epsy[IJK(i,j,k)] *= 1.0;
10180  }
10181  if (dist2 <= sctot2) {
10182  thee->epsy[IJK(i,j,k)] = 0.0;
10183  }
10184  if ((dist2 > sctot2) && (dist2 < stot2)) {
10185  dist = VSQRT(dist2);
10186  sm = dist;
10187  sm2 = VSQR(sm);
10188  sm3 = sm2 * sm;
10189  sm4 = sm3 * sm;
10190  sm5 = sm4 * sm;
10191  sm6 = sm5 * sm;
10192  sm7 = sm6 * sm;
10193  value = c0 + c1*sm + c2*sm2 + c3*sm3
10194  + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
10195  if (value > 1.0) {
10196  value = 1.0;
10197  } else if (value < 0.0){
10198  value = 0.0;
10199  }
10200  thee->epsy[IJK(i,j,k)] *= value;
10201  }
10202  }
10203 
10204  /* ASSIGN A3CF */
10205  if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
10206  dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
10207  if (dist2 >= stot2) {
10208  thee->epsz[IJK(i,j,k)] *= 1.0;
10209  }
10210  if (dist2 <= sctot2) {
10211  thee->epsz[IJK(i,j,k)] = 0.0;
10212  }
10213  if ((dist2 > sctot2) && (dist2 < stot2)) {
10214  dist = VSQRT(dist2);
10215  sm = dist;
10216  sm2 = dist2;
10217  sm3 = sm2 * sm;
10218  sm4 = sm3 * sm;
10219  sm5 = sm4 * sm;
10220  sm6 = sm5 * sm;
10221  sm7 = sm6 * sm;
10222  value = c0 + c1*sm + c2*sm2 + c3*sm3
10223  + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
10224  if (value > 1.0) {
10225  value = 1.0;
10226  } else if (value < 0.0){
10227  value = 0.0;
10228  }
10229  thee->epsz[IJK(i,j,k)] *= value;
10230  }
10231  }
10232 
10233 
10234  } /* k loop */
10235  } /* j loop */
10236  } /* i loop */
10237  } /* endif (on the mesh) */
10238  } /* endfor (over all atoms) */
10239 
10240  Vnm_print(0, "Vpmg_fillco: filling coefficient arrays\n");
10241  /* Interpret markings and fill the coefficient arrays */
10242  for (k=0; k<nz; k++) {
10243  for (j=0; j<ny; j++) {
10244  for (i=0; i<nx; i++) {
10245 
10246  thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
10247  thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)]
10248  + epsp;
10249  thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)]
10250  + epsp;
10251  thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)]
10252  + epsp;
10253 
10254  } /* i loop */
10255  } /* j loop */
10256  } /* k loop */
10257 
10258 }
10259 
10260 VPUBLIC void fillcoPermanentInduced(Vpmg *thee) {
10261 
10262  Valist *alist;
10263  Vpbe *pbe;
10264  Vatom *atom;
10265  /* Coversions */
10266  double zmagic, f;
10267  /* Grid */
10268  double xmin, xmax, ymin, ymax, zmin, zmax;
10269  double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
10270  double hx, hy, hzed, *apos;
10271  /* Multipole */
10272  double charge, *dipole,*quad;
10273  double c,ux,uy,uz,qxx,qyx,qyy,qzx,qzy,qzz,qave;
10274  /* B-spline weights */
10275  double mx,my,mz,dmx,dmy,dmz,d2mx,d2my,d2mz;
10276  double mi,mj,mk;
10277  /* Loop variables */
10278  int i, ii, jj, kk, nx, ny, nz, iatom;
10279  int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
10280 
10281  VASSERT(thee != VNULL);
10282 
10283  /* Get PBE info */
10284  pbe = thee->pbe;
10285  alist = pbe->alist;
10286  zmagic = Vpbe_getZmagic(pbe);
10287 
10288  /* Mesh info */
10289  nx = thee->pmgp->nx;
10290  ny = thee->pmgp->ny;
10291  nz = thee->pmgp->nz;
10292  hx = thee->pmgp->hx;
10293  hy = thee->pmgp->hy;
10294  hzed = thee->pmgp->hzed;
10295 
10296  /* Conversion */
10297  f = zmagic/(hx*hy*hzed);
10298 
10299  /* Define the total domain size */
10300  xlen = thee->pmgp->xlen;
10301  ylen = thee->pmgp->ylen;
10302  zlen = thee->pmgp->zlen;
10303 
10304  /* Define the min/max dimensions */
10305  xmin = thee->pmgp->xcent - (xlen/2.0);
10306  ymin = thee->pmgp->ycent - (ylen/2.0);
10307  zmin = thee->pmgp->zcent - (zlen/2.0);
10308  xmax = thee->pmgp->xcent + (xlen/2.0);
10309  ymax = thee->pmgp->ycent + (ylen/2.0);
10310  zmax = thee->pmgp->zcent + (zlen/2.0);
10311 
10312  /* Fill in the source term (permanent atomic multipoles
10313  and induced dipoles) */
10314  Vnm_print(0, "fillcoPermanentInduced: filling in source term.\n");
10315  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
10316 
10317  atom = Valist_getAtom(alist, iatom);
10318  apos = Vatom_getPosition(atom);
10319 
10320  c = Vatom_getCharge(atom)*f;
10321 
10322 #if defined(WITH_TINKER)
10323  dipole = Vatom_getDipole(atom);
10324  ux = dipole[0]/hx*f;
10325  uy = dipole[1]/hy*f;
10326  uz = dipole[2]/hzed*f;
10327  dipole = Vatom_getInducedDipole(atom);
10328  ux = ux + dipole[0]/hx*f;
10329  uy = uy + dipole[1]/hy*f;
10330  uz = uz + dipole[2]/hzed*f;
10331  quad = Vatom_getQuadrupole(atom);
10332  qxx = (1.0/3.0)*quad[0]/(hx*hx)*f;
10333  qyx = (2.0/3.0)*quad[3]/(hx*hy)*f;
10334  qyy = (1.0/3.0)*quad[4]/(hy*hy)*f;
10335  qzx = (2.0/3.0)*quad[6]/(hzed*hx)*f;
10336  qzy = (2.0/3.0)*quad[7]/(hzed*hy)*f;
10337  qzz = (1.0/3.0)*quad[8]/(hzed*hzed)*f;
10338 #else
10339  ux = 0.0;
10340  uy = 0.0;
10341  uz = 0.0;
10342  qxx = 0.0;
10343  qyx = 0.0;
10344  qyy = 0.0;
10345  qzx = 0.0;
10346  qzy = 0.0;
10347  qzz = 0.0;
10348 #endif /* if defined(WITH_TINKER) */
10349 
10350  /* Make sure we're on the grid */
10351  if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx)) || \
10352  (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy)) || \
10353  (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
10354  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]);
10355  Vnm_print(2, "fillcoPermanentMultipole: xmin = %g, xmax = %g\n", xmin, xmax);
10356  Vnm_print(2, "fillcoPermanentMultipole: ymin = %g, ymax = %g\n", ymin, ymax);
10357  Vnm_print(2, "fillcoPermanentMultipole: zmin = %g, zmax = %g\n", zmin, zmax);
10358  fflush(stderr);
10359  } else {
10360 
10361  /* Convert the atom position to grid reference frame */
10362  position[0] = apos[0] - xmin;
10363  position[1] = apos[1] - ymin;
10364  position[2] = apos[2] - zmin;
10365 
10366  /* Figure out which vertices we're next to */
10367  ifloat = position[0]/hx;
10368  jfloat = position[1]/hy;
10369  kfloat = position[2]/hzed;
10370 
10371  ip1 = (int)ceil(ifloat);
10372  ip2 = ip1 + 2;
10373  im1 = (int)floor(ifloat);
10374  im2 = im1 - 2;
10375  jp1 = (int)ceil(jfloat);
10376  jp2 = jp1 + 2;
10377  jm1 = (int)floor(jfloat);
10378  jm2 = jm1 - 2;
10379  kp1 = (int)ceil(kfloat);
10380  kp2 = kp1 + 2;
10381  km1 = (int)floor(kfloat);
10382  km2 = km1 - 2;
10383 
10384  /* This step shouldn't be necessary, but it saves nasty debugging
10385  * later on if something goes wrong */
10386  ip2 = VMIN2(ip2,nx-1);
10387  ip1 = VMIN2(ip1,nx-1);
10388  im1 = VMAX2(im1,0);
10389  im2 = VMAX2(im2,0);
10390  jp2 = VMIN2(jp2,ny-1);
10391  jp1 = VMIN2(jp1,ny-1);
10392  jm1 = VMAX2(jm1,0);
10393  jm2 = VMAX2(jm2,0);
10394  kp2 = VMIN2(kp2,nz-1);
10395  kp1 = VMIN2(kp1,nz-1);
10396  km1 = VMAX2(km1,0);
10397  km2 = VMAX2(km2,0);
10398 
10399  /* Now assign fractions of the charge to the nearby verts */
10400  for (ii=im2; ii<=ip2; ii++) {
10401  mi = VFCHI4(ii,ifloat);
10402  mx = bspline4(mi);
10403  dmx = dbspline4(mi);
10404  d2mx = d2bspline4(mi);
10405  for (jj=jm2; jj<=jp2; jj++) {
10406  mj = VFCHI4(jj,jfloat);
10407  my = bspline4(mj);
10408  dmy = dbspline4(mj);
10409  d2my = d2bspline4(mj);
10410  for (kk=km2; kk<=kp2; kk++) {
10411  mk = VFCHI4(kk,kfloat);
10412  mz = bspline4(mk);
10413  dmz = dbspline4(mk);
10414  d2mz = d2bspline4(mk);
10415  charge = mx*my*mz*c -
10416  dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz +
10417  d2mx*my*mz*qxx +
10418  dmx*dmy*mz*qyx + mx*d2my*mz*qyy +
10419  dmx*my*dmz*qzx + mx*dmy*dmz*qzy + mx*my*d2mz*qzz;
10420  thee->charge[IJK(ii,jj,kk)] += charge;
10421 
10422  }
10423  }
10424  }
10425  } /* endif (on the mesh) */
10426 
10427  } /* endfor (each atom) */
10428 }
10429 
10430 VPRIVATE void fillcoCoefSpline3(Vpmg *thee) {
10431 
10432  Valist *alist;
10433  Vpbe *pbe;
10434  Vatom *atom;
10435  double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
10436  double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
10437  double irad, dx, dy, dz, epsw, epsp, w2i;
10438  double hx, hy, hzed, *apos, arad, sctot2;
10439  double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin;
10440  double dist, value, denom, sm, sm2, sm3, sm4, sm5;
10441  double e, e2, e3, e4, e5;
10442  double b, b2, b3, b4, b5;
10443  double c0, c1, c2, c3, c4, c5;
10444  double ic0, ic1, ic2, ic3, ic4, ic5;
10445  int i, j, k, nx, ny, nz, iatom;
10446  int imin, imax, jmin, jmax, kmin, kmax;
10447 
10448  VASSERT(thee != VNULL);
10449  splineWin = thee->splineWin;
10450 
10451  /* Get PBE info */
10452  pbe = thee->pbe;
10453  alist = pbe->alist;
10454  irad = Vpbe_getMaxIonRadius(pbe);
10455  ionstr = Vpbe_getBulkIonicStrength(pbe);
10456  epsw = Vpbe_getSolventDiel(pbe);
10457  epsp = Vpbe_getSoluteDiel(pbe);
10458 
10459  /* Mesh info */
10460  nx = thee->pmgp->nx;
10461  ny = thee->pmgp->ny;
10462  nz = thee->pmgp->nz;
10463  hx = thee->pmgp->hx;
10464  hy = thee->pmgp->hy;
10465  hzed = thee->pmgp->hzed;
10466 
10467  /* Define the total domain size */
10468  xlen = thee->pmgp->xlen;
10469  ylen = thee->pmgp->ylen;
10470  zlen = thee->pmgp->zlen;
10471 
10472  /* Define the min/max dimensions */
10473  xmin = thee->pmgp->xcent - (xlen/2.0);
10474  ymin = thee->pmgp->ycent - (ylen/2.0);
10475  zmin = thee->pmgp->zcent - (zlen/2.0);
10476  xmax = thee->pmgp->xcent + (xlen/2.0);
10477  ymax = thee->pmgp->ycent + (ylen/2.0);
10478  zmax = thee->pmgp->zcent + (zlen/2.0);
10479 
10480  /* This is a floating point parameter related to the non-zero nature of the
10481  * bulk ionic strength. If the ionic strength is greater than zero; this
10482  * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
10483  * Otherwise, this parameter is set to 0.0 */
10484  if (ionstr > VPMGSMALL) ionmask = 1.0;
10485  else ionmask = 0.0;
10486 
10487  /* Reset the kappa, epsx, epsy, and epsz arrays */
10488  for (i=0; i<(nx*ny*nz); i++) {
10489  thee->kappa[i] = 1.0;
10490  thee->epsx[i] = 1.0;
10491  thee->epsy[i] = 1.0;
10492  thee->epsz[i] = 1.0;
10493  }
10494 
10495  /* Loop through the atoms and do assign the dielectric */
10496  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
10497 
10498  atom = Valist_getAtom(alist, iatom);
10499  apos = Vatom_getPosition(atom);
10500  arad = Vatom_getRadius(atom);
10501 
10502  b = arad - splineWin;
10503  e = arad + splineWin;
10504  e2 = e * e;
10505  e3 = e2 * e;
10506  e4 = e3 * e;
10507  e5 = e4 * e;
10508  b2 = b * b;
10509  b3 = b2 * b;
10510  b4 = b3 * b;
10511  b5 = b4 * b;
10512  denom = pow((e - b), 5.0);
10513  c0 = -10.0*e2*b3 + 5.0*e*b4 - b5;
10514  c1 = 30.0*e2*b2;
10515  c2 = -30.0*(e2*b + e*b2);
10516  c3 = 10.0*(e2 + 4.0*e*b + b2);
10517  c4 = -15.0*(e + b);
10518  c5 = 6;
10519  c0 = c0/denom;
10520  c1 = c1/denom;
10521  c2 = c2/denom;
10522  c3 = c3/denom;
10523  c4 = c4/denom;
10524  c5 = c5/denom;
10525 
10526  b = irad + arad - splineWin;
10527  e = irad + arad + splineWin;
10528  e2 = e * e;
10529  e3 = e2 * e;
10530  e4 = e3 * e;
10531  e5 = e4 * e;
10532  b2 = b * b;
10533  b3 = b2 * b;
10534  b4 = b3 * b;
10535  b5 = b4 * b;
10536  denom = pow((e - b), 5.0);
10537  ic0 = -10.0*e2*b3 + 5.0*e*b4 - b5;
10538  ic1 = 30.0*e2*b2;
10539  ic2 = -30.0*(e2*b + e*b2);
10540  ic3 = 10.0*(e2 + 4.0*e*b + b2);
10541  ic4 = -15.0*(e + b);
10542  ic5 = 6;
10543  ic0 = c0/denom;
10544  ic1 = c1/denom;
10545  ic2 = c2/denom;
10546  ic3 = c3/denom;
10547  ic4 = c4/denom;
10548  ic5 = c5/denom;
10549 
10550  /* Make sure we're on the grid */
10551  if ((apos[0]<=xmin) || (apos[0]>=xmax) || \
10552  (apos[1]<=ymin) || (apos[1]>=ymax) || \
10553  (apos[2]<=zmin) || (apos[2]>=zmax)) {
10554  if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
10555  (thee->pmgp->bcfl != BCFL_MAP)) {
10556  Vnm_print(2, "Vpmg_fillco: Atom #%d at (%4.3f, %4.3f,\
10557  %4.3f) is off the mesh (ignoring):\n",
10558  iatom, apos[0], apos[1], apos[2]);
10559  Vnm_print(2, "Vpmg_fillco: xmin = %g, xmax = %g\n",
10560  xmin, xmax);
10561  Vnm_print(2, "Vpmg_fillco: ymin = %g, ymax = %g\n",
10562  ymin, ymax);
10563  Vnm_print(2, "Vpmg_fillco: zmin = %g, zmax = %g\n",
10564  zmin, zmax);
10565  }
10566  fflush(stderr);
10567 
10568  } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
10569 
10570  /* Convert the atom position to grid reference frame */
10571  position[0] = apos[0] - xmin;
10572  position[1] = apos[1] - ymin;
10573  position[2] = apos[2] - zmin;
10574 
10575  /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
10576  * ASSIGNMENT (Steps #1-3) */
10577  itot = irad + arad + splineWin;
10578  itot2 = VSQR(itot);
10579  ictot = VMAX2(0, (irad + arad - splineWin));
10580  ictot2 = VSQR(ictot);
10581  stot = arad + splineWin;
10582  stot2 = VSQR(stot);
10583  sctot = VMAX2(0, (arad - splineWin));
10584  sctot2 = VSQR(sctot);
10585 
10586  /* We'll search over grid points which are in the greater of
10587  * these two radii */
10588  rtot = VMAX2(itot, stot);
10589  rtot2 = VMAX2(itot2, stot2);
10590  dx = rtot + 0.5*hx;
10591  dy = rtot + 0.5*hy;
10592  dz = rtot + 0.5*hzed;
10593  imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
10594  imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
10595  jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
10596  jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
10597  kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
10598  kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
10599  for (i=imin; i<=imax; i++) {
10600  dx2 = VSQR(position[0] - hx*i);
10601  for (j=jmin; j<=jmax; j++) {
10602  dy2 = VSQR(position[1] - hy*j);
10603  for (k=kmin; k<=kmax; k++) {
10604  dz2 = VSQR(position[2] - k*hzed);
10605 
10606  /* ASSIGN CCF */
10607  if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
10608  dist2 = dz2 + dy2 + dx2;
10609  if (dist2 >= itot2) {
10610  ;
10611  }
10612  if (dist2 <= ictot2) {
10613  thee->kappa[IJK(i,j,k)] = 0.0;
10614  }
10615  if ((dist2 < itot2) && (dist2 > ictot2)) {
10616  dist = VSQRT(dist2);
10617  sm = dist;
10618  sm2 = dist2;
10619  sm3 = sm2 * sm;
10620  sm4 = sm3 * sm;
10621  sm5 = sm4 * sm;
10622  value = ic0 + ic1*sm + ic2*sm2 + ic3*sm3
10623  + ic4*sm4 + ic5*sm5;
10624  if (value > 1.0) {
10625  value = 1.0;
10626  } else if (value < 0.0){
10627  value = 0.0;
10628  }
10629  thee->kappa[IJK(i,j,k)] *= value;
10630  }
10631  }
10632 
10633  /* ASSIGN A1CF */
10634  if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
10635  dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
10636  if (dist2 >= stot2) {
10637  thee->epsx[IJK(i,j,k)] *= 1.0;
10638  }
10639  if (dist2 <= sctot2) {
10640  thee->epsx[IJK(i,j,k)] = 0.0;
10641  }
10642  if ((dist2 > sctot2) && (dist2 < stot2)) {
10643  dist = VSQRT(dist2);
10644  sm = dist;
10645  sm2 = VSQR(sm);
10646  sm3 = sm2 * sm;
10647  sm4 = sm3 * sm;
10648  sm5 = sm4 * sm;
10649  value = c0 + c1*sm + c2*sm2 + c3*sm3
10650  + c4*sm4 + c5*sm5;
10651  if (value > 1.0) {
10652  value = 1.0;
10653  } else if (value < 0.0){
10654  value = 0.0;
10655  }
10656  thee->epsx[IJK(i,j,k)] *= value;
10657  }
10658  }
10659 
10660  /* ASSIGN A2CF */
10661  if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
10662  dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
10663  if (dist2 >= stot2) {
10664  thee->epsy[IJK(i,j,k)] *= 1.0;
10665  }
10666  if (dist2 <= sctot2) {
10667  thee->epsy[IJK(i,j,k)] = 0.0;
10668  }
10669  if ((dist2 > sctot2) && (dist2 < stot2)) {
10670  dist = VSQRT(dist2);
10671  sm = dist;
10672  sm2 = VSQR(sm);
10673  sm3 = sm2 * sm;
10674  sm4 = sm3 * sm;
10675  sm5 = sm4 * sm;
10676  value = c0 + c1*sm + c2*sm2 + c3*sm3
10677  + c4*sm4 + c5*sm5;
10678  if (value > 1.0) {
10679  value = 1.0;
10680  } else if (value < 0.0){
10681  value = 0.0;
10682  }
10683  thee->epsy[IJK(i,j,k)] *= value;
10684  }
10685  }
10686 
10687  /* ASSIGN A3CF */
10688  if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
10689  dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
10690  if (dist2 >= stot2) {
10691  thee->epsz[IJK(i,j,k)] *= 1.0;
10692  }
10693  if (dist2 <= sctot2) {
10694  thee->epsz[IJK(i,j,k)] = 0.0;
10695  }
10696  if ((dist2 > sctot2) && (dist2 < stot2)) {
10697  dist = VSQRT(dist2);
10698  sm = dist;
10699  sm2 = dist2;
10700  sm3 = sm2 * sm;
10701  sm4 = sm3 * sm;
10702  sm5 = sm4 * sm;
10703  value = c0 + c1*sm + c2*sm2 + c3*sm3
10704  + c4*sm4 + c5*sm5;
10705  if (value > 1.0) {
10706  value = 1.0;
10707  } else if (value < 0.0){
10708  value = 0.0;
10709  }
10710  thee->epsz[IJK(i,j,k)] *= value;
10711  }
10712  }
10713 
10714 
10715  } /* k loop */
10716  } /* j loop */
10717  } /* i loop */
10718  } /* endif (on the mesh) */
10719  } /* endfor (over all atoms) */
10720 
10721  Vnm_print(0, "Vpmg_fillco: filling coefficient arrays\n");
10722  /* Interpret markings and fill the coefficient arrays */
10723  for (k=0; k<nz; k++) {
10724  for (j=0; j<ny; j++) {
10725  for (i=0; i<nx; i++) {
10726 
10727  thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
10728  thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)]
10729  + epsp;
10730  thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)]
10731  + epsp;
10732  thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)]
10733  + epsp;
10734 
10735  } /* i loop */
10736  } /* j loop */
10737  } /* k loop */
10738 
10739 }
10740 
10741 VPRIVATE void bcolcomp(int *iparm, double *rparm, int *iwork, double *rwork,
10742  double *values, int *rowind, int *colptr, int *flag) {
10743  int nrow, ncol, nnzero, i;
10744  int nxc, nyc, nzc, nf, nc, narr, narrc, n_rpc;
10745  int n_iz, n_ipc, iretot, iintot;
10746  int nrwk, niwk, nx, ny, nz, nlev, ierror, maxlev, mxlv;
10747  int mgcoar, mgdisc, mgsolv;
10748  int k_iz;
10749  int k_ipc, k_rpc, k_ac, k_cc, k_fc, k_pc;
10750 
10751  WARN_UNTESTED;
10752 
10753  // Decode some parameters
10754  nrwk = VAT(iparm, 1);
10755  niwk = VAT(iparm, 2);
10756  nx = VAT(iparm, 3);
10757  ny = VAT(iparm, 4);
10758  nz = VAT(iparm, 5);
10759  nlev = VAT(iparm, 6);
10760 
10761  // Some checks on input
10762  mxlv = Vmaxlev(nx, ny, nz);
10763 
10764  // Basic grid sizes, etc.
10765  mgcoar = VAT(iparm, 18);
10766  mgdisc = VAT(iparm, 19);
10767  mgsolv = VAT(iparm, 21);
10768  Vmgsz(&mgcoar, &mgdisc, &mgsolv,
10769  &nx, &ny, &nz,
10770  &nlev,
10771  &nxc, &nyc, &nzc,
10772  &nf, &nc,
10773  &narr, &narrc,
10774  &n_rpc, &n_iz, &n_ipc,
10775  &iretot, &iintot);
10776 
10777  // Split up the integer work array
10778  k_iz = 1;
10779  k_ipc = k_iz + n_iz;
10780 
10781  // Split up the real work array
10782  k_rpc = 1;
10783  k_cc = k_rpc + n_rpc;
10784  k_fc = k_cc + narr;
10785  k_pc = k_fc + narr;
10786  k_ac = k_pc + 27*narrc;
10787 
10788  bcolcomp2(iparm, rparm,
10789  &nx, &ny, &nz, RAT(iwork, k_iz),
10790  RAT(iwork, k_ipc), RAT(rwork, k_rpc),
10791  RAT(rwork, k_ac), RAT(rwork, k_cc),
10792  values, rowind, colptr, flag);
10793 }
10794 
10795 VPRIVATE void bcolcomp2(int *iparm, double *rparm,
10796  int *nx, int *ny, int *nz,
10797  int *iz, int *ipc, double *rpc,
10798  double *ac, double *cc, double *values,
10799  int *rowind, int *colptr, int *flag) {
10800 
10801  int nlev = 1;
10802  int lev = VAT(iparm, 6);
10803 
10804  MAT2(iz, 50, nlev);
10805 
10806  WARN_UNTESTED;
10807 
10808  /*
10809  * Build the multigrid data structure in iz
10810  * THIS MAY HAVE BEEN DONE ALREADY, BUT IT'S OK TO DO IT AGAIN,
10811  * RIGHT?
10812  * call buildstr (nx,ny,nz,nlev,iz)
10813  *
10814  * We're interested in the finest level
10815  */
10816  bcolcomp3(nx, ny, nz,
10817  RAT(ipc, VAT2(iz, 5, lev)), RAT(rpc, VAT2(iz, 6, lev)),
10818  RAT(ac, VAT2(iz, 7, lev)), RAT(cc, VAT2(iz, 1, lev)),
10819  values, rowind, colptr, flag);
10820 }
10821 
10822 /**************************************************************************
10823  * Routine: bcolcomp3
10824  * Purpose: Build a column-compressed matrix in Harwell-Boeing format
10825  * Args: flag 0 ==> Use Poisson operator only
10826  * 1 ==> Use linearization of full operator around current
10827  * solution
10828  * Author: Nathan Baker (mostly ripped off from Harwell-Boeing format
10829  * documentation)
10830  **************************************************************************/
10831 VPRIVATE void bcolcomp3(int *nx, int *ny, int *nz,
10832  int *ipc, double *rpc,
10833  double *ac, double *cc,
10834  double *values, int *rowind, int *colptr, int *flag) {
10835 
10836  MAT2(ac, *nx * *ny * *nz, 1);
10837 
10838  WARN_UNTESTED;
10839 
10840  bcolcomp4(nx, ny, nz,
10841  ipc, rpc,
10842  RAT2(ac, 1, 1), cc,
10843  RAT2(ac, 1, 2), RAT2(ac, 1, 3), RAT2(ac, 1, 4),
10844  values, rowind, colptr, flag);
10845 }
10846 
10847 
10848 
10849 /**************************************************************************
10850  * Routine: bcolcomp4
10851  * Purpose: Build a column-compressed matrix in Harwell-Boeing format
10852  * Args: flag 0 ==> Use Poisson operator only
10853  * 1 ==> Use linearization of full operator around current
10854  * solution
10855  * Author: Nathan Baker (mostly ripped off from Harwell-Boeing format
10856  * documentation)
10857  **************************************************************************/
10858 VPRIVATE void bcolcomp4(int *nx, int *ny, int *nz,
10859  int *ipc, double *rpc,
10860  double *oC, double *cc, double *oE, double *oN, double *uC,
10861  double *values, int *rowind, int *colptr, int *flag) {
10862 
10863  int nxm2, nym2, nzm2;
10864  int ii, jj, kk, ll;
10865  int i, j, k, l;
10866  int inonz, iirow, nn, nrow, ncol, nonz, irow, n;
10867 
10868  int doit;
10869 
10870  MAT3(oE, *nx, *ny, *nz);
10871  MAT3(oN, *nx, *ny, *nz);
10872  MAT3(uC, *nx, *ny, *nz);
10873  MAT3(cc, *nx, *ny, *nz);
10874  MAT3(oC, *nx, *ny, *nz);
10875 
10876  WARN_UNTESTED;
10877 
10878  // Get some column, row, and nonzero information
10879  n = *nx * *ny * *nz;
10880  nxm2 = *nx - 2;
10881  nym2 = *ny - 2;
10882  nzm2 = *nz - 2;
10883  nn = nxm2 * nym2 * nzm2;
10884  ncol = nn;
10885  nrow = nn;
10886  nonz = 7 * nn - 2 * nxm2 * nym2 - 2 * nxm2 - 2;
10887 
10888  // Intialize some pointers
10889  inonz = 1;
10890 
10891  /*
10892  * Run over the dimensions of the matrix (non-zero only in the interior
10893  * of the mesh
10894  */
10895  for (k=2; k<=*nz-1; k++) {
10896  // Offset the index to the output grid index
10897  kk = k - 1;
10898 
10899  for (j=2; j<=*ny-1; j++) {
10900  // Offset the index to the output grid index
10901  jj = j - 1;
10902 
10903  for (i=2; i<=*nx-1; i++) {
10904  // Offset the index to the output grid index
10905  ii = i - 1;
10906 
10907  // Get the output (i,j,k) row number in natural ordering
10908  ll = (kk - 1) * nxm2 * nym2 + (jj - 1) * nxm2 + (ii - 1) + 1;
10909  l = (k - 1) * *nx * *ny + (j - 1) * *nx + (i - 1) + 1;
10910 
10911  // Store where this column starts
10912  VAT(colptr,ll) = inonz;
10913 
10914  // SUB-DIAGONAL 3
10915  iirow = ll - nxm2 * nym2;
10916  irow = l - *nx * *ny;
10917 
10918  doit = (iirow >= 1) && (iirow <= nn);
10919  doit = doit && (irow >= 1) && (irow <= n);
10920 
10921  if (doit) {
10922  VAT(values, inonz) = -VAT3(uC, i, j, k-1);
10923  VAT(rowind, inonz) = iirow;
10924  inonz++;
10925  }
10926 
10927 
10928 
10929  // SUB-DIAGONAL 2
10930  iirow = ll - nxm2;
10931  irow = l - *nx;
10932 
10933  doit = (iirow >= 1) && (iirow <= nn);
10934  doit = doit && (irow >= 1) && (irow <= n);
10935 
10936  if (doit) {
10937  VAT(values, inonz) = -VAT3(oN, i, j-1, k);
10938  VAT(rowind, inonz) = iirow;
10939  inonz++;
10940  }
10941 
10942 
10943 
10944  // SUB-DIAGONAL 1
10945  iirow = ll - 1;
10946  irow = l - 1;
10947 
10948  doit = (iirow >= 1) && (iirow <= nn);
10949  doit = doit && (irow <= 1) && (irow <= n);
10950  if (doit) {
10951  VAT(values, inonz) = -VAT3(oE, i-1, j, k);
10952  VAT(rowind, inonz) = iirow;
10953  inonz++;
10954  }
10955 
10956 
10957 
10958  // DIAGONAL
10959  iirow = ll;
10960  irow = l;
10961 
10962  if (*flag == 0) {
10963  VAT(values, inonz) = VAT3(oC, i, j, k);
10964  } else if (*flag == 1) {
10965  VAT(values, inonz) = VAT3(oC, i, j, k)
10966  + VAT3(cc, i, j, k);
10967  } else {
10968  VABORT_MSG0("PMGF1");
10969  }
10970 
10971  VAT(rowind, inonz) = iirow;
10972  inonz++;
10973 
10974  // SUPER-DIAGONAL 1
10975  iirow = ll + 1;
10976  irow = l + 1;
10977  doit = (iirow >= 1) && (iirow <= nn);
10978  doit = doit && (irow >= 1) && (irow <= n);
10979  if (doit) {
10980  VAT(values, inonz) = -VAT3(oE, i, j, k);
10981  VAT(rowind, inonz) = iirow;
10982  inonz++;
10983  }
10984 
10985 
10986 
10987  // SUPER-DIAGONAL 2
10988  iirow = ll + nxm2;
10989  irow = l + *nx;
10990  doit = (iirow >= 1) && (iirow <= nn);
10991  doit = doit && (irow >= 1) && (irow <= n);
10992  if (doit) {
10993  VAT(values, inonz) = -VAT3(oN, i, j, k);
10994  VAT(rowind, inonz) = iirow;
10995  inonz++;
10996  }
10997 
10998 
10999 
11000  // SUPER-DIAGONAL 3
11001  iirow = ll + nxm2 * nym2;
11002  irow = l + *nx * *ny;
11003  doit = (iirow >= 1) && (iirow <= nn);
11004  doit = doit && (irow >= 1) && (irow <= n);
11005  if (doit) {
11006  VAT(values, inonz) = -VAT3(uC, i, j, k);
11007  VAT(rowind, inonz) = iirow;
11008  inonz++;
11009  }
11010  }
11011  }
11012  }
11013 
11014  VAT(colptr, ncol + 1) = inonz;
11015 
11016  if (inonz != (nonz + 1)) {
11017  VABORT_MSG2("BCOLCOMP4: ERROR -- INONZ = %d, NONZ = %d", inonz, nonz);
11018  }
11019 }
11020 
11021 
11022 
11023 VPRIVATE void pcolcomp(int *nrow, int *ncol, int *nnzero,
11024  double *values, int *rowind, int *colptr,
11025  char *path, char *title, char *mxtype) {
11026 
11027  char key[] = "key";
11028  char ptrfmt[] = "(10I8)";
11029  char indfmt[] = "(10I8)";
11030  char valfmt[] = "(5E15.8)";
11031  char rhsfmt[] = "(5E15.8)";
11032 
11033  int i, totcrd, ptrcrd, indcrd, valcrd, neltvl, rhscrd;
11034 
11035  FILE *outFile;
11036 
11037  WARN_UNTESTED;
11038 
11039  // Open the file for reading
11040  outFile = fopen(path, "w");
11041 
11042  // Set some default values
11043  ptrcrd = (int)(*ncol / 10 + 1) - 1;
11044  indcrd = (int)(*nnzero / 10 + 1) - 1;
11045  valcrd = (int)(*nnzero / 10 + 1) - 1;
11046  totcrd = ptrcrd + indcrd + valcrd;
11047  rhscrd = 0;
11048  neltvl = 0;
11049 
11050  // Print the header
11051  fprintf(outFile, "%72s%8s\n",
11052  title, key);
11053  fprintf(outFile, "%14d%14d%14d%14d%14d\n",
11054  totcrd, ptrcrd, indcrd, valcrd, rhscrd);
11055  fprintf(outFile, "%3s\n", mxtype);
11056  fprintf(outFile, " %14d%14d%14d%14d\n",
11057  *nrow, *ncol, *nnzero, neltvl);
11058  fprintf(outFile, "%16s%16s%20s%20s\n",
11059  ptrfmt, indfmt, valfmt, rhsfmt);
11060 
11061  // Write the matrix structure
11062  for (i=1; i<=*ncol+1; i++)
11063  fprintf(outFile, "%8d", VAT(colptr, i));
11064  fprintf(outFile, "\n");
11065 
11066  for (i=1; i<=*nnzero; i++)
11067  fprintf(outFile, "%8d", VAT(rowind, i));
11068  fprintf(outFile, "\n");
11069 
11070  // Write out the values
11071  if (valcrd > 0) {
11072  for (i=1; i<=*nnzero; i++)
11073  fprintf(outFile, "%15.8e", VAT(values, i));
11074  fprintf(outFile, "\n");
11075  }
11076 
11077  // Close the file
11078  fclose (outFile);
11079 }
Vacc * acc
Definition: vpbe.h:90
Definition: vhal.h:271
Definition: vhal.h:231
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:4891
double zmax
Definition: vpmgp.h:197
VPRIVATE void fillcoCoefMol(Vpmg *thee)
Fill operator coefficient arrays from a molecular surface calculation.
Definition: vpmg.c:4612
VPUBLIC double Vpmg_qmEnergy(Vpmg *thee, int extFlag)
Get the "mobile charge" contribution to the electrostatic energy.
Definition: vpmg.c:1386
#define SINH_MAX
Used to set the max values acceptable for sinh chopping.
Definition: vhal.h:456
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's equation with a simple Laplacian operator and zero-valued Dirichl...
Definition: vpmg.c:6898
double xmin
Definition: vgrid.h:89
int ipcon
Definition: vpmgp.h:183
Definition: vhal.h:215
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:872
VPUBLIC double Vpmg_qfEnergy(Vpmg *thee, int extFlag)
Get the "fixed charge" contribution to the electrostatic energy.
Definition: vpmg.c:1687
VPUBLIC Vacc * Vpbe_getVacc(Vpbe *thee)
Get accessibility oracle.
Definition: vpbe.c:76
double * xpts
Definition: vacc.h:86
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
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
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:5022
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:133
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:10795
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:7229
VPRIVATE void bcCalc(Vpmg *thee)
Fill boundary condition arrays.
Definition: vpmg.c:4382
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:438
VPUBLIC void Vgrid_dtor(Vgrid **thee)
Object destructor.
Definition: vgrid.c:152
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:5343
double hzed
Definition: vpmgp.h:89
VPUBLIC double dbspline4(double x)
Evaluate a 5th Order B-Spline derivative (4th order polynomial)
Definition: vpmg.c:7170
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....
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:10430
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:7132
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:3487
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:627
VPRIVATE void fillcoCoefMolIon(Vpmg *thee)
Fill ion (nonlinear) operator coefficient array from a molecular surface calculation.
Definition: vpmg.c:4628
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:1006
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:10741
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:6010
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:420
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:6311
VPRIVATE Vrc_Codes fillcoCharge(Vpmg *thee)
Top-level driver to fill source term charge array.
Definition: vpmg.c:5287
Definition: vhal.h:209
VPRIVATE void fillcoCoef(Vpmg *thee)
Top-level driver to fill all operator coefficient arrays.
Definition: vpmg.c:5247
#define VPMGSMALL
A small number used in Vpmg to decide if points are on/off grid-lines or non-zer0 (etc....
Definition: vhal.h:444
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:299
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 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:7240
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
size_t nrwk
Definition: vpmgp.h:106
VPUBLIC double Vpmg_dielGradNorm(Vpmg *thee)
Get the integral of the gradient of the dielectric function.
Definition: vpmg.c:1342
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:1099
VPRIVATE void fillcoCoefMolDiel(Vpmg *thee)
Fill differential operator coefficient arrays from a molecular surface calculation.
Definition: vpmg.c:4726
VPUBLIC double Vpmg_qfAtomEnergy(Vpmg *thee, Vatom *atom)
Get the per-atom "fixed charge" contribution to the electrostatic energy.
Definition: vpmg.c:1791
VPUBLIC void Vpmg_dtor2(Vpmg *thee)
FORTRAN stub object destructor.
Definition: vpmg.c:571
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:561
VPRIVATE double bspline2(double x)
Evaluate a cubic B-spline.
Definition: vpmg.c:5496
VPRIVATE double Vpmg_qfEnergyPoint(Vpmg *thee, int extFlag)
Calculates charge-potential energy using summation over delta function positions (i....
Definition: vpmg.c:1704
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:151
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:5822
Definition: vhal.h:141
Definition: vhal.h:277
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:5512
VPUBLIC double Vpmg_qmEnergySMPBE(Vpmg *thee, int extFlag)
Vpmg_qmEnergy for SMPBE.
Definition: vpmg.c:1490
VPUBLIC double Vpbe_getmemv(Vpbe *thee)
Get membrane potential (kT)
Definition: vpbe.c:233
double * rparm
Definition: vpmg.h:135
Definition: vhal.h:279
#define VEMBED(rctag)
Allows embedding of RCS ID tags in object files.
Definition: vhal.h:556
VPRIVATE double Vpmg_qfEnergyVolume(Vpmg *thee, int extFlag)
Calculates charge-potential energy as integral over a volume.
Definition: vpmg.c:1861
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:892
double * rwork
Definition: vpmg.h:137
double smsize
Definition: vpbe.h:121
int nu1
Definition: vpmgp.h:158
Definition: vhal.h:103
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:7202
double solventRadius
Definition: vpbe.h:95
#define Vunit_Na
Avogadro'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:6561
VPUBLIC void Vpackmg(int *iparm, double *rparm, size_t *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
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:10858
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:5528
VPUBLIC int Vgrid_gradient(Vgrid *thee, double pt[3], double grad[3])
Get first derivative values at a point.
Definition: vgrid.c:379
VPUBLIC int Vpmg_solveLaplace(Vpmg *thee)
Solve Poisson's equation with a homogeneous Laplacian operator using the solvent dielectric constant....
Definition: vpmg.c:7042
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:275
VPRIVATE void qfForceSpline2(Vpmg *thee, double *force, int atomID)
Charge-field force due to a cubic spline charge function.
Definition: vpmg.c:6448
Definition: vhal.h:216
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:1279
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:273
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:9939
double zmin
Definition: vgrid.h:91
VPRIVATE void fillcoCoefMap(Vpmg *thee)
Fill operator coefficient arrays from pre-calculated maps.
Definition: vpmg.c:4489
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's solvent-accessible surface.
Definition: vacc.c:982
#define VAPBS_BACK
Face definition for a volume.
Definition: vhal.h:432
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:10831
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:211
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:5655
Definition: vhal.h:140
int mgprol
Definition: vpmgp.h:166
int nx
Definition: vpmgp.h:83
#define VAPBS_FRONT
Face definition for a volume.
Definition: vhal.h:414
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:7136
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:281
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
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
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:302
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:6849
#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:450
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:5845
double omegal
Definition: vpmgp.h:180
int number
Definition: valist.h:80
double * a3cf
Definition: vpmg.h:142
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:179
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:5391
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:6267
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:11023
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:377
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:401
int nlev
Definition: vpmgp.h:86
VPUBLIC double Vpmg_energy(Vpmg *thee, int extFlag)
Get the total electrostatic energy.
Definition: vpmg.c:1248
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:1893
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:1148
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:4737
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:244
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:2564
double * a2cf
Definition: vpmg.h:140
double * pvec
Definition: vpmg.h:154
#define VAPBS_LEFT
Face definition for a volume.
Definition: vhal.h:426
double xmax
Definition: vpmgp.h:195
#define VAPBS_RIGHT
Face definition for a volume.
Definition: vhal.h:408
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:86
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