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