1 /**
2  *  @file    vpmg.c
3  *  @author  Nathan Baker
4  *  @brief   Class Vpmg methods
5  *  @ingroup Vpmg
6  *  @version $Id$
7  *  @attention
8  *  @verbatim
9  *
10  * APBS -- Adaptive Poisson-Boltzmann Solver
11  *
12  *  Nathan A. Baker (nathan.baker@pnnl.gov)
13  *  Pacific Northwest National Laboratory
14  *
15  *  Additional contributing authors listed in the code documentation.
16  *
17  * Copyright (c) 2010-2014 Battelle Memorial Institute. Developed at the
18  * Pacific Northwest National Laboratory, operated by Battelle Memorial
19  * Institute, Pacific Northwest Division for the U.S. Department of Energy.
20  *
21  * Portions Copyright (c) 2002-2010, Washington University in St. Louis.
22  * Portions Copyright (c) 2002-2010, Nathan A. Baker.
23  * Portions Copyright (c) 1999-2002, The Regents of the University of
24  * California.
25  * Portions Copyright (c) 1995, Michael Holst.
26  * All rights reserved.
27  *
28  * Redistribution and use in source and binary forms, with or without
29  * modification, are permitted provided that the following conditions are met:
30  *
31  * -  Redistributions of source code must retain the above copyright notice, this
32  * list of conditions and the following disclaimer.
33  *
34  * - Redistributions in binary form must reproduce the above copyright notice,
35  * this list of conditions and the following disclaimer in the documentation
36  * and/or other materials provided with the distribution.
37  *
38  * - Neither the name of Washington University in St. Louis nor the names of its
39  * contributors may be used to endorse or promote products derived from this
40  * software without specific prior written permission.
41  *
42  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
46  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
47  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
48  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
49  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
50  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
51  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
52  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53  *
54  * Neither the name of the developer nor the names of its contributors may be
55  * used to endorse or promote products derived from this software without
56  * specific prior written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
59  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
62  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
63  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
64  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
65  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
66  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
67  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
68  * THE POSSIBILITY OF SUCH DAMAGE.
69  *
70  * @endverbatim
71  */
72 
73 #include "vpmg.h"
74 
75 VEMBED(rcsid="$Id$")
76 
77 #if !defined(VINLINE_VPMG)
78 
Vpmg_memChk(Vpmg * thee)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 
Vpmg_printColComp(Vpmg * thee,char path[72],char title[72],char mxtype[3],int flag)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 
Vpmg_ctor(Vpmgp * pmgp,Vpbe * pbe,int focusFlag,Vpmg * pmgOLD,MGparm * mgparm,PBEparm_calcEnergy energyFlag)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 
Vpmg_ctor2(Vpmg * thee,Vpmgp * pmgp,Vpbe * pbe,int focusFlag,Vpmg * pmgOLD,MGparm * mgparm,PBEparm_calcEnergy energyFlag)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 
172     /// @note  this is common to both replace/noreplace options
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 */
334     /** @note  The fortran replacement functions are run along side the old
335      *         fortran functions.  This is due to the use of common variables
336      *         in the fortran sub-routines.  Once the fortran code has been
337      *         successfully excised, these functions will no longer need to be
338      *         called in tandem, and the fortran version may be dropped
339      */
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 
Vpmg_solve(Vpmg * thee)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 
Vpmg_dtor(Vpmg ** thee)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 
Vpmg_dtor2(Vpmg * thee)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 
Vpmg_setPart(Vpmg * thee,double lowerCorner[3],double upperCorner[3],int bflags[6])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 
Vpmg_unsetPart(Vpmg * thee)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 
Vpmg_fillArray(Vpmg * thee,double * vec,Vdata_Type type,double parm,Vhal_PBEType pbetype,PBEparm * pbeparm)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 
Vpmg_polarizEnergy(Vpmg * thee,int extFlag)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 
Vpmg_energy(Vpmg * thee,int extFlag)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 
Vpmg_dielEnergy(Vpmg * thee,int extFlag)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 
Vpmg_dielGradNorm(Vpmg * thee)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 
Vpmg_qmEnergy(Vpmg * thee,int extFlag)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 
Vpmg_qmEnergyNONLIN(Vpmg * thee,int extFlag)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 
Vpmg_qmEnergySMPBE(Vpmg * thee,int extFlag)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 
1592     /// @todo remove if no conflicts are caused
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 
Vpmg_qfEnergy(Vpmg * thee,int extFlag)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 
Vpmg_qfEnergyPoint(Vpmg * thee,int extFlag)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 
Vpmg_qfAtomEnergy(Vpmg * thee,Vatom * atom)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 
Vpmg_qfEnergyVolume(Vpmg * thee,int extFlag)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 
Vpmg_splineSelect(int srfm,Vacc * acc,double * gpos,double win,double infrad,Vatom * atom,double * force)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 
focusFillBound(Vpmg * thee,Vpmg * pmgOLD)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 
extEnergy(Vpmg * thee,Vpmg * pmgOLD,PBEparm_calcEnergy extFlag,double partMin[3],double partMax[3],int bflags[6])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 
bcfl1sp(double size,double * apos,double charge,double xkappa,double pre1,double * pos)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 
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)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 
bcfl2(double size,double * apos,double charge,double * dipole,double * quad,double xkappa,double eps_p,double eps_w,double T,double * gxcf,double * gycf,double * gzcf,double * xf,double * yf,double * zf,int nx,int ny,int nz)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 
bcCalcOrig(Vpmg * thee)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  */
gridPointIsValid(int i,int j,int k,int nx,int ny,int nz)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"
packAtomsOpenCL(float * ax,float * ay,float * az,float * charge,float * size,Vpmg * thee)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  */
packUnpackOpenCL(int nx,int ny,int nz,int ngrid,float * gx,float * gy,float * gz,float * value,Vpmg * thee,int pack)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  */
bcflnewOpenCL(Vpmg * thee)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 
packAtoms(double * ax,double * ay,double * az,double * charge,double * size,Vpmg * thee)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  */
packUnpack(int nx,int ny,int nz,int ngrid,double * gx,double * gy,double * gz,double * value,Vpmg * thee,int pack)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 
bcflnew(Vpmg * thee)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 
multipolebc(double r,double kappa,double eps_p,double eps_w,double rad,double tsr[3])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 
bcfl_sdh(Vpmg * thee)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 
bcfl_mdh(Vpmg * thee)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
3994  /////////////////////////////////////////////////////////////////////////// */
bcfl_mem(double zmem,double L,double eps_m,double eps_w,double V,double xkappa,double * gxcf,double * gycf,double * gzcf,double * xf,double * yf,double * zf,int nx,int ny,int nz)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 
3999     ///////////////////////////////////////////////////
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      */
4005     ///////////////////////////////////////////////////
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 
bcfl_map(Vpmg * thee)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)
bcfl_mdh_tinker(Vpmg * thee)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 
bcCalc(Vpmg * thee)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 
fillcoCoefMap(Vpmg * thee)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 
fillcoCoefMol(Vpmg * thee)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 
fillcoCoefMolIon(Vpmg * thee)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 
fillcoCoefMolDiel(Vpmg * thee)4726 VPRIVATE void fillcoCoefMolDiel(Vpmg *thee) {
4727 
4728     /* Always call NoSmooth to fill the epsilon arrays */
4729     fillcoCoefMolDielNoSmooth(thee);
4730 
4731     /* Call the smoothing algorithm as needed */
4732     if (thee->surfMeth == VSM_MOLSMOOTH) {
4733         fillcoCoefMolDielSmooth(thee);
4734     }
4735 }
4736 
fillcoCoefMolDielNoSmooth(Vpmg * thee)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 
fillcoCoefMolDielSmooth(Vpmg * thee)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 
fillcoCoefSpline(Vpmg * thee)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 
fillcoCoef(Vpmg * thee)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 
fillcoCharge(Vpmg * thee)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");
5310                     fillcoPermanentMultipole(thee);
5311                     break;
5312 #if defined(WITH_TINKER)
5313                 case VCM_PERMANENT:
5314                     Vnm_print(0, "fillcoCharge: Calling fillcoPermanentMultipole...\n");
5315                     fillcoPermanentMultipole(thee);
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 
fillcoChargeMap(Vpmg * thee)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 
fillcoChargeSpline1(Vpmg * thee)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 
bspline2(double x)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 
dbspline2(double x)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 
fillcoChargeSpline2(Vpmg * thee)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 
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)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 
Vpmg_force(Vpmg * thee,double * force,int atomID,Vsurf_Meth srfm,Vchrg_Meth chgm)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 
Vpmg_ibForce(Vpmg * thee,double * force,int atomID,Vsurf_Meth srfm)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 
Vpmg_dbForce(Vpmg * thee,double * dbForce,int atomID,Vsurf_Meth srfm)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);
6060     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
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 
Vpmg_qfForce(Vpmg * thee,double * force,int atomID,Vchrg_Meth chgm)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 
qfForceSpline1(Vpmg * thee,double * force,int atomID)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 
qfForceSpline2(Vpmg * thee,double * force,int atomID)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 
qfForceSpline4(Vpmg * thee,double * force,int atomID)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 
markFrac(double rtot,double * tpos,int nx,int ny,int nz,double hx,double hy,double hzed,double xmin,double ymin,double zmin,double * xarray,double * yarray,double * zarray)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 */
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)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 
zlapSolve(Vpmg * thee,double ** solution,double ** source,double ** work1)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 
Vpmg_solveLaplace(Vpmg * thee)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 
7088                 /// @todo  Fix this to use Vmatrices
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 
VFCHI4(int i,double f)7132 VPRIVATE double VFCHI4(int i, double f) {
7133   return (2.5+((double)(i)-(f)));
7134 }
7135 
bspline4(double x)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 
dbspline4(double x)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 
d2bspline4(double x)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 
d3bspline4(double x)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 
fillcoPermanentMultipole(Vpmg * thee)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 
fillcoInducedDipole(Vpmg * thee)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 
fillcoNLInducedDipole(Vpmg * thee)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 
Vpmg_qfPermanentMultipoleEnergy(Vpmg * thee,int atomID)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 
Vpmg_fieldSpline4(Vpmg * thee,int atomID,double field[3])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 
Vpmg_qfPermanentMultipoleForce(Vpmg * thee,int atomID,double force[3],double torque[3])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 
Vpmg_ibPermanentMultipoleForce(Vpmg * thee,int atomID,double force[3])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 
Vpmg_dbPermanentMultipoleForce(Vpmg * thee,int atomID,double force[3])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);
8447     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
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 
Vpmg_qfDirectPolForce(Vpmg * thee,Vgrid * perm,Vgrid * induced,int atomID,double force[3],double torque[3])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 
Vpmg_qfNLDirectPolForce(Vpmg * thee,Vgrid * perm,Vgrid * nlInduced,int atomID,double force[3],double torque[3])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 
Vpmg_ibDirectPolForce(Vpmg * thee,Vgrid * perm,Vgrid * induced,int atomID,double force[3])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 
Vpmg_ibNLDirectPolForce(Vpmg * thee,Vgrid * perm,Vgrid * nlInduced,int atomID,double force[3])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 
Vpmg_dbDirectPolForce(Vpmg * thee,Vgrid * perm,Vgrid * induced,int atomID,double force[3])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);
9290     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
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 
Vpmg_dbNLDirectPolForce(Vpmg * thee,Vgrid * perm,Vgrid * nlInduced,int atomID,double force[3])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 
Vpmg_qfMutualPolForce(Vpmg * thee,Vgrid * induced,Vgrid * nlinduced,int atomID,double force[3])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 
Vpmg_ibMutualPolForce(Vpmg * thee,Vgrid * induced,Vgrid * nlinduced,int atomID,double force[3])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 
Vpmg_dbMutualPolForce(Vpmg * thee,Vgrid * induced,Vgrid * nlinduced,int atomID,double force[3])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);
9777     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
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 
fillcoCoefSpline4(Vpmg * thee)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 
fillcoPermanentInduced(Vpmg * thee)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 
fillcoCoefSpline3(Vpmg * thee)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 
bcolcomp(int * iparm,double * rparm,int * iwork,double * rwork,double * values,int * rowind,int * colptr,int * flag)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 
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)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  **************************************************************************/
bcolcomp3(int * nx,int * ny,int * nz,int * ipc,double * rpc,double * ac,double * cc,double * values,int * rowind,int * colptr,int * flag)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  **************************************************************************/
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)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 
pcolcomp(int * nrow,int * ncol,int * nnzero,double * values,int * rowind,int * colptr,char * path,char * title,char * mxtype)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 }
11080