• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/mg/vpmg.c

Go to the documentation of this file.
00001 
00049 #include "apbscfg.h"
00050 #include "vpmg-private.h"
00051 #include "apbs/vpmg.h"
00052 #include "apbs/vhal.h"
00053 
00054 VEMBED(rcsid="$Id: vpmg.c 1611 2010-10-07 20:35:26Z yhuang01 $")
00055 
00056 #if !defined(VINLINE_VPMG)
00057 
00058 VPUBLIC unsigned long int Vpmg_memChk(Vpmg *thee) {
00059     if (thee == VNULL) return 0;
00060     return Vmem_bytes(thee->vmem);
00061 }
00062 
00063 #endif /* if !defined(VINLINE_VPMG) */
00064 
00065 
00066 VPUBLIC void Vpmg_printColComp(Vpmg *thee, char path[72], char title[72], 
00067   char mxtype[3], int flag) {
00068 
00069     int nn, nxm2, nym2, nzm2, ncol, nrow, nonz; 
00070     double *nzval;
00071     int *colptr, *rowind;
00072 
00073     /* Calculate the total number of unknowns */
00074     nxm2 = thee->pmgp->nx - 2;
00075     nym2 = thee->pmgp->ny - 2;
00076     nzm2 = thee->pmgp->nz - 2;
00077     nn = nxm2*nym2*nzm2;
00078     ncol = nn;
00079     nrow = nn;
00080 
00081     /* Calculate the number of non-zero matrix entries:
00082      *    nn       nonzeros on diagonal
00083      *    nn-1     nonzeros on first off-diagonal
00084      *    nn-nx    nonzeros on second off-diagonal
00085      *    nn-nx*ny nonzeros on third off-diagonal
00086      *
00087      *    7*nn-2*nx*ny-2*nx-2 TOTAL non-zeros
00088      */
00089     nonz = 7*nn - 2*nxm2*nym2 - 2*nxm2 - 2;
00090     nzval  = Vmem_malloc(thee->vmem, nonz, sizeof(double));
00091     rowind = Vmem_malloc(thee->vmem, nonz, sizeof(int));
00092     colptr = Vmem_malloc(thee->vmem, (ncol+1), sizeof(int));
00093 
00094 #ifndef VAPBSQUIET
00095     Vnm_print(1, "Vpmg_printColComp:  Allocated space for %d nonzeros\n",
00096       nonz);
00097 #endif
00098 
00099     F77BCOLCOMP(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00100       nzval, rowind, colptr, &flag);
00101 
00102 #if 0
00103     for (i=0; i<nn; i++) {
00104         Vnm_print(1, "nnz(%d) = %g\n", i, nzval[i]);
00105     }
00106 #endif
00107 
00108     /* I do not understand why I need to pass nzval in this way, but it
00109      * works... */
00110     F77PCOLCOMP(&nrow, &ncol, &nonz, &(nzval[0]), rowind, colptr, path, title, 
00111       mxtype);
00112 
00113     Vmem_free(thee->vmem, (ncol+1), sizeof(int), (void **)&colptr);
00114     Vmem_free(thee->vmem, nonz, sizeof(int), (void **)&rowind);
00115     Vmem_free(thee->vmem, nonz, sizeof(double), (void **)&nzval);
00116 
00117 }
00118 
00119 VPUBLIC Vpmg* Vpmg_ctor(Vpmgp *pmgp, Vpbe *pbe, int focusFlag, 
00120         Vpmg *pmgOLD, MGparm *mgparm, PBEparm_calcEnergy energyFlag) {
00121 
00122     Vpmg *thee = VNULL;
00123 
00124     thee = Vmem_malloc(VNULL, 1, sizeof(Vpmg) );
00125     VASSERT(thee != VNULL);
00126     VASSERT( Vpmg_ctor2(thee, pmgp, pbe, focusFlag, pmgOLD, mgparm, 
00127                 energyFlag) );
00128     return thee;
00129 }
00130 
00131 VPUBLIC int Vpmg_ctor2(Vpmg *thee, Vpmgp *pmgp, Vpbe *pbe, int focusFlag,
00132         Vpmg *pmgOLD, MGparm *mgparm, PBEparm_calcEnergy energyFlag) {
00133  
00134     int i, j, nion;
00135     double ionConc[MAXION], ionQ[MAXION], ionRadii[MAXION], zkappa2, zks2;
00136     double ionstr, partMin[3], partMax[3];
00137  
00138     /* Get the parameters */
00139     VASSERT(pmgp != VNULL);
00140     VASSERT(pbe != VNULL);
00141     thee->pmgp = pmgp;
00142     thee->pbe = pbe;
00143  
00144     /* Set up the memory */
00145     thee->vmem = Vmem_ctor("APBS:VPMG");
00146  
00147  /* TEMPORARY USEAQUA */
00148     /* Calculate storage requirements */
00149  if(mgparm->useAqua == 0){
00150   Vpmgp_size(thee->pmgp);
00151  }else{
00152   F77MGSZAQUA(
00153     &(thee->pmgp->mgcoar), &(thee->pmgp->mgdisc),
00154     &(thee->pmgp->mgsolv), 
00155     &(thee->pmgp->nx), &(thee->pmgp->ny), &(thee->pmgp->nz), 
00156     &(thee->pmgp->nlev), 
00157     &(thee->pmgp->nxc), &(thee->pmgp->nyc), &(thee->pmgp->nzc), 
00158     &(thee->pmgp->nf), &(thee->pmgp->nc), 
00159     &(thee->pmgp->narr), &(thee->pmgp->narrc), 
00160     &(thee->pmgp->n_rpc), &(thee->pmgp->n_iz), &(thee->pmgp->n_ipc), 
00161     &(thee->pmgp->nrwk), &(thee->pmgp->niwk)
00162     );
00163  }
00164  
00165     /* We need some additional storage if: nonlinear & newton OR cgmg */
00166  /* SMPBE Added - nonlin = 2 added since it mimics NPBE */
00167     if ( ( ((thee->pmgp->nonlin == NONLIN_NPBE) || (thee->pmgp->nonlin == NONLIN_SMPBE)) 
00168      && (thee->pmgp->meth == VSOL_Newton) ) || (thee->pmgp->meth == VSOL_CGMG) ) 
00169  { 
00170   thee->pmgp->nrwk += (2*(thee->pmgp->nf));
00171     }
00172  
00173  Vnm_print(0, "Vpmg_ctor2:  PMG chose nx = %d, ny = %d, nz = %d\n", 
00174      thee->pmgp->nx, thee->pmgp->ny, thee->pmgp->nz);
00175  Vnm_print(0, "Vpmg_ctor2:  PMG chose nlev = %d\n", 
00176      thee->pmgp->nlev);
00177  Vnm_print(0, "Vpmg_ctor2:  PMG chose nxc = %d, nyc = %d, nzc = %d\n", 
00178      thee->pmgp->nxc, thee->pmgp->nyc, thee->pmgp->nzc);
00179  Vnm_print(0, "Vpmg_ctor2:  PMG chose nf = %d, nc = %d\n", 
00180      thee->pmgp->nf, thee->pmgp->nc);
00181  Vnm_print(0, "Vpmg_ctor2:  PMG chose narr = %d, narrc = %d\n", 
00182      thee->pmgp->narr, thee->pmgp->narrc);
00183  Vnm_print(0, "Vpmg_ctor2:  PMG chose n_rpc = %d, n_iz = %d, n_ipc = %d\n", 
00184      thee->pmgp->n_rpc, thee->pmgp->n_iz, thee->pmgp->n_ipc);
00185  Vnm_print(0, "Vpmg_ctor2:  PMG chose nrwk = %d, niwk = %d\n", 
00186      thee->pmgp->nrwk, thee->pmgp->niwk);
00187  
00188  /* Allocate boundary storage */
00189  thee->gxcf = (double *)Vmem_malloc(thee->vmem,
00190             10*(thee->pmgp->ny)*(thee->pmgp->nz), sizeof(double));
00191  thee->gycf = (double *)Vmem_malloc(thee->vmem,
00192             10*(thee->pmgp->nx)*(thee->pmgp->nz), sizeof(double));
00193  thee->gzcf = (double *)Vmem_malloc(thee->vmem,
00194             10*(thee->pmgp->nx)*(thee->pmgp->ny), sizeof(double));
00195  
00196  /* Warn users if they are using BCFL_MAP that 
00197     we do not include external energies */
00198  if (thee->pmgp->bcfl == BCFL_MAP)
00199   Vnm_print(2,"Vpmg_ctor2: \nWarning: External energies are not used in BCFL_MAP calculations!\n");
00200  
00201  if (focusFlag) {
00202   /* Overwrite any default or user-specified boundary condition
00203   * arguments; we are now committed to a calculation via focusing */
00204   if (thee->pmgp->bcfl != BCFL_FOCUS) {
00205    Vnm_print(2, 
00206        "Vpmg_ctor2: reset boundary condition flag to BCFL_FOCUS!\n");
00207    thee->pmgp->bcfl = BCFL_FOCUS;
00208   }
00209   
00210   /* Fill boundaries */
00211   Vnm_print(0, "Vpmg_ctor2:  Filling boundary with old solution!\n");
00212   focusFillBound(thee, pmgOLD);
00213   
00214   /* Calculate energetic contributions from region outside focusing
00215    * domain */
00216   if (energyFlag != PCE_NO) {
00217    
00218    if (mgparm->type == MCT_PARALLEL) {
00219     
00220     for (j=0; j<3; j++) {
00221      partMin[j] = mgparm->partDisjCenter[j]
00222      - 0.5*mgparm->partDisjLength[j];
00223      partMax[j] = mgparm->partDisjCenter[j]
00224       + 0.5*mgparm->partDisjLength[j];
00225     }
00226     
00227    } else {
00228     for (j=0; j<3; j++) {
00229      partMin[j] = mgparm->center[j] - 0.5*mgparm->glen[j];
00230      partMax[j] = mgparm->center[j] + 0.5*mgparm->glen[j];
00231     }
00232    }
00233    extEnergy(thee, pmgOLD, energyFlag, partMin, partMax, 
00234        mgparm->partDisjOwnSide);
00235   }
00236   
00237  } else {
00238   
00239   /* Ignore external energy contributions */
00240   thee->extQmEnergy = 0;
00241   thee->extDiEnergy = 0;
00242   thee->extQfEnergy = 0;
00243  }
00244 
00245  /*
00246   * TODO: Move the dtor out of here. The current ctor is done in routines.c,
00247   *       This was originally moved out to kill a memory leak. The dtor has
00248   *       has been removed from initMG and placed back here to keep memory 
00249   *       usage low. killMG has been modified accordingly.
00250   */
00251  Vpmg_dtor(&pmgOLD);   
00252  
00253  /* Allocate partition vector storage */
00254  thee->pvec = (double *)Vmem_malloc(thee->vmem,
00255             (thee->pmgp->nx)*(thee->pmgp->ny)*(thee->pmgp->nz), sizeof(double));
00256  
00257  /* Allocate remaining storage */
00258  thee->iparm = (int *)Vmem_malloc(thee->vmem, 100, sizeof(int));
00259  thee->rparm = (double *)Vmem_malloc(thee->vmem, 100, sizeof(double));
00260  thee->iwork = (int *)Vmem_malloc(thee->vmem, thee->pmgp->niwk,
00261           sizeof(int));
00262  thee->rwork = (double *)Vmem_malloc(thee->vmem, thee->pmgp->nrwk,
00263           sizeof(double));
00264  thee->charge = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00265            sizeof(double));
00266  thee->kappa = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00267           sizeof(double));
00268  thee->pot = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00269           sizeof(double));
00270  thee->epsx = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00271             sizeof(double));
00272  thee->epsy = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00273             sizeof(double));
00274  thee->epsz = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00275             sizeof(double));
00276  thee->a1cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00277             sizeof(double));
00278  thee->a2cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00279             sizeof(double));
00280  thee->a3cf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00281             sizeof(double));
00282  thee->ccf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00283            sizeof(double));
00284  thee->fcf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00285            sizeof(double));
00286  thee->tcf = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00287            sizeof(double));
00288  thee->u = (double *)Vmem_malloc(thee->vmem, thee->pmgp->narr,
00289          sizeof(double));
00290  thee->xf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->nx),
00291           sizeof(double));
00292  thee->yf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->ny),
00293           sizeof(double));
00294  thee->zf = (double *)Vmem_malloc(thee->vmem, 5*(thee->pmgp->nz),
00295           sizeof(double));
00296  
00297  /* Plop some of the parameters into the iparm and rparm arrays */
00298  F77PACKMG(thee->iparm, thee->rparm, &(thee->pmgp->nrwk), &(thee->pmgp->niwk),
00299      &(thee->pmgp->nx), &(thee->pmgp->ny), &(thee->pmgp->nz),
00300      &(thee->pmgp->nlev), &(thee->pmgp->nu1), &(thee->pmgp->nu2),
00301      &(thee->pmgp->mgkey), &(thee->pmgp->itmax), &(thee->pmgp->istop),
00302      &(thee->pmgp->ipcon), &(thee->pmgp->nonlin), &(thee->pmgp->mgsmoo),
00303      &(thee->pmgp->mgprol), &(thee->pmgp->mgcoar), &(thee->pmgp->mgsolv),
00304      &(thee->pmgp->mgdisc), &(thee->pmgp->iinfo), &(thee->pmgp->errtol),
00305      &(thee->pmgp->ipkey), &(thee->pmgp->omegal), &(thee->pmgp->omegan),
00306      &(thee->pmgp->irite), &(thee->pmgp->iperf));
00307  
00308  
00309  /* Initialize ion concentrations and valencies in PMG routines */
00310  zkappa2 = Vpbe_getZkappa2(thee->pbe);
00311  ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
00312  if (ionstr > 0.0) zks2 = 0.5/ionstr;
00313  else zks2 = 0.0;
00314  Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
00315  
00316  /* Currently for SMPBE type calculations we do not want to apply a scale
00317   factor to the ionConc */
00318  switch(pmgp->ipkey){
00319   case IPKEY_SMPBE:
00320    F77MYPDEFINITSMPBE(&nion, ionQ, ionConc, &pbe->smvolume,&pbe->smsize);
00321    break;
00322   case IPKEY_NPBE:
00323    /* Else adjust the inoConc by scaling factor zks2 */
00324    for (i=0; i<nion; i++) ionConc[i] = zks2 * ionConc[i];
00325    F77MYPDEFINITNPBE(&nion, ionQ, ionConc);
00326    break;
00327   case IPKEY_LPBE:
00328    /* Else adjust the inoConc by scaling factor zks2 */
00329    for (i=0; i<nion; i++) ionConc[i] = zks2 * ionConc[i];
00330    F77MYPDEFINITLPBE(&nion, ionQ, ionConc);
00331    break;
00332   default:
00333    /* Else adjust the inoConc by scaling factor zks2 */
00334    for (i=0; i<nion; i++) ionConc[i] = zks2 * ionConc[i];
00335    break;
00336  }
00337  
00338  /* Set the default chargeSrc for 5th order splines */
00339  thee->chargeSrc = mgparm->chgs;
00340 
00341  /* Turn off restriction of observable calculations to a specific
00342  * partition */
00343  Vpmg_unsetPart(thee);
00344 
00345  /* The coefficient arrays have not been filled */
00346  thee->filled = 0;
00347 
00348  return 1;
00349 }
00350 
00351 VPUBLIC int Vpmg_solve(Vpmg *thee) {
00352 
00353     int i, nx, ny, nz, n;
00354     double zkappa2;
00355 
00356     nx = thee->pmgp->nx;
00357     ny = thee->pmgp->ny;
00358     nz = thee->pmgp->nz;
00359     n = nx*ny*nz;
00360 
00361     if (!(thee->filled)) {
00362         Vnm_print(2, "Vpmg_solve:  Need to call Vpmg_fillco()!\n");
00363         return 0;
00364     }
00365 
00366     /* Fill the "true solution" array */
00367     for (i=0; i<n; i++) {
00368         thee->tcf[i] = 0.0;
00369     }
00370 
00371     /* Fill the RHS array */
00372     for (i=0; i<n; i++) {
00373         thee->fcf[i] = thee->charge[i];
00374     }
00375 
00376     /* Fill the operator coefficient array. */
00377     for (i=0; i<n; i++) {
00378         thee->a1cf[i] = thee->epsx[i];
00379         thee->a2cf[i] = thee->epsy[i];
00380         thee->a3cf[i] = thee->epsz[i];
00381     }
00382 
00383     /* Fill the nonlinear coefficient array by multiplying the kappa
00384      * accessibility array (containing values between 0 and 1) by zkappa2. */
00385     zkappa2 = Vpbe_getZkappa2(thee->pbe);
00386     if (zkappa2 > VPMGSMALL) {
00387         for (i=0; i<n; i++) {
00388             thee->ccf[i] = zkappa2*thee->kappa[i];
00389         }
00390     } else {
00391         for (i=0; i<n; i++) {
00392             thee->ccf[i] = 0.0;
00393         }
00394     }
00395 
00396     switch(thee->pmgp->meth) {
00397         /* CGMG (linear) */
00398         case VSOL_CGMG:
00399             F77CGMGDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00400       thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00401       thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00402       thee->fcf, thee->tcf);
00403             break;
00404    /* Newton (nonlinear) */
00405         case VSOL_Newton:
00406             F77NEWDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork, 
00407         thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00408         thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf, 
00409         thee->fcf, thee->tcf);
00410             break;
00411    /* MG (linear/nonlinear) */
00412         case VSOL_MG:
00413 #if 1
00414    F77MGDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00415        thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00416        thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00417        thee->fcf, thee->tcf);
00418 #else
00419    mgdrivc(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00420        thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00421        thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00422        thee->fcf, thee->tcf);
00423 #endif
00424             break;
00425    /* CGHS (linear/nonlinear) */
00426         case VSOL_CG: 
00427    F77NCGHSDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00428        thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00429        thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00430        thee->fcf, thee->tcf);
00431             break;
00432    /* SOR (linear/nonlinear) */
00433         case VSOL_SOR:
00434    F77NSORDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00435       thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00436       thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00437       thee->fcf, thee->tcf);
00438             break;
00439    /* GSRB (linear/nonlinear) */
00440         case VSOL_RBGS:
00441    F77NGSRBDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00442        thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00443        thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00444        thee->fcf, thee->tcf); 
00445             break;
00446    /* WJAC (linear/nonlinear) */
00447         case VSOL_WJ:
00448    F77NWJACDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00449        thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00450        thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00451        thee->fcf, thee->tcf);
00452             break;
00453    /* RICH (linear/nonlinear) */
00454         case VSOL_Richardson:
00455    F77NRICHDRIV(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00456        thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00457        thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00458        thee->fcf, thee->tcf);
00459             break;
00460    /* CGMG (linear) TEMPORARY USEAQUA */
00461         case VSOL_CGMGAqua:
00462             F77CGMGDRIVAQUA(thee->iparm, thee->rparm, thee->iwork, thee->rwork,
00463       thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00464       thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf,
00465       thee->fcf);
00466             break;
00467    /* Newton (nonlinear) TEMPORARY USEAQUA */
00468         case VSOL_NewtonAqua:
00469             F77NEWDRIVAQUA(thee->iparm, thee->rparm, thee->iwork, thee->rwork, 
00470         thee->u, thee->xf, thee->yf, thee->zf, thee->gxcf, thee->gycf,
00471         thee->gzcf, thee->a1cf, thee->a2cf, thee->a3cf, thee->ccf, 
00472         thee->fcf);
00473             break;   
00474         /* Error handling */
00475         default: 
00476             Vnm_print(2, "Vpmg_solve: invalid solver method key (%d)\n",
00477               thee->pmgp->key);
00478             return 0;
00479             break;
00480     }
00481 
00482     return 1;
00483 
00484 }
00485 
00486     
00487 VPUBLIC void Vpmg_dtor(Vpmg **thee) {
00488     
00489     if ((*thee) != VNULL) {
00490         Vpmg_dtor2(*thee);
00491         Vmem_free(VNULL, 1, sizeof(Vpmg), (void **)thee);
00492         (*thee) = VNULL;
00493     }
00494 
00495 }
00496 
00497 VPUBLIC void Vpmg_dtor2(Vpmg *thee) { 
00498 
00499     /* Clear out the FORTRAN arrays */
00500     F77MYPDEFCLEAR();
00501 
00502     /* Clean up the storage */
00503     Vmem_free(thee->vmem, 100, sizeof(int), (void **)&(thee->iparm));
00504     Vmem_free(thee->vmem, 100, sizeof(double), (void **)&(thee->rparm));
00505     Vmem_free(thee->vmem, thee->pmgp->niwk, sizeof(int), 
00506       (void **)&(thee->iwork));
00507     Vmem_free(thee->vmem, thee->pmgp->nrwk, sizeof(double), 
00508       (void **)&(thee->rwork));
00509     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00510       (void **)&(thee->charge));
00511     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00512       (void **)&(thee->kappa));
00513  Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00514      (void **)&(thee->pot));
00515     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00516       (void **)&(thee->epsx));
00517     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00518       (void **)&(thee->epsy));
00519     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00520       (void **)&(thee->epsz));
00521     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00522       (void **)&(thee->a1cf));
00523     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double), 
00524       (void **)&(thee->a2cf));
00525     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00526       (void **)&(thee->a3cf));
00527     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double),
00528       (void **)&(thee->ccf));
00529     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double), 
00530       (void **)&(thee->fcf));
00531     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double), 
00532       (void **)&(thee->tcf));
00533     Vmem_free(thee->vmem, thee->pmgp->narr, sizeof(double), 
00534       (void **)&(thee->u));
00535     Vmem_free(thee->vmem, 5*(thee->pmgp->nx), sizeof(double),
00536       (void **)&(thee->xf));
00537     Vmem_free(thee->vmem, 5*(thee->pmgp->ny), sizeof(double),
00538       (void **)&(thee->yf));
00539     Vmem_free(thee->vmem, 5*(thee->pmgp->nz), sizeof(double),
00540       (void **)&(thee->zf));
00541     Vmem_free(thee->vmem, 10*(thee->pmgp->ny)*(thee->pmgp->nz), sizeof(double),
00542       (void **)&(thee->gxcf));
00543     Vmem_free(thee->vmem, 10*(thee->pmgp->nx)*(thee->pmgp->nz), sizeof(double),
00544       (void **)&(thee->gycf));
00545     Vmem_free(thee->vmem, 10*(thee->pmgp->nx)*(thee->pmgp->ny), sizeof(double),
00546       (void **)&(thee->gzcf));
00547     Vmem_free(thee->vmem, (thee->pmgp->nx)*(thee->pmgp->ny)*(thee->pmgp->nz), 
00548       sizeof(double), (void **)&(thee->pvec));
00549 
00550     Vmem_dtor(&(thee->vmem));
00551 }
00552 
00553 VPUBLIC void Vpmg_setPart(Vpmg *thee, double lowerCorner[3], 
00554         double upperCorner[3], int bflags[6]) {
00555 
00556     Valist *alist;
00557     Vatom *atom;
00558     int i, j, k, nx, ny, nz;
00559     double xmin, ymin, zmin, x, y, z, hx, hy, hzed, xok, yok, zok;
00560     double x0,x1,y0,y1,z0,z1;
00561 
00562     nx = thee->pmgp->nx;
00563     ny = thee->pmgp->ny;
00564     nz = thee->pmgp->nz;
00565     hx = thee->pmgp->hx;
00566     hy = thee->pmgp->hy;
00567     hzed = thee->pmgp->hzed;
00568     xmin = thee->pmgp->xcent - 0.5*hx*(nx-1);
00569     ymin = thee->pmgp->ycent - 0.5*hy*(ny-1);
00570     zmin = thee->pmgp->zcent - 0.5*hzed*(nz-1);
00571 
00572     xok = 0;
00573     yok = 0;
00574     zok = 0;
00575 
00576     /* We need have called Vpmg_fillco first */
00577 
00578     alist = thee->pbe->alist;
00579 
00580     Vnm_print(0, "Vpmg_setPart:  lower corner = (%g, %g, %g)\n",
00581       lowerCorner[0], lowerCorner[1], lowerCorner[2]);
00582     Vnm_print(0, "Vpmg_setPart:  upper corner = (%g, %g, %g)\n",
00583       upperCorner[0], upperCorner[1], upperCorner[2]);
00584     Vnm_print(0, "Vpmg_setPart:  actual minima = (%g, %g, %g)\n",
00585       xmin, ymin, zmin);
00586     Vnm_print(0, "Vpmg_setPart:  actual maxima = (%g, %g, %g)\n",
00587       xmin+hx*(nx-1), ymin+hy*(ny-1), zmin+hzed*(nz-1));
00588     Vnm_print(0, "Vpmg_setPart:  bflag[FRONT] = %d\n", 
00589       bflags[VAPBS_FRONT]);
00590     Vnm_print(0, "Vpmg_setPart:  bflag[BACK] = %d\n", 
00591       bflags[VAPBS_BACK]);
00592     Vnm_print(0, "Vpmg_setPart:  bflag[LEFT] = %d\n", 
00593       bflags[VAPBS_LEFT]);
00594     Vnm_print(0, "Vpmg_setPart:  bflag[RIGHT] = %d\n", 
00595       bflags[VAPBS_RIGHT]);
00596     Vnm_print(0, "Vpmg_setPart:  bflag[UP] = %d\n", 
00597       bflags[VAPBS_UP]);
00598     Vnm_print(0, "Vpmg_setPart:  bflag[DOWN] = %d\n", 
00599       bflags[VAPBS_DOWN]);
00600 
00601     /* Identify atoms as inside, outside, or on the border
00602        If on the border, use the bflags to determine if there
00603        is an adjacent processor - if so, this atom should be equally
00604        shared. */
00605 
00606     for (i=0; i<Valist_getNumberAtoms(alist); i++) {
00607         atom = Valist_getAtom(alist, i);
00608  
00609         if ((atom->position[0] < upperCorner[0]) &&
00610             (atom->position[0] > lowerCorner[0])) xok = 1;
00611         else {
00612             if ((VABS(atom->position[0] - lowerCorner[0]) < VPMGSMALL) && 
00613                 (bflags[VAPBS_LEFT] == 0)) xok = 1;
00614             else if ((VABS(atom->position[0] - lowerCorner[0]) < VPMGSMALL) && 
00615                 (bflags[VAPBS_LEFT] == 1)) xok = 0.5;
00616             else if ((VABS(atom->position[0] - upperCorner[0]) < VPMGSMALL) &&
00617                 (bflags[VAPBS_RIGHT] == 0)) xok = 1;
00618             else if ((VABS(atom->position[0] - upperCorner[0]) < VPMGSMALL) &&
00619                 (bflags[VAPBS_RIGHT] == 1)) xok = 0.5;
00620             else xok = 0;
00621         }
00622         if ((atom->position[1] < upperCorner[1]) &&
00623             (atom->position[1] > lowerCorner[1])) yok = 1;
00624         else {
00625             if ((VABS(atom->position[1] - lowerCorner[1]) < VPMGSMALL) && 
00626                 (bflags[VAPBS_BACK] == 0)) yok = 1;
00627             else if ((VABS(atom->position[1] - lowerCorner[1]) < VPMGSMALL) && 
00628                 (bflags[VAPBS_BACK] == 1)) yok = 0.5;
00629             else if ((VABS(atom->position[1] - upperCorner[1]) < VPMGSMALL) &&
00630                 (bflags[VAPBS_FRONT] == 0)) yok = 1;
00631             else if ((VABS(atom->position[1] - upperCorner[1]) < VPMGSMALL) &&
00632                 (bflags[VAPBS_FRONT] == 1)) yok = 0.5;
00633             else yok = 0;
00634         }
00635         if ((atom->position[2] < upperCorner[2]) &&
00636             (atom->position[2] > lowerCorner[2])) zok = 1;
00637         else {
00638             if ((VABS(atom->position[2] - lowerCorner[2]) < VPMGSMALL) && 
00639                 (bflags[VAPBS_DOWN] == 0)) zok = 1;
00640             else if ((VABS(atom->position[2] - lowerCorner[2]) < VPMGSMALL) && 
00641                 (bflags[VAPBS_DOWN] == 1)) zok = 0.5;
00642             else if ((VABS(atom->position[2] - upperCorner[2]) < VPMGSMALL) &&
00643                 (bflags[VAPBS_UP] == 0)) zok = 1;
00644             else if ((VABS(atom->position[2] - upperCorner[2]) < VPMGSMALL) &&
00645                 (bflags[VAPBS_UP] == 1)) zok = 0.5;
00646             else zok = 0;
00647         }
00648 
00649         atom->partID = xok*yok*zok; 
00650   /*
00651   Vnm_print(1, "DEBUG (%s, %d):  atom->position[0] - upperCorner[0] = %g\n",
00652       __FILE__, __LINE__, atom->position[0] - upperCorner[0]); 
00653   Vnm_print(1, "DEBUG (%s, %d):  atom->position[0] - lowerCorner[0] = %g\n",
00654       __FILE__, __LINE__, atom->position[0] - lowerCorner[0]); 
00655   Vnm_print(1, "DEBUG (%s, %d):  atom->position[1] - upperCorner[1] = %g\n",
00656       __FILE__, __LINE__, atom->position[1] - upperCorner[1]); 
00657   Vnm_print(1, "DEBUG (%s, %d):  atom->position[1] - lowerCorner[1] = %g\n",
00658       __FILE__, __LINE__, atom->position[1] - lowerCorner[1]); 
00659   Vnm_print(1, "DEBUG (%s, %d):  atom->position[2] - upperCorner[2] = %g\n",
00660       __FILE__, __LINE__, atom->position[2] - upperCorner[2]); 
00661   Vnm_print(1, "DEBUG (%s, %d):  atom->position[2] - lowerCorner[0] = %g\n",
00662       __FILE__, __LINE__, atom->position[2] - lowerCorner[2]); 
00663   Vnm_print(1, "DEBUG (%s, %d):  xok = %g, yok = %g, zok = %g\n", 
00664       __FILE__, __LINE__, xok, yok, zok);
00665    */
00666 
00667  }
00668 
00669     /* Load up pvec -
00670        For all points within h{axis}/2 of a border - use a gradient
00671        to determine the pvec weight.
00672        Points on the boundary depend on the presence of an adjacent
00673        processor. */
00674 
00675     for (i=0; i<(nx*ny*nz); i++) thee->pvec[i] = 0.0;
00676 
00677     for (i=0; i<nx; i++) {
00678         xok = 0.0;
00679         x = i*hx + xmin;
00680         if ( (x < (upperCorner[0]-hx/2)) && 
00681              (x > (lowerCorner[0]+hx/2))
00682            ) xok = 1.0;
00683         else if ( (VABS(x - lowerCorner[0]) < VPMGSMALL) && 
00684                   (bflags[VAPBS_LEFT] == 0)) xok = 1.0;
00685         else if ((VABS(x - lowerCorner[0]) < VPMGSMALL) && 
00686                  (bflags[VAPBS_LEFT] == 1)) xok = 0.5;
00687         else if ((VABS(x - upperCorner[0]) < VPMGSMALL) &&
00688                  (bflags[VAPBS_RIGHT] == 0)) xok = 1.0;
00689         else if ((VABS(x - upperCorner[0]) < VPMGSMALL) &&
00690                  (bflags[VAPBS_RIGHT] == 1)) xok = 0.5;
00691         else if ((x > (upperCorner[0] + hx/2)) || (x < (lowerCorner[0] - hx/2))) xok = 0.0;
00692         else if ((x < (upperCorner[0] + hx/2)) || (x > (lowerCorner[0] - hx/2))) {
00693             x0 = VMAX2(x - hx/2, lowerCorner[0]);
00694             x1 = VMIN2(x + hx/2, upperCorner[0]);
00695             xok = VABS(x1-x0)/hx;
00696 
00697             if (xok < 0.0) {
00698                 if (VABS(xok) < VPMGSMALL) xok = 0.0;
00699                 else {
00700                     Vnm_print(2, "Vpmg_setPart:  fell off x-interval (%1.12E)!\n",
00701                             xok);
00702                     VASSERT(0);
00703                 }
00704             } 
00705             if (xok > 1.0) {
00706                 if (VABS(xok - 1.0) < VPMGSMALL) xok = 1.0;
00707                 else {
00708                     Vnm_print(2, "Vpmg_setPart:  fell off x-interval (%1.12E)!\n",
00709                             xok);
00710                     VASSERT(0);
00711                 }
00712             } 
00713 
00714         } else xok = 0.0;
00715      
00716         for (j=0; j<ny; j++) {
00717             yok = 0.0;
00718             y = j*hy + ymin;
00719             if ((y < (upperCorner[1]-hy/2)) && (y > (lowerCorner[1]+hy/2))) yok = 1.0;
00720             else if ((VABS(y - lowerCorner[1]) < VPMGSMALL) && 
00721                      (bflags[VAPBS_BACK] == 0)) yok = 1.0;
00722             else if ((VABS(y - lowerCorner[1]) < VPMGSMALL) && 
00723                      (bflags[VAPBS_BACK] == 1)) yok = 0.5;
00724             else if ((VABS(y - upperCorner[1]) < VPMGSMALL) &&
00725                      (bflags[VAPBS_FRONT] == 0)) yok = 1.0;
00726             else if ((VABS(y - upperCorner[1]) < VPMGSMALL) &&
00727                      (bflags[VAPBS_FRONT] == 1)) yok = 0.5;
00728             else if ((y > (upperCorner[1] + hy/2)) || (y < (lowerCorner[1] - hy/2))) yok=0.0;
00729             else if ((y < (upperCorner[1] + hy/2)) || (y > (lowerCorner[1] - hy/2))){
00730                 y0 = VMAX2(y - hy/2, lowerCorner[1]);
00731                 y1 = VMIN2(y + hy/2, upperCorner[1]);
00732                 yok = VABS(y1-y0)/hy;
00733 
00734                 if (yok < 0.0) {
00735                     if (VABS(yok) < VPMGSMALL) yok = 0.0;
00736                     else {
00737                         Vnm_print(2, "Vpmg_setPart:  fell off y-interval (%1.12E)!\n",
00738                                 yok);
00739                         VASSERT(0);
00740                     }
00741                 } 
00742                 if (yok > 1.0) {
00743                     if (VABS(yok - 1.0) < VPMGSMALL) yok = 1.0;
00744                     else {
00745                         Vnm_print(2, "Vpmg_setPart:  fell off y-interval (%1.12E)!\n",
00746                                 yok);
00747                         VASSERT(0);
00748                     }
00749                 } 
00750             }
00751             else yok=0.0;
00752 
00753             for (k=0; k<nz; k++) {
00754                 zok = 0.0; 
00755                 z = k*hzed + zmin;
00756                 if ((z < (upperCorner[2]-hzed/2)) && (z > (lowerCorner[2]+hzed/2))) zok = 1.0;
00757                 else if ((VABS(z - lowerCorner[2]) < VPMGSMALL) && 
00758                          (bflags[VAPBS_DOWN] == 0)) zok = 1.0;
00759                 else if ((VABS(z - lowerCorner[2]) < VPMGSMALL) && 
00760                          (bflags[VAPBS_DOWN] == 1)) zok = 0.5;
00761                 else if ((VABS(z - upperCorner[2]) < VPMGSMALL) &&
00762                          (bflags[VAPBS_UP] == 0)) zok = 1.0;
00763                 else if ((VABS(z - upperCorner[2]) < VPMGSMALL) &&
00764                          (bflags[VAPBS_UP] == 1)) zok = 0.5;
00765                 else if ((z > (upperCorner[2] + hzed/2)) || (z < (lowerCorner[2] - hzed/2))) zok=0.0;
00766                 else if ((z < (upperCorner[2] + hzed/2)) || (z > (lowerCorner[2] - hzed/2))){
00767                     z0 = VMAX2(z - hzed/2, lowerCorner[2]);
00768                     z1 = VMIN2(z + hzed/2, upperCorner[2]);
00769                     zok = VABS(z1-z0)/hzed;
00770 
00771                     if (zok < 0.0) {
00772                         if (VABS(zok) < VPMGSMALL) zok = 0.0;
00773                         else {
00774                             Vnm_print(2, "Vpmg_setPart:  fell off z-interval (%1.12E)!\n",
00775                                     zok);
00776                             VASSERT(0);
00777                         }
00778                     } 
00779                     if (zok > 1.0) {
00780                         if (VABS(zok - 1.0) < VPMGSMALL) zok = 1.0;
00781                         else {
00782                             Vnm_print(2, "Vpmg_setPart:  fell off z-interval (%1.12E)!\n",
00783                                     zok);
00784                             VASSERT(0);
00785                         }
00786                     } 
00787                 }
00788                 else zok = 0.0;
00789                 
00790                 if (VABS(xok*yok*zok) < VPMGSMALL) thee->pvec[IJK(i,j,k)] = 0.0;
00791                 else thee->pvec[IJK(i,j,k)] = xok*yok*zok;
00792                
00793             }
00794         }
00795     } 
00796 }
00797 
00798 VPUBLIC void Vpmg_unsetPart(Vpmg *thee) {
00799 
00800     int i, nx, ny, nz;
00801     Vatom *atom;
00802     Valist *alist;
00803 
00804     VASSERT(thee != VNULL);
00805 
00806     nx = thee->pmgp->nx;
00807     ny = thee->pmgp->ny;
00808     nz = thee->pmgp->nz;
00809     alist = thee->pbe->alist;
00810 
00811     for (i=0; i<(nx*ny*nz); i++) thee->pvec[i] = 1;
00812     for (i=0; i<Valist_getNumberAtoms(alist); i++) {
00813         atom = Valist_getAtom(alist, i);
00814         atom->partID = 1;
00815     }
00816 }
00817 
00818 VPUBLIC int Vpmg_fillArray(Vpmg *thee, double *vec, Vdata_Type type, 
00819   double parm, Vhal_PBEType pbetype, PBEparm *pbeparm) {
00820 
00821     Vacc *acc = VNULL;
00822     Vpbe *pbe = VNULL;
00823     Vgrid *grid = VNULL;
00824  Vatom *atoms = VNULL;
00825  Valist *alist = VNULL;
00826     double position[3], hx, hy, hzed, xmin, ymin, zmin;
00827     double grad[3], eps, epsp, epss, zmagic;
00828     int i, j, k, l, nx, ny, nz, ichop;
00829  
00830     pbe = thee->pbe;
00831     acc = Vpbe_getVacc(pbe);
00832     nx = thee->pmgp->nx;
00833     ny = thee->pmgp->ny;
00834     nz = thee->pmgp->nz;
00835     hx = thee->pmgp->hx;
00836     hy = thee->pmgp->hy;
00837     hzed = thee->pmgp->hzed;
00838     xmin = thee->pmgp->xmin;
00839     ymin = thee->pmgp->ymin;
00840     zmin = thee->pmgp->zmin;
00841     epsp = Vpbe_getSoluteDiel(pbe);
00842     epss = Vpbe_getSolventDiel(pbe);
00843     zmagic = Vpbe_getZmagic(pbe);
00844  
00845     if (!(thee->filled)) {
00846         Vnm_print(2, "Vpmg_fillArray:  need to call Vpmg_fillco first!\n");
00847         return 0;
00848     }
00849 
00850     switch (type) {
00851 
00852         case VDT_CHARGE:
00853 
00854             for (i=0; i<nx*ny*nz; i++) vec[i] = thee->charge[i]/zmagic;
00855             break;
00856 
00857         case VDT_DIELX:
00858 
00859             for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsx[i];
00860             break;
00861 
00862         case VDT_DIELY:
00863 
00864             for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsy[i];
00865             break;
00866 
00867         case VDT_DIELZ:
00868 
00869             for (i=0; i<nx*ny*nz; i++) vec[i] = thee->epsz[i];
00870             break;
00871 
00872         case VDT_KAPPA:
00873 
00874             for (i=0; i<nx*ny*nz; i++) vec[i] = thee->kappa[i];
00875             break;
00876 
00877         case VDT_POT:
00878 
00879             for (i=0; i<nx*ny*nz; i++) vec[i] = thee->u[i];
00880             break;
00881   
00882   case VDT_ATOMPOT:
00883    alist = thee->pbe->alist;
00884    atoms = alist[pbeparm->molid-1].atoms;
00885    grid = Vgrid_ctor(nx, ny, nz, hx, hy,
00886          hzed, xmin, ymin, zmin,thee->u);
00887    for (i=0; i<alist[pbeparm->molid-1].number;i++) {
00888     position[0] = atoms[i].position[0];
00889     position[1] = atoms[i].position[1];
00890     position[2] = atoms[i].position[2]; 
00891     
00892     Vgrid_value(grid, position, &vec[i]);
00893    }
00894    Vgrid_dtor(&grid);
00895    break;
00896    
00897         case VDT_SMOL:
00898  
00899             for (k=0; k<nz; k++) {
00900                 for (j=0; j<ny; j++) {
00901                     for (i=0; i<nx; i++) {
00902 
00903                         position[0] = i*hx + xmin;
00904                         position[1] = j*hy + ymin;
00905                         position[2] = k*hzed + zmin;
00906 
00907                         vec[IJK(i,j,k)] = (Vacc_molAcc(acc,position,parm));
00908                     }
00909                 }
00910             }
00911             break;
00912 
00913         case VDT_SSPL:
00914 
00915             for (k=0; k<nz; k++) {
00916                 for (j=0; j<ny; j++) {
00917                     for (i=0; i<nx; i++) {
00918 
00919                         position[0] = i*hx + xmin;
00920                         position[1] = j*hy + ymin;
00921                         position[2] = k*hzed + zmin;
00922 
00923                         vec[IJK(i,j,k)] = Vacc_splineAcc(acc,position,parm,0);
00924                     }
00925                 }   
00926             }
00927             break;
00928 
00929         case VDT_VDW:
00930 
00931             for (k=0; k<nz; k++) {
00932                 for (j=0; j<ny; j++) {
00933                     for (i=0; i<nx; i++) {
00934 
00935                         position[0] = i*hx + xmin;
00936                         position[1] = j*hy + ymin;
00937                         position[2] = k*hzed + zmin;
00938 
00939                         vec[IJK(i,j,k)] = Vacc_vdwAcc(acc,position);
00940                     }
00941                 }
00942             }
00943             break;
00944 
00945         case VDT_IVDW:
00946 
00947             for (k=0; k<nz; k++) {
00948                 for (j=0; j<ny; j++) {
00949                     for (i=0; i<nx; i++) {
00950 
00951                         position[0] = i*hx + xmin;
00952                         position[1] = j*hy + ymin;
00953                         position[2] = k*hzed + zmin;
00954 
00955                         vec[IJK(i,j,k)] = Vacc_ivdwAcc(acc,position,parm);
00956                     }
00957                 }
00958             }
00959             break;
00960 
00961         case VDT_LAP:
00962 
00963             grid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin,
00964               thee->u);
00965             for (k=0; k<nz; k++) {
00966                 for (j=0; j<ny; j++) {
00967                     for (i=0; i<nx; i++) {
00968 
00969                         if ((k==0) || (k==(nz-1)) ||
00970                             (j==0) || (j==(ny-1)) ||
00971                             (i==0) || (i==(nx-1))) {
00972 
00973                             vec[IJK(i,j,k)] = 0;
00974 
00975                         } else { 
00976                                 position[0] = i*hx + xmin;
00977                                 position[1] = j*hy + ymin;
00978                                 position[2] = k*hzed + zmin;
00979                                 VASSERT(Vgrid_curvature(grid,position, 1,
00980                                   &(vec[IJK(i,j,k)])));
00981                         }
00982                     }
00983                 }
00984             }
00985             Vgrid_dtor(&grid);
00986             break;
00987 
00988         case VDT_EDENS:
00989 
00990             grid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin,
00991               thee->u);
00992             for (k=0; k<nz; k++) {
00993                 for (j=0; j<ny; j++) {
00994                     for (i=0; i<nx; i++) {
00995 
00996                         position[0] = i*hx + xmin;
00997                         position[1] = j*hy + ymin;
00998                         position[2] = k*hzed + zmin;
00999                         VASSERT(Vgrid_gradient(grid, position, grad));
01000                         eps = epsp + (epss-epsp)*Vacc_molAcc(acc, position, 
01001                           pbe->solventRadius);
01002                         vec[IJK(i,j,k)] = 0.0;
01003                         for (l=0; l<3; l++) 
01004                           vec[IJK(i,j,k)] += eps*VSQR(grad[l]);
01005                     }
01006                 }
01007             }
01008             Vgrid_dtor(&grid);
01009             break;
01010 
01011         case VDT_NDENS:
01012 
01013             for (k=0; k<nz; k++) {
01014                 for (j=0; j<ny; j++) {
01015                     for (i=0; i<nx; i++) {
01016 
01017                         position[0] = i*hx + xmin;
01018                         position[1] = j*hy + ymin;
01019                         position[2] = k*hzed + zmin;
01020                         vec[IJK(i,j,k)] = 0.0;
01021                         if ( VABS(Vacc_ivdwAcc(acc, 
01022                                position, pbe->maxIonRadius) - 1.0) < VSMALL) {
01023                             for (l=0; l<pbe->numIon; l++) {
01024                                 if (pbetype == PBE_NPBE || pbetype == PBE_SMPBE /*  SMPBE Added */) {
01025                                     vec[IJK(i,j,k)] += (pbe->ionConc[l]
01026                                         * Vcap_exp(-pbe->ionQ[l]*thee->u[IJK(i,j,k)], 
01027                                         &ichop));
01028                                 } else if (pbetype == PBE_LPBE){
01029                                     vec[IJK(i,j,k)] += (pbe->ionConc[l]
01030                                         * (1 - pbe->ionQ[l]*thee->u[IJK(i,j,k)]));
01031                                 }
01032                             }
01033                         } 
01034                     }
01035                 }
01036             }
01037             break;
01038 
01039         case VDT_QDENS:
01040 
01041             for (k=0; k<nz; k++) {
01042                 for (j=0; j<ny; j++) {
01043                     for (i=0; i<nx; i++) {
01044 
01045                         position[0] = i*hx + xmin;
01046                         position[1] = j*hy + ymin;
01047                         position[2] = k*hzed + zmin;
01048                         vec[IJK(i,j,k)] = 0.0;
01049                         if ( VABS(Vacc_ivdwAcc(acc, 
01050                                position, pbe->maxIonRadius) - 1.0) < VSMALL) {
01051                             for (l=0; l<pbe->numIon; l++) {
01052                                 if (pbetype == PBE_NPBE || pbetype == PBE_SMPBE /*  SMPBE Added */) {
01053                                     vec[IJK(i,j,k)] += (pbe->ionConc[l] 
01054                                         * pbe->ionQ[l]
01055                                         * Vcap_exp(-pbe->ionQ[l]*thee->u[IJK(i,j,k)],
01056                                         &ichop));
01057                                 } else if (pbetype == PBE_LPBE) {
01058                                     vec[IJK(i,j,k)] += (pbe->ionConc[l] 
01059                                         * pbe->ionQ[l]
01060                                         * (1 - pbe->ionQ[l]*thee->u[IJK(i,j,k)]));
01061                                 }
01062                             }
01063                         }
01064                     }
01065                 }
01066             }
01067             break;
01068 
01069         default:
01070 
01071             Vnm_print(2, "main:  Bogus data type (%d)!\n", type);
01072             return 0;
01073             break;
01074 
01075     }
01076 
01077     return 1;
01078 
01079 }
01080 
01081 VPRIVATE double Vpmg_polarizEnergy(Vpmg *thee, int extFlag) {
01082 
01083     int i, j, k, ijk, nx, ny, nz, iatom;
01084     double xmin, ymin, zmin, x, y, z, hx, hy, hzed, epsp, lap, pt[3];
01085     double T, pre, polq, dist2, dist, energy, q;
01086     double *charge, *pos, eps_w;
01087     Vgrid *potgrid;
01088     Vpbe *pbe;
01089     Valist *alist;
01090     Vatom *atom;
01091 
01092     xmin = thee->pmgp->xmin;
01093     ymin = thee->pmgp->ymin;
01094     zmin = thee->pmgp->ymin;
01095     hx = thee->pmgp->hx;
01096     hy = thee->pmgp->hy;
01097     hzed = thee->pmgp->hzed;
01098     nx = thee->pmgp->nx;
01099     ny = thee->pmgp->ny;
01100     nz = thee->pmgp->nz;
01101     pbe = thee->pbe;
01102     epsp = Vpbe_getSoluteDiel(pbe);
01103     eps_w = Vpbe_getSolventDiel(pbe);
01104     alist = pbe->alist;
01105     charge = thee->charge;
01106    
01107     /* Calculate the prefactor for Coulombic calculations */
01108     T = Vpbe_getTemperature(pbe); 
01109     pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
01110     pre = pre*(1.0e10);
01111 
01112     /* Set up Vgrid object with solution */
01113     potgrid = Vgrid_ctor(nx, ny, nz, hx, hy, hzed, xmin, ymin, zmin, thee->u);
01114 
01115     /* Calculate polarization charge */
01116     energy = 0.0;
01117     for (i=1; i<(nx-1); i++) {
01118         pt[0] = xmin + hx*i;
01119         for (j=1; j<(ny-1); j++) {
01120             pt[1] = ymin + hy*j;
01121             for (k=1; k<(nz-1); k++) {
01122                 pt[2] = zmin + hzed*k;
01123 
01124                 /* Calculate polarization charge */
01125                 VASSERT(Vgrid_curvature(potgrid, pt, 1, &lap));
01126                 ijk = IJK(i,j,k);
01127                 polq = charge[ijk] + epsp*lap*3.0;
01128 
01129                 /* Calculate interaction energy with atoms */
01130                 if (VABS(polq) > VSMALL) {
01131                     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
01132                         atom = Valist_getAtom(alist, iatom);
01133                         q = Vatom_getCharge(atom);
01134                         pos = Vatom_getPosition(atom);
01135                         dist2 = VSQR(pos[0]-pt[0]) + VSQR(pos[1]-pt[1]) \
01136                                 + VSQR(pos[2]-pt[2]);
01137                         dist = VSQRT(dist2);
01138                      
01139                         if (dist < VSMALL) {
01140                             Vnm_print(2, "Vpmg_polarizEnergy:  atom on grid point; ignoring!\n");
01141                         } else {
01142                             energy = energy + polq*q/dist;
01143                         }
01144                     }
01145                 }
01146             }
01147         }
01148     }
01149 
01150     return pre*energy;
01151 }
01152 
01153 VPUBLIC double Vpmg_energy(Vpmg *thee, int extFlag) {
01154 
01155     double totEnergy = 0.0;
01156     double dielEnergy = 0.0;
01157     double qmEnergy = 0.0;
01158     double qfEnergy = 0.0;
01159 
01160     VASSERT(thee != VNULL);
01161  
01162     if ((thee->pmgp->nonlin) && (Vpbe_getBulkIonicStrength(thee->pbe) > 0.)) {
01163         Vnm_print(0, "Vpmg_energy:  calculating full PBE energy\n");
01164         qmEnergy = Vpmg_qmEnergy(thee, extFlag);
01165         Vnm_print(0, "Vpmg_energy:  qmEnergy = %1.12E kT\n", qmEnergy);
01166         qfEnergy = Vpmg_qfEnergy(thee, extFlag);
01167         Vnm_print(0, "Vpmg_energy:  qfEnergy = %1.12E kT\n", qfEnergy);
01168         dielEnergy = Vpmg_dielEnergy(thee, extFlag);
01169         Vnm_print(0, "Vpmg_energy:  dielEnergy = %1.12E kT\n", dielEnergy);
01170         totEnergy = qfEnergy - dielEnergy - qmEnergy;
01171     } else {
01172         Vnm_print(0, "Vpmg_energy:  calculating only q-phi energy\n");
01173         qfEnergy = Vpmg_qfEnergy(thee, extFlag);
01174         Vnm_print(0, "Vpmg_energy:  qfEnergy = %1.12E kT\n", qfEnergy);
01175         totEnergy = 0.5*qfEnergy;
01176     }
01177 
01178     return totEnergy;
01179 
01180 }
01181 
01182 VPUBLIC double Vpmg_dielEnergy(Vpmg *thee, int extFlag) {
01183 
01184     double hx, hy, hzed, energy, nrgx, nrgy, nrgz, pvecx, pvecy, pvecz;
01185     int i, j, k, nx, ny, nz;
01186  
01187     VASSERT(thee != VNULL);
01188 
01189     /* Get the mesh information */
01190     nx = thee->pmgp->nx;
01191     ny = thee->pmgp->ny;
01192     nz = thee->pmgp->nz;
01193     hx = thee->pmgp->hx;
01194     hy = thee->pmgp->hy;
01195     hzed = thee->pmgp->hzed;
01196 
01197     energy = 0.0;
01198 
01199     if (!thee->filled) {
01200         Vnm_print(2, "Vpmg_dielEnergy:  Need to call Vpmg_fillco!\n");
01201         VASSERT(0);
01202     }
01203 
01204     for (k=0; k<(nz-1); k++) {
01205         for (j=0; j<(ny-1); j++) {
01206             for (i=0; i<(nx-1); i++) {
01207                 pvecx = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i+1,j,k)]);
01208                 pvecy = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j+1,k)]);
01209                 pvecz = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j,k+1)]);
01210                 nrgx = thee->epsx[IJK(i,j,k)]*pvecx
01211                   * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i+1,j,k)])/hx);
01212                 nrgy = thee->epsy[IJK(i,j,k)]*pvecy
01213                   * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i,j+1,k)])/hy);
01214                 nrgz = thee->epsz[IJK(i,j,k)]*pvecz
01215                   * VSQR((thee->u[IJK(i,j,k)]-thee->u[IJK(i,j,k+1)])/hzed);
01216                 energy += (nrgx + nrgy + nrgz);
01217             }
01218         }
01219     }
01220 
01221     energy = 0.5*energy*hx*hy*hzed;
01222     energy = energy/Vpbe_getZmagic(thee->pbe);
01223 
01224     if (extFlag == 1) energy += (thee->extDiEnergy);
01225 
01226     return energy;
01227 }
01228 
01229 VPUBLIC double Vpmg_dielGradNorm(Vpmg *thee) {
01230 
01231     double hx, hy, hzed, energy, nrgx, nrgy, nrgz, pvecx, pvecy, pvecz;
01232     int i, j, k, nx, ny, nz;
01233  
01234     VASSERT(thee != VNULL);
01235 
01236     /* Get the mesh information */
01237     nx = thee->pmgp->nx;
01238     ny = thee->pmgp->ny;
01239     nz = thee->pmgp->nz;
01240     hx = thee->pmgp->hx;
01241     hy = thee->pmgp->hy;
01242     hzed = thee->pmgp->hzed;
01243 
01244     energy = 0.0;
01245 
01246     if (!thee->filled) {
01247         Vnm_print(2, "Vpmg_dielGradNorm:  Need to call Vpmg_fillco!\n");
01248         VASSERT(0);
01249     }
01250 
01251     for (k=1; k<nz; k++) {
01252         for (j=1; j<ny; j++) {
01253             for (i=1; i<nx; i++) {
01254                 pvecx = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i-1,j,k)]);
01255                 pvecy = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j-1,k)]);
01256                 pvecz = 0.5*(thee->pvec[IJK(i,j,k)]+thee->pvec[IJK(i,j,k-1)]);
01257                 nrgx = pvecx
01258                  * VSQR((thee->epsx[IJK(i,j,k)]-thee->epsx[IJK(i-1,j,k)])/hx);
01259                 nrgy = pvecy
01260                  * VSQR((thee->epsy[IJK(i,j,k)]-thee->epsy[IJK(i,j-1,k)])/hy);
01261                 nrgz = pvecz
01262                  * VSQR((thee->epsz[IJK(i,j,k)]-thee->epsz[IJK(i,j,k-1)])/hzed);
01263                 energy += VSQRT(nrgx + nrgy + nrgz);
01264             }
01265         }
01266     }
01267 
01268     energy = energy*hx*hy*hzed;
01269 
01270     return energy;
01271 }
01272 
01273 VPUBLIC double Vpmg_qmEnergy(Vpmg *thee, int extFlag) {
01274 
01275  double energy;
01276  
01277  if(thee->pbe->ipkey == IPKEY_SMPBE){
01278   energy = Vpmg_qmEnergySMPBE(thee,extFlag);
01279  }else{
01280   energy = Vpmg_qmEnergyNONLIN(thee,extFlag);
01281  }
01282  
01283  return energy;
01284 }
01285 
01286 VPRIVATE double Vpmg_qmEnergyNONLIN(Vpmg *thee, int extFlag) {
01287 
01288     double hx, hy, hzed, energy, ionConc[MAXION], ionRadii[MAXION];
01289     double ionQ[MAXION], zkappa2, ionstr, zks2;
01290     int i, j, nx, ny, nz, nion, ichop, nchop;
01291  
01292     VASSERT(thee != VNULL);
01293 
01294     /* Get the mesh information */
01295     nx = thee->pmgp->nx;
01296     ny = thee->pmgp->ny;
01297     nz = thee->pmgp->nz;
01298     hx = thee->pmgp->hx;
01299     hy = thee->pmgp->hy;
01300     hzed = thee->pmgp->hzed;
01301     zkappa2 = Vpbe_getZkappa2(thee->pbe);
01302     ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
01303 
01304     /* Bail if we're at zero ionic strength */
01305     if (zkappa2 < VSMALL) {
01306 
01307 #ifndef VAPBSQUIET
01308         Vnm_print(0, "Vpmg_qmEnergy:  Zero energy for zero ionic strength!\n");
01309 #endif
01310 
01311         return 0.0;
01312     }
01313     zks2 = 0.5*zkappa2/ionstr;
01314 
01315     if (!thee->filled) {
01316         Vnm_print(2, "Vpmg_qmEnergy:  Need to call Vpmg_fillco()!\n");
01317         VASSERT(0);
01318     }
01319 
01320     energy = 0.0;
01321     nchop = 0;
01322     Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
01323     if (thee->pmgp->nonlin) {
01324         Vnm_print(0, "Vpmg_qmEnergy:  Calculating nonlinear energy\n");
01325         for (i=0; i<(nx*ny*nz); i++) {
01326             if (thee->pvec[i]*thee->kappa[i] > VSMALL) {
01327                 for (j=0; j<nion; j++) {
01328                     energy += (thee->pvec[i]*thee->kappa[i]*zks2
01329                       * ionConc[j] 
01330                       * (Vcap_exp(-ionQ[j]*thee->u[i], &ichop)-1.0));
01331                     nchop += ichop;
01332                 }
01333             }
01334         }
01335         if (nchop > 0){
01336    Vnm_print(2, "Vpmg_qmEnergy:  Chopped EXP %d times!\n",nchop);
01337    Vnm_print(2, "\nERROR!  Detected large potential values in energy evaluation! \nERROR!  This calculation failed -- please report to the APBS developers!\n\n");
01338    VASSERT(0);
01339   }
01340     } else {
01341         /* Zkappa2 OK here b/c LPBE approx */
01342         Vnm_print(0, "Vpmg_qmEnergy:  Calculating linear energy\n");
01343         for (i=0; i<(nx*ny*nz); i++) {
01344             if (thee->pvec[i]*thee->kappa[i] > VSMALL) 
01345               energy += (thee->pvec[i]*zkappa2*thee->kappa[i]*VSQR(thee->u[i]));
01346         }
01347         energy = 0.5*energy;
01348     }
01349     energy = energy*hx*hy*hzed;
01350     energy = energy/Vpbe_getZmagic(thee->pbe);
01351 
01352     if (extFlag == 1) energy += thee->extQmEnergy;
01353 
01354     return energy;
01355 }
01356 
01357 VPUBLIC double Vpmg_qmEnergySMPBE(Vpmg *thee, int extFlag) {
01358  
01359     double hx, hy, hzed, energy, ionConc[MAXION], ionRadii[MAXION];
01360     double ionQ[MAXION], zkappa2, ionstr, zks2;
01361     int i, j, nx, ny, nz, nion, ichop, nchop;
01362  
01363     /* SMPB Modification (vchu, 09/21/06)*/
01364     /* variable declarations for SMPB energy terms */
01365     double a, k, z1, z2, z3, cb1, cb2, cb3;
01366     double a1, a2, a3, c1, c2, c3, currEnergy;
01367     double fracOccA, fracOccB, fracOccC, phi, gpark, denom, Na;
01368     int ichop1, ichop2, ichop3;
01369  
01370     VASSERT(thee != VNULL);
01371  
01372     /* Get the mesh information */
01373     nx = thee->pmgp->nx;
01374     ny = thee->pmgp->ny;
01375     nz = thee->pmgp->nz;
01376     hx = thee->pmgp->hx;
01377     hy = thee->pmgp->hy;
01378     hzed = thee->pmgp->hzed;
01379     zkappa2 = Vpbe_getZkappa2(thee->pbe);
01380     ionstr = Vpbe_getBulkIonicStrength(thee->pbe);
01381  
01382     /* Bail if we're at zero ionic strength */
01383     if (zkappa2 < VSMALL) {
01384   
01385 #ifndef VAPBSQUIET
01386         Vnm_print(0, "Vpmg_qmEnergySMPBE:  Zero energy for zero ionic strength!\n");
01387 #endif
01388   
01389         return 0.0;
01390     }
01391     zks2 = 0.5*zkappa2/ionstr;
01392  
01393     if (!thee->filled) {
01394         Vnm_print(2, "Vpmg_qmEnergySMPBE:  Need to call Vpmg_fillco()!\n");
01395         VASSERT(0);
01396     }
01397  
01398     energy = 0.0;
01399     nchop = 0;
01400     Vpbe_getIons(thee->pbe, &nion, ionConc, ionRadii, ionQ);
01401  
01402     /* SMPB Modification (vchu, 09/21/06) */
01403     /* Extensive modification to the first part of the if statement
01404   where that handles the thee->pmgp->nonlin part. Basically, I've 
01405   deleted all of the original code and written my own code that computes
01406   the electrostatic free energy in the SMPB framework. Definitely really hacky
01407   at this stage of the game, but gets the job done. The second part of the 
01408   if statement (the part that handles linear poisson-boltzmann) has been deleted
01409   because there will be no linearized SMPB energy.. */
01410  
01411     z1 = ionQ[0];
01412     z2 = ionQ[1];
01413     z3 = ionQ[2];
01414     cb1 = ionConc[0];
01415     cb2 = ionConc[1];
01416     cb3 = ionConc[2];
01417     a  = thee->pbe->smvolume;
01418     k  = thee->pbe->smsize;
01419     Na = 6.022045000e-04; /* Converts from Molar to N/A^3 */
01420  
01421     fracOccA = Na*cb1*VCUB(a);
01422     fracOccB = Na*cb2*VCUB(a);
01423     fracOccC = Na*cb3*VCUB(a);
01424  
01425     phi = (fracOccA/k) + fracOccB + fracOccC;
01426  
01427     if (thee->pmgp->nonlin) {
01428         Vnm_print(0, "Vpmg_qmEnergySMPBE:  Calculating nonlinear energy using SMPB functional!\n");
01429         for (i=0; i<(nx*ny*nz); i++) {
01430    if (((k-1) > VSMALL) && (thee->pvec[i]*thee->kappa[i] > VSMALL)) {
01431     
01432     a1 = Vcap_exp(-1.0*z1*thee->u[i], &ichop1);
01433     a2 = Vcap_exp(-1.0*z2*thee->u[i], &ichop2);
01434     a3 = Vcap_exp(-1.0*z3*thee->u[i], &ichop3);
01435     
01436     nchop += ichop1 + ichop2 + ichop3; 
01437     
01438     gpark = (1 - phi + (fracOccA/k)*a1);
01439     denom = VPOW(gpark, k) + VPOW(1-fracOccB-fracOccC, k-1)*(fracOccB*a2+fracOccC*a3);
01440     
01441     if (cb1 > VSMALL) {
01442      c1 = Na*cb1*VPOW(gpark, k-1)*a1/denom;
01443      if(c1 != c1) c1 = 0.;
01444     } else c1 = 0.;
01445     
01446     if (cb2 > VSMALL) {
01447      c2 = Na*cb2*VPOW(1-fracOccB-fracOccC,k-1)*a2/denom;
01448      if(c2 != c2) c2 = 0.;
01449     } else c2 = 0.;
01450     
01451     if (cb3 > VSMALL) {
01452      c3 = Na*cb3*VPOW(1-fracOccB-fracOccC,k-1)*a3/denom;
01453      if(c3 != c3) c3 = 0.;
01454     } else c3 = 0.;
01455     
01456     currEnergy = k*VLOG((1-(c1*VCUB(a)/k)-c2*VCUB(a)-c3*VCUB(a))/(1-phi))
01457      -(k-1)*VLOG((1-c2*VCUB(a)-c3*VCUB(a))/(1-phi+(fracOccA/k)));
01458     
01459     energy += thee->pvec[i]*thee->kappa[i]*currEnergy;
01460     
01461    } else if (thee->pvec[i]*thee->kappa[i] > VSMALL){
01462     
01463     a1 = Vcap_exp(-1.0*z1*thee->u[i], &ichop1);
01464     a2 = Vcap_exp(-1.0*z2*thee->u[i], &ichop2);
01465     a3 = Vcap_exp(-1.0*z3*thee->u[i], &ichop3);
01466     
01467     nchop += ichop1 + ichop2 + ichop3;
01468     
01469     gpark = (1 - phi + (fracOccA)*a1);
01470     denom = gpark + (fracOccB*a2+fracOccC*a3);
01471     
01472     if (cb1 > VSMALL) {
01473      c1 = Na*cb1*a1/denom;
01474      if(c1 != c1) c1 = 0.;
01475     } else c1 = 0.;
01476     
01477     if (cb2 > VSMALL) {
01478      c2 = Na*cb2*a2/denom;
01479      if(c2 != c2) c2 = 0.;
01480     } else c2 = 0.;
01481     
01482     if (cb3 > VSMALL) {
01483      c3 = Na*cb3*a3/denom;
01484      if(c3 != c3) c3 = 0.;
01485     } else c3 = 0.;
01486     
01487     currEnergy = VLOG((1-c1*VCUB(a)-c2*VCUB(a)-c3*VCUB(a))/(1-fracOccA-fracOccB-fracOccC));
01488     
01489     energy += thee->pvec[i]*thee->kappa[i]*currEnergy;
01490    }
01491         }
01492   
01493   energy = -energy/VCUB(a);
01494   
01495         if (nchop > 0) Vnm_print(2, "Vpmg_qmEnergySMPBE:  Chopped EXP %d times!\n",
01496          nchop);
01497   
01498     } else {
01499         /* Zkappa2 OK here b/c LPBE approx */
01500         Vnm_print(0, "Vpmg_qmEnergySMPBE:  ERROR: NO LINEAR ENERGY!! Returning 0!\n");
01501   
01502   energy = 0.0;
01503   
01504     }
01505     energy = energy*hx*hy*hzed;
01506  
01507     if (extFlag == 1) energy += thee->extQmEnergy;
01508  
01509     return energy;
01510 }
01511 
01512 VPUBLIC double Vpmg_qfEnergy(Vpmg *thee, int extFlag) {
01513 
01514     double energy = 0.0;
01515 
01516     VASSERT(thee != VNULL);
01517 
01518     if ((thee->useChargeMap) || (thee->chargeMeth == VCM_BSPL2)) { 
01519         energy = Vpmg_qfEnergyVolume(thee, extFlag); 
01520     } else { 
01521         energy = Vpmg_qfEnergyPoint(thee, extFlag); 
01522     } 
01523  
01524     return energy;
01525 }
01526 
01527 VPRIVATE double Vpmg_qfEnergyPoint(Vpmg *thee, int extFlag) {
01528 
01529     int iatom, nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
01530     double xmax, ymax, zmax, xmin, ymin, zmin, hx, hy, hzed, ifloat, jfloat;
01531     double charge, kfloat, dx, dy, dz, energy, uval, *position;
01532     double *u;
01533     double *pvec;
01534     Valist *alist;
01535     Vatom *atom; 
01536     Vpbe *pbe;
01537 
01538     pbe = thee->pbe;
01539     alist = pbe->alist;
01540     VASSERT(alist != VNULL);
01541 
01542     /* Get the mesh information */
01543     nx = thee->pmgp->nx;
01544     ny = thee->pmgp->ny;
01545     nz = thee->pmgp->nz;
01546     hx = thee->pmgp->hx;
01547     hy = thee->pmgp->hy;
01548     hzed = thee->pmgp->hzed;
01549     xmax = thee->pmgp->xmax;
01550     ymax = thee->pmgp->ymax;
01551     zmax = thee->pmgp->zmax;
01552     xmin = thee->pmgp->xmin;
01553     ymin = thee->pmgp->ymin;
01554     zmin = thee->pmgp->zmin;
01555 
01556     u = thee->u;
01557     pvec = thee->pvec;
01558   
01559     energy = 0.0;
01560 
01561     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
01562 
01563         /* Get atomic information */
01564         atom = Valist_getAtom(alist, iatom);
01565 
01566         position = Vatom_getPosition(atom);
01567         charge = Vatom_getCharge(atom);
01568 
01569         /* Figure out which vertices we're next to */
01570         ifloat = (position[0] - xmin)/hx;
01571         jfloat = (position[1] - ymin)/hy;
01572         kfloat = (position[2] - zmin)/hzed;
01573         ihi = (int)ceil(ifloat);
01574         ilo = (int)floor(ifloat);
01575         jhi = (int)ceil(jfloat);
01576         jlo = (int)floor(jfloat);
01577         khi = (int)ceil(kfloat);
01578         klo = (int)floor(kfloat);
01579 
01580         if (atom->partID > 0) {
01581 
01582             if ((ihi<nx) && (jhi<ny) && (khi<nz) &&
01583                 (ilo>=0) && (jlo>=0) && (klo>=0)) {
01584 
01585                 /* Now get trilinear interpolation constants */
01586                 dx = ifloat - (double)(ilo);
01587                 dy = jfloat - (double)(jlo);
01588                 dz = kfloat - (double)(klo);
01589                 uval =  
01590                   dx*dy*dz*u[IJK(ihi,jhi,khi)]
01591                 + dx*(1.0-dy)*dz*u[IJK(ihi,jlo,khi)]
01592                 + dx*dy*(1.0-dz)*u[IJK(ihi,jhi,klo)]
01593                 + dx*(1.0-dy)*(1.0-dz)*u[IJK(ihi,jlo,klo)]
01594                 + (1.0-dx)*dy*dz*u[IJK(ilo,jhi,khi)]
01595                 + (1.0-dx)*(1.0-dy)*dz*u[IJK(ilo,jlo,khi)]
01596                 + (1.0-dx)*dy*(1.0-dz)*u[IJK(ilo,jhi,klo)]
01597                 + (1.0-dx)*(1.0-dy)*(1.0-dz)*u[IJK(ilo,jlo,klo)];
01598                 energy += (uval*charge*atom->partID);
01599             } else if (thee->pmgp->bcfl != BCFL_FOCUS) {
01600                 Vnm_print(2, "Vpmg_qfEnergy:  Atom #%d at (%4.3f, %4.3f, \
01601 %4.3f) is off the mesh (ignoring)!\n",
01602                 iatom, position[0], position[1], position[2]);
01603             }
01604         } 
01605     }
01606 
01607     if (extFlag) energy += thee->extQfEnergy;
01608  
01609     return energy;
01610 }
01611 
01612 VPUBLIC double Vpmg_qfAtomEnergy(Vpmg *thee, Vatom *atom) {
01613 
01614     int nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
01615     double xmax, xmin, ymax, ymin, zmax, zmin, hx, hy, hzed, ifloat, jfloat;
01616     double charge, kfloat, dx, dy, dz, energy, uval, *position;
01617     double *u;
01618 
01619 
01620     /* Get the mesh information */
01621     nx = thee->pmgp->nx;
01622     ny = thee->pmgp->ny;
01623     nz = thee->pmgp->nz;
01624     hx = thee->pmgp->hx;
01625     hy = thee->pmgp->hy;
01626     hzed = thee->pmgp->hzed;
01627     xmax = thee->xf[nx-1];
01628     ymax = thee->yf[ny-1];
01629     zmax = thee->zf[nz-1];
01630     xmin = thee->xf[0];
01631     ymin = thee->yf[0];
01632     zmin = thee->zf[0];
01633 
01634     u = thee->u;
01635 
01636     energy = 0.0;
01637 
01638 
01639     position = Vatom_getPosition(atom);
01640     charge = Vatom_getCharge(atom);
01641 
01642     /* Figure out which vertices we're next to */
01643     ifloat = (position[0] - xmin)/hx;
01644     jfloat = (position[1] - ymin)/hy;
01645     kfloat = (position[2] - zmin)/hzed;
01646     ihi = (int)ceil(ifloat);
01647     ilo = (int)floor(ifloat);
01648     jhi = (int)ceil(jfloat);
01649     jlo = (int)floor(jfloat);
01650     khi = (int)ceil(kfloat);
01651     klo = (int)floor(kfloat);
01652 
01653     if (atom->partID > 0) {
01654 
01655         if ((ihi<nx) && (jhi<ny) && (khi<nz) &&
01656             (ilo>=0) && (jlo>=0) && (klo>=0)) {
01657 
01658             /* Now get trilinear interpolation constants */
01659             dx = ifloat - (double)(ilo);
01660             dy = jfloat - (double)(jlo);
01661             dz = kfloat - (double)(klo);
01662             uval =
01663               dx*dy*dz*u[IJK(ihi,jhi,khi)]
01664             + dx*(1.0-dy)*dz*u[IJK(ihi,jlo,khi)]
01665             + dx*dy*(1.0-dz)*u[IJK(ihi,jhi,klo)]
01666             + dx*(1.0-dy)*(1.0-dz)*u[IJK(ihi,jlo,klo)]
01667             + (1.0-dx)*dy*dz*u[IJK(ilo,jhi,khi)]
01668             + (1.0-dx)*(1.0-dy)*dz*u[IJK(ilo,jlo,khi)]
01669             + (1.0-dx)*dy*(1.0-dz)*u[IJK(ilo,jhi,klo)]
01670             + (1.0-dx)*(1.0-dy)*(1.0-dz)*u[IJK(ilo,jlo,klo)];
01671             energy += (uval*charge*atom->partID);
01672         } else if (thee->pmgp->bcfl != BCFL_FOCUS) {
01673             Vnm_print(2, "Vpmg_qfAtomEnergy:  Atom at (%4.3f, %4.3f, \
01674 %4.3f) is off the mesh (ignoring)!\n",
01675             position[0], position[1], position[2]);
01676         }
01677     } 
01678 
01679     return energy; 
01680 }
01681     
01682 VPRIVATE double Vpmg_qfEnergyVolume(Vpmg *thee, int extFlag) {
01683 
01684     double hx, hy, hzed, energy;
01685     int i, nx, ny, nz;
01686  
01687     VASSERT(thee != VNULL);
01688 
01689     /* Get the mesh information */
01690     nx = thee->pmgp->nx;
01691     ny = thee->pmgp->ny;
01692     nz = thee->pmgp->nz;
01693     hx = thee->pmgp->hx;
01694     hy = thee->pmgp->hy;
01695     hzed = thee->pmgp->hzed;
01696 
01697     if (!thee->filled) {
01698         Vnm_print(2, "Vpmg_qfEnergyVolume:  need to call Vpmg_fillco!\n");
01699         VASSERT(0);
01700     }
01701 
01702     energy = 0.0;
01703     Vnm_print(0, "Vpmg_qfEnergyVolume:  Calculating energy\n");
01704     for (i=0; i<(nx*ny*nz); i++) {
01705         energy += (thee->pvec[i]*thee->u[i]*thee->charge[i]);
01706     }
01707     energy = energy*hx*hy*hzed/Vpbe_getZmagic(thee->pbe);
01708 
01709     if (extFlag == 1) energy += thee->extQfEnergy;
01710 
01711     return energy;
01712 }
01713 
01714 VPRIVATE void Vpmg_splineSelect(int srfm,Vacc *acc,double *gpos,double win,
01715            double infrad,Vatom *atom,double *force){
01716 
01717  switch (srfm) {
01718   case VSM_SPLINE :
01719    Vacc_splineAccGradAtomNorm(acc, gpos, win, infrad, atom, force);
01720    break;
01721   case VSM_SPLINE3:
01722    Vacc_splineAccGradAtomNorm3(acc, gpos, win, infrad, atom, force);
01723    break;
01724   case VSM_SPLINE4 :
01725    Vacc_splineAccGradAtomNorm4(acc, gpos, win, infrad, atom, force);
01726    break;
01727   default:
01728    Vnm_print(2, "Vpmg_dbnbForce: Unknown surface method.\n");
01729    return;
01730  }
01731  
01732  return;
01733 }
01734 
01735 VPRIVATE void focusFillBound(Vpmg *thee, Vpmg *pmgOLD) {
01736  
01737     Vpbe *pbe;
01738     double hxOLD, hyOLD, hzOLD, xminOLD, yminOLD, zminOLD, xmaxOLD, ymaxOLD;
01739     double zmaxOLD;
01740     int nxOLD, nyOLD, nzOLD;
01741     double hxNEW, hyNEW, hzNEW, xminNEW, yminNEW, zminNEW, xmaxNEW, ymaxNEW;
01742     double zmaxNEW;
01743     int nxNEW, nyNEW, nzNEW;
01744     int i, j, k, ihi, ilo, jhi, jlo, khi, klo, nx, ny, nz;
01745     double x, y, z, dx, dy, dz, ifloat, jfloat, kfloat, uval;
01746     double eps_w, T, pre1, xkappa, size, *apos, charge, pos[3];
01747  
01748  double uvalMin, uvalMax;
01749  double *data;
01750  
01751     /* Calculate new problem dimensions */
01752     hxNEW = thee->pmgp->hx;
01753     hyNEW = thee->pmgp->hy;
01754     hzNEW = thee->pmgp->hzed;
01755     nx =  thee->pmgp->nx;
01756     ny =  thee->pmgp->ny;
01757     nz =  thee->pmgp->nz;
01758     nxNEW = thee->pmgp->nx;
01759     nyNEW = thee->pmgp->ny;
01760     nzNEW = thee->pmgp->nz;
01761     xminNEW = thee->pmgp->xcent - ((double)(nxNEW-1)*hxNEW)/2.0;
01762     xmaxNEW = thee->pmgp->xcent + ((double)(nxNEW-1)*hxNEW)/2.0;
01763     yminNEW = thee->pmgp->ycent - ((double)(nyNEW-1)*hyNEW)/2.0;
01764     ymaxNEW = thee->pmgp->ycent + ((double)(nyNEW-1)*hyNEW)/2.0;
01765     zminNEW = thee->pmgp->zcent - ((double)(nzNEW-1)*hzNEW)/2.0;
01766     zmaxNEW = thee->pmgp->zcent + ((double)(nzNEW-1)*hzNEW)/2.0;
01767  
01768  if(pmgOLD != VNULL){
01769   /* Relevant old problem parameters */
01770   hxOLD = pmgOLD->pmgp->hx;
01771   hyOLD = pmgOLD->pmgp->hy;
01772   hzOLD = pmgOLD->pmgp->hzed;
01773   nxOLD = pmgOLD->pmgp->nx;
01774   nyOLD = pmgOLD->pmgp->ny;
01775   nzOLD = pmgOLD->pmgp->nz;
01776   xminOLD = pmgOLD->pmgp->xcent - ((double)(nxOLD-1)*hxOLD)/2.0;
01777   xmaxOLD = pmgOLD->pmgp->xcent + ((double)(nxOLD-1)*hxOLD)/2.0;
01778   yminOLD = pmgOLD->pmgp->ycent - ((double)(nyOLD-1)*hyOLD)/2.0;
01779   ymaxOLD = pmgOLD->pmgp->ycent + ((double)(nyOLD-1)*hyOLD)/2.0;
01780   zminOLD = pmgOLD->pmgp->zcent - ((double)(nzOLD-1)*hzOLD)/2.0;
01781   zmaxOLD = pmgOLD->pmgp->zcent + ((double)(nzOLD-1)*hzOLD)/2.0;
01782   
01783   data = pmgOLD->u;
01784  }else{
01785   /* Relevant old problem parameters */
01786   hxOLD = thee->potMap->hx;
01787   hyOLD = thee->potMap->hy;
01788   hzOLD = thee->potMap->hzed;
01789   nxOLD = thee->potMap->nx;
01790   nyOLD = thee->potMap->ny;
01791   nzOLD = thee->potMap->nz;
01792   xminOLD = thee->potMap->xmin;
01793   xmaxOLD = thee->potMap->xmax;
01794   yminOLD = thee->potMap->ymin;
01795   ymaxOLD = thee->potMap->ymax;
01796   zminOLD = thee->potMap->zmin;
01797   zmaxOLD = thee->potMap->zmax;
01798   
01799   data = thee->potMap->data;
01800  }
01801     /* BOUNDARY CONDITION SETUP FOR POINTS OFF OLD MESH:
01802      * For each "atom" (only one for bcfl=1), we use the following formula to
01803      * calculate the boundary conditions:
01804      *    g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
01805      *          * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
01806      *          * 1/d
01807      * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
01808      * We only need to evaluate some of these prefactors once:
01809      *    pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
01810      * which gives the potential as
01811      *    g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
01812      */
01813     pbe = thee->pbe;
01814     eps_w = Vpbe_getSolventDiel(pbe);           /* Dimensionless */
01815     T = Vpbe_getTemperature(pbe);               /* K             */
01816     pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
01817  
01818     /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
01819      * m/A, then we will only need to deal with distances and sizes in
01820      * Angstroms rather than meters.                                       */
01821     xkappa = Vpbe_getXkappa(pbe);              /* A^{-1}        */
01822     pre1 = pre1*(1.0e10);
01823     size = Vpbe_getSoluteRadius(pbe);
01824     apos = Vpbe_getSoluteCenter(pbe);
01825     charge = Vunit_ec*Vpbe_getSoluteCharge(pbe);
01826  
01827     /* Check for rounding error */
01828     if (VABS(xminOLD-xminNEW) < VSMALL) xminNEW = xminOLD;
01829     if (VABS(xmaxOLD-xmaxNEW) < VSMALL) xmaxNEW = xmaxOLD;
01830     if (VABS(yminOLD-yminNEW) < VSMALL) yminNEW = yminOLD;
01831     if (VABS(ymaxOLD-ymaxNEW) < VSMALL) ymaxNEW = ymaxOLD;
01832     if (VABS(zminOLD-zminNEW) < VSMALL) zminNEW = zminOLD;
01833     if (VABS(zmaxOLD-zmaxNEW) < VSMALL) zmaxNEW = zmaxOLD;
01834     
01835  
01836     /* Sanity check: make sure we're within the old mesh */
01837     Vnm_print(0, "VPMG::focusFillBound -- New mesh mins = %g, %g, %g\n",
01838      xminNEW, yminNEW, zminNEW);
01839     Vnm_print(0, "VPMG::focusFillBound -- New mesh maxs = %g, %g, %g\n",
01840      xmaxNEW, ymaxNEW, zmaxNEW);
01841     Vnm_print(0, "VPMG::focusFillBound -- Old mesh mins = %g, %g, %g\n",
01842      xminOLD, yminOLD, zminOLD);
01843     Vnm_print(0, "VPMG::focusFillBound -- Old mesh maxs = %g, %g, %g\n",
01844      xmaxOLD, ymaxOLD, zmaxOLD);
01845  
01846     /* The following is obsolete; we'll substitute analytical boundary
01847      * condition values when the new mesh falls outside the old */
01848     if ((xmaxNEW>xmaxOLD) || (ymaxNEW>ymaxOLD) || (zmaxNEW>zmaxOLD) ||
01849         (xminOLD>xminNEW) || (yminOLD>yminNEW) || (zminOLD>zminNEW)) {
01850   
01851         Vnm_print(2, "Vpmg::focusFillBound -- new mesh not contained in old!\n");
01852   Vnm_print(2, "Vpmg::focusFillBound -- old mesh min = (%g, %g, %g)\n",
01853       xminOLD, yminOLD, zminOLD);
01854         Vnm_print(2, "Vpmg::focusFillBound -- old mesh max = (%g, %g, %g)\n",
01855       xmaxOLD, ymaxOLD, zmaxOLD);
01856   Vnm_print(2, "Vpmg::focusFillBound -- new mesh min = (%g, %g, %g)\n",
01857       xminNEW, yminNEW, zminNEW);
01858         Vnm_print(2, "Vpmg::focusFillBound -- new mesh max = (%g, %g, %g)\n",
01859       xmaxNEW, ymaxNEW, zmaxNEW);
01860   fflush(stderr);
01861         VASSERT(0);
01862     }
01863  
01864  uvalMin = VPMGSMALL;
01865  uvalMax = -VPMGSMALL;
01866     
01867     /* Fill the "i" boundaries (dirichlet) */
01868     for (k=0; k<nzNEW; k++) {
01869         for (j=0; j<nyNEW; j++) {
01870             /* Low X face */
01871             x = xminNEW;
01872             y = yminNEW + j*hyNEW;
01873             z = zminNEW + k*hzNEW;
01874             if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
01875                 (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
01876                 ifloat = (x - xminOLD)/hxOLD;
01877                 jfloat = (y - yminOLD)/hyOLD;
01878                 kfloat = (z - zminOLD)/hzOLD;
01879                 ihi = (int)ceil(ifloat);
01880                 if (ihi > (nxOLD-1)) ihi = nxOLD-1;
01881                 ilo = (int)floor(ifloat);
01882                 if (ilo < 0) ilo = 0;
01883                 jhi = (int)ceil(jfloat);
01884                 if (jhi > (nyOLD-1)) jhi = nyOLD-1;
01885                 jlo = (int)floor(jfloat);
01886                 if (jlo < 0) jlo = 0;
01887                 khi = (int)ceil(kfloat);
01888                 if (khi > (nzOLD-1)) khi = nzOLD-1;
01889                 klo = (int)floor(kfloat);
01890                 if (klo < 0) klo = 0;
01891                 dx = ifloat - (double)(ilo);
01892                 dy = jfloat - (double)(jlo);
01893                 dz = kfloat - (double)(klo);
01894                 nx = nxOLD; ny = nyOLD; nz = nzOLD;
01895                 uval =  dx*dy*dz*(data[IJK(ihi,jhi,khi)])
01896     + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
01897     + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
01898     + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
01899     + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
01900     + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
01901     + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
01902     + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
01903                 nx = nxNEW; ny = nyNEW; nz = nzNEW;
01904             } else {
01905     Vnm_print(2, "focusFillBound (%s, %d):  Off old mesh at %g, %g \
01906         %g!\n", __FILE__, __LINE__, x, y, z);
01907     Vnm_print(2, "focusFillBound (%s, %d):  old mesh lower corner at \
01908         %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
01909                 Vnm_print(2, "focusFillBound (%s, %d):  old mesh upper corner at \
01910         %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
01911     VASSERT(0);
01912             }
01913             nx = nxNEW; ny = nyNEW; nz = nzNEW;
01914             thee->gxcf[IJKx(j,k,0)] = uval;
01915    if(uval < uvalMin) uvalMin = uval;
01916    if(uval > uvalMax) uvalMax = uval;
01917    
01918             /* High X face */
01919             x = xmaxNEW;
01920             if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
01921                 (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
01922                 ifloat = (x - xminOLD)/hxOLD;
01923                 jfloat = (y - yminOLD)/hyOLD;
01924                 kfloat = (z - zminOLD)/hzOLD;
01925                 ihi = (int)ceil(ifloat);
01926                 if (ihi > (nxOLD-1)) ihi = nxOLD-1;
01927                 ilo = (int)floor(ifloat);
01928                 if (ilo < 0) ilo = 0;
01929                 jhi = (int)ceil(jfloat);
01930                 if (jhi > (nyOLD-1)) jhi = nyOLD-1;
01931                 jlo = (int)floor(jfloat);
01932                 if (jlo < 0) jlo = 0;
01933                 khi = (int)ceil(kfloat);
01934                 if (khi > (nzOLD-1)) khi = nzOLD-1;
01935                 klo = (int)floor(kfloat);
01936                 if (klo < 0) klo = 0;
01937                 dx = ifloat - (double)(ilo);
01938                 dy = jfloat - (double)(jlo);
01939                 dz = kfloat - (double)(klo);
01940                 nx = nxOLD; ny = nyOLD; nz = nzOLD;
01941                 uval =  dx*dy*dz*(data[IJK(ihi,jhi,khi)])
01942     + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
01943     + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
01944     + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
01945     + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
01946     + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
01947     + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
01948     + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
01949                 nx = nxNEW; ny = nyNEW; nz = nzNEW;
01950             } else {
01951     Vnm_print(2, "focusFillBound (%s, %d):  Off old mesh at %g, %g \
01952         %g!\n", __FILE__, __LINE__, x, y, z);
01953     Vnm_print(2, "focusFillBound (%s, %d):  old mesh lower corner at \
01954         %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
01955                 Vnm_print(2, "focusFillBound (%s, %d):  old mesh upper corner at \
01956         %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
01957     VASSERT(0);
01958             }
01959             nx = nxNEW; ny = nyNEW; nz = nzNEW;
01960             thee->gxcf[IJKx(j,k,1)] = uval;
01961    if(uval < uvalMin) uvalMin = uval;
01962    if(uval > uvalMax) uvalMax = uval;
01963             
01964             /* Zero Neumann conditions */             
01965             nx = nxNEW; ny = nyNEW; nz = nzNEW;
01966             thee->gxcf[IJKx(j,k,2)] = 0.0;
01967             nx = nxNEW; ny = nyNEW; nz = nzNEW;
01968             thee->gxcf[IJKx(j,k,3)] = 0.0;
01969         }
01970     }
01971  
01972     /* Fill the "j" boundaries (dirichlet) */
01973     for (k=0; k<nzNEW; k++) {
01974         for (i=0; i<nxNEW; i++) {
01975             /* Low Y face */
01976             x = xminNEW + i*hxNEW;
01977             y = yminNEW;
01978             z = zminNEW + k*hzNEW;
01979             if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
01980                 (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
01981                 ifloat = (x - xminOLD)/hxOLD;
01982                 jfloat = (y - yminOLD)/hyOLD;
01983                 kfloat = (z - zminOLD)/hzOLD;
01984                 ihi = (int)ceil(ifloat);
01985                 if (ihi > (nxOLD-1)) ihi = nxOLD-1;
01986                 ilo = (int)floor(ifloat);
01987                 if (ilo < 0) ilo = 0;
01988                 jhi = (int)ceil(jfloat);
01989                 if (jhi > (nyOLD-1)) jhi = nyOLD-1;
01990                 jlo = (int)floor(jfloat);
01991                 if (jlo < 0) jlo = 0;
01992                 khi = (int)ceil(kfloat);
01993                 if (khi > (nzOLD-1)) khi = nzOLD-1;
01994                 klo = (int)floor(kfloat);
01995                 if (klo < 0) klo = 0;
01996                 dx = ifloat - (double)(ilo);
01997                 dy = jfloat - (double)(jlo);
01998                 dz = kfloat - (double)(klo);
01999                 nx = nxOLD; ny = nyOLD; nz = nzOLD;
02000                 uval =  dx*dy*dz*(data[IJK(ihi,jhi,khi)])
02001     + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
02002     + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
02003     + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
02004     + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
02005     + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
02006     + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
02007     + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
02008                 nx = nxNEW; ny = nyNEW; nz = nzNEW;
02009             } else {
02010     Vnm_print(2, "focusFillBound (%s, %d):  Off old mesh at %g, %g \
02011         %g!\n", __FILE__, __LINE__, x, y, z);
02012     Vnm_print(2, "focusFillBound (%s, %d):  old mesh lower corner at \
02013         %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
02014                 Vnm_print(2, "focusFillBound (%s, %d):  old mesh upper corner at \
02015         %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
02016     VASSERT(0);
02017             }
02018             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02019             thee->gycf[IJKy(i,k,0)] = uval;
02020    if(uval < uvalMin) uvalMin = uval;
02021    if(uval > uvalMax) uvalMax = uval;
02022    
02023             /* High Y face */
02024             y = ymaxNEW;
02025             if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
02026                 (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
02027                 ifloat = (x - xminOLD)/hxOLD;
02028                 jfloat = (y - yminOLD)/hyOLD;
02029                 kfloat = (z - zminOLD)/hzOLD;
02030                 ihi = (int)ceil(ifloat);
02031                 if (ihi > (nxOLD-1)) ihi = nxOLD-1;
02032                 ilo = (int)floor(ifloat);
02033                 if (ilo < 0) ilo = 0;
02034                 jhi = (int)ceil(jfloat);
02035                 if (jhi > (nyOLD-1)) jhi = nyOLD-1;
02036                 jlo = (int)floor(jfloat);
02037                 if (jlo < 0) jlo = 0;
02038                 khi = (int)ceil(kfloat);
02039                 if (khi > (nzOLD-1)) khi = nzOLD-1;
02040                 klo = (int)floor(kfloat);
02041                 if (klo < 0) klo = 0;
02042                 dx = ifloat - (double)(ilo);
02043                 dy = jfloat - (double)(jlo);
02044                 dz = kfloat - (double)(klo);
02045                 nx = nxOLD; ny = nyOLD; nz = nzOLD;
02046                 uval =  dx*dy*dz*(data[IJK(ihi,jhi,khi)])
02047     + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
02048     + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
02049     + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
02050     + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
02051     + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
02052     + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
02053     + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
02054                 nx = nxNEW; ny = nyNEW; nz = nzNEW;
02055             } else {
02056     Vnm_print(2, "focusFillBound (%s, %d):  Off old mesh at %g, %g \
02057         %g!\n", __FILE__, __LINE__, x, y, z);
02058     Vnm_print(2, "focusFillBound (%s, %d):  old mesh lower corner at \
02059         %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
02060                 Vnm_print(2, "focusFillBound (%s, %d):  old mesh upper corner at \
02061         %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
02062     VASSERT(0);
02063             }
02064             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02065             thee->gycf[IJKy(i,k,1)] = uval;
02066    if(uval < uvalMin) uvalMin = uval;
02067    if(uval > uvalMax) uvalMax = uval;
02068    
02069             /* Zero Neumann conditions */
02070             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02071             thee->gycf[IJKy(i,k,2)] = 0.0;
02072             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02073             thee->gycf[IJKy(i,k,3)] = 0.0;
02074         }
02075     }
02076  
02077     /* Fill the "k" boundaries (dirichlet) */
02078     for (j=0; j<nyNEW; j++) {
02079         for (i=0; i<nxNEW; i++) {
02080             /* Low Z face */
02081             x = xminNEW + i*hxNEW;
02082             y = yminNEW + j*hyNEW;
02083             z = zminNEW;
02084             if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
02085                 (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
02086                 ifloat = (x - xminOLD)/hxOLD;
02087                 jfloat = (y - yminOLD)/hyOLD;
02088                 kfloat = (z - zminOLD)/hzOLD;
02089                 ihi = (int)ceil(ifloat);
02090                 if (ihi > (nxOLD-1)) ihi = nxOLD-1;
02091                 ilo = (int)floor(ifloat);
02092                 if (ilo < 0) ilo = 0;
02093                 jhi = (int)ceil(jfloat);
02094                 if (jhi > (nyOLD-1)) jhi = nyOLD-1;
02095                 jlo = (int)floor(jfloat);
02096                 if (jlo < 0) jlo = 0;
02097                 khi = (int)ceil(kfloat);
02098                 if (khi > (nzOLD-1)) khi = nzOLD-1;
02099                 klo = (int)floor(kfloat);
02100                 if (klo < 0) klo = 0;
02101                 dx = ifloat - (double)(ilo);
02102                 dy = jfloat - (double)(jlo);
02103                 dz = kfloat - (double)(klo);
02104                 nx = nxOLD; ny = nyOLD; nz = nzOLD;
02105                 uval =  dx*dy*dz*(data[IJK(ihi,jhi,khi)])
02106     + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
02107     + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
02108     + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
02109     + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
02110     + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
02111     + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
02112     + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
02113                 nx = nxNEW; ny = nyNEW; nz = nzNEW;
02114             } else {
02115     Vnm_print(2, "focusFillBound (%s, %d):  Off old mesh at %g, %g \
02116         %g!\n", __FILE__, __LINE__, x, y, z);
02117     Vnm_print(2, "focusFillBound (%s, %d):  old mesh lower corner at \
02118         %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
02119                 Vnm_print(2, "focusFillBound (%s, %d):  old mesh upper corner at \
02120         %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
02121     VASSERT(0);
02122             }
02123             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02124             thee->gzcf[IJKz(i,j,0)] = uval;
02125    if(uval < uvalMin) uvalMin = uval;
02126    if(uval > uvalMax) uvalMax = uval;
02127    
02128             /* High Z face */
02129             z = zmaxNEW;
02130             if ((x >= (xminOLD-VSMALL)) && (y >= (yminOLD-VSMALL)) && (z >= (zminOLD-VSMALL)) &&
02131                 (x <= (xmaxOLD+VSMALL)) && (y <= (ymaxOLD+VSMALL)) && (z <= (zmaxOLD+VSMALL))) {
02132                 ifloat = (x - xminOLD)/hxOLD;
02133                 jfloat = (y - yminOLD)/hyOLD;
02134                 kfloat = (z - zminOLD)/hzOLD;
02135                 ihi = (int)ceil(ifloat);
02136                 if (ihi > (nxOLD-1)) ihi = nxOLD-1;
02137                 ilo = (int)floor(ifloat);
02138                 if (ilo < 0) ilo = 0;
02139                 jhi = (int)ceil(jfloat);
02140                 if (jhi > (nyOLD-1)) jhi = nyOLD-1;
02141                 jlo = (int)floor(jfloat);
02142                 if (jlo < 0) jlo = 0;
02143                 khi = (int)ceil(kfloat);
02144                 if (khi > (nzOLD-1)) khi = nzOLD-1;
02145                 klo = (int)floor(kfloat);
02146                 if (klo < 0) klo = 0;
02147                 dx = ifloat - (double)(ilo);
02148                 dy = jfloat - (double)(jlo);
02149                 dz = kfloat - (double)(klo);
02150                 nx = nxOLD; ny = nyOLD; nz = nzOLD;
02151                 uval =  dx*dy*dz*(data[IJK(ihi,jhi,khi)])
02152     + dx*(1.0-dy)*dz*(data[IJK(ihi,jlo,khi)])
02153     + dx*dy*(1.0-dz)*(data[IJK(ihi,jhi,klo)])
02154     + dx*(1.0-dy)*(1.0-dz)*(data[IJK(ihi,jlo,klo)])
02155     + (1.0-dx)*dy*dz*(data[IJK(ilo,jhi,khi)])
02156     + (1.0-dx)*(1.0-dy)*dz*(data[IJK(ilo,jlo,khi)])
02157     + (1.0-dx)*dy*(1.0-dz)*(data[IJK(ilo,jhi,klo)])
02158     + (1.0-dx)*(1.0-dy)*(1.0-dz)*(data[IJK(ilo,jlo,klo)]);
02159                 nx = nxNEW; ny = nyNEW; nz = nzNEW;
02160             } else {
02161     Vnm_print(2, "focusFillBound (%s, %d):  Off old mesh at %g, %g \
02162         %g!\n", __FILE__, __LINE__, x, y, z);
02163     Vnm_print(2, "focusFillBound (%s, %d):  old mesh lower corner at \
02164         %g %g %g.\n", __FILE__, __LINE__, xminOLD, yminOLD, zminOLD);
02165                 Vnm_print(2, "focusFillBound (%s, %d):  old mesh upper corner at \
02166         %g %g %g.\n", __FILE__, __LINE__, xmaxOLD, ymaxOLD, zmaxOLD);
02167     VASSERT(0);
02168             }
02169             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02170             thee->gzcf[IJKz(i,j,1)] = uval;
02171    if(uval < uvalMin) uvalMin = uval;
02172    if(uval > uvalMax) uvalMax = uval;
02173    
02174             /* Zero Neumann conditions */
02175             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02176             thee->gzcf[IJKz(i,j,2)] = 0.0;
02177             nx = nxNEW; ny = nyNEW; nz = nzNEW;
02178             thee->gzcf[IJKz(i,j,3)] = 0.0;
02179         }
02180     }
02181  
02182  if((uvalMin < SINH_MIN) || (uvalMax > SINH_MAX)){
02183   Vnm_print(2, "\nfocusFillBound:  WARNING! Unusually large potential values\n" \
02184         "                   detected on the focusing boundary!  \n" \
02185         "                   Convergence not guaranteed for NPBE/NRPBE calculations!\n");
02186  }
02187  
02188 }
02189 
02190 VPRIVATE void extEnergy(Vpmg *thee, Vpmg *pmgOLD, PBEparm_calcEnergy extFlag, 
02191       double partMin[3], double partMax[3], int bflags[6]) {
02192  
02193     Vatom *atom;
02194     double hxNEW, hyNEW, hzNEW;
02195     double lowerCorner[3], upperCorner[3];
02196     int nxNEW, nyNEW, nzNEW;
02197     int nxOLD, nyOLD, nzOLD;
02198     int i,j,k;
02199     double xmin, xmax, ymin, ymax, zmin, zmax;
02200     double hxOLD, hyOLD, hzOLD;
02201     double xval, yval, zval;
02202     double x,y,z;
02203     int nx, ny, nz;
02204     
02205     /* Set the new external energy contribution to zero.  Any external
02206      * contributions from higher levels will be included in the appropriate
02207      * energy function call. */
02208     thee->extQmEnergy = 0;
02209     thee->extQfEnergy = 0;
02210     thee->extDiEnergy = 0;
02211  
02212     /* New problem dimensions */
02213     hxNEW = thee->pmgp->hx;
02214     hyNEW = thee->pmgp->hy;
02215     hzNEW = thee->pmgp->hzed;
02216     nxNEW = thee->pmgp->nx;
02217     nyNEW = thee->pmgp->ny;
02218     nzNEW = thee->pmgp->nz;
02219     lowerCorner[0] = thee->pmgp->xcent - ((double)(nxNEW-1)*hxNEW)/2.0;
02220     upperCorner[0] = thee->pmgp->xcent + ((double)(nxNEW-1)*hxNEW)/2.0;
02221     lowerCorner[1] = thee->pmgp->ycent - ((double)(nyNEW-1)*hyNEW)/2.0;
02222     upperCorner[1] = thee->pmgp->ycent + ((double)(nyNEW-1)*hyNEW)/2.0;
02223     lowerCorner[2] = thee->pmgp->zcent - ((double)(nzNEW-1)*hzNEW)/2.0;
02224     upperCorner[2] = thee->pmgp->zcent + ((double)(nzNEW-1)*hzNEW)/2.0;
02225  
02226     Vnm_print(0, "VPMG::extEnergy:  energy flag = %d\n", extFlag);
02227  
02228     /* Old problem dimensions */
02229     nxOLD = pmgOLD->pmgp->nx;
02230     nyOLD = pmgOLD->pmgp->ny;
02231     nzOLD = pmgOLD->pmgp->nz;
02232  
02233     /* Create a partition based on the new problem dimensions */
02234     /* Vnm_print(1, "DEBUG (%s, %d):  extEnergy calling Vpmg_setPart for old PMG.\n",
02235   __FILE__, __LINE__); */
02236     Vpmg_setPart(pmgOLD, lowerCorner, upperCorner, bflags);
02237  
02238     
02239     Vnm_print(0,"VPMG::extEnergy:   Finding extEnergy dimensions...\n");
02240     Vnm_print(0,"VPMG::extEnergy    Disj part lower corner = (%g, %g, %g)\n",
02241      partMin[0], partMin[1], partMin[2]);
02242     Vnm_print(0,"VPMG::extEnergy    Disj part upper corner = (%g, %g, %g)\n",
02243      partMax[0], partMax[1], partMax[2]);
02244     
02245     /* Find the old dimensions */
02246  
02247     hxOLD = pmgOLD->pmgp->hx;
02248     hyOLD = pmgOLD->pmgp->hy;
02249     hzOLD = pmgOLD->pmgp->hzed;
02250     xmin =  pmgOLD->pmgp->xcent - 0.5*hxOLD*(nxOLD-1);
02251     ymin =  pmgOLD->pmgp->ycent - 0.5*hyOLD*(nyOLD-1);
02252     zmin =  pmgOLD->pmgp->zcent - 0.5*hzOLD*(nzOLD-1);
02253     xmax =  xmin+hxOLD*(nxOLD-1);
02254     ymax =  ymin+hyOLD*(nyOLD-1);
02255     zmax =  zmin+hzOLD*(nzOLD-1);
02256     
02257     Vnm_print(0,"VPMG::extEnergy    Old lower corner = (%g, %g, %g)\n",
02258      xmin, ymin, zmin);
02259     Vnm_print(0,"VPMG::extEnergy    Old upper corner = (%g, %g, %g)\n",
02260      xmax, ymax, zmax);
02261  
02262     /* Flip the partition, but do not include any points that will
02263   be included by another processor */
02264     
02265     nx = nxOLD;
02266     ny = nyOLD;
02267     nz = nzOLD;
02268     
02269     for(i=0; i<nx; i++) {
02270         xval = 1;
02271         x = i*hxOLD + xmin;
02272         if (x < partMin[0] && bflags[VAPBS_LEFT] == 1) xval = 0;
02273         else if (x > partMax[0] && bflags[VAPBS_RIGHT] == 1) xval = 0;
02274   
02275         for(j=0; j<ny; j++) {
02276             yval = 1;
02277             y = j*hyOLD + ymin;  
02278             if (y < partMin[1] && bflags[VAPBS_BACK] == 1) yval = 0;
02279             else if (y > partMax[1] && bflags[VAPBS_FRONT] == 1) yval = 0;
02280    
02281             for(k=0; k<nz; k++) {
02282                 zval = 1;
02283                 z = k*hzOLD + zmin;
02284                 if (z < partMin[2] && bflags[VAPBS_DOWN] == 1) zval = 0;
02285                 else if (z > partMax[2] && bflags[VAPBS_UP] == 1) zval = 0;
02286     
02287                 if (pmgOLD->pvec[IJK(i,j,k)] > VSMALL) pmgOLD->pvec[IJK(i,j,k)] = 1.0;
02288                 pmgOLD->pvec[IJK(i,j,k)] = (1 - (pmgOLD->pvec[IJK(i,j,k)])) * (xval*yval*zval);
02289             }
02290         }
02291     }
02292  
02293     for (i=0; i<Valist_getNumberAtoms(thee->pbe->alist); i++) {
02294         xval=1;
02295         yval=1;
02296         zval=1;
02297         atom = Valist_getAtom(thee->pbe->alist, i);
02298         x = atom->position[0];
02299         y = atom->position[1];
02300         z = atom->position[2];
02301         if (x < partMin[0] && bflags[VAPBS_LEFT] == 1) xval = 0;
02302         else if (x > partMax[0] && bflags[VAPBS_RIGHT] == 1) xval = 0;
02303         if (y < partMin[1] && bflags[VAPBS_BACK] == 1) yval = 0;
02304         else if (y > partMax[1] && bflags[VAPBS_FRONT] == 1) yval = 0;
02305         if (z < partMin[2] && bflags[VAPBS_DOWN] == 1) zval = 0;
02306         else if (z > partMax[2] && bflags[VAPBS_UP] == 1) zval = 0;
02307         if (atom->partID > VSMALL) atom->partID = 1.0;
02308         atom->partID = (1 - atom->partID) * (xval*yval*zval);
02309     }
02310  
02311     /* Now calculate the energy on inverted subset of the domain */
02312     thee->extQmEnergy = Vpmg_qmEnergy(pmgOLD, 1);
02313     Vnm_print(0, "VPMG::extEnergy: extQmEnergy = %g kT\n", thee->extQmEnergy);
02314     thee->extQfEnergy = Vpmg_qfEnergy(pmgOLD, 1);
02315     Vnm_print(0, "VPMG::extEnergy: extQfEnergy = %g kT\n", thee->extQfEnergy);
02316     thee->extDiEnergy = Vpmg_dielEnergy(pmgOLD, 1);
02317     Vnm_print(0, "VPMG::extEnergy: extDiEnergy = %g kT\n", thee->extDiEnergy);
02318     Vpmg_unsetPart(pmgOLD);
02319 }
02320 
02321 VPRIVATE double bcfl1sp(double size, double *apos, double charge, 
02322       double xkappa, double pre1, double *pos) {
02323  
02324     double dist, val;
02325  
02326     dist = VSQRT(VSQR(pos[0]-apos[0]) + VSQR(pos[1]-apos[1])
02327      + VSQR(pos[2]-apos[2]));
02328     if (xkappa > VSMALL) {
02329         val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
02330   / (1+xkappa*size);
02331     } else {
02332         val = pre1*(charge/dist);
02333     } 
02334  
02335     return val;
02336 }
02337 
02338 VPRIVATE void bcfl1(double size, double *apos, double charge, 
02339      double xkappa, double pre1, double *gxcf, double *gycf, double *gzcf,
02340      double *xf, double *yf, double *zf, int nx, int ny, int nz) {
02341  
02342     int i, j, k;
02343     double dist, val;
02344     double gpos[3];
02345  
02346     /* the "i" boundaries (dirichlet) */
02347     for (k=0; k<nz; k++) {
02348         gpos[2] = zf[k];
02349         for (j=0; j<ny; j++) {
02350             gpos[1] = yf[j];
02351             gpos[0] = xf[0];
02352             dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
02353        + VSQR(gpos[2]-apos[2]));
02354             if (xkappa > VSMALL) {
02355                 val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
02356     / (1+xkappa*size);
02357             } else {
02358                 val = pre1*(charge/dist);
02359             } 
02360             gxcf[IJKx(j,k,0)] += val;
02361             gpos[0] = xf[nx-1];
02362             dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
02363        + VSQR(gpos[2]-apos[2]));
02364             if (xkappa > VSMALL) {
02365                 val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
02366     / (1+xkappa*size);
02367             } else {
02368                 val = pre1*(charge/dist);
02369             }
02370             gxcf[IJKx(j,k,1)] += val;
02371         }
02372     }
02373  
02374     /* the "j" boundaries (dirichlet) */
02375     for (k=0; k<nz; k++) {
02376         gpos[2] = zf[k];
02377         for (i=0; i<nx; i++) {
02378             gpos[0] = xf[i];
02379             gpos[1] = yf[0];
02380             dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
02381        + VSQR(gpos[2]-apos[2]));
02382             if (xkappa > VSMALL) {
02383                 val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
02384     / (1+xkappa*size);
02385             } else {
02386                 val = pre1*(charge/dist);
02387             }
02388             gycf[IJKy(i,k,0)] += val;
02389             gpos[1] = yf[ny-1];
02390             dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
02391        + VSQR(gpos[2]-apos[2]));
02392             if (xkappa > VSMALL) {
02393                 val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
02394     / (1+xkappa*size);
02395             } else {
02396                 val = pre1*(charge/dist);
02397             }
02398             gycf[IJKy(i,k,1)] += val;
02399         }
02400     }
02401  
02402     /* the "k" boundaries (dirichlet) */
02403     for (j=0; j<ny; j++) {
02404         gpos[1] = yf[j];
02405         for (i=0; i<nx; i++) {
02406             gpos[0] = xf[i];
02407             gpos[2] = zf[0];
02408             dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
02409        + VSQR(gpos[2]-apos[2]));
02410             if (xkappa > VSMALL) {
02411                 val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
02412     / (1+xkappa*size);
02413             } else {
02414                 val = pre1*(charge/dist);
02415             }
02416             gzcf[IJKz(i,j,0)] += val;
02417             gpos[2] = zf[nz-1];
02418             dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
02419        + VSQR(gpos[2]-apos[2]));
02420             if (xkappa > VSMALL) {
02421                 val = pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
02422     / (1+xkappa*size);
02423             } else {
02424                 val = pre1*(charge/dist);
02425             }
02426             gzcf[IJKz(i,j,1)] += val;
02427         }
02428     }
02429 }
02430 
02431 VPRIVATE void bcfl2(double size, double *apos,
02432                     double charge, double *dipole, double *quad, 
02433                     double xkappa, double eps_p, double eps_w, double T, 
02434                     double *gxcf, double *gycf, double *gzcf, 
02435                     double *xf, double *yf, double *zf, 
02436                     int nx, int ny, int nz) {
02437  
02438     int i, j, k;
02439     double val;
02440     double gpos[3],tensor[3];
02441     double ux,uy,uz,xr,yr,zr;
02442     double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
02443     double dist, pre;
02444  
02445     VASSERT(dipole != VNULL);
02446     ux = dipole[0];
02447     uy = dipole[1];
02448     uz = dipole[2];
02449     if (quad != VNULL) {
02450   /* The factor of 1/3 results from using a 
02451    traceless quadrupole definition. See, for example,
02452    "The Theory of Intermolecular Forces" by A.J. Stone,
02453    Chapter 3. */
02454   qxx = quad[0] / 3.0;
02455   qxy = quad[1] / 3.0;
02456   qxz = quad[2] / 3.0;
02457   qyx = quad[3] / 3.0;
02458   qyy = quad[4] / 3.0;
02459   qyz = quad[5] / 3.0;
02460   qzx = quad[6] / 3.0;
02461   qzy = quad[7] / 3.0;
02462   qzz = quad[8] / 3.0;
02463     } else {
02464   qxx = 0.0;
02465   qxy = 0.0;
02466   qxz = 0.0;
02467   qyx = 0.0;
02468   qyy = 0.0;
02469   qyz = 0.0;
02470   qzx = 0.0;
02471   qzy = 0.0;
02472   qzz = 0.0;
02473     }
02474  
02475     pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
02476     pre = pre*(1.0e10);
02477  
02478     /* the "i" boundaries (dirichlet) */
02479     for (k=0; k<nz; k++) {
02480         gpos[2] = zf[k];
02481         for (j=0; j<ny; j++) {
02482             gpos[1] = yf[j];
02483             gpos[0] = xf[0];
02484             xr = gpos[0] - apos[0];
02485             yr = gpos[1] - apos[1];
02486             zr = gpos[2] - apos[2];
02487             dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
02488             multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
02489             val = pre*charge*tensor[0];
02490             val -= pre*ux*xr*tensor[1];
02491             val -= pre*uy*yr*tensor[1];
02492             val -= pre*uz*zr*tensor[1];
02493             val += pre*qxx*xr*xr*tensor[2]; 
02494             val += pre*qyy*yr*yr*tensor[2]; 
02495             val += pre*qzz*zr*zr*tensor[2]; 
02496             val += pre*2.0*qxy*xr*yr*tensor[2]; 
02497             val += pre*2.0*qxz*xr*zr*tensor[2]; 
02498             val += pre*2.0*qyz*yr*zr*tensor[2]; 
02499             gxcf[IJKx(j,k,0)] += val;
02500             
02501             gpos[0] = xf[nx-1];
02502             xr = gpos[0] - apos[0];
02503             dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
02504             multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
02505             val = pre*charge*tensor[0];
02506             val -= pre*ux*xr*tensor[1];
02507             val -= pre*uy*yr*tensor[1];
02508             val -= pre*uz*zr*tensor[1];
02509             val += pre*qxx*xr*xr*tensor[2]; 
02510             val += pre*qyy*yr*yr*tensor[2]; 
02511             val += pre*qzz*zr*zr*tensor[2]; 
02512             val += pre*2.0*qxy*xr*yr*tensor[2]; 
02513             val += pre*2.0*qxz*xr*zr*tensor[2]; 
02514             val += pre*2.0*qyz*yr*zr*tensor[2]; 
02515             gxcf[IJKx(j,k,1)] += val;
02516         }
02517     }
02518  
02519     /* the "j" boundaries (dirichlet) */
02520     for (k=0; k<nz; k++) {
02521         gpos[2] = zf[k];
02522         for (i=0; i<nx; i++) {
02523             gpos[0] = xf[i];
02524             gpos[1] = yf[0];
02525             xr = gpos[0] - apos[0];
02526             yr = gpos[1] - apos[1];
02527             zr = gpos[2] - apos[2];
02528             dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
02529             multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
02530             val = pre*charge*tensor[0];
02531             val -= pre*ux*xr*tensor[1];
02532             val -= pre*uy*yr*tensor[1];
02533             val -= pre*uz*zr*tensor[1];
02534             val += pre*qxx*xr*xr*tensor[2]; 
02535             val += pre*qyy*yr*yr*tensor[2]; 
02536             val += pre*qzz*zr*zr*tensor[2]; 
02537             val += pre*2.0*qxy*xr*yr*tensor[2]; 
02538             val += pre*2.0*qxz*xr*zr*tensor[2]; 
02539             val += pre*2.0*qyz*yr*zr*tensor[2]; 
02540             gycf[IJKy(i,k,0)] += val;
02541    
02542             gpos[1] = yf[ny-1];
02543             yr = gpos[1] - apos[1];
02544             dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
02545             multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
02546             val = pre*charge*tensor[0];
02547             val -= pre*ux*xr*tensor[1];
02548             val -= pre*uy*yr*tensor[1];
02549             val -= pre*uz*zr*tensor[1];
02550             val += pre*qxx*xr*xr*tensor[2]; 
02551             val += pre*qyy*yr*yr*tensor[2]; 
02552             val += pre*qzz*zr*zr*tensor[2]; 
02553             val += pre*2.0*qxy*xr*yr*tensor[2]; 
02554             val += pre*2.0*qxz*xr*zr*tensor[2]; 
02555             val += pre*2.0*qyz*yr*zr*tensor[2]; 
02556             gycf[IJKy(i,k,1)] += val;
02557         }
02558     }
02559  
02560     /* the "k" boundaries (dirichlet) */
02561     for (j=0; j<ny; j++) {
02562         gpos[1] = yf[j];
02563         for (i=0; i<nx; i++) {
02564             gpos[0] = xf[i];
02565             gpos[2] = zf[0];
02566             xr = gpos[0] - apos[0];
02567             yr = gpos[1] - apos[1];
02568             zr = gpos[2] - apos[2];
02569             dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
02570             multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
02571             val = pre*charge*tensor[0];
02572             val -= pre*ux*xr*tensor[1];
02573             val -= pre*uy*yr*tensor[1];
02574             val -= pre*uz*zr*tensor[1];
02575             val += pre*qxx*xr*xr*tensor[2]; 
02576             val += pre*qyy*yr*yr*tensor[2]; 
02577             val += pre*qzz*zr*zr*tensor[2]; 
02578             val += pre*2.0*qxy*xr*yr*tensor[2]; 
02579             val += pre*2.0*qxz*xr*zr*tensor[2]; 
02580             val += pre*2.0*qyz*yr*zr*tensor[2]; 
02581             gzcf[IJKz(i,j,0)] += val;
02582             
02583             gpos[2] = zf[nz-1];
02584             zr = gpos[2] - apos[2];
02585             dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
02586             multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
02587             val = pre*charge*tensor[0];
02588             val -= pre*ux*xr*tensor[1];
02589             val -= pre*uy*yr*tensor[1];
02590             val -= pre*uz*zr*tensor[1];
02591             val += pre*qxx*xr*xr*tensor[2]; 
02592             val += pre*qyy*yr*yr*tensor[2]; 
02593             val += pre*qzz*zr*zr*tensor[2]; 
02594             val += pre*2.0*qxy*xr*yr*tensor[2]; 
02595             val += pre*2.0*qxz*xr*zr*tensor[2]; 
02596             val += pre*2.0*qyz*yr*zr*tensor[2]; 
02597             gzcf[IJKz(i,j,1)] += val;
02598         }
02599     }
02600 }
02601 
02602 VPRIVATE void bcCalcOrig(Vpmg *thee) {
02603  
02604     int nx, ny, nz;
02605     double size, *position, charge, xkappa, eps_w, T, pre1;
02606     double *dipole, *quadrupole, debye, eps_p; 
02607     double xr,yr,zr,qave,*apos;
02608     double sdhcharge, sdhdipole[3], traced[9], sdhquadrupole[9];
02609     int i, j, k, iatom;
02610     Vpbe *pbe;
02611     Vatom *atom;
02612     Valist *alist;
02613     
02614     pbe = thee->pbe;
02615     alist = thee->pbe->alist;
02616     nx = thee->pmgp->nx;
02617     ny = thee->pmgp->ny;
02618     nz = thee->pmgp->nz;
02619  
02620     /* Zero out the boundaries */
02621     /* the "i" boundaries (dirichlet) */
02622     for (k=0; k<nz; k++) {
02623         for (j=0; j<ny; j++) {
02624             thee->gxcf[IJKx(j,k,0)] = 0.0;
02625             thee->gxcf[IJKx(j,k,1)] = 0.0;
02626             thee->gxcf[IJKx(j,k,2)] = 0.0;
02627             thee->gxcf[IJKx(j,k,3)] = 0.0;
02628         }
02629     }
02630  
02631     /* the "j" boundaries (dirichlet) */
02632     for (k=0; k<nz; k++) {
02633         for (i=0; i<nx; i++) {
02634             thee->gycf[IJKy(i,k,0)] = 0.0;
02635             thee->gycf[IJKy(i,k,1)] = 0.0;
02636             thee->gycf[IJKy(i,k,2)] = 0.0;
02637             thee->gycf[IJKy(i,k,3)] = 0.0;
02638         }
02639     }
02640  
02641     /* the "k" boundaries (dirichlet) */
02642     for (j=0; j<ny; j++) {
02643         for (i=0; i<nx; i++) {
02644             thee->gzcf[IJKz(i,j,0)] = 0.0;
02645             thee->gzcf[IJKz(i,j,1)] = 0.0;
02646             thee->gzcf[IJKz(i,j,2)] = 0.0;
02647             thee->gzcf[IJKz(i,j,3)] = 0.0;
02648         }
02649     }
02650  
02651     /* For each "atom" (only one for bcfl=1), we use the following formula to
02652  * calculate the boundary conditions: 
02653  *    g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
02654  *          * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
02655  *          * 1/d
02656  * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
02657  * We only need to evaluate some of these prefactors once:
02658  *    pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
02659  * which gives the potential as
02660  *    g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a} 
02661  */
02662     eps_w = Vpbe_getSolventDiel(pbe);           /* Dimensionless */
02663     eps_p = Vpbe_getSoluteDiel(pbe);           /* Dimensionless */
02664     T = Vpbe_getTemperature(pbe);               /* K             */
02665     pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
02666  
02667     /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
02668   * m/A, then we will only need to deal with distances and sizes in
02669   * Angstroms rather than meters.                                       */
02670     xkappa = Vpbe_getXkappa(pbe);              /* A^{-1}        */
02671     pre1 = pre1*(1.0e10);
02672  
02673     switch (thee->pmgp->bcfl) {
02674         /*  If we have zero boundary conditions, we're done */
02675         case BCFL_ZERO: 
02676             return;
02677    
02678    /*  For single DH sphere BC's, we only have one "atom" to deal with;
02679    *  get its information and */
02680         case BCFL_SDH:
02681             size = Vpbe_getSoluteRadius(pbe);
02682             position = Vpbe_getSoluteCenter(pbe);
02683    
02684             /* 
02685     For AMOEBA SDH boundary conditions, we need to find the
02686     total monopole, dipole and traceless quadrupole moments 
02687     of either the permanent multipoles, induced dipoles or
02688     non-local induced dipoles. 
02689     */ 
02690             
02691             sdhcharge = 0.0;
02692             for (i=0; i<3; i++) sdhdipole[i] = 0.0;
02693    for (i=0; i<9; i++) sdhquadrupole[i] = 0.0;
02694    
02695    for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
02696     atom = Valist_getAtom(alist, iatom);
02697     apos = Vatom_getPosition(atom);
02698     xr = apos[0] - position[0];
02699     yr = apos[1] - position[1];
02700     zr = apos[2] - position[2];
02701     switch (thee->chargeSrc) {
02702      case VCM_CHARGE:
02703       charge = Vatom_getCharge(atom);
02704       sdhcharge += charge;
02705       sdhdipole[0] += xr * charge; 
02706       sdhdipole[1] += yr * charge; 
02707       sdhdipole[2] += zr * charge; 
02708       traced[0] = xr*xr*charge;
02709       traced[1] = xr*yr*charge;
02710       traced[2] = xr*zr*charge;
02711       traced[3] = yr*xr*charge;
02712       traced[4] = yr*yr*charge;
02713       traced[5] = yr*zr*charge;
02714       traced[6] = zr*xr*charge;
02715       traced[7] = zr*yr*charge;
02716       traced[8] = zr*zr*charge;
02717       qave = (traced[0] + traced[4] + traced[8]) / 3.0;
02718       sdhquadrupole[0] += 1.5*(traced[0] - qave);
02719       sdhquadrupole[1] += 1.5*(traced[1]); 
02720       sdhquadrupole[2] += 1.5*(traced[2]); 
02721       sdhquadrupole[3] += 1.5*(traced[3]); 
02722       sdhquadrupole[4] += 1.5*(traced[4] - qave); 
02723       sdhquadrupole[5] += 1.5*(traced[5]); 
02724       sdhquadrupole[6] += 1.5*(traced[6]); 
02725       sdhquadrupole[7] += 1.5*(traced[7]); 
02726       sdhquadrupole[8] += 1.5*(traced[8] - qave);
02727 #if defined(WITH_TINKER)
02728      case VCM_PERMANENT:
02729       charge = Vatom_getCharge(atom);
02730       dipole = Vatom_getDipole(atom);
02731       quadrupole = Vatom_getQuadrupole(atom);
02732       sdhcharge += charge;
02733       sdhdipole[0] += xr * charge; 
02734       sdhdipole[1] += yr * charge; 
02735       sdhdipole[2] += zr * charge; 
02736       traced[0] = xr*xr*charge;
02737       traced[1] = xr*yr*charge;
02738       traced[2] = xr*zr*charge;
02739       traced[3] = yr*xr*charge;
02740       traced[4] = yr*yr*charge;
02741       traced[5] = yr*zr*charge;
02742       traced[6] = zr*xr*charge;
02743       traced[7] = zr*yr*charge;
02744       traced[8] = zr*zr*charge;
02745       sdhdipole[0] += dipole[0];
02746       sdhdipole[1] += dipole[1];
02747       sdhdipole[2] += dipole[2];
02748       traced[0] += 2.0*xr*dipole[0];
02749       traced[1] += xr*dipole[1] + yr*dipole[0];
02750       traced[2] += xr*dipole[2] + zr*dipole[0];
02751       traced[3] += yr*dipole[0] + xr*dipole[1];
02752       traced[4] += 2.0*yr*dipole[1];
02753       traced[5] += yr*dipole[2] + zr*dipole[1];
02754       traced[6] += zr*dipole[0] + xr*dipole[2];
02755       traced[7] += zr*dipole[1] + yr*dipole[2];
02756       traced[8] += 2.0*zr*dipole[2];
02757       qave = (traced[0] + traced[4] + traced[8]) / 3.0;
02758       sdhquadrupole[0] += 1.5*(traced[0] - qave);
02759       sdhquadrupole[1] += 1.5*(traced[1]); 
02760       sdhquadrupole[2] += 1.5*(traced[2]); 
02761       sdhquadrupole[3] += 1.5*(traced[3]); 
02762       sdhquadrupole[4] += 1.5*(traced[4] - qave); 
02763       sdhquadrupole[5] += 1.5*(traced[5]); 
02764       sdhquadrupole[6] += 1.5*(traced[6]); 
02765       sdhquadrupole[7] += 1.5*(traced[7]); 
02766       sdhquadrupole[8] += 1.5*(traced[8] - qave); 
02767       sdhquadrupole[0] += quadrupole[0];
02768       sdhquadrupole[1] += quadrupole[1];
02769       sdhquadrupole[2] += quadrupole[2];
02770       sdhquadrupole[3] += quadrupole[3];
02771       sdhquadrupole[4] += quadrupole[4];
02772       sdhquadrupole[5] += quadrupole[5];
02773       sdhquadrupole[6] += quadrupole[6];
02774       sdhquadrupole[7] += quadrupole[7];
02775       sdhquadrupole[8] += quadrupole[8];
02776      case VCM_INDUCED:
02777       dipole = Vatom_getInducedDipole(atom);
02778       sdhdipole[0] += dipole[0];
02779       sdhdipole[1] += dipole[1];
02780       sdhdipole[2] += dipole[2];
02781       traced[0] = 2.0*xr*dipole[0];
02782       traced[1] = xr*dipole[1] + yr*dipole[0];
02783       traced[2] = xr*dipole[2] + zr*dipole[0];
02784       traced[3] = yr*dipole[0] + xr*dipole[1];
02785       traced[4] = 2.0*yr*dipole[1];
02786       traced[5] = yr*dipole[2] + zr*dipole[1];
02787       traced[6] = zr*dipole[0] + xr*dipole[2];
02788       traced[7] = zr*dipole[1] + yr*dipole[2];
02789       traced[8] = 2.0*zr*dipole[2];
02790       qave = (traced[0] + traced[4] + traced[8]) / 3.0;
02791       sdhquadrupole[0] += 1.5*(traced[0] - qave);
02792       sdhquadrupole[1] += 1.5*(traced[1]);
02793       sdhquadrupole[2] += 1.5*(traced[2]);
02794       sdhquadrupole[3] += 1.5*(traced[3]);
02795       sdhquadrupole[4] += 1.5*(traced[4] - qave);
02796       sdhquadrupole[5] += 1.5*(traced[5]);
02797       sdhquadrupole[6] += 1.5*(traced[6]);
02798       sdhquadrupole[7] += 1.5*(traced[7]);
02799       sdhquadrupole[8] += 1.5*(traced[8] - qave);
02800      case VCM_NLINDUCED:
02801       dipole = Vatom_getNLInducedDipole(atom);
02802       sdhdipole[0] += dipole[0];
02803       sdhdipole[1] += dipole[1];
02804       sdhdipole[2] += dipole[2];
02805       traced[0] = 2.0*xr*dipole[0];
02806       traced[1] = xr*dipole[1] + yr*dipole[0];
02807       traced[2] = xr*dipole[2] + zr*dipole[0];
02808       traced[3] = yr*dipole[0] + xr*dipole[1];
02809       traced[4] = 2.0*yr*dipole[1];
02810       traced[5] = yr*dipole[2] + zr*dipole[1];
02811       traced[6] = zr*dipole[0] + xr*dipole[2];
02812       traced[7] = zr*dipole[1] + yr*dipole[2];
02813       traced[8] = 2.0*zr*dipole[2];
02814       qave = (traced[0] + traced[4] + traced[8]) / 3.0;
02815       sdhquadrupole[0] += 1.5*(traced[0] - qave);
02816       sdhquadrupole[1] += 1.5*(traced[1]);
02817       sdhquadrupole[2] += 1.5*(traced[2]);
02818       sdhquadrupole[3] += 1.5*(traced[3]);
02819       sdhquadrupole[4] += 1.5*(traced[4] - qave);
02820       sdhquadrupole[5] += 1.5*(traced[5]);
02821       sdhquadrupole[6] += 1.5*(traced[6]);
02822       sdhquadrupole[7] += 1.5*(traced[7]);
02823       sdhquadrupole[8] += 1.5*(traced[8] - qave);
02824 #endif /* if defined(WITH_TINKER) */
02825     }
02826    }
02827    /* SDH dipole and traceless quadrupole values
02828     were checked against similar routines in TINKER
02829     for large proteins.
02830     
02831     debye=4.8033324;
02832     printf("%6.3f, %6.3f, %6.3f\n", sdhdipole[0]*debye, 
02833     sdhdipole[1]*debye, sdhdipole[2]*debye);
02834     printf("%6.3f\n", sdhquadrupole[0]*debye);
02835     printf("%6.3f %6.3f\n", sdhquadrupole[3]*debye,
02836     sdhquadrupole[4]*debye);
02837     printf("%6.3f %6.3f %6.3f\n", sdhquadrupole[6]*debye,
02838     sdhquadrupole[7]*debye, sdhquadrupole[8]*debye);
02839     */
02840 
02841    bcfl2(size, position, sdhcharge, sdhdipole, sdhquadrupole, 
02842       xkappa, eps_p, eps_w, T, thee->gxcf, thee->gycf, 
02843       thee->gzcf, thee->xf, thee->yf, thee->zf, nx, ny, nz);
02844             break;
02845    
02846         case BCFL_MDH: 
02847    for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
02848     atom = Valist_getAtom(alist, iatom);
02849     position = Vatom_getPosition(atom);
02850     charge = Vunit_ec*Vatom_getCharge(atom);
02851     dipole = VNULL;
02852     quadrupole = VNULL;
02853     size = Vatom_getRadius(atom);
02854     switch (thee->chargeSrc)
02855     {
02856      case VCM_CHARGE:
02857       ;
02858 #if  defined(WITH_TINKER)   
02859      case VCM_PERMANENT:
02860       dipole = Vatom_getDipole(atom);
02861       quadrupole = Vatom_getQuadrupole(atom);
02862       
02863      case VCM_INDUCED:
02864       dipole = Vatom_getInducedDipole(atom);
02865       
02866      case VCM_NLINDUCED:
02867       dipole = Vatom_getNLInducedDipole(atom);
02868 #endif
02869     }
02870     bcfl1(size, position, charge, xkappa, pre1,
02871        thee->gxcf, thee->gycf, thee->gzcf, 
02872        thee->xf, thee->yf, thee->zf, nx, ny, nz);
02873    }
02874    break;
02875    
02876         case BCFL_UNUSED:
02877             Vnm_print(2, "bcCalc:  Invalid bcfl (%d)!\n", thee->pmgp->bcfl);
02878             VASSERT(0);
02879    
02880         case BCFL_FOCUS:
02881             Vnm_print(2, "VPMG::bcCalc -- not appropriate for focusing!\n");
02882             VASSERT(0);
02883    
02884         default:
02885             Vnm_print(2, "VPMG::bcCalc -- invalid boundary condition \
02886 flag (%d)!\n", thee->pmgp->bcfl);
02887             VASSERT(0);
02888     }
02889 }
02890 
02891 /* 
02892  Used by bcflnew
02893  */
02894 VPRIVATE int gridPointIsValid(int i, int j, int k, int nx, int ny, int nz){
02895  
02896  int isValid = 0;
02897  
02898  if((k==0) || (k==nz-1)){
02899   isValid = 1;
02900  }else if((j==0) || (j==ny-1)){
02901   isValid = 1;
02902  }else if((i==0) || (i==nx-1)){
02903   isValid = 1;
02904  }
02905  
02906  return isValid;
02907 }
02908 
02909 /* 
02910  Used by bcflnew
02911  */
02912 #ifdef DEBUG_MAC_OSX_OCL
02913 #include "mach_chud.h"
02914 VPRIVATE void packAtomsOpenCL(float *ax, float *ay, float *az,
02915       float *charge, float *size, Vpmg *thee){
02916  
02917  int i;
02918  int natoms;
02919  
02920  Vatom *atom = VNULL;
02921  Valist *alist = VNULL;
02922  
02923  alist = thee->pbe->alist;
02924  natoms = Valist_getNumberAtoms(alist);
02925  
02926  for(i=0;i<natoms;i++){
02927   atom = &(alist->atoms[i]);
02928   charge[i] = Vunit_ec*atom->charge;
02929   ax[i] = atom->position[0];
02930   ay[i] = atom->position[1];
02931   az[i] = atom->position[2];
02932   size[i] = atom->radius;
02933  }
02934 }
02935 
02936 /* 
02937  Used by bcflnew
02938  */
02939 VPRIVATE void packUnpackOpenCL(int nx, int ny, int nz, int ngrid,
02940        float *gx, float *gy, float *gz, float *value,
02941        Vpmg *thee, int pack){
02942  
02943  int i,j,k,igrid;
02944  int x0,x1,y0,y1,z0,z1;
02945  
02946  float gpos[3];
02947  double *xf, *yf, *zf;
02948  double *gxcf, *gycf, *gzcf;
02949  
02950  xf = thee->xf;
02951  yf = thee->yf;
02952  zf = thee->zf;
02953  
02954  gxcf = thee->gxcf;
02955  gycf = thee->gycf;
02956  gzcf = thee->gzcf;
02957  
02958  igrid = 0;
02959  for(k=0;k<nz;k++){
02960   gpos[2] = zf[k];
02961   for(j=0;j<ny;j++){
02962    gpos[1] = yf[j];
02963    for(i=0;i<nx;i++){
02964     gpos[0] = xf[i];
02965     if(gridPointIsValid(i, j, k, nx, ny, nz)){
02966      if(pack != 0){
02967       gx[igrid] = gpos[0];
02968       gy[igrid] = gpos[1];
02969       gz[igrid] = gpos[2];
02970       
02971       value[igrid] = 0.0;
02972      }else{
02973       x0 = IJKx(j,k,0);
02974       x1 = IJKx(j,k,1);
02975       y0 = IJKy(i,k,0);
02976       y1 = IJKy(i,k,1);
02977       z0 = IJKz(i,j,0);
02978       z1 = IJKz(i,j,1);
02979       
02980       if(i==0){
02981        gxcf[x0] += value[igrid];
02982       }
02983       if(i==nx-1){
02984        gxcf[x1] += value[igrid];
02985       }
02986       if(j==0){
02987        gycf[y0] += value[igrid];
02988       }
02989       if(j==ny-1){
02990        gycf[y1] += value[igrid];
02991       }
02992       if(k==0){
02993        gzcf[z0] += value[igrid];
02994       }
02995       if(k==nz-1){
02996        gzcf[z1] += value[igrid];
02997       }
02998      }
02999      
03000      igrid++;
03001     } //end is valid point
03002    } //end i
03003   } //end j
03004  } //end k
03005 
03006 }
03007 
03008 /*
03009  bcflnew is an optimized replacement for bcfl1. bcfl1 is still used when TINKER 
03010  support is compiled in.
03011  bcflnew uses: packUnpack, packAtoms, gridPointIsValid
03012  */
03013 VPRIVATE void bcflnewOpenCL(Vpmg *thee){
03014  
03015  int i,j,k, iatom, igrid;
03016  int x0, x1, y0, y1, z0, z1;
03017  
03018  int nx, ny, nz;
03019  int natoms, ngrid, ngadj;
03020  
03021  float dist, pre1, eps_w, eps_p, T, xkappa;
03022  
03023  float *ax, *ay, *az;
03024  float *charge, *size, *val;
03025  
03026  float *gx, *gy, *gz;
03027  
03028  Vpbe *pbe = thee->pbe;
03029  
03030  nx = thee->pmgp->nx;
03031     ny = thee->pmgp->ny;
03032     nz = thee->pmgp->nz;
03033  
03034  eps_w = Vpbe_getSolventDiel(pbe);           /* Dimensionless */
03035     eps_p = Vpbe_getSoluteDiel(pbe);           /* Dimensionless */
03036     T = Vpbe_getTemperature(pbe);               /* K             */
03037     pre1 = ((Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T))*(1.0e10);
03038  xkappa = Vpbe_getXkappa(pbe);
03039  
03040  natoms = Valist_getNumberAtoms(thee->pbe->alist);
03041  ngrid = 2*((nx*ny) + (ny*nz) + (nx*nz));
03042  ngadj = ngrid + (512 - (ngrid & 511));
03043  
03044  ax = (float*)malloc(natoms * sizeof(float));
03045  ay = (float*)malloc(natoms * sizeof(float));
03046  az = (float*)malloc(natoms * sizeof(float));
03047  
03048  charge = (float*)malloc(natoms * sizeof(float));
03049  size = (float*)malloc(natoms * sizeof(float));
03050  
03051  gx = (float*)malloc(ngrid * sizeof(float));
03052  gy = (float*)malloc(ngrid * sizeof(float));
03053  gz = (float*)malloc(ngrid * sizeof(float));
03054  
03055  val = (float*)malloc(ngrid * sizeof(float));
03056  
03057  packAtomsOpenCL(ax,ay,az,charge,size,thee);
03058  packUnpackOpenCL(nx,ny,nz,ngrid,gx,gy,gz,val,thee,1);
03059 
03060  runMDHCL(ngrid,natoms,ngadj,ax,ay,az,gx,gy,gz,charge,size,xkappa,pre1,val);
03061 
03062  packUnpackOpenCL(nx,ny,nz,ngrid,gx,gy,gz,val,thee,0);
03063  
03064  free(ax);
03065  free(ay);
03066  free(az);
03067  free(charge);
03068  free(size);
03069  
03070  free(gx);
03071  free(gy);
03072  free(gz);
03073  free(val);
03074 }
03075 #endif
03076 
03077 VPRIVATE void packAtoms(double *ax, double *ay, double *az,
03078       double *charge, double *size, Vpmg *thee){
03079  
03080  int i;
03081  int natoms;
03082  
03083  Vatom *atom = VNULL;
03084  Valist *alist = VNULL;
03085  
03086  alist = thee->pbe->alist;
03087  natoms = Valist_getNumberAtoms(alist);
03088  
03089  for(i=0;i<natoms;i++){
03090   atom = &(alist->atoms[i]);
03091   charge[i] = Vunit_ec*atom->charge;
03092   ax[i] = atom->position[0];
03093   ay[i] = atom->position[1];
03094   az[i] = atom->position[2];
03095   size[i] = atom->radius;
03096  }
03097 }
03098 
03099 /* 
03100  Used by bcflnew
03101  */
03102 VPRIVATE void packUnpack(int nx, int ny, int nz, int ngrid,
03103        double *gx, double *gy, double *gz, double *value,
03104        Vpmg *thee, int pack){
03105  
03106  int i,j,k,igrid;
03107  int x0,x1,y0,y1,z0,z1;
03108  
03109  double gpos[3];
03110  double *xf, *yf, *zf;
03111  double *gxcf, *gycf, *gzcf;
03112  
03113  xf = thee->xf;
03114  yf = thee->yf;
03115  zf = thee->zf;
03116  
03117  gxcf = thee->gxcf;
03118  gycf = thee->gycf;
03119  gzcf = thee->gzcf;
03120  
03121  igrid = 0;
03122  for(k=0;k<nz;k++){
03123   gpos[2] = zf[k];
03124   for(j=0;j<ny;j++){
03125    gpos[1] = yf[j];
03126    for(i=0;i<nx;i++){
03127     gpos[0] = xf[i];
03128     if(gridPointIsValid(i, j, k, nx, ny, nz)){
03129      if(pack != 0){
03130       gx[igrid] = gpos[0];
03131       gy[igrid] = gpos[1];
03132       gz[igrid] = gpos[2];
03133       
03134       value[igrid] = 0.0;
03135      }else{
03136       x0 = IJKx(j,k,0);
03137       x1 = IJKx(j,k,1);
03138       y0 = IJKy(i,k,0);
03139       y1 = IJKy(i,k,1);
03140       z0 = IJKz(i,j,0);
03141       z1 = IJKz(i,j,1);
03142       
03143       if(i==0){
03144        gxcf[x0] += value[igrid];
03145       }
03146       if(i==nx-1){
03147        gxcf[x1] += value[igrid];
03148       }
03149       if(j==0){
03150        gycf[y0] += value[igrid];
03151       }
03152       if(j==ny-1){
03153        gycf[y1] += value[igrid];
03154       }
03155       if(k==0){
03156        gzcf[z0] += value[igrid];
03157       }
03158       if(k==nz-1){
03159        gzcf[z1] += value[igrid];
03160       }
03161      }
03162      
03163      igrid++;
03164     } //end is valid point
03165    } //end i
03166   } //end j
03167  } //end k
03168  
03169 }
03170 
03171 VPRIVATE void bcflnew(Vpmg *thee){
03172  
03173  int i,j,k, iatom, igrid;
03174  int x0, x1, y0, y1, z0, z1;
03175  
03176  int nx, ny, nz;
03177  int natoms, ngrid;
03178  
03179  double dist, pre1, eps_w, eps_p, T, xkappa;
03180  
03181  double *ax, *ay, *az;
03182  double *charge, *size, *val;
03183  
03184  double *gx, *gy, *gz;
03185  
03186  Vpbe *pbe = thee->pbe;
03187  
03188  nx = thee->pmgp->nx;
03189     ny = thee->pmgp->ny;
03190     nz = thee->pmgp->nz;
03191  
03192  eps_w = Vpbe_getSolventDiel(pbe);           /* Dimensionless */
03193     eps_p = Vpbe_getSoluteDiel(pbe);           /* Dimensionless */
03194     T = Vpbe_getTemperature(pbe);               /* K             */
03195     pre1 = ((Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T))*(1.0e10);
03196  xkappa = Vpbe_getXkappa(pbe);
03197  
03198  natoms = Valist_getNumberAtoms(thee->pbe->alist);
03199  ngrid = 2*((nx*ny) + (ny*nz) + (nx*nz));
03200  
03201  ax = (double*)malloc(natoms * sizeof(double));
03202  ay = (double*)malloc(natoms * sizeof(double));
03203  az = (double*)malloc(natoms * sizeof(double));
03204  
03205  charge = (double*)malloc(natoms * sizeof(double));
03206  size = (double*)malloc(natoms * sizeof(double));
03207  
03208  gx = (double*)malloc(ngrid * sizeof(double));
03209  gy = (double*)malloc(ngrid * sizeof(double));
03210  gz = (double*)malloc(ngrid * sizeof(double));
03211  
03212  val = (double*)malloc(ngrid * sizeof(double));
03213  
03214  packAtoms(ax,ay,az,charge,size,thee);
03215  packUnpack(nx,ny,nz,ngrid,gx,gy,gz,val,thee,1);
03216  
03217  if(xkappa > VSMALL){
03218 #pragma omp parallel for default(shared) private(igrid,iatom,dist)
03219   for(igrid=0;igrid<ngrid;igrid++){
03220    for(iatom=0; iatom<natoms; iatom++){
03221     dist = VSQRT(VSQR(gx[igrid]-ax[iatom]) + VSQR(gy[igrid]-ay[iatom])
03222         + VSQR(gz[igrid]-az[iatom]));
03223     val[igrid] += pre1*(charge[iatom]/dist)*VEXP(-xkappa*(dist-size[iatom]))
03224     / (1+xkappa*size[iatom]);
03225    }
03226   }
03227  }else{
03228 #pragma omp parallel for default(shared) private(igrid,iatom,dist)
03229   for(igrid=0;igrid<ngrid;igrid++){
03230    for(iatom=0; iatom<natoms; iatom++){
03231     dist = VSQRT(VSQR(gx[igrid]-ax[iatom]) + VSQR(gy[igrid]-ay[iatom])
03232         + VSQR(gz[igrid]-az[iatom]));
03233     val[igrid] += pre1*(charge[iatom]/dist); 
03234    }
03235   }
03236  }
03237  packUnpack(nx,ny,nz,ngrid,gx,gy,gz,val,thee,0);
03238  
03239  free(ax);
03240  free(ay);
03241  free(az);
03242  free(charge);
03243  free(size);
03244  
03245  free(gx);
03246  free(gy);
03247  free(gz);
03248  free(val);
03249 }
03250 
03251 VPRIVATE void multipolebc(double r, double kappa, double eps_p, 
03252                           double eps_w, double rad, double tsr[3]) {
03253     double r2,r3,r5;
03254     double eps_r;
03255     double ka,ka2,ka3;
03256     double kr,kr2,kr3;
03257  
03258     /*
03259   Below an attempt is made to explain the potential outside of a
03260   multipole located at the center of spherical cavity of dieletric
03261   eps_p, with dielectric eps_w outside (and possibly kappa > 0).
03262   
03263   
03264   First, eps_p = 1.0
03265   eps_w = 1.0 
03266   kappa = 0.0
03267   
03268   The general form for the potential of a traceless multipole tensor
03269   of rank n in vacuum is:
03270   
03271   V(r) = (-1)^n * u . n . Del^n (1/r)
03272   
03273   where 
03274   u                     is a multipole of order n (3^n components)
03275   u . n. Del^n (1/r)    is the contraction of u with the nth 
03276   derivative of 1/r
03277   
03278   for example, if n = 1, the dipole potential is
03279   V_vac(r) = (-1)*[ux*x + uy*y + uz*z]/r^3
03280   
03281   This function returns the parts of V(r) for multipoles of
03282   order 0, 1 and 2 that are independent of the contraction.  
03283   
03284   For the vacuum example, this would be 1/r, -1/r^3 and 3/r^5
03285   respectively. 
03286   
03287   *** Note that this requires that the quadrupole is
03288   traceless. If not, the diagonal quadrupole potential changes
03289   from
03290   qaa *  3*a^2/r^5 
03291   to
03292   qaa * (3*a^2/r^5 - 1/r^3a )
03293   where we sum over the trace; a = x, y and z.
03294   
03295   (In other words, the -1/r^3 term cancels for a traceless quadrupole.
03296   qxx + qyy + qzz = 0
03297   such that
03298   -(qxx + qyy + qzz)/r^3 = 0
03299   
03300   For quadrupole with trace:
03301   qxx + qyy + qzz != 0
03302   such that
03303   -(qxx + qyy + qzz)/r^3 != 0
03304   )
03305   
03306   ======================================================================== 
03307   
03308   eps_p != 1 or eps_w != 1
03309   kappa = 0.0
03310   
03311   If the multipole is placed at the center of a sphere with
03312   dieletric eps_p in a solvent of dielectric eps_w, the potential
03313   outside the sphere is the solution to the Laplace equation:
03314   
03315   V(r) = 1/eps_w * (2*n+1)*eps_r/(n+(n+1)*eps_r) 
03316   * (-1)^n * u . n . Del^n (1/r)
03317   where
03318   eps_r = eps_w / eps_p 
03319   is the ratio of solvent to solute dielectric
03320   
03321   ========================================================================
03322   
03323   kappa > 0
03324   
03325   Finally, if the region outside the sphere is treated by the linearized
03326   PB equation with Debye-Huckel parameter kappa, the solution is:
03327   
03328   V(r) = kappa/eps_w * alpha_n(kappa*a) * K_n(kappa*r) * r^(n+1)/a^n 
03329   * (-1)^n * u . n . Del^n (1/r)
03330   where 
03331   alpha_n(x) is [(2n + 1) / x] / [(n*K_n(x)/eps_r) - x*K_n'(x)]
03332   K_n(x) are modified spherical Bessel functions of the third kind. 
03333   K_n'(x) is the derivative of K_n(x)
03334      */
03335  
03336     eps_r = eps_w/eps_p;
03337     r2 = r*r;
03338     r3 = r2*r;
03339     r5 = r3*r2;
03340     tsr[0] = (1.0/eps_w)/r;
03341     tsr[1] = (1.0/eps_w)*(-1.0)/r3;
03342     tsr[2] = (1.0/eps_w)*(3.0)/r5;
03343     if (kappa < VSMALL) {
03344         tsr[1] = (3.0*eps_r)/(1.0 + 2.0*eps_r)*tsr[1];
03345         tsr[2] = (5.0*eps_r)/(2.0 + 3.0*eps_r)*tsr[2];
03346     } else {
03347         ka = kappa*rad;
03348         ka2 = ka*ka;
03349         ka3 = ka2*ka;
03350         kr = kappa*r;
03351         kr2 = kr*kr;
03352         kr3 = kr2*kr;
03353         tsr[0] = exp(ka-kr) / (1.0 + ka) * tsr[0];
03354         tsr[1] = 3.0*eps_r*exp(ka-kr)*(1.0 + kr) * tsr[1];
03355         tsr[1] = tsr[1] / (1.0 + ka + eps_r*(2.0 + 2.0*ka + ka2)); 
03356         tsr[2] = 5.0*eps_r*exp(ka-kr)*(3.0 + 3.0*kr + kr2) * tsr[2];
03357         tsr[2] = tsr[2]/(6.0+6.0*ka+2.0*ka2+eps_r*(9.0+9.0*ka+4.0*ka2+ka3)); 
03358     }
03359 }
03360 
03361 VPRIVATE void bcfl_sdh(Vpmg *thee){
03362  
03363  int i,j,k,iatom;
03364  int nx, ny, nz;
03365  
03366  double size, *position, charge, xkappa, eps_w, eps_p, T, pre, dist;
03367  double sdhcharge, sdhdipole[3], traced[9], sdhquadrupole[9];
03368  double *dipole, *quadrupole;
03369  
03370  double val, *apos, gpos[3], tensor[3], qave;
03371  double ux, uy, uz, xr, yr, zr;
03372  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
03373  
03374  double *xf, *yf, *zf;
03375  double *gxcf, *gycf, *gzcf;
03376  
03377  Vpbe *pbe;
03378  Vatom *atom;
03379     Valist *alist;
03380  
03381  pbe = thee->pbe;
03382  alist = thee->pbe->alist;
03383     nx = thee->pmgp->nx;
03384     ny = thee->pmgp->ny;
03385     nz = thee->pmgp->nz;
03386  
03387  xf = thee->xf;
03388  yf = thee->yf;
03389  zf = thee->zf;
03390  
03391  gxcf = thee->gxcf;
03392  gycf = thee->gycf;
03393  gzcf = thee->gzcf;
03394  
03395  /* For each "atom" (only one for bcfl=1), we use the following formula to
03396   * calculate the boundary conditions: 
03397   *    g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
03398   *          * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
03399   *          * 1/d
03400   * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
03401   * We only need to evaluate some of these prefactors once:
03402   *    pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
03403   * which gives the potential as
03404   *    g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a} 
03405   */
03406     eps_w = Vpbe_getSolventDiel(pbe);           /* Dimensionless */
03407     eps_p = Vpbe_getSoluteDiel(pbe);           /* Dimensionless */
03408     T = Vpbe_getTemperature(pbe);               /* K             */
03409  
03410     pre = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
03411     pre = pre*(1.0e10);
03412     
03413  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
03414   * m/A, then we will only need to deal with distances and sizes in
03415   * Angstroms rather than meters.                                       */
03416     xkappa = Vpbe_getXkappa(pbe);              /* A^{-1}        */
03417  
03418  /* Solute size and position */
03419  size = Vpbe_getSoluteRadius(pbe);
03420  position = Vpbe_getSoluteCenter(pbe);
03421  
03422  sdhcharge = 0.0;
03423  for (i=0; i<3; i++) sdhdipole[i] = 0.0;
03424  for (i=0; i<9; i++) sdhquadrupole[i] = 0.0;
03425  
03426  for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
03427   atom = Valist_getAtom(alist, iatom);
03428   apos = Vatom_getPosition(atom);
03429   xr = apos[0] - position[0];
03430   yr = apos[1] - position[1];
03431   zr = apos[2] - position[2];
03432   switch (thee->chargeSrc) {
03433    case VCM_CHARGE:
03434     charge = Vatom_getCharge(atom);
03435     sdhcharge += charge;
03436     sdhdipole[0] += xr * charge; 
03437     sdhdipole[1] += yr * charge; 
03438     sdhdipole[2] += zr * charge; 
03439     traced[0] = xr*xr*charge;
03440     traced[1] = xr*yr*charge;
03441     traced[2] = xr*zr*charge;
03442     traced[3] = yr*xr*charge;
03443     traced[4] = yr*yr*charge;
03444     traced[5] = yr*zr*charge;
03445     traced[6] = zr*xr*charge;
03446     traced[7] = zr*yr*charge;
03447     traced[8] = zr*zr*charge;
03448     qave = (traced[0] + traced[4] + traced[8]) / 3.0;
03449     sdhquadrupole[0] += 1.5*(traced[0] - qave);
03450     sdhquadrupole[1] += 1.5*(traced[1]); 
03451     sdhquadrupole[2] += 1.5*(traced[2]); 
03452     sdhquadrupole[3] += 1.5*(traced[3]); 
03453     sdhquadrupole[4] += 1.5*(traced[4] - qave); 
03454     sdhquadrupole[5] += 1.5*(traced[5]); 
03455     sdhquadrupole[6] += 1.5*(traced[6]); 
03456     sdhquadrupole[7] += 1.5*(traced[7]); 
03457     sdhquadrupole[8] += 1.5*(traced[8] - qave);
03458 #if defined(WITH_TINKER)
03459    case VCM_PERMANENT:
03460     charge = Vatom_getCharge(atom);
03461     dipole = Vatom_getDipole(atom);
03462     quadrupole = Vatom_getQuadrupole(atom);
03463     sdhcharge += charge;
03464     sdhdipole[0] += xr * charge; 
03465     sdhdipole[1] += yr * charge; 
03466     sdhdipole[2] += zr * charge; 
03467     traced[0] = xr*xr*charge;
03468     traced[1] = xr*yr*charge;
03469     traced[2] = xr*zr*charge;
03470     traced[3] = yr*xr*charge;
03471     traced[4] = yr*yr*charge;
03472     traced[5] = yr*zr*charge;
03473     traced[6] = zr*xr*charge;
03474     traced[7] = zr*yr*charge;
03475     traced[8] = zr*zr*charge;
03476     sdhdipole[0] += dipole[0];
03477     sdhdipole[1] += dipole[1];
03478     sdhdipole[2] += dipole[2];
03479     traced[0] += 2.0*xr*dipole[0];
03480     traced[1] += xr*dipole[1] + yr*dipole[0];
03481     traced[2] += xr*dipole[2] + zr*dipole[0];
03482     traced[3] += yr*dipole[0] + xr*dipole[1];
03483     traced[4] += 2.0*yr*dipole[1];
03484     traced[5] += yr*dipole[2] + zr*dipole[1];
03485     traced[6] += zr*dipole[0] + xr*dipole[2];
03486     traced[7] += zr*dipole[1] + yr*dipole[2];
03487     traced[8] += 2.0*zr*dipole[2];
03488     qave = (traced[0] + traced[4] + traced[8]) / 3.0;
03489     sdhquadrupole[0] += 1.5*(traced[0] - qave);
03490     sdhquadrupole[1] += 1.5*(traced[1]); 
03491     sdhquadrupole[2] += 1.5*(traced[2]); 
03492     sdhquadrupole[3] += 1.5*(traced[3]); 
03493     sdhquadrupole[4] += 1.5*(traced[4] - qave); 
03494     sdhquadrupole[5] += 1.5*(traced[5]); 
03495     sdhquadrupole[6] += 1.5*(traced[6]); 
03496     sdhquadrupole[7] += 1.5*(traced[7]); 
03497     sdhquadrupole[8] += 1.5*(traced[8] - qave); 
03498     sdhquadrupole[0] += quadrupole[0];
03499     sdhquadrupole[1] += quadrupole[1];
03500     sdhquadrupole[2] += quadrupole[2];
03501     sdhquadrupole[3] += quadrupole[3];
03502     sdhquadrupole[4] += quadrupole[4];
03503     sdhquadrupole[5] += quadrupole[5];
03504     sdhquadrupole[6] += quadrupole[6];
03505     sdhquadrupole[7] += quadrupole[7];
03506     sdhquadrupole[8] += quadrupole[8];
03507    case VCM_INDUCED:
03508     dipole = Vatom_getInducedDipole(atom);
03509     sdhdipole[0] += dipole[0];
03510     sdhdipole[1] += dipole[1];
03511     sdhdipole[2] += dipole[2];
03512     traced[0] = 2.0*xr*dipole[0];
03513     traced[1] = xr*dipole[1] + yr*dipole[0];
03514     traced[2] = xr*dipole[2] + zr*dipole[0];
03515     traced[3] = yr*dipole[0] + xr*dipole[1];
03516     traced[4] = 2.0*yr*dipole[1];
03517     traced[5] = yr*dipole[2] + zr*dipole[1];
03518     traced[6] = zr*dipole[0] + xr*dipole[2];
03519     traced[7] = zr*dipole[1] + yr*dipole[2];
03520     traced[8] = 2.0*zr*dipole[2];
03521     qave = (traced[0] + traced[4] + traced[8]) / 3.0;
03522     sdhquadrupole[0] += 1.5*(traced[0] - qave);
03523     sdhquadrupole[1] += 1.5*(traced[1]);
03524     sdhquadrupole[2] += 1.5*(traced[2]);
03525     sdhquadrupole[3] += 1.5*(traced[3]);
03526     sdhquadrupole[4] += 1.5*(traced[4] - qave);
03527     sdhquadrupole[5] += 1.5*(traced[5]);
03528     sdhquadrupole[6] += 1.5*(traced[6]);
03529     sdhquadrupole[7] += 1.5*(traced[7]);
03530     sdhquadrupole[8] += 1.5*(traced[8] - qave);
03531    case VCM_NLINDUCED:
03532     dipole = Vatom_getNLInducedDipole(atom);
03533     sdhdipole[0] += dipole[0];
03534     sdhdipole[1] += dipole[1];
03535     sdhdipole[2] += dipole[2];
03536     traced[0] = 2.0*xr*dipole[0];
03537     traced[1] = xr*dipole[1] + yr*dipole[0];
03538     traced[2] = xr*dipole[2] + zr*dipole[0];
03539     traced[3] = yr*dipole[0] + xr*dipole[1];
03540     traced[4] = 2.0*yr*dipole[1];
03541     traced[5] = yr*dipole[2] + zr*dipole[1];
03542     traced[6] = zr*dipole[0] + xr*dipole[2];
03543     traced[7] = zr*dipole[1] + yr*dipole[2];
03544     traced[8] = 2.0*zr*dipole[2];
03545     qave = (traced[0] + traced[4] + traced[8]) / 3.0;
03546     sdhquadrupole[0] += 1.5*(traced[0] - qave);
03547     sdhquadrupole[1] += 1.5*(traced[1]);
03548     sdhquadrupole[2] += 1.5*(traced[2]);
03549     sdhquadrupole[3] += 1.5*(traced[3]);
03550     sdhquadrupole[4] += 1.5*(traced[4] - qave);
03551     sdhquadrupole[5] += 1.5*(traced[5]);
03552     sdhquadrupole[6] += 1.5*(traced[6]);
03553     sdhquadrupole[7] += 1.5*(traced[7]);
03554     sdhquadrupole[8] += 1.5*(traced[8] - qave);
03555 #endif /* if defined(WITH_TINKER) */
03556   }
03557  }
03558  
03559  ux = sdhdipole[0];
03560  uy = sdhdipole[1];
03561     uz = sdhdipole[2];
03562  
03563  /* The factor of 1/3 results from using a 
03564   traceless quadrupole definition. See, for example,
03565   "The Theory of Intermolecular Forces" by A.J. Stone,
03566   Chapter 3. */
03567  qxx = sdhquadrupole[0] / 3.0;
03568  qxy = sdhquadrupole[1] / 3.0;
03569  qxz = sdhquadrupole[2] / 3.0;
03570  qyx = sdhquadrupole[3] / 3.0;
03571  qyy = sdhquadrupole[4] / 3.0;
03572  qyz = sdhquadrupole[5] / 3.0;
03573  qzx = sdhquadrupole[6] / 3.0;
03574  qzy = sdhquadrupole[7] / 3.0;
03575  qzz = sdhquadrupole[8] / 3.0; 
03576  
03577  for(k=0;k<nz;k++){
03578   gpos[2] = zf[k];
03579   for(j=0;j<ny;j++){
03580    gpos[1] = yf[j];
03581    for(i=0;i<nx;i++){
03582     gpos[0] = xf[i];
03583     if(gridPointIsValid(i, j, k, nx, ny, nz)){
03584      xr = gpos[0] - position[0];
03585      yr = gpos[1] - position[1];
03586      zr = gpos[2] - position[2];
03587      
03588      dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
03589      multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
03590      
03591      val = pre*sdhcharge*tensor[0];
03592      val -= pre*ux*xr*tensor[1];
03593      val -= pre*uy*yr*tensor[1];
03594      val -= pre*uz*zr*tensor[1];
03595      val += pre*qxx*xr*xr*tensor[2]; 
03596      val += pre*qyy*yr*yr*tensor[2]; 
03597      val += pre*qzz*zr*zr*tensor[2]; 
03598      val += pre*2.0*qxy*xr*yr*tensor[2]; 
03599      val += pre*2.0*qxz*xr*zr*tensor[2]; 
03600      val += pre*2.0*qyz*yr*zr*tensor[2]; 
03601      
03602      if(i==0){
03603       gxcf[IJKx(j,k,0)] = val;
03604      }
03605      if(i==nx-1){
03606       gxcf[IJKx(j,k,1)] = val;
03607      }
03608      if(j==0){
03609       gycf[IJKy(i,k,0)] = val;
03610      }
03611      if(j==ny-1){
03612       gycf[IJKy(i,k,1)] = val;
03613      }
03614      if(k==0){
03615       gzcf[IJKz(i,j,0)] = val;
03616      }
03617      if(k==nz-1){
03618       gzcf[IJKz(i,j,1)] = val;
03619      }
03620     } /* End grid point is valid */
03621    } /* End i loop */
03622   } /* End j loop */
03623  } /* End k loop */
03624 
03625 }
03626 
03627 VPRIVATE void bcfl_mdh(Vpmg *thee){
03628  
03629  int i,j,k,iatom;
03630  int nx, ny, nz;
03631  
03632  double val, *apos, gpos[3];
03633  double *dipole, *quadrupole;
03634  double size, charge, xkappa, eps_w, eps_p, T, pre1, dist;
03635  
03636  double *xf, *yf, *zf;
03637  double *gxcf, *gycf, *gzcf;
03638  
03639  Vpbe *pbe;
03640  Vatom *atom;
03641     Valist *alist;
03642  
03643  pbe = thee->pbe;
03644  alist = thee->pbe->alist;
03645     nx = thee->pmgp->nx;
03646     ny = thee->pmgp->ny;
03647     nz = thee->pmgp->nz;
03648  
03649  xf = thee->xf;
03650  yf = thee->yf;
03651  zf = thee->zf;
03652  
03653  gxcf = thee->gxcf;
03654  gycf = thee->gycf;
03655  gzcf = thee->gzcf;
03656  
03657  /* For each "atom" (only one for bcfl=1), we use the following formula to
03658   * calculate the boundary conditions: 
03659   *    g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
03660   *          * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
03661   *          * 1/d
03662   * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
03663   * We only need to evaluate some of these prefactors once:
03664   *    pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
03665   * which gives the potential as
03666   *    g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a} 
03667   */
03668     eps_w = Vpbe_getSolventDiel(pbe);           /* Dimensionless */
03669     eps_p = Vpbe_getSoluteDiel(pbe);           /* Dimensionless */
03670     T = Vpbe_getTemperature(pbe);               /* K             */
03671     pre1 = (Vunit_ec)/(4*VPI*Vunit_eps0*eps_w*Vunit_kb*T);
03672  
03673     /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
03674   * m/A, then we will only need to deal with distances and sizes in
03675   * Angstroms rather than meters.                                       */
03676     xkappa = Vpbe_getXkappa(pbe);              /* A^{-1}        */
03677     pre1 = pre1*(1.0e10);
03678     
03679  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
03680   * m/A, then we will only need to deal with distances and sizes in
03681   * Angstroms rather than meters.                                       */
03682     xkappa = Vpbe_getXkappa(pbe);              /* A^{-1}        */
03683  
03684  for(k=0;k<nz;k++){
03685   gpos[2] = zf[k];
03686   for(j=0;j<ny;j++){
03687    gpos[1] = yf[j];
03688    for(i=0;i<nx;i++){
03689     gpos[0] = xf[i];
03690     if(gridPointIsValid(i, j, k, nx, ny, nz)){
03691      
03692      val = 0.0;
03693      
03694      for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
03695       atom = Valist_getAtom(alist, iatom);
03696       apos = Vatom_getPosition(atom);
03697       charge = Vunit_ec*Vatom_getCharge(atom);
03698       size = Vatom_getRadius(atom);
03699 
03700       dist = VSQRT(VSQR(gpos[0]-apos[0]) + VSQR(gpos[1]-apos[1])
03701           + VSQR(gpos[2]-apos[2]));
03702       if (xkappa > VSMALL) {
03703        val += pre1*(charge/dist)*VEXP(-xkappa*(dist-size))
03704        / (1+xkappa*size);
03705       } else {
03706        val += pre1*(charge/dist);
03707       } 
03708       
03709      }
03710      
03711      if(i==0){
03712       gxcf[IJKx(j,k,0)] = val;
03713      }
03714      if(i==nx-1){
03715       gxcf[IJKx(j,k,1)] = val;
03716      }
03717      if(j==0){
03718       gycf[IJKy(i,k,0)] = val;
03719      }
03720      if(j==ny-1){
03721       gycf[IJKy(i,k,1)] = val;
03722      }
03723      if(k==0){
03724       gzcf[IJKz(i,j,0)] = val;
03725      }
03726      if(k==nz-1){
03727       gzcf[IJKz(i,j,1)] = val;
03728      }
03729     } /* End grid point is valid */
03730    } /* End i loop */
03731   } /* End j loop */
03732  } /* End k loop */
03733  
03734 }
03735 
03736 /* ///////////////////////////////////////////////////////////////////////////
03737  // Routine:  bcfl_mem
03738  //
03739  // Purpose:  Increment all the boundary points by the
03740  //           analytic expression for a membrane system in
03741  //           the presence of a membrane potential. This
03742  //           Boundary flag should only be used for systems
03743  //           that explicitly have membranes in the dielectric
03744  //           and solvent maps.
03745  //
03746  //           There should be several input variables add to this
03747  //           function such as membrane potential, membrane thickness
03748  //           and height.
03749  //
03750  // Args:     apos is a 3-vector
03751  //
03752  // Author: Michael Grabe
03754 VPRIVATE void bcfl_mem(double zmem, double L, double eps_m, double eps_w,
03755      double V, double xkappa, double *gxcf, double *gycf, double *gzcf,
03756      double *xf, double *yf, double *zf, int nx, int ny, int nz) {
03757  
03759     /* some definitions                              */
03760     /* L = total length of the membrane              */
03761     /* xkappa = inverse Debeye length                */
03762     /* zmem = z value of membrane bottom (Cytoplasm) */
03763     /* V = electrical potential inside the cell      */
03765     int i, j, k;
03766     double dist, val, z_low, z_high, z_shift;
03767     double A, B, C, D, edge_L, l;
03768     double G, z_0, z_rel;
03769     double gpos[3];
03770  
03771  printf("Here is the value of kappa: %f\n",xkappa);
03772  printf("Here is the value of L: %f\n",L);
03773  printf("Here is the value of zmem: %f\n",zmem);
03774  printf("Here is the value of mdie: %f\n",eps_m);
03775  printf("Here is the value of memv: %f\n",V);
03776  
03777  /* no salt symmetric BC's at +/- infinity */
03778  // B=V/(edge_L - l*(1-eps_w/eps_m));
03779  // A=V + B*edge_L;
03780  // D=eps_w/eps_m*B;
03781  z_low = zmem;     /* this defines the bottom of the membrane */
03782  z_high = zmem + L;  /* this is the top of the membrane */
03783  
03784  /******************************************************/
03785  /* proper boundary conditions for V = 0 extracellular */
03786  /* and psi=-V cytoplasm.                              */
03787  /* Implicit in this formulation is that the membrane  */
03788  /* center be at z = 0                                 */
03789  /******************************************************/
03790  
03791  l=L/2;                     /* half of the membrane length */
03792  z_0 = z_low + l;           /* center of the membrane      */
03793  G=l*eps_w/eps_m*xkappa;
03794  A=-V/2*(1/(G+1))*exp(xkappa*l);
03795  B=V/2;
03796  C=-V/2*eps_w/eps_m*xkappa*(1/(G+1));
03797  D=-A;
03798  /* The analytic expression for the boundary conditions      */
03799  /* had the cytoplasmic surface of the membrane set to zero. */
03800  /* This requires an off-set of the BC equations.            */
03801  
03802     /* the "i" boundaries (dirichlet) */
03803     for (k=0; k<nz; k++) {
03804         gpos[2] = zf[k];
03805         z_rel = gpos[2] - z_0;    /* relative position for BCs */
03806   
03807         for (j=0; j<ny; j++) {
03808    
03809    if (gpos[2] <= z_low) {                       /* cytoplasmic */
03810     
03811     val = A*exp(xkappa*z_rel) + V;
03812     gxcf[IJKx(j,k,0)] += val;    /* assign low side BC */
03813     gxcf[IJKx(j,k,1)] += val;    /* assign high side BC */
03814     
03815    }
03816    
03817    else if (gpos[2] > z_low && gpos[2] <= z_high) {  /* in membrane */
03818     
03819     val = B + C*z_rel;
03820     gxcf[IJKx(j,k,0)] += val;    /* assign low side BC */
03821     gxcf[IJKx(j,k,1)] += val;    /* assign high side BC */
03822     
03823    }
03824    
03825    else if (gpos[2] > z_high)  {                  /* extracellular */
03826     
03827     val = D*exp(-xkappa*z_rel);
03828     gxcf[IJKx(j,k,0)] += val;    /* assign low side BC */
03829     gxcf[IJKx(j,k,1)] += val;    /* assign high side BC */
03830     
03831    }
03832    
03833         }
03834     }
03835  
03836     /* the "j" boundaries (dirichlet) */
03837     for (k=0; k<nz; k++) {
03838         gpos[2] = zf[k];
03839         z_rel = gpos[2] - z_0;
03840         for (i=0; i<nx; i++) {
03841    
03842    if (gpos[2] <= z_low) {                       /* cytoplasmic */
03843     
03844     val = A*exp(xkappa*z_rel) + V;
03845     gycf[IJKy(i,k,0)] += val;    /* assign low side BC */
03846     gycf[IJKy(i,k,1)] += val;    /* assign high side BC */
03847     //printf("%f \n",val);
03848     
03849    }
03850    
03851    else if (gpos[2] > z_low && gpos[2] <= z_high) {  /* in membrane */
03852     
03853     val = B + C*z_rel;
03854     gycf[IJKy(i,k,0)] += val;    /* assign low side BC */
03855     gycf[IJKy(i,k,1)] += val;    /* assign high side BC */
03856     //printf("%f \n",val);
03857     
03858    }
03859    else if (gpos[2] > z_high)  {                  /* extracellular */
03860     
03861     val = D*exp(-xkappa*z_rel);
03862     gycf[IJKy(i,k,0)] += val;    /* assign low side BC */
03863     gycf[IJKy(i,k,1)] += val;    /* assign high side BC */
03864     //printf("%f \n",val);
03865     
03866    }
03867    
03868         }
03869     }
03870  
03871     /* the "k" boundaries (dirichlet) */
03872     for (j=0; j<ny; j++) {
03873         for (i=0; i<nx; i++) {
03874    
03875    /* first assign the bottom boundary */
03876    
03877    gpos[2] = zf[0];
03878    z_rel = gpos[2] - z_0;
03879    
03880    if (gpos[2] <= z_low) {                       /* cytoplasmic */
03881     
03882     val = A*exp(xkappa*z_rel) + V;
03883     gzcf[IJKz(i,j,0)] += val;    /* assign low side BC */
03884     //printf("%f \n",val);
03885     
03886    }
03887    
03888    else if (gpos[2] > z_low && gpos[2] <= z_high) {  /* in membrane */
03889     
03890     val = B + C*z_rel;
03891     gzcf[IJKz(i,j,0)] += val;    /* assign low side BC */
03892     
03893    }
03894    
03895    else if (gpos[2] > z_high)  {                  /* extracellular */
03896     
03897     val = D*exp(-xkappa*z_rel);
03898     gzcf[IJKz(i,j,0)] += val;    /* assign low side BC */
03899     
03900    }
03901    
03902    /* now assign the top boundary */
03903    
03904    gpos[2] = zf[nz-1];
03905    z_rel = gpos[2] - z_0;
03906    
03907    if (gpos[2] <= z_low) {                       /* cytoplasmic */
03908     
03909     val = A*exp(xkappa*z_rel) + V;
03910     gzcf[IJKz(i,j,1)] += val;    /* assign high side BC */
03911     
03912    }
03913    
03914    else if (gpos[2] > z_low && gpos[2] <= z_high) {  /* in membrane */
03915     
03916     val = B + C*z_rel;
03917     gzcf[IJKz(i,j,1)] += val;    /* assign high side BC */
03918     
03919    }
03920    
03921    else if (gpos[2] > z_high)  {                  /* extracellular */
03922     
03923     val = D*exp(-xkappa*z_rel);
03924     gzcf[IJKz(i,j,1)] += val;    /* assign high side BC */
03925     //printf("%f \n",val);
03926     
03927    }
03928    
03929         } 
03930     }
03931 }
03932 
03933 VPRIVATE void bcfl_map(Vpmg *thee){
03934  
03935  Vpbe *pbe;
03936     double position[3], pot, hx, hy, hzed;
03937     int i, j, k, nx, ny, nz, rc;
03938  
03939  
03940     VASSERT(thee != VNULL);
03941  
03942     /* Mesh info */
03943     nx = thee->pmgp->nx;
03944     ny = thee->pmgp->ny;
03945     nz = thee->pmgp->nz;
03946     hx = thee->pmgp->hx;
03947     hy = thee->pmgp->hy;
03948     hzed = thee->pmgp->hzed;
03949  
03950     /* Reset the potential array */
03951     for (i=0; i<(nx*ny*nz); i++) thee->pot[i] = 0.0;
03952  
03953     /* Fill in the source term (atomic potentials) */
03954     Vnm_print(0, "Vpmg_fillco:  filling in source term.\n");
03955     for (k=0; k<nz; k++) {
03956         for (j=0; j<ny; j++) {
03957             for (i=0; i<nx; i++) {
03958                 position[0] = thee->xf[i];
03959                 position[1] = thee->yf[j];
03960                 position[2] = thee->zf[k];
03961     rc = Vgrid_value(thee->potMap, position, &pot);
03962     if (!rc) {
03963      Vnm_print(2, "fillcoChargeMap:  Error -- fell off of potential map at (%g, %g, %g)!\n",
03964          position[0], position[1], position[2]);
03965      VASSERT(0);
03966     }
03967                 thee->pot[IJK(i,j,k)] = pot;
03968             }
03969         }
03970     }
03971  
03972 }
03973 
03974 #if  defined(WITH_TINKER) 
03975 VPRIVATE void bcfl_mdh_tinker(Vpmg *thee){
03976  
03977  int i,j,k,iatom;
03978  int nx, ny, nz;
03979  
03980  double val, *apos, gpos[3], tensor[9];
03981  double *dipole, *quadrupole;
03982  double size, charge, xkappa, eps_w, eps_p, T, pre1, dist;
03983  
03984  double ux,uy,uz,xr,yr,zr;
03985  double qxx,qxy,qxz,qyx,qyy,qyz,qzx,qzy,qzz;
03986  
03987  double *xf, *yf, *zf;
03988  double *gxcf, *gycf, *gzcf;
03989  
03990  Vpbe *pbe;
03991  Vatom *atom;
03992     Valist *alist;
03993  
03994  pbe = thee->pbe;
03995  alist = thee->pbe->alist;
03996     nx = thee->pmgp->nx;
03997     ny = thee->pmgp->ny;
03998     nz = thee->pmgp->nz;
03999  
04000  xf = thee->xf;
04001  yf = thee->yf;
04002  zf = thee->zf;
04003  
04004  gxcf = thee->gxcf;
04005  gycf = thee->gycf;
04006  gzcf = thee->gzcf;
04007  
04008  /* For each "atom" (only one for bcfl=1), we use the following formula to
04009   * calculate the boundary conditions: 
04010   *    g(x) = \frac{q e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
04011   *          * \frac{exp(-xkappa*(d - a))}{1+xkappa*a}
04012   *          * 1/d
04013   * where d = ||x - x_0|| (in m) and a is the size of the atom (in m).
04014   * We only need to evaluate some of these prefactors once:
04015   *    pre1 = \frac{e_c}{4*\pi*\eps_0*\eps_w*k_b*T}
04016   * which gives the potential as
04017   *    g(x) = pre1 * q/d * \frac{exp(-xkappa*(d - a))}{1+xkappa*a} 
04018   */
04019     eps_w = Vpbe_getSolventDiel(pbe);           /* Dimensionless */
04020     eps_p = Vpbe_getSoluteDiel(pbe);           /* Dimensionless */
04021     T = Vpbe_getTemperature(pbe);               /* K             */
04022     pre1 = (Vunit_ec*Vunit_ec)/(4*VPI*Vunit_eps0*Vunit_kb*T);
04023  
04024     /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
04025   * m/A, then we will only need to deal with distances and sizes in
04026   * Angstroms rather than meters.                                       */
04027     xkappa = Vpbe_getXkappa(pbe);              /* A^{-1}        */
04028     pre1 = pre1*(1.0e10);
04029     
04030  /* Finally, if we convert keep xkappa in A^{-1} and scale pre1 by
04031   * m/A, then we will only need to deal with distances and sizes in
04032   * Angstroms rather than meters.                                       */
04033     xkappa = Vpbe_getXkappa(pbe);              /* A^{-1}        */
04034  
04035  for(k=0;k<nz;k++){
04036   gpos[2] = zf[k];
04037   for(j=0;j<ny;j++){
04038    gpos[1] = yf[j];
04039    for(i=0;i<nx;i++){
04040     gpos[0] = xf[i];
04041     if(gridPointIsValid(i, j, k, nx, ny, nz)){
04042      
04043      val = 0.0;
04044      
04045      for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
04046       atom = Valist_getAtom(alist, iatom);
04047       apos = Vatom_getPosition(atom);
04048       size = Vatom_getRadius(atom);
04049       
04050       charge = 0.0;
04051       
04052       dipole = VNULL;
04053       quadrupole = VNULL;
04054       
04055       if (thee->chargeSrc == VCM_PERMANENT) {
04056        charge = Vatom_getCharge(atom);
04057        dipole = Vatom_getDipole(atom);
04058        quadrupole = Vatom_getQuadrupole(atom);
04059       } else if (thee->chargeSrc == VCM_INDUCED) {
04060        dipole = Vatom_getInducedDipole(atom);
04061       } else {
04062        dipole = Vatom_getNLInducedDipole(atom);
04063       }
04064       
04065       ux = dipole[0];
04066       uy = dipole[1];
04067       uz = dipole[2];
04068       
04069       if (quadrupole != VNULL) {
04070        /* The factor of 1/3 results from using a
04071         traceless quadrupole definition. See, for example,
04072         "The Theory of Intermolecular Forces" by A.J. Stone,
04073         Chapter 3. */
04074        qxx = quadrupole[0] / 3.0;
04075        qxy = quadrupole[1] / 3.0;
04076        qxz = quadrupole[2] / 3.0;
04077        qyx = quadrupole[3] / 3.0;
04078        qyy = quadrupole[4] / 3.0;
04079        qyz = quadrupole[5] / 3.0;
04080        qzx = quadrupole[6] / 3.0;
04081        qzy = quadrupole[7] / 3.0;
04082        qzz = quadrupole[8] / 3.0;
04083       } else {
04084        qxx = 0.0;
04085        qxy = 0.0;
04086        qxz = 0.0;
04087        qyx = 0.0;
04088        qyy = 0.0;
04089        qyz = 0.0;
04090        qzx = 0.0;
04091        qzy = 0.0;
04092        qzz = 0.0;
04093       }
04094       
04095       xr = gpos[0] - apos[0];
04096       yr = gpos[1] - apos[1];
04097       zr = gpos[2] - apos[2];
04098       
04099       dist = VSQRT(VSQR(xr) + VSQR(yr) + VSQR(zr));
04100       multipolebc(dist, xkappa, eps_p, eps_w, size, tensor);
04101       
04102       val += pre1*charge*tensor[0];
04103       val -= pre1*ux*xr*tensor[1];
04104       val -= pre1*uy*yr*tensor[1];
04105       val -= pre1*uz*zr*tensor[1];
04106       val += pre1*qxx*xr*xr*tensor[2]; 
04107       val += pre1*qyy*yr*yr*tensor[2]; 
04108       val += pre1*qzz*zr*zr*tensor[2]; 
04109       val += pre1*2.0*qxy*xr*yr*tensor[2]; 
04110       val += pre1*2.0*qxz*xr*zr*tensor[2]; 
04111       val += pre1*2.0*qyz*yr*zr*tensor[2];  
04112       
04113      }
04114      
04115      if(i==0){
04116       gxcf[IJKx(j,k,0)] = val;
04117      }
04118      if(i==nx-1){
04119       gxcf[IJKx(j,k,1)] = val;
04120      }
04121      if(j==0){
04122       gycf[IJKy(i,k,0)] = val;
04123      }
04124      if(j==ny-1){
04125       gycf[IJKy(i,k,1)] = val;
04126      }
04127      if(k==0){
04128       gzcf[IJKz(i,j,0)] = val;
04129      }
04130      if(k==nz-1){
04131       gzcf[IJKz(i,j,1)] = val;
04132      }
04133     } /* End grid point is valid */
04134    } /* End i loop */
04135   } /* End j loop */
04136  } /* End k loop */
04137  
04138 }
04139 #endif
04140 
04141 VPRIVATE void bcCalc(Vpmg *thee){
04142  
04143  int i, j, k;
04144  int nx, ny, nz;
04145     
04146  double zmem, eps_m, Lmem, memv, eps_w, xkappa; 
04147  
04148     nx = thee->pmgp->nx;
04149     ny = thee->pmgp->ny;
04150     nz = thee->pmgp->nz;
04151  
04152     /* Zero out the boundaries */
04153     /* the "i" boundaries (dirichlet) */
04154     for (k=0; k<nz; k++) {
04155         for (j=0; j<ny; j++) {
04156             thee->gxcf[IJKx(j,k,0)] = 0.0;
04157             thee->gxcf[IJKx(j,k,1)] = 0.0;
04158             thee->gxcf[IJKx(j,k,2)] = 0.0;
04159             thee->gxcf[IJKx(j,k,3)] = 0.0;
04160         }
04161     }
04162  
04163     /* the "j" boundaries (dirichlet) */
04164     for (k=0; k<nz; k++) {
04165         for (i=0; i<nx; i++) {
04166             thee->gycf[IJKy(i,k,0)] = 0.0;
04167             thee->gycf[IJKy(i,k,1)] = 0.0;
04168             thee->gycf[IJKy(i,k,2)] = 0.0;
04169             thee->gycf[IJKy(i,k,3)] = 0.0;
04170         }
04171     }
04172  
04173     /* the "k" boundaries (dirichlet) */
04174     for (j=0; j<ny; j++) {
04175         for (i=0; i<nx; i++) {
04176             thee->gzcf[IJKz(i,j,0)] = 0.0;
04177             thee->gzcf[IJKz(i,j,1)] = 0.0;
04178             thee->gzcf[IJKz(i,j,2)] = 0.0;
04179             thee->gzcf[IJKz(i,j,3)] = 0.0;
04180         }
04181     }
04182  
04183  switch (thee->pmgp->bcfl) {
04184    /*  If we have zero boundary conditions, we're done */
04185         case BCFL_ZERO: 
04186             return;
04187   case BCFL_SDH:
04188    bcfl_sdh(thee);
04189    break;
04190   case BCFL_MDH:
04191 #if defined(WITH_TINKER)
04192    bcfl_mdh_tinker(thee);
04193 #else
04194    
04195 #ifdef DEBUG_MAC_OSX_OCL
04196 #include "mach_chud.h"
04197    uint64_t mbeg = mach_absolute_time();
04198    
04199    /* 
04200     * If OpenCL is available we use it, otherwise fall back to
04201     * normal route (CPU multithreaded w/ OpenMP)
04202     */
04203    if (kOpenCLAvailable == 1) bcflnewOpenCL(thee);
04204    else bcflnew(thee);
04205    
04206    mets_(&mbeg, "MDH");
04207 #else
04208    /* bcfl_mdh(thee); */
04209    bcflnew(thee);
04210 #endif /* DEBUG_MAC_OSX_OCL */
04211    
04212 #endif /* WITH_TINKER */
04213    break;
04214   case BCFL_MEM:
04215    
04216    zmem  = Vpbe_getzmem(thee->pbe);
04217    Lmem  = Vpbe_getLmem(thee->pbe);
04218    eps_m = Vpbe_getmembraneDiel(thee->pbe);
04219    memv =  Vpbe_getmemv(thee->pbe);
04220    
04221    eps_w = Vpbe_getSolventDiel(thee->pbe);
04222    xkappa = Vpbe_getXkappa(thee->pbe);
04223    
04224    bcfl_mem(zmem, Lmem, eps_m, eps_w, memv, xkappa,
04225       thee->gxcf, thee->gycf, thee->gzcf,
04226       thee->xf, thee->yf, thee->zf, nx, ny, nz);
04227    break;
04228         case BCFL_UNUSED:
04229             Vnm_print(2, "bcCalc:  Invalid bcfl (%d)!\n", thee->pmgp->bcfl);
04230             VASSERT(0);
04231    break;
04232         case BCFL_FOCUS:
04233             Vnm_print(2, "VPMG::bcCalc -- not appropriate for focusing!\n");
04234             VASSERT(0);
04235    break;
04236   case BCFL_MAP:
04237    bcfl_map(thee);
04238    focusFillBound(thee,VNULL);
04239    break;
04240         default:
04241             Vnm_print(2, "VPMG::bcCalc -- invalid boundary condition \
04242        flag (%d)!\n", thee->pmgp->bcfl);
04243             VASSERT(0);
04244    break;
04245     }
04246 }
04247 
04248 VPRIVATE void fillcoCoefMap(Vpmg *thee) {
04249 
04250     Vpbe *pbe;
04251     double ionstr, position[3], tkappa, eps, pot, hx, hy, hzed;
04252     int i, j, k, nx, ny, nz;
04253     double kappamax;
04254     VASSERT(thee != VNULL);
04255 
04256     /* Get PBE info */
04257     pbe = thee->pbe;
04258     ionstr = Vpbe_getBulkIonicStrength(pbe);
04259 
04260     /* Mesh info */
04261     nx = thee->pmgp->nx;
04262     ny = thee->pmgp->ny;
04263     nz = thee->pmgp->nz;
04264     hx = thee->pmgp->hx;
04265     hy = thee->pmgp->hy;
04266     hzed = thee->pmgp->hzed;
04267 
04268     if ((!thee->useDielXMap) || (!thee->useDielYMap) 
04269   || (!thee->useDielZMap) || ((!thee->useKappaMap) && (ionstr>VPMGSMALL))) {
04270 
04271         Vnm_print(2, "fillcoCoefMap:  You need to use all coefficient maps!\n");
04272         VASSERT(0);
04273 
04274     }
04275 
04276     /* Scale the kappa map to values between 0 and 1 
04277        Thus get the maximum value in the map - this 
04278        is theoretically unnecessary, but a good check.*/
04279     kappamax = -1.00;
04280     for (k=0; k<nz; k++) {
04281         for (j=0; j<ny; j++) {
04282             for (i=0; i<nx; i++) {
04283                 if (ionstr > VPMGSMALL) {
04284                      position[0] = thee->xf[i];
04285                      position[1] = thee->yf[j];
04286                      position[2] = thee->zf[k];
04287                      if (!Vgrid_value(thee->kappaMap, position, &tkappa)) {
04288                          Vnm_print(2, "Vpmg_fillco:  Off kappaMap at:\n");
04289                          Vnm_print(2, "Vpmg_fillco:  (x,y,z) = (%g,%g %g)\n",
04290                                    position[0], position[1], position[2]);
04291                          VASSERT(0);
04292                      }
04293                      if (tkappa > kappamax) {
04294                          kappamax = tkappa;
04295                      }
04296                      if (tkappa < 0.0){
04297                        Vnm_print(2, "Vpmg_fillcoCoefMap: Kappa map less than 0\n");
04298                        Vnm_print(2, "Vpmg_fillcoCoefMap: at (x,y,z) = (%g,%g %g)\n",
04299                                  position[0], position[1], position[2]);
04300                        VASSERT(0);
04301                      }
04302                 }
04303             }
04304         }
04305     }
04306     
04307     if (kappamax > 1.0){
04308       Vnm_print(2, "Vpmg_fillcoCoefMap:  Maximum Kappa value\n");
04309       Vnm_print(2, "%g is greater than 1 - will scale appropriately!\n",
04310                 kappamax);
04311     }
04312     else {
04313       kappamax = 1.0;
04314     }
04315 
04316     for (k=0; k<nz; k++) {
04317         for (j=0; j<ny; j++) {
04318             for (i=0; i<nx; i++) {
04319 
04320                 if (ionstr > VPMGSMALL) {
04321                      position[0] = thee->xf[i];
04322                      position[1] = thee->yf[j];
04323                      position[2] = thee->zf[k];
04324                      if (!Vgrid_value(thee->kappaMap, position, &tkappa)) {
04325                          Vnm_print(2, "Vpmg_fillco:  Off kappaMap at:\n");
04326                          Vnm_print(2, "Vpmg_fillco:  (x,y,z) = (%g,%g %g)\n",
04327                            position[0], position[1], position[2]);
04328                          VASSERT(0);
04329                      }
04330                      if (tkappa < VPMGSMALL) tkappa = 0.0;
04331                      thee->kappa[IJK(i,j,k)] = (tkappa / kappamax);
04332                 }
04333     
04334                 position[0] = thee->xf[i] + 0.5*hx;
04335                 position[1] = thee->yf[j];
04336                 position[2] = thee->zf[k];
04337                 if (!Vgrid_value(thee->dielXMap, position, &eps)) {
04338                     Vnm_print(2, "Vpmg_fillco:  Off dielXMap at:\n");
04339                     Vnm_print(2, "Vpmg_fillco:  (x,y,z) = (%g,%g %g)\n",
04340                       position[0], position[1], position[2]);
04341                     VASSERT(0);
04342                  }
04343                  thee->epsx[IJK(i,j,k)] = eps;
04344         
04345                  position[0] = thee->xf[i];
04346                  position[1] = thee->yf[j] + 0.5*hy;
04347                  position[2] = thee->zf[k];
04348                  if (!Vgrid_value(thee->dielYMap, position, &eps)) {
04349                     Vnm_print(2, "Vpmg_fillco:  Off dielYMap at:\n");
04350                     Vnm_print(2, "Vpmg_fillco:  (x,y,z) = (%g,%g %g)\n",
04351                       position[0], position[1], position[2]);
04352                     VASSERT(0);
04353                  }
04354                  thee->epsy[IJK(i,j,k)] = eps;
04355             
04356                  position[0] = thee->xf[i];
04357                  position[1] = thee->yf[j];
04358                  position[2] = thee->zf[k] + 0.5*hzed;
04359                  if (!Vgrid_value(thee->dielZMap, position, &eps)) {
04360                     Vnm_print(2, "Vpmg_fillco:  Off dielZMap at:\n");
04361                     Vnm_print(2, "Vpmg_fillco:  (x,y,z) = (%g,%g %g)\n",
04362                       position[0], position[1], position[2]);
04363                     VASSERT(0);
04364                  }
04365                  thee->epsz[IJK(i,j,k)] = eps;
04366             }
04367         }
04368     }
04369 }
04370 
04371 VPRIVATE void fillcoCoefMol(Vpmg *thee) {
04372 
04373     if (thee->useDielXMap || thee->useDielYMap || thee->useDielZMap ||
04374       thee->useKappaMap)  {
04375 
04376         fillcoCoefMap(thee);
04377 
04378     } else { 
04379 
04380         fillcoCoefMolDiel(thee); 
04381         fillcoCoefMolIon(thee);
04382 
04383     }
04384 
04385 }
04386 
04387 VPRIVATE void fillcoCoefMolIon(Vpmg *thee) {
04388 
04389     Vacc *acc;
04390     Valist *alist;
04391     Vpbe *pbe;
04392     Vatom *atom;
04393     double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr;
04394     double xlen, ylen, zlen, irad;
04395     double hx, hy, hzed, *apos, arad;
04396     int i, nx, ny, nz, iatom;
04397     Vsurf_Meth surfMeth;
04398 
04399     VASSERT(thee != VNULL);
04400     surfMeth = thee->surfMeth;
04401 
04402     /* Get PBE info */
04403     pbe = thee->pbe;
04404     acc = pbe->acc;
04405     alist = pbe->alist;
04406     irad = Vpbe_getMaxIonRadius(pbe);
04407     ionstr = Vpbe_getBulkIonicStrength(pbe);
04408 
04409     /* Mesh info */
04410     nx = thee->pmgp->nx;
04411     ny = thee->pmgp->ny;
04412     nz = thee->pmgp->nz;
04413     hx = thee->pmgp->hx;
04414     hy = thee->pmgp->hy;
04415     hzed = thee->pmgp->hzed;
04416 
04417     /* Define the total domain size */
04418     xlen = thee->pmgp->xlen;
04419     ylen = thee->pmgp->ylen;
04420     zlen = thee->pmgp->zlen;
04421 
04422     /* Define the min/max dimensions */
04423     xmin = thee->pmgp->xcent - (xlen/2.0);
04424     ymin = thee->pmgp->ycent - (ylen/2.0);
04425     zmin = thee->pmgp->zcent - (zlen/2.0);
04426     xmax = thee->pmgp->xcent + (xlen/2.0);
04427     ymax = thee->pmgp->ycent + (ylen/2.0);
04428     zmax = thee->pmgp->zcent + (zlen/2.0);
04429 
04430     /* This is a floating point parameter related to the non-zero nature of the
04431      * bulk ionic strength.  If the ionic strength is greater than zero; this
04432      * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
04433      * Otherwise, this parameter is set to 0.0 */
04434     if (ionstr > VPMGSMALL) ionmask = 1.0;
04435     else ionmask = 0.0;
04436 
04437     /* Reset the kappa array, marking everything accessible */
04438     for (i=0; i<(nx*ny*nz); i++) thee->kappa[i] = ionmask;
04439 
04440     if (ionstr < VPMGSMALL) return;
04441 
04442     /* Loop through the atoms and set kappa = 0.0 (inaccessible) if a point
04443      * is inside the ion-inflated van der Waals radii */
04444     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
04445 
04446         atom = Valist_getAtom(alist, iatom);
04447         apos = Vatom_getPosition(atom);
04448         arad = Vatom_getRadius(atom);
04449 
04450         if (arad > VSMALL) {
04451 
04452             /* Make sure we're on the grid */
04453             if ((apos[0]<(xmin-irad-arad)) || (apos[0]>(xmax+irad+arad))  || \
04454                 (apos[1]<(ymin-irad-arad)) || (apos[1]>(ymax+irad+arad))  || \
04455                 (apos[2]<(zmin-irad-arad)) || (apos[2]>(zmax+irad+arad))) {
04456                 if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
04457      (thee->pmgp->bcfl != BCFL_MAP)) {
04458                     Vnm_print(2, 
04459     "Vpmg_fillco:  Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
04460                       iatom, apos[0], apos[1], apos[2]);
04461                     Vnm_print(2, "Vpmg_fillco:  xmin = %g, xmax = %g\n", 
04462                       xmin, xmax);
04463                     Vnm_print(2, "Vpmg_fillco:  ymin = %g, ymax = %g\n", 
04464                       ymin, ymax);
04465                     Vnm_print(2, "Vpmg_fillco:  zmin = %g, zmax = %g\n", 
04466                       zmin, zmax);
04467                 }
04468                 fflush(stderr);
04469 
04470             } else { /* if we're on the mesh */
04471     
04472                 /* Mark ions */
04473                 markSphere((irad+arad), apos, 
04474                         nx, ny, nz,
04475                         hx, hy, hzed,
04476                         xmin, ymin, zmin,
04477                         thee->kappa, 0.0);
04478     
04479             } /* endif (on the mesh) */
04480         }
04481     } /* endfor (over all atoms) */
04482 
04483 }
04484 
04485 VPRIVATE void fillcoCoefMolDiel(Vpmg *thee) {
04486 
04487     /* Always call NoSmooth to fill the epsilon arrays */
04488     fillcoCoefMolDielNoSmooth(thee);
04489 
04490     /* Call the smoothing algorithm as needed */
04491     if (thee->surfMeth == VSM_MOLSMOOTH) {
04492         fillcoCoefMolDielSmooth(thee);
04493     }
04494 }
04495 
04496 VPRIVATE void fillcoCoefMolDielNoSmooth(Vpmg *thee) {
04497 
04498     Vacc *acc;
04499     VaccSurf *asurf;
04500     Valist *alist;
04501     Vpbe *pbe;
04502     Vatom *atom;
04503     double xmin, xmax, ymin, ymax, zmin, zmax;
04504     double xlen, ylen, zlen, position[3];
04505     double srad, epsw, epsp, deps, area;
04506     double hx, hy, hzed, *apos, arad;
04507     int i, nx, ny, nz, ntot, iatom, ipt;
04508 
04509     /* Get PBE info */
04510     pbe = thee->pbe;
04511     acc = pbe->acc;
04512     alist = pbe->alist;
04513     srad = Vpbe_getSolventRadius(pbe);
04514     epsw = Vpbe_getSolventDiel(pbe);
04515     epsp = Vpbe_getSoluteDiel(pbe);
04516 
04517     /* Mesh info */
04518     nx = thee->pmgp->nx;
04519     ny = thee->pmgp->ny;
04520     nz = thee->pmgp->nz;
04521     hx = thee->pmgp->hx;
04522     hy = thee->pmgp->hy;
04523     hzed = thee->pmgp->hzed;
04524 
04525     /* Define the total domain size */
04526     xlen = thee->pmgp->xlen;
04527     ylen = thee->pmgp->ylen;
04528     zlen = thee->pmgp->zlen;
04529 
04530     /* Define the min/max dimensions */
04531     xmin = thee->pmgp->xcent - (xlen/2.0);
04532     ymin = thee->pmgp->ycent - (ylen/2.0);
04533     zmin = thee->pmgp->zcent - (zlen/2.0);
04534     xmax = thee->pmgp->xcent + (xlen/2.0);
04535     ymax = thee->pmgp->ycent + (ylen/2.0);
04536     zmax = thee->pmgp->zcent + (zlen/2.0);
04537 
04538     /* Reset the arrays */
04539  ntot = nx*ny*nz;
04540     for (i=0; i<ntot; i++) {
04541         thee->epsx[i] = epsw;
04542         thee->epsy[i] = epsw;
04543         thee->epsz[i] = epsw;
04544     }
04545 
04546     /* Loop through the atoms and set a{123}cf = 0.0 (inaccessible)
04547      * if a point is inside the solvent-inflated van der Waals radii */
04548 #pragma omp parallel for default(shared) private(iatom,atom,apos,arad) 
04549     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
04550 
04551         atom = Valist_getAtom(alist, iatom);
04552         apos = Vatom_getPosition(atom);
04553         arad = Vatom_getRadius(atom);
04554 
04555         /* Make sure we're on the grid */
04556         if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
04557             (apos[1]<=ymin) || (apos[1]>=ymax)  || \
04558             (apos[2]<=zmin) || (apos[2]>=zmax)) {
04559    if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
04560     (thee->pmgp->bcfl != BCFL_MAP)) {
04561                 Vnm_print(2, "Vpmg_fillco:  Atom #%d at (%4.3f, %4.3f,\
04562  %4.3f) is off the mesh (ignoring):\n",
04563                   iatom, apos[0], apos[1], apos[2]);
04564                 Vnm_print(2, "Vpmg_fillco:  xmin = %g, xmax = %g\n", 
04565                   xmin, xmax);
04566                 Vnm_print(2, "Vpmg_fillco:  ymin = %g, ymax = %g\n", 
04567                   ymin, ymax);
04568                 Vnm_print(2, "Vpmg_fillco:  zmin = %g, zmax = %g\n", 
04569                   zmin, zmax);
04570             }
04571             fflush(stderr);
04572 
04573         } else { /* if we're on the mesh */
04574 
04575             if (arad > VSMALL) {
04576                 /* Mark x-shifted dielectric */
04577                 markSphere((arad+srad), apos, 
04578                         nx, ny, nz,
04579                         hx, hy, hzed,
04580                         (xmin+0.5*hx), ymin, zmin,
04581                         thee->epsx, epsp);
04582 
04583                 /* Mark y-shifted dielectric */
04584                 markSphere((arad+srad), apos, 
04585                         nx, ny, nz,
04586                         hx, hy, hzed,
04587                         xmin, (ymin+0.5*hy), zmin,
04588                         thee->epsy, epsp);
04589 
04590                 /* Mark z-shifted dielectric */
04591                 markSphere((arad+srad), apos, 
04592                         nx, ny, nz,
04593                         hx, hy, hzed,
04594                         xmin, ymin, (zmin+0.5*hzed),
04595                         thee->epsz, epsp);
04596             }
04597 
04598         } /* endif (on the mesh) */
04599     } /* endfor (over all atoms) */
04600 
04601  area = Vacc_SASA(acc, srad);
04602 
04603     /* We only need to do the next step for non-zero solvent radii */
04604     if (srad > VSMALL) {
04605   
04606         /* Now loop over the solvent accessible surface points */
04607   
04608 #pragma omp parallel for default(shared) private(iatom,atom,area,asurf,ipt,position)  
04609         for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
04610             atom = Valist_getAtom(alist, iatom);
04611    area = Vacc_atomSASA(acc, srad, atom);
04612    if (area > 0.0 ) {
04613     asurf = Vacc_atomSASPoints(acc, srad, atom);
04614     
04615     /* Use each point on the SAS to reset the solvent accessibility */
04616     /* TODO:  Make sure we're not still wasting time here. */
04617     for (ipt=0; ipt<(asurf->npts); ipt++) {
04618      
04619      position[0] = asurf->xpts[ipt];
04620      position[1] = asurf->ypts[ipt];
04621      position[2] = asurf->zpts[ipt];
04622      
04623      /* Mark x-shifted dielectric */
04624      markSphere(srad, position, 
04625           nx, ny, nz,
04626           hx, hy, hzed,
04627           (xmin+0.5*hx), ymin, zmin,
04628           thee->epsx, epsw);
04629      
04630      /* Mark y-shifted dielectric */
04631      markSphere(srad, position, 
04632           nx, ny, nz,
04633           hx, hy, hzed,
04634           xmin, (ymin+0.5*hy), zmin,
04635           thee->epsy, epsw);
04636      
04637      /* Mark z-shifted dielectric */
04638      markSphere(srad, position, 
04639           nx, ny, nz,
04640           hx, hy, hzed,
04641           xmin, ymin, (zmin+0.5*hzed),
04642           thee->epsz, epsw);
04643      
04644     }
04645    }
04646         }
04647     }
04648 }
04649 
04650 VPRIVATE void fillcoCoefMolDielSmooth(Vpmg *thee) {
04651 
04652   /* This function smoothes using a 9 point method based on
04653      Bruccoleri, et al. J Comput Chem 18 268-276 (1997).  The nine points
04654      used are the shifted grid point and the 8 points that are 1/sqrt(2)
04655      grid spacings away.  The harmonic mean of the 9 points is then used to 
04656      find the overall dielectric value for the point in question. The use of 
04657      this function assumes that the non-smoothed values were placed in the 
04658      dielectric arrays by the fillcoCoefMolDielNoSmooth function.*/
04659 
04660     Vpbe *pbe;
04661     double frac, epsw;
04662     int i, j, k, nx, ny, nz, numpts;
04663 
04664     /* Mesh info */
04665     nx = thee->pmgp->nx;
04666     ny = thee->pmgp->ny;
04667     nz = thee->pmgp->nz;
04668     
04669     pbe = thee->pbe;
04670     epsw = Vpbe_getSolventDiel(pbe);
04671 
04672     /* Copy the existing diel arrays to work arrays */
04673     for (i=0; i<(nx*ny*nz); i++) {
04674         thee->a1cf[i] = thee->epsx[i];
04675         thee->a2cf[i] = thee->epsy[i];
04676         thee->a3cf[i] = thee->epsz[i];
04677         thee->epsx[i] = epsw;
04678         thee->epsy[i] = epsw;
04679         thee->epsz[i] = epsw;
04680     }
04681 
04682     /* Smooth the dielectric values */
04683     for (i=0; i<nx; i++) {
04684         for (j=0; j<ny; j++) {
04685             for (k=0; k<nz; k++) {
04686                 
04687                 /* Get the 8 points that are 1/sqrt(2) grid spacings away */
04688 
04689                 /* Points for the X-shifted array */
04690                 frac = 1.0/thee->a1cf[IJK(i,j,k)];
04691                 frac += 1.0/thee->a2cf[IJK(i,j,k)];
04692                 frac += 1.0/thee->a3cf[IJK(i,j,k)];
04693                 numpts = 3;
04694 
04695                 if (j > 0) {
04696                     frac += 1.0/thee->a2cf[IJK(i,j-1,k)];
04697                     numpts += 1;
04698                 } 
04699                 if (k > 0) {
04700                     frac += 1.0/thee->a3cf[IJK(i,j,k-1)];
04701                     numpts += 1;
04702                 }
04703                 if (i < (nx-1)){
04704                     frac += 1.0/thee->a2cf[IJK(i+1,j,k)];
04705                     frac += 1.0/thee->a3cf[IJK(i+1,j,k)];
04706                     numpts += 2;
04707                     if (j > 0) {
04708                         frac += 1.0/thee->a2cf[IJK(i+1,j-1,k)];
04709                         numpts += 1;
04710                     }
04711                     if (k > 0) {
04712                         frac += 1.0/thee->a3cf[IJK(i+1,j,k-1)];
04713                         numpts += 1;
04714                     }
04715                 } 
04716                 thee->epsx[IJK(i,j,k)] = numpts/frac;
04717                 
04718                 /* Points for the Y-shifted array */
04719                 frac = 1.0/thee->a2cf[IJK(i,j,k)];
04720                 frac += 1.0/thee->a1cf[IJK(i,j,k)];
04721                 frac += 1.0/thee->a3cf[IJK(i,j,k)];
04722                 numpts = 3;
04723 
04724                 if (i > 0) {
04725                     frac += 1.0/thee->a1cf[IJK(i-1,j,k)];
04726                     numpts += 1;
04727                 } 
04728                 if (k > 0) {
04729                     frac += 1.0/thee->a3cf[IJK(i,j,k-1)];
04730                     numpts += 1;
04731                 }
04732                 if (j < (ny-1)){
04733                     frac += 1.0/thee->a1cf[IJK(i,j+1,k)];
04734                     frac += 1.0/thee->a3cf[IJK(i,j+1,k)];
04735                     numpts += 2;
04736                     if (i > 0) {
04737                         frac += 1.0/thee->a1cf[IJK(i-1,j+1,k)];
04738                         numpts += 1;
04739                     }                
04740                     if (k > 0) {
04741                         frac += 1.0/thee->a3cf[IJK(i,j+1,k-1)];
04742                         numpts += 1;
04743                     }
04744                 } 
04745                 thee->epsy[IJK(i,j,k)] = numpts/frac;
04746 
04747                 /* Points for the Z-shifted array */
04748                 frac = 1.0/thee->a3cf[IJK(i,j,k)];
04749                 frac += 1.0/thee->a1cf[IJK(i,j,k)];
04750                 frac += 1.0/thee->a2cf[IJK(i,j,k)];
04751                 numpts = 3;
04752 
04753                 if (i > 0) {
04754                     frac += 1.0/thee->a1cf[IJK(i-1,j,k)];
04755                     numpts += 1;
04756                 } 
04757                 if (j > 0) {
04758                     frac += 1.0/thee->a2cf[IJK(i,j-1,k)];
04759                     numpts += 1;
04760                 }
04761                 if (k < (nz-1)){
04762                     frac += 1.0/thee->a1cf[IJK(i,j,k+1)];
04763                     frac += 1.0/thee->a2cf[IJK(i,j,k+1)];
04764                     numpts += 2;
04765                     if (i > 0) {
04766                         frac += 1.0/thee->a1cf[IJK(i-1,j,k+1)];
04767                         numpts += 1;
04768                     }      
04769                     if (j > 0) {
04770                         frac += 1.0/thee->a2cf[IJK(i,j-1,k+1)];
04771                         numpts += 1;
04772                     }
04773                 } 
04774                 thee->epsz[IJK(i,j,k)] = numpts/frac;
04775             }
04776         }
04777     }
04778 }
04779 
04780 
04781 VPRIVATE void fillcoCoefSpline(Vpmg *thee) {
04782 
04783     Valist *alist;
04784     Vpbe *pbe;
04785     Vatom *atom;
04786     double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
04787     double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
04788     double irad, dx, dy, dz, epsw, epsp, w2i;
04789     double hx, hy, hzed, *apos, arad, sctot2;
04790     double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin, w3i;
04791     double dist, value, sm, sm2;
04792     int i, j, k, nx, ny, nz, iatom;
04793     int imin, imax, jmin, jmax, kmin, kmax;
04794 
04795     VASSERT(thee != VNULL);
04796     splineWin = thee->splineWin;
04797     w2i = 1.0/(splineWin*splineWin);
04798     w3i = 1.0/(splineWin*splineWin*splineWin);
04799 
04800     /* Get PBE info */
04801     pbe = thee->pbe;
04802     alist = pbe->alist;
04803     irad = Vpbe_getMaxIonRadius(pbe);
04804     ionstr = Vpbe_getBulkIonicStrength(pbe);
04805     epsw = Vpbe_getSolventDiel(pbe);
04806     epsp = Vpbe_getSoluteDiel(pbe);
04807 
04808     /* Mesh info */
04809     nx = thee->pmgp->nx;
04810     ny = thee->pmgp->ny;
04811     nz = thee->pmgp->nz;
04812     hx = thee->pmgp->hx;
04813     hy = thee->pmgp->hy;
04814     hzed = thee->pmgp->hzed;
04815 
04816     /* Define the total domain size */
04817     xlen = thee->pmgp->xlen;
04818     ylen = thee->pmgp->ylen;
04819     zlen = thee->pmgp->zlen;
04820 
04821     /* Define the min/max dimensions */
04822     xmin = thee->pmgp->xcent - (xlen/2.0);
04823     ymin = thee->pmgp->ycent - (ylen/2.0);
04824     zmin = thee->pmgp->zcent - (zlen/2.0);
04825     xmax = thee->pmgp->xcent + (xlen/2.0);
04826     ymax = thee->pmgp->ycent + (ylen/2.0);
04827     zmax = thee->pmgp->zcent + (zlen/2.0);
04828 
04829     /* This is a floating point parameter related to the non-zero nature of the
04830      * bulk ionic strength.  If the ionic strength is greater than zero; this
04831      * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
04832      * Otherwise, this parameter is set to 0.0 */
04833     if (ionstr > VPMGSMALL) ionmask = 1.0;
04834     else ionmask = 0.0;
04835 
04836     /* Reset the kappa, epsx, epsy, and epsz arrays */
04837     for (i=0; i<(nx*ny*nz); i++) {
04838         thee->kappa[i] = 1.0;
04839         thee->epsx[i] = 1.0;
04840         thee->epsy[i] = 1.0;
04841         thee->epsz[i] = 1.0;
04842     }
04843 
04844     /* Loop through the atoms and do assign the dielectric */
04845     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
04846 
04847         atom = Valist_getAtom(alist, iatom);
04848         apos = Vatom_getPosition(atom);
04849         arad = Vatom_getRadius(atom);
04850 
04851         /* Make sure we're on the grid */
04852         if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
04853             (apos[1]<=ymin) || (apos[1]>=ymax)  || \
04854             (apos[2]<=zmin) || (apos[2]>=zmax)) {
04855    if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
04856     (thee->pmgp->bcfl != BCFL_MAP)) {
04857                 Vnm_print(2, "Vpmg_fillco:  Atom #%d at (%4.3f, %4.3f,\
04858  %4.3f) is off the mesh (ignoring):\n",
04859                   iatom, apos[0], apos[1], apos[2]);
04860                 Vnm_print(2, "Vpmg_fillco:    xmin = %g, xmax = %g\n", 
04861                   xmin, xmax);
04862                 Vnm_print(2, "Vpmg_fillco:    ymin = %g, ymax = %g\n", 
04863                   ymin, ymax);
04864                 Vnm_print(2, "Vpmg_fillco:    zmin = %g, zmax = %g\n", 
04865                   zmin, zmax);
04866             }
04867             fflush(stderr);
04868 
04869         } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
04870 
04871             /* Convert the atom position to grid reference frame */
04872             position[0] = apos[0] - xmin;
04873             position[1] = apos[1] - ymin;
04874             position[2] = apos[2] - zmin;
04875 
04876             /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
04877              * ASSIGNMENT (Steps #1-3) */
04878             itot = irad + arad + splineWin;
04879             itot2 = VSQR(itot);     
04880             ictot = VMAX2(0, (irad + arad - splineWin));
04881             ictot2 = VSQR(ictot);
04882             stot = arad + splineWin;
04883             stot2 = VSQR(stot);
04884             sctot = VMAX2(0, (arad - splineWin));
04885             sctot2 = VSQR(sctot);
04886 
04887            /* We'll search over grid points which are in the greater of
04888              * these two radii */
04889             rtot = VMAX2(itot, stot);
04890             rtot2 = VMAX2(itot2, stot2);
04891             dx = rtot + 0.5*hx;
04892             dy = rtot + 0.5*hy;
04893             dz = rtot + 0.5*hzed;
04894             imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
04895             imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
04896             jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
04897             jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
04898             kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
04899             kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
04900             for (i=imin; i<=imax; i++) {
04901                 dx2 = VSQR(position[0] - hx*i);
04902                 for (j=jmin; j<=jmax; j++) {
04903                     dy2 = VSQR(position[1] - hy*j);
04904                     for (k=kmin; k<=kmax; k++) {
04905                         dz2 = VSQR(position[2] - k*hzed);
04906 
04907                         /* ASSIGN CCF */
04908                         if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
04909                             dist2 = dz2 + dy2 + dx2;
04910                             if (dist2 >= itot2) {
04911                                 ;
04912                             } 
04913                             if (dist2 <= ictot2) {
04914                                 thee->kappa[IJK(i,j,k)] = 0.0;
04915                             }
04916                             if ((dist2 < itot2) && (dist2 > ictot2)) {
04917                                 dist = VSQRT(dist2);
04918                                 sm = dist - (arad + irad) + splineWin;
04919                                 sm2 = VSQR(sm);
04920                                 value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
04921                                 thee->kappa[IJK(i,j,k)] *= value;
04922                             }
04923                         }
04924 
04925                         /* ASSIGN A1CF */
04926                         if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
04927                             dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
04928                             if (dist2 >= stot2) {
04929                                 thee->epsx[IJK(i,j,k)] *= 1.0;
04930                             } 
04931                             if (dist2 <= sctot2) {
04932                                 thee->epsx[IJK(i,j,k)] = 0.0;
04933                             } 
04934                             if ((dist2 > sctot2) && (dist2 < stot2)) {
04935                                 dist = VSQRT(dist2);
04936                                 sm = dist - arad + splineWin;
04937                                 sm2 = VSQR(sm);
04938                                 value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
04939                                 thee->epsx[IJK(i,j,k)] *= value;
04940                             } 
04941                         }
04942 
04943                         /* ASSIGN A2CF */
04944                         if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
04945                             dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
04946                             if (dist2 >= stot2) {
04947                                 thee->epsy[IJK(i,j,k)] *= 1.0;
04948                             } 
04949                             if (dist2 <= sctot2) {
04950                                 thee->epsy[IJK(i,j,k)] = 0.0;
04951                             }
04952                             if ((dist2 > sctot2) && (dist2 < stot2)) {
04953                                 dist = VSQRT(dist2);
04954                                 sm = dist - arad + splineWin;
04955                                 sm2 = VSQR(sm);
04956                                 value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
04957                                 thee->epsy[IJK(i,j,k)] *= value;
04958                             }
04959                         }
04960 
04961                         /* ASSIGN A3CF */
04962                         if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
04963                             dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
04964                             if (dist2 >= stot2) {
04965                                 thee->epsz[IJK(i,j,k)] *= 1.0;
04966                             } 
04967                             if (dist2 <= sctot2) {
04968                                 thee->epsz[IJK(i,j,k)] = 0.0;
04969                             } 
04970                             if ((dist2 > sctot2) && (dist2 < stot2)) {
04971                                 dist = VSQRT(dist2);
04972                                 sm = dist - arad + splineWin;
04973                                 sm2 = VSQR(sm);
04974                                 value = 0.75*sm2*w2i - 0.25*sm*sm2*w3i;
04975                                 thee->epsz[IJK(i,j,k)] *= value;
04976                             }
04977                         }
04978 
04979 
04980                     } /* k loop */
04981                 } /* j loop */
04982             } /* i loop */
04983         } /* endif (on the mesh) */
04984     } /* endfor (over all atoms) */
04985 
04986     Vnm_print(0, "Vpmg_fillco:  filling coefficient arrays\n");
04987     /* Interpret markings and fill the coefficient arrays */
04988     for (k=0; k<nz; k++) {
04989         for (j=0; j<ny; j++) {
04990             for (i=0; i<nx; i++) {
04991 
04992                 thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
04993                 thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)] 
04994                   + epsp;
04995                 thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)] 
04996                   + epsp;
04997                 thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)] 
04998                   + epsp;
04999 
05000             } /* i loop */
05001         } /* j loop */
05002     } /* k loop */
05003 
05004 }
05005 
05006 VPRIVATE void fillcoCoef(Vpmg *thee) {
05007  
05008     VASSERT(thee != VNULL);
05009  
05010     if (thee->useDielXMap || thee->useDielYMap || 
05011   thee->useDielZMap || thee->useKappaMap) {
05012         fillcoCoefMap(thee);
05013         return;
05014     }
05015 
05016     switch(thee->surfMeth) {
05017         case VSM_MOL:
05018             Vnm_print(0, "fillcoCoef:  Calling fillcoCoefMol...\n");
05019             fillcoCoefMol(thee);
05020             break;
05021         case VSM_MOLSMOOTH:
05022             Vnm_print(0, "fillcoCoef:  Calling fillcoCoefMol...\n");
05023             fillcoCoefMol(thee);
05024             break;
05025         case VSM_SPLINE:
05026             Vnm_print(0, "fillcoCoef:  Calling fillcoCoefSpline...\n");
05027             fillcoCoefSpline(thee);
05028             break;
05029   case VSM_SPLINE3:
05030             Vnm_print(0, "fillcoCoef:  Calling fillcoCoefSpline3...\n");
05031             fillcoCoefSpline3(thee);
05032             break;
05033         case VSM_SPLINE4:
05034             Vnm_print(0, "fillcoCoef:  Calling fillcoCoefSpline4...\n");
05035             fillcoCoefSpline4(thee);
05036             break;
05037         default:
05038             Vnm_print(2, "fillcoCoef:  Invalid surfMeth (%d)!\n",
05039               thee->surfMeth);
05040             VASSERT(0);
05041             break;
05042     }
05043 }
05044 
05045 
05046 VPRIVATE Vrc_Codes fillcoCharge(Vpmg *thee) {
05047  
05048  Vrc_Codes rc;
05049 
05050     VASSERT(thee != VNULL);
05051 
05052     if (thee->useChargeMap) {
05053         return fillcoChargeMap(thee);
05054     }
05055 
05056     switch(thee->chargeMeth) {
05057         case VCM_TRIL:
05058             Vnm_print(0, "fillcoCharge:  Calling fillcoChargeSpline1...\n");
05059             fillcoChargeSpline1(thee);
05060             break;
05061         case VCM_BSPL2:
05062             Vnm_print(0, "fillcoCharge:  Calling fillcoChargeSpline2...\n");
05063             fillcoChargeSpline2(thee);
05064             break;
05065         case VCM_BSPL4:
05066             switch (thee->chargeSrc) {
05067                 case VCM_CHARGE:
05068                     Vnm_print(0, "fillcoCharge: Calling fillcoPermanentMultipole...\n");
05069                     fillcoPermanentMultipole(thee);
05070                     break;
05071 #if defined(WITH_TINKER)
05072                 case VCM_PERMANENT:
05073                     Vnm_print(0, "fillcoCharge: Calling fillcoPermanentMultipole...\n");
05074                     fillcoPermanentMultipole(thee);
05075                     break;
05076                 case VCM_INDUCED:
05077                     Vnm_print(0, "fillcoCharge: Calling fillcoInducedDipole...\n");
05078                     fillcoInducedDipole(thee); 
05079                     break; 
05080                 case VCM_NLINDUCED: 
05081                      Vnm_print(0, "fillcoCharge: Calling fillcoNLInducedDipole...\n"); 
05082                      fillcoNLInducedDipole(thee); 
05083                      break;
05084 #endif /* if defined(WITH_TINKER) */
05085                 default:
05086                     Vnm_print(2, "fillcoCharge:  Invalid chargeSource (%d)!\n",
05087                       thee->chargeSrc);
05088                     return VRC_FAILURE;
05089                     break;
05090             }
05091             break;
05092         default:
05093             Vnm_print(2, "fillcoCharge:  Invalid chargeMeth (%d)!\n",
05094               thee->chargeMeth);
05095             return VRC_FAILURE;
05096             break;
05097     }
05098  
05099  return VRC_SUCCESS;
05100 }
05101 
05102 VPRIVATE Vrc_Codes fillcoChargeMap(Vpmg *thee) {
05103 
05104     Vpbe *pbe;
05105     double position[3], charge, zmagic, hx, hy, hzed;
05106     int i, j, k, nx, ny, nz, rc;
05107 
05108 
05109     VASSERT(thee != VNULL);
05110 
05111     /* Get PBE info */
05112     pbe = thee->pbe;
05113     zmagic = Vpbe_getZmagic(pbe);
05114 
05115     /* Mesh info */
05116     nx = thee->pmgp->nx;
05117     ny = thee->pmgp->ny;
05118     nz = thee->pmgp->nz;
05119     hx = thee->pmgp->hx;
05120     hy = thee->pmgp->hy;
05121     hzed = thee->pmgp->hzed;
05122    
05123     /* Reset the charge array */
05124     for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
05125 
05126     /* Fill in the source term (atomic charges) */
05127     Vnm_print(0, "Vpmg_fillco:  filling in source term.\n");
05128     for (k=0; k<nz; k++) {
05129         for (j=0; j<ny; j++) {
05130             for (i=0; i<nx; i++) {
05131                 position[0] = thee->xf[i];
05132                 position[1] = thee->yf[j];
05133                 position[2] = thee->zf[k];
05134     rc = Vgrid_value(thee->chargeMap, position, &charge);
05135     if (!rc) {
05136      Vnm_print(2, "fillcoChargeMap:  Error -- fell off of charge map at (%g, %g, %g)!\n",
05137         position[0], position[1], position[2]);
05138      return VRC_FAILURE;
05139     }
05140     /* Scale the charge to internal units */
05141                 charge = charge*zmagic;
05142                 thee->charge[IJK(i,j,k)] = charge;
05143             }
05144         }
05145     }
05146  
05147  return VRC_SUCCESS;
05148 }
05149 
05150 VPRIVATE void fillcoChargeSpline1(Vpmg *thee) {
05151 
05152     Valist *alist;
05153     Vpbe *pbe;
05154     Vatom *atom;
05155     double xmin, xmax, ymin, ymax, zmin, zmax;
05156     double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
05157     double charge, dx, dy, dz, zmagic, hx, hy, hzed, *apos;
05158     int i, nx, ny, nz, iatom, ihi, ilo, jhi, jlo, khi, klo;
05159 
05160 
05161     VASSERT(thee != VNULL);
05162 
05163     /* Get PBE info */
05164     pbe = thee->pbe;
05165     alist = pbe->alist;
05166     zmagic = Vpbe_getZmagic(pbe);
05167 
05168     /* Mesh info */
05169     nx = thee->pmgp->nx;
05170     ny = thee->pmgp->ny;
05171     nz = thee->pmgp->nz;
05172     hx = thee->pmgp->hx;
05173     hy = thee->pmgp->hy;
05174     hzed = thee->pmgp->hzed;
05175    
05176     /* Define the total domain size */
05177     xlen = thee->pmgp->xlen;
05178     ylen = thee->pmgp->ylen;
05179     zlen = thee->pmgp->zlen;
05180 
05181     /* Define the min/max dimensions */
05182     xmin = thee->pmgp->xcent - (xlen/2.0);
05183     ymin = thee->pmgp->ycent - (ylen/2.0);
05184     zmin = thee->pmgp->zcent - (zlen/2.0);
05185     xmax = thee->pmgp->xcent + (xlen/2.0);
05186     ymax = thee->pmgp->ycent + (ylen/2.0);
05187     zmax = thee->pmgp->zcent + (zlen/2.0);
05188 
05189     /* Reset the charge array */
05190     for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
05191 
05192     /* Fill in the source term (atomic charges) */
05193     Vnm_print(0, "Vpmg_fillco:  filling in source term.\n");
05194     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
05195 
05196         atom = Valist_getAtom(alist, iatom);
05197         apos = Vatom_getPosition(atom);
05198         charge = Vatom_getCharge(atom);
05199 
05200         /* Make sure we're on the grid */
05201         if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
05202             (apos[1]<=ymin) || (apos[1]>=ymax)  || \
05203             (apos[2]<=zmin) || (apos[2]>=zmax)) {
05204    if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
05205     (thee->pmgp->bcfl != BCFL_MAP)) {
05206                 Vnm_print(2, "Vpmg_fillco:  Atom #%d at (%4.3f, %4.3f, \
05207 %4.3f) is off the mesh (ignoring):\n",
05208                   iatom, apos[0], apos[1], apos[2]);
05209                 Vnm_print(2, "Vpmg_fillco:    xmin = %g, xmax = %g\n", 
05210                   xmin, xmax);
05211                 Vnm_print(2, "Vpmg_fillco:    ymin = %g, ymax = %g\n", 
05212                   ymin, ymax);
05213                 Vnm_print(2, "Vpmg_fillco:    zmin = %g, zmax = %g\n", 
05214                   zmin, zmax);
05215             }
05216             fflush(stderr);
05217         } else {
05218 
05219             /* Convert the atom position to grid reference frame */
05220             position[0] = apos[0] - xmin;
05221             position[1] = apos[1] - ymin;
05222             position[2] = apos[2] - zmin;
05223 
05224             /* Scale the charge to be a delta function */
05225             charge = charge*zmagic/(hx*hy*hzed);
05226 
05227             /* Figure out which vertices we're next to */
05228             ifloat = position[0]/hx;
05229             jfloat = position[1]/hy;
05230             kfloat = position[2]/hzed;
05231 
05232             ihi = (int)ceil(ifloat);
05233             ilo = (int)floor(ifloat);
05234             jhi = (int)ceil(jfloat);
05235             jlo = (int)floor(jfloat);
05236             khi = (int)ceil(kfloat);
05237             klo = (int)floor(kfloat);
05238 
05239             /* Now assign fractions of the charge to the nearby verts */
05240             dx = ifloat - (double)(ilo);
05241             dy = jfloat - (double)(jlo);
05242             dz = kfloat - (double)(klo);
05243             thee->charge[IJK(ihi,jhi,khi)] += (dx*dy*dz*charge);
05244             thee->charge[IJK(ihi,jlo,khi)] += (dx*(1.0-dy)*dz*charge);
05245             thee->charge[IJK(ihi,jhi,klo)] += (dx*dy*(1.0-dz)*charge);
05246             thee->charge[IJK(ihi,jlo,klo)] += (dx*(1.0-dy)*(1.0-dz)*charge);
05247             thee->charge[IJK(ilo,jhi,khi)] += ((1.0-dx)*dy*dz *charge);
05248             thee->charge[IJK(ilo,jlo,khi)] += ((1.0-dx)*(1.0-dy)*dz *charge);
05249             thee->charge[IJK(ilo,jhi,klo)] += ((1.0-dx)*dy*(1.0-dz)*charge);
05250             thee->charge[IJK(ilo,jlo,klo)] += ((1.0-dx)*(1.0-dy)*(1.0-dz)*charge);
05251         } /* endif (on the mesh) */
05252     } /* endfor (each atom) */
05253 }
05254 
05255 VPRIVATE double bspline2(double x) {
05256 
05257     double m2m, m2, m3; 
05258 
05259     if ((x >= 0.0) && (x <= 2.0)) m2m = 1.0 - VABS(x - 1.0);
05260     else m2m = 0.0;
05261     if ((x >= 1.0) && (x <= 3.0)) m2 = 1.0 - VABS(x - 2.0);
05262     else m2 = 0.0;
05263 
05264     if ((x >= 0.0) && (x <= 3.0)) m3 = 0.5*x*m2m + 0.5*(3.0-x)*m2;
05265     else m3 = 0.0;
05266 
05267     return m3;
05268 
05269 }
05270 
05271 VPRIVATE double dbspline2(double x) {
05272 
05273     double m2m, m2, dm3;
05274 
05275     if ((x >= 0.0) && (x <= 2.0)) m2m = 1.0 - VABS(x - 1.0);
05276     else m2m = 0.0;
05277     if ((x >= 1.0) && (x <= 3.0)) m2 = 1.0 - VABS(x - 2.0);
05278     else m2 = 0.0;
05279 
05280     dm3 = m2m - m2;
05281 
05282     return dm3;
05283 
05284 }
05285 
05286 
05287 VPRIVATE void fillcoChargeSpline2(Vpmg *thee) {
05288 
05289     Valist *alist;
05290     Vpbe *pbe;
05291     Vatom *atom;
05292     double xmin, xmax, ymin, ymax, zmin, zmax, zmagic;
05293     double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
05294     double charge, hx, hy, hzed, *apos, mx, my, mz;
05295     int i, ii, jj, kk, nx, ny, nz, iatom; 
05296     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
05297 
05298 
05299     VASSERT(thee != VNULL);
05300 
05301     /* Get PBE info */
05302     pbe = thee->pbe;
05303     alist = pbe->alist;
05304     zmagic = Vpbe_getZmagic(pbe);
05305 
05306     /* Mesh info */
05307     nx = thee->pmgp->nx;
05308     ny = thee->pmgp->ny;
05309     nz = thee->pmgp->nz;
05310     hx = thee->pmgp->hx;
05311     hy = thee->pmgp->hy;
05312     hzed = thee->pmgp->hzed;
05313    
05314     /* Define the total domain size */
05315     xlen = thee->pmgp->xlen;
05316     ylen = thee->pmgp->ylen;
05317     zlen = thee->pmgp->zlen;
05318 
05319     /* Define the min/max dimensions */
05320     xmin = thee->pmgp->xcent - (xlen/2.0);
05321     ymin = thee->pmgp->ycent - (ylen/2.0);
05322     zmin = thee->pmgp->zcent - (zlen/2.0);
05323     xmax = thee->pmgp->xcent + (xlen/2.0);
05324     ymax = thee->pmgp->ycent + (ylen/2.0);
05325     zmax = thee->pmgp->zcent + (zlen/2.0);
05326 
05327     /* Reset the charge array */
05328     for (i=0; i<(nx*ny*nz); i++) thee->charge[i] = 0.0;
05329 
05330     /* Fill in the source term (atomic charges) */
05331     Vnm_print(0, "Vpmg_fillco:  filling in source term.\n");
05332     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
05333 
05334         atom = Valist_getAtom(alist, iatom);
05335         apos = Vatom_getPosition(atom);
05336         charge = Vatom_getCharge(atom);
05337 
05338         /* Make sure we're on the grid */
05339         if ((apos[0]<=(xmin-hx)) || (apos[0]>=(xmax+hx))  || \
05340             (apos[1]<=(ymin-hy)) || (apos[1]>=(ymax+hy))  || \
05341             (apos[2]<=(zmin-hzed)) || (apos[2]>=(zmax+hzed))) {
05342    if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
05343     (thee->pmgp->bcfl != BCFL_MAP)) {
05344                 Vnm_print(2, "Vpmg_fillco:  Atom #%d at (%4.3f, %4.3f, \
05345 %4.3f) is off the mesh (for cubic splines!!) (ignoring this atom):\n",
05346                   iatom, apos[0], apos[1], apos[2]);
05347                 Vnm_print(2, "Vpmg_fillco:    xmin = %g, xmax = %g\n", 
05348                   xmin, xmax);
05349                 Vnm_print(2, "Vpmg_fillco:    ymin = %g, ymax = %g\n", 
05350                   ymin, ymax);
05351                 Vnm_print(2, "Vpmg_fillco:    zmin = %g, zmax = %g\n", 
05352                   zmin, zmax);
05353             }
05354             fflush(stderr);
05355         } else {
05356 
05357             /* Convert the atom position to grid reference frame */
05358             position[0] = apos[0] - xmin;
05359             position[1] = apos[1] - ymin;
05360             position[2] = apos[2] - zmin;
05361 
05362             /* Scale the charge to be a delta function */
05363             charge = charge*zmagic/(hx*hy*hzed);
05364 
05365             /* Figure out which vertices we're next to */
05366             ifloat = position[0]/hx;
05367             jfloat = position[1]/hy;
05368             kfloat = position[2]/hzed;
05369 
05370             ip1   = (int)ceil(ifloat);
05371             ip2   = ip1 + 1;
05372             im1   = (int)floor(ifloat);
05373             im2   = im1 - 1;
05374             jp1   = (int)ceil(jfloat);
05375             jp2   = jp1 + 1;
05376             jm1   = (int)floor(jfloat);
05377             jm2   = jm1 - 1;
05378             kp1   = (int)ceil(kfloat);
05379             kp2   = kp1 + 1;
05380             km1   = (int)floor(kfloat);
05381             km2   = km1 - 1;
05382 
05383             /* This step shouldn't be necessary, but it saves nasty debugging
05384              * later on if something goes wrong */
05385             ip2 = VMIN2(ip2,nx-1);
05386             ip1 = VMIN2(ip1,nx-1);
05387             im1 = VMAX2(im1,0);
05388             im2 = VMAX2(im2,0);
05389             jp2 = VMIN2(jp2,ny-1);
05390             jp1 = VMIN2(jp1,ny-1);
05391             jm1 = VMAX2(jm1,0);
05392             jm2 = VMAX2(jm2,0);
05393             kp2 = VMIN2(kp2,nz-1);
05394             kp1 = VMIN2(kp1,nz-1);
05395             km1 = VMAX2(km1,0);
05396             km2 = VMAX2(km2,0);
05397 
05398             /* Now assign fractions of the charge to the nearby verts */
05399             for (ii=im2; ii<=ip2; ii++) {
05400                 mx = bspline2(VFCHI(ii,ifloat));
05401                 for (jj=jm2; jj<=jp2; jj++) {
05402                     my = bspline2(VFCHI(jj,jfloat));
05403                     for (kk=km2; kk<=kp2; kk++) {
05404                         mz = bspline2(VFCHI(kk,kfloat));
05405                         thee->charge[IJK(ii,jj,kk)] += (charge*mx*my*mz);
05406                     }
05407                 }
05408             }
05409 
05410         } /* endif (on the mesh) */
05411     } /* endfor (each atom) */
05412 }
05413 
05414 VPUBLIC int Vpmg_fillco(Vpmg *thee, 
05415       Vsurf_Meth surfMeth, double splineWin, Vchrg_Meth chargeMeth,
05416       int useDielXMap,   Vgrid *dielXMap, 
05417       int useDielYMap,   Vgrid *dielYMap, 
05418       int useDielZMap,   Vgrid *dielZMap, 
05419       int useKappaMap,   Vgrid *kappaMap, 
05420       int usePotMap,    Vgrid *potMap, 
05421       int useChargeMap,  Vgrid *chargeMap) {
05422  
05423     Vpbe *pbe;
05424     double xmin, xmax, ymin, ymax, zmin, zmax;
05425     double xlen, ylen, zlen, hx, hy, hzed;
05426     double epsw, epsp, ionstr;
05427     int i, nx, ny, nz, islap;
05428  Vrc_Codes rc;
05429 
05430     if (thee == VNULL) {
05431         Vnm_print(2, "Vpmg_fillco:  got NULL thee!\n");
05432         return 0;
05433     }
05434 
05435     thee->surfMeth = surfMeth;
05436     thee->splineWin = splineWin;
05437     thee->chargeMeth = chargeMeth;
05438     thee->useDielXMap = useDielXMap;
05439     if (thee->useDielXMap) thee->dielXMap = dielXMap;
05440     thee->useDielYMap = useDielYMap;
05441     if (thee->useDielYMap) thee->dielYMap = dielYMap;
05442     thee->useDielZMap = useDielZMap;
05443     if (thee->useDielZMap) thee->dielZMap = dielZMap;
05444     thee->useKappaMap = useKappaMap;
05445     if (thee->useKappaMap) thee->kappaMap = kappaMap;
05446  thee->usePotMap = usePotMap;
05447     if (thee->usePotMap) thee->potMap = potMap;
05448     thee->useChargeMap = useChargeMap;
05449     if (thee->useChargeMap) thee->chargeMap = chargeMap;
05450 
05451     /* Get PBE info */
05452     pbe = thee->pbe;
05453     ionstr = Vpbe_getBulkIonicStrength(pbe);
05454     epsw = Vpbe_getSolventDiel(pbe);
05455     epsp = Vpbe_getSoluteDiel(pbe);
05456 
05457     /* Mesh info */
05458     nx = thee->pmgp->nx;
05459     ny = thee->pmgp->ny;
05460     nz = thee->pmgp->nz;
05461     hx = thee->pmgp->hx;
05462     hy = thee->pmgp->hy;
05463     hzed = thee->pmgp->hzed;
05464    
05465     /* Define the total domain size */
05466     xlen = thee->pmgp->xlen;
05467     ylen = thee->pmgp->ylen;
05468     zlen = thee->pmgp->zlen;
05469 
05470     /* Define the min/max dimensions */
05471     xmin = thee->pmgp->xcent - (xlen/2.0);
05472     thee->pmgp->xmin = xmin;
05473     ymin = thee->pmgp->ycent - (ylen/2.0);
05474     thee->pmgp->ymin = ymin;
05475     zmin = thee->pmgp->zcent - (zlen/2.0);
05476     thee->pmgp->zmin = zmin;
05477     xmax = thee->pmgp->xcent + (xlen/2.0);
05478     thee->pmgp->xmax = xmax;
05479     ymax = thee->pmgp->ycent + (ylen/2.0);
05480     thee->pmgp->ymax = ymax;
05481     zmax = thee->pmgp->zcent + (zlen/2.0);
05482     thee->pmgp->zmax = zmax;
05483     thee->rparm[2] = xmin;
05484     thee->rparm[3] = xmax;
05485     thee->rparm[4] = ymin;
05486     thee->rparm[5] = ymax;
05487     thee->rparm[6] = zmin;
05488     thee->rparm[7] = zmax;
05489 
05490     /* This is a flag that gets set if the operator is a simple Laplacian;
05491      * i.e., in the case of a homogenous dielectric and zero ionic strength
05492   * The operator cannot be a simple Laplacian if maps are read in. */
05493  if(thee->useDielXMap || thee->useDielYMap || thee->useDielZMap ||
05494     thee->useKappaMap || thee->usePotMap){
05495   islap = 0;
05496  }else if ( (ionstr < VPMGSMALL) && (VABS(epsp-epsw) < VPMGSMALL) ){
05497   islap = 1;
05498  }else{
05499   islap = 0;
05500  }
05501 
05502     /* Fill the mesh point coordinate arrays */
05503     for (i=0; i<nx; i++) thee->xf[i] = xmin + i*hx;
05504     for (i=0; i<ny; i++) thee->yf[i] = ymin + i*hy;
05505     for (i=0; i<nz; i++) thee->zf[i] = zmin + i*hzed;
05506 
05507     /* Reset the tcf array */
05508     for (i=0; i<(nx*ny*nz); i++) thee->tcf[i] = 0.0;
05509 
05510     /* Fill in the source term (atomic charges) */
05511     Vnm_print(0, "Vpmg_fillco:  filling in source term.\n");
05512     rc = fillcoCharge(thee);
05513  switch(rc) {
05514   case VRC_SUCCESS:
05515    break;
05516   case VRC_WARNING:
05517    Vnm_print(2, "Vpmg_fillco:  non-fatal errors while filling charge map!\n");
05518    break;
05519   case VRC_FAILURE:
05520    Vnm_print(2, "Vpmg_fillco:  fatal errors while filling charge map!\n");
05521    return 0;
05522    break;
05523  }
05524  
05525     /* THE FOLLOWING NEEDS TO BE DONE IF WE'RE NOT USING A SIMPLE LAPLACIAN
05526      * OPERATOR */
05527     if (!islap) {
05528         Vnm_print(0, "Vpmg_fillco:  marking ion and solvent accessibility.\n");
05529         fillcoCoef(thee);
05530         Vnm_print(0, "Vpmg_fillco:  done filling coefficient arrays\n");
05531 
05532     } else { /* else (!islap) ==> It's a Laplacian operator! */
05533 
05534         for (i=0; i<(nx*ny*nz); i++) {
05535             thee->kappa[i] = 0.0;
05536             thee->epsx[i] = epsp;
05537             thee->epsy[i] = epsp;
05538             thee->epsz[i] = epsp;
05539         }
05540 
05541     } /* endif (!islap) */
05542 
05543     /* Fill the boundary arrays (except when focusing, bcfl = 4) */
05544     if (thee->pmgp->bcfl != BCFL_FOCUS) {
05545         Vnm_print(0, "Vpmg_fillco:  filling boundary arrays\n");
05546         bcCalc(thee);
05547         Vnm_print(0, "Vpmg_fillco:  done filling boundary arrays\n");
05548     }
05549 
05550     thee->filled = 1;
05551 
05552     return 1;
05553 }
05554 
05555 
05556 VPUBLIC int Vpmg_force(Vpmg *thee, double *force, int atomID, 
05557   Vsurf_Meth srfm, Vchrg_Meth chgm) {
05558 
05559     int rc = 1;
05560     double qfF[3];                  /* Charge-field force */  
05561     double dbF[3];                  /* Dielectric boundary force */
05562     double ibF[3];                  /* Ion boundary force */
05563     double npF[3];                  /* Non-polar boundary force */
05564 
05565     VASSERT(thee != VNULL);
05566  
05567     rc = rc && Vpmg_dbForce(thee, qfF, atomID, srfm);
05568     rc = rc && Vpmg_ibForce(thee, dbF, atomID, srfm); 
05569     rc = rc && Vpmg_qfForce(thee, ibF, atomID, chgm); 
05570 
05571     force[0] = qfF[0] + dbF[0] + ibF[0];
05572     force[1] = qfF[1] + dbF[1] + ibF[1];
05573     force[2] = qfF[2] + dbF[2] + ibF[2];
05574 
05575     return rc;
05576 
05577 }
05578 
05579 VPUBLIC int Vpmg_ibForce(Vpmg *thee, double *force, int atomID, 
05580   Vsurf_Meth srfm) {
05581 
05582     Valist *alist;
05583     Vacc *acc;
05584     Vpbe *pbe;
05585     Vatom *atom;
05586 
05587     double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
05588     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
05589     double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
05590     double izmagic;
05591     int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
05592  
05593  /* For nonlinear forces */
05594  int ichop, nchop, nion, m;
05595     double ionConc[MAXION], ionRadii[MAXION], ionQ[MAXION], ionstr;
05596    
05597     VASSERT(thee != VNULL);
05598    
05599     acc = thee->pbe->acc;
05600     atom = Valist_getAtom(thee->pbe->alist, atomID);
05601     apos = Vatom_getPosition(atom);
05602     arad = Vatom_getRadius(atom);
05603 
05604     /* Reset force */
05605     force[0] = 0.0;
05606     force[1] = 0.0;
05607     force[2] = 0.0;
05608 
05609     /* Check surface definition */
05610     if ((srfm != VSM_SPLINE) && (srfm!=VSM_SPLINE3) && (srfm!=VSM_SPLINE4)) {
05611         Vnm_print(2, "Vpmg_ibForce:  Forces *must* be calculated with \
05612 spline-based surfaces!\n");
05613         Vnm_print(2, "Vpmg_ibForce:  Skipping ionic boundary force \
05614 calculation!\n");
05615         return 0;
05616     }
05617 
05618     /* If we aren't in the current position, then we're done */
05619     if (atom->partID == 0) return 1;
05620 
05621     /* Get PBE info */
05622     pbe = thee->pbe;
05623     acc = pbe->acc;
05624     alist = pbe->alist;
05625     irad = Vpbe_getMaxIonRadius(pbe);
05626     zkappa2 = Vpbe_getZkappa2(pbe);
05627     izmagic = 1.0/Vpbe_getZmagic(pbe);
05628  
05629  ionstr = Vpbe_getBulkIonicStrength(pbe);
05630     Vpbe_getIons(pbe, &nion, ionConc, ionRadii, ionQ);
05631  
05632     /* Mesh info */
05633     nx = thee->pmgp->nx;
05634     ny = thee->pmgp->ny;
05635     nz = thee->pmgp->nz;
05636     hx = thee->pmgp->hx;
05637     hy = thee->pmgp->hy;
05638     hzed = thee->pmgp->hzed;
05639     xlen = thee->pmgp->xlen;
05640     ylen = thee->pmgp->ylen;
05641     zlen = thee->pmgp->zlen;
05642     xmin = thee->pmgp->xmin;
05643     ymin = thee->pmgp->ymin;
05644     zmin = thee->pmgp->zmin;
05645     xmax = thee->pmgp->xmax;
05646     ymax = thee->pmgp->ymax;
05647     zmax = thee->pmgp->zmax;
05648 
05649     /* Sanity check: there is no force if there is zero ionic strength */
05650     if (zkappa2 < VPMGSMALL) {
05651 #ifndef VAPBSQUIET
05652         Vnm_print(2, "Vpmg_ibForce:  No force for zero ionic strength!\n");
05653 #endif
05654         return 1;
05655     }
05656 
05657     /* Make sure we're on the grid */
05658     if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
05659       (apos[1]<=ymin) || (apos[1]>=ymax)  || \
05660       (apos[2]<=zmin) || (apos[2]>=zmax)) {
05661   if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
05662    (thee->pmgp->bcfl != BCFL_MAP)) {
05663             Vnm_print(2, "Vpmg_ibForce:  Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
05664                   atom, apos[0], apos[1], apos[2]);
05665             Vnm_print(2, "Vpmg_ibForce:    xmin = %g, xmax = %g\n",
05666               xmin, xmax);
05667             Vnm_print(2, "Vpmg_ibForce:    ymin = %g, ymax = %g\n",
05668               ymin, ymax);
05669             Vnm_print(2, "Vpmg_ibForce:    zmin = %g, zmax = %g\n",
05670               zmin, zmax);
05671         }
05672         fflush(stderr);
05673     } else {
05674 
05675         /* Convert the atom position to grid reference frame */
05676         position[0] = apos[0] - xmin;
05677         position[1] = apos[1] - ymin;
05678         position[2] = apos[2] - zmin;
05679 
05680         /* Integrate over points within this atom's (inflated) radius */
05681         rtot = (irad + arad + thee->splineWin);
05682         rtot2 = VSQR(rtot);
05683         dx = rtot + 0.5*hx;
05684         imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
05685         imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
05686         for (i=imin; i<=imax; i++) { 
05687             dx2 = VSQR(position[0] - hx*i);
05688             if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
05689             else dy = 0.5*hy;
05690             jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
05691             jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
05692             for (j=jmin; j<=jmax; j++) { 
05693                 dy2 = VSQR(position[1] - hy*j);
05694                 if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
05695                 else dz = 0.5*hzed;
05696                 kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
05697                 kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
05698                 for (k=kmin; k<=kmax; k++) {
05699                     dz2 = VSQR(k*hzed - position[2]);
05700                     /* See if grid point is inside ivdw radius and set kappa
05701                      * accordingly (do spline assignment here) */
05702                     if ((dz2 + dy2 + dx2) <= rtot2) {
05703                         gpos[0] = i*hx + xmin;
05704                         gpos[1] = j*hy + ymin;
05705                         gpos[2] = k*hzed + zmin;
05706       
05707       /* Select the correct function based on the surface definition 
05708        * (now including the 7th order polynomial) */
05709       Vpmg_splineSelect(srfm,acc, gpos,thee->splineWin, irad, atom, tgrad);
05710       
05711                         if (thee->pmgp->nonlin) {
05712                             /* Nonlinear forces */
05713                             fmag = 0.0;
05714                             nchop = 0;
05715                             for (m=0; m<nion; m++) {
05716                                 fmag += (thee->kappa[IJK(i,j,k)])*ionConc[m]*(Vcap_exp(-ionQ[m]*thee->u[IJK(i,j,k)], &ichop)-1.0)/ionstr;
05717                                 nchop += ichop;
05718                             }
05719        /*          if (nchop > 0) Vnm_print(2, "Vpmg_ibForece:  Chopped EXP %d times!\n", nchop);*/
05720                             force[0] += (zkappa2*fmag*tgrad[0]);
05721                             force[1] += (zkappa2*fmag*tgrad[1]);
05722                             force[2] += (zkappa2*fmag*tgrad[2]);
05723                         } else {
05724                             /* Use of bulk factor (zkappa2) OK here becuase
05725                              * LPBE force approximation */
05726                             /* NAB -- did we forget a kappa factor here??? */
05727                             fmag = VSQR(thee->u[IJK(i,j,k)])*(thee->kappa[IJK(i,j,k)]);
05728                             force[0] += (zkappa2*fmag*tgrad[0]);
05729                             force[1] += (zkappa2*fmag*tgrad[1]);
05730                             force[2] += (zkappa2*fmag*tgrad[2]);
05731                         }
05732                     }
05733                 } /* k loop */
05734             } /* j loop */
05735         } /* i loop */
05736     } 
05737     force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
05738     force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
05739     force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
05740 
05741     return 1;
05742 }
05743 
05744 VPUBLIC int Vpmg_dbForce(Vpmg *thee, double *dbForce, int atomID, 
05745        Vsurf_Meth srfm) {
05746  
05747     Vacc *acc;
05748     Vpbe *pbe;
05749     Vatom *atom;
05750  
05751     double *apos, position[3], arad, srad, hx, hy, hzed, izmagic, deps, depsi;
05752     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
05753     double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
05754     double *u, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
05755     double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3]; 
05756     double dHzijkm1[3];
05757     int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
05758  
05759     VASSERT(thee != VNULL);
05760     if (!thee->filled) {
05761         Vnm_print(2, "Vpmg_dbForce:  Need to callVpmg_fillco!\n");
05762         return 0;
05763     }
05764  
05765     acc = thee->pbe->acc;
05766     atom = Valist_getAtom(thee->pbe->alist, atomID);
05767     apos = Vatom_getPosition(atom);
05768     arad = Vatom_getRadius(atom);
05769     srad = Vpbe_getSolventRadius(thee->pbe);
05770  
05771     /* Reset force */
05772     dbForce[0] = 0.0;
05773     dbForce[1] = 0.0;
05774     dbForce[2] = 0.0;
05775  
05776     /* Check surface definition */
05777     if ((srfm != VSM_SPLINE) && (srfm!=VSM_SPLINE3) && (srfm!=VSM_SPLINE4)) {
05778         Vnm_print(2, "Vpmg_dbForce:  Forces *must* be calculated with \
05779 spline-based surfaces!\n");
05780         Vnm_print(2, "Vpmg_dbForce:  Skipping dielectric/apolar boundary \
05781 force calculation!\n");
05782         return 0;
05783     }
05784  
05785  
05786     /* If we aren't in the current position, then we're done */
05787     if (atom->partID == 0) return 1;
05788  
05789     /* Get PBE info */
05790     pbe = thee->pbe;
05791     acc = pbe->acc;
05792     epsp = Vpbe_getSoluteDiel(pbe);
05793     epsw = Vpbe_getSolventDiel(pbe);
05794     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
05795     izmagic = 1.0/Vpbe_getZmagic(pbe);
05796  
05797     /* Mesh info */
05798     nx = thee->pmgp->nx;
05799     ny = thee->pmgp->ny;
05800     nz = thee->pmgp->nz;
05801     hx = thee->pmgp->hx;
05802     hy = thee->pmgp->hy;
05803     hzed = thee->pmgp->hzed;
05804     xlen = thee->pmgp->xlen;
05805     ylen = thee->pmgp->ylen;
05806     zlen = thee->pmgp->zlen;
05807     xmin = thee->pmgp->xmin;
05808     ymin = thee->pmgp->ymin;
05809     zmin = thee->pmgp->zmin;
05810     xmax = thee->pmgp->xmax;
05811     ymax = thee->pmgp->ymax;
05812     zmax = thee->pmgp->zmax;
05813     u = thee->u;
05814  
05815     /* Sanity check: there is no force if there is zero ionic strength */
05816     if (VABS(epsp-epsw) < VPMGSMALL) {
05817   Vnm_print(0, "Vpmg_dbForce: No force for uniform dielectric!\n");
05818   return 1;
05819     }
05820     deps = (epsw - epsp);
05821     depsi = 1.0/deps;
05822     rtot = (arad + thee->splineWin + srad); 
05823 
05824     /* Make sure we're on the grid */
05825     /* Grid checking modified by Matteo Rotter */
05826     if ((apos[0]<=xmin + rtot) || (apos[0]>=xmax - rtot)  || \
05827   (apos[1]<=ymin + rtot) || (apos[1]>=ymax - rtot)  || \
05828   (apos[2]<=zmin + rtot) || (apos[2]>=zmax - rtot)) {
05829   if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
05830    (thee->pmgp->bcfl != BCFL_MAP)) {
05831             Vnm_print(2, "Vpmg_dbForce:  Atom #%d at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
05832        atomID, apos[0], apos[1], apos[2]);
05833             Vnm_print(2, "Vpmg_dbForce:    xmin = %g, xmax = %g\n",
05834        xmin, xmax);
05835             Vnm_print(2, "Vpmg_dbForce:    ymin = %g, ymax = %g\n",
05836        ymin, ymax);
05837             Vnm_print(2, "Vpmg_dbForce:    zmin = %g, zmax = %g\n",
05838        zmin, zmax);
05839         }
05840         fflush(stderr);
05841     } else {
05842   
05843         /* Convert the atom position to grid reference frame */
05844         position[0] = apos[0] - xmin;
05845         position[1] = apos[1] - ymin;
05846         position[2] = apos[2] - zmin;
05847   
05848         /* Integrate over points within this atom's (inflated) radius */
05849         rtot2 = VSQR(rtot);
05850         dx = rtot/hx;
05851         imin = (int)floor((position[0]-rtot)/hx);
05852         if (imin < 1) {
05853             Vnm_print(2, "Vpmg_dbForce:  Atom %d off grid!\n", atomID); 
05854             return 0;
05855         }
05856         imax = (int)ceil((position[0]+rtot)/hx);
05857         if (imax > (nx-2)) {
05858             Vnm_print(2, "Vpmg_dbForce:  Atom %d off grid!\n", atomID); 
05859             return 0;
05860         }
05861         jmin = (int)floor((position[1]-rtot)/hy);
05862         if (jmin < 1) {
05863             Vnm_print(2, "Vpmg_dbForce:  Atom %d off grid!\n", atomID); 
05864             return 0;
05865         }
05866         jmax = (int)ceil((position[1]+rtot)/hy);
05867         if (jmax > (ny-2)) {
05868             Vnm_print(2, "Vpmg_dbForce:  Atom %d off grid!\n", atomID); 
05869             return 0;
05870         }
05871         kmin = (int)floor((position[2]-rtot)/hzed);
05872         if (kmin < 1) {
05873             Vnm_print(2, "Vpmg_dbForce:  Atom %d off grid!\n", atomID); 
05874             return 0;
05875         }
05876         kmax = (int)ceil((position[2]+rtot)/hzed);
05877         if (kmax > (nz-2)) {
05878             Vnm_print(2, "Vpmg_dbForce:  Atom %d off grid!\n", atomID); 
05879             return 0;
05880         }
05881         for (i=imin; i<=imax; i++) {
05882             for (j=jmin; j<=jmax; j++) {
05883                 for (k=kmin; k<=kmax; k++) {
05884                     /* i,j,k */
05885                     gpos[0] = (i+0.5)*hx + xmin;
05886                     gpos[1] = j*hy + ymin;
05887                     gpos[2] = k*hzed + zmin;
05888                     Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
05889                     
05890      /* Select the correct function based on the surface definition 
05891       * (now including the 7th order polynomial) */
05892      Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHxijk);
05893      /*
05894       switch (srfm) {
05895        case VSM_SPLINE :
05896         Vacc_splineAccGradAtomNorm(acc, gpos, thee->splineWin, 0., 
05897               atom, dHxijk);
05898         break;
05899        case VSM_SPLINE4 :
05900         Vacc_splineAccGradAtomNorm4(acc, gpos, thee->splineWin, 0., 
05901                atom, dHxijk);
05902         break;
05903        default:
05904         Vnm_print(2, "Vpmg_dbnbForce: Unknown surface method.\n");
05905         return;
05906       }
05907       */
05908                     for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
05909                     gpos[0] = i*hx + xmin;
05910                     gpos[1] = (j+0.5)*hy + ymin;
05911                     gpos[2] = k*hzed + zmin;
05912                     Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
05913                     
05914      /* Select the correct function based on the surface definition 
05915       * (now including the 7th order polynomial) */
05916      Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHyijk);
05917                     
05918      for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
05919                     gpos[0] = i*hx + xmin;
05920                     gpos[1] = j*hy + ymin;
05921                     gpos[2] = (k+0.5)*hzed + zmin;
05922                     Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
05923                     
05924      /* Select the correct function based on the surface definition 
05925       * (now including the 7th order polynomial) */
05926      Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHzijk);
05927                     
05928      for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
05929                     /* i-1,j,k */
05930                     gpos[0] = (i-0.5)*hx + xmin;
05931                     gpos[1] = j*hy + ymin;
05932                     gpos[2] = k*hzed + zmin;
05933                     Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
05934                     
05935      /* Select the correct function based on the surface definition 
05936       * (now including the 7th order polynomial) */
05937      Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHxim1jk);
05938                     
05939      for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
05940                     /* i,j-1,k */
05941                     gpos[0] = i*hx + xmin;
05942                     gpos[1] = (j-0.5)*hy + ymin;
05943                     gpos[2] = k*hzed + zmin;
05944                     Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
05945                     
05946      /* Select the correct function based on the surface definition 
05947       * (now including the 7th order polynomial) */
05948      Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHyijm1k);
05949                     
05950      for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
05951                     /* i,j,k-1 */
05952                     gpos[0] = i*hx + xmin;
05953                     gpos[1] = j*hy + ymin;
05954                     gpos[2] = (k-0.5)*hzed + zmin;
05955                     Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
05956                     
05957      /* Select the correct function based on the surface definition 
05958       * (now including the 7th order polynomial) */
05959      Vpmg_splineSelect(srfm,acc, gpos, thee->splineWin, 0.,atom, dHzijkm1);
05960                     
05961      for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
05962                     /* *** CALCULATE DIELECTRIC BOUNDARY FORCES *** */
05963                     dbFmag = u[IJK(i,j,k)];
05964                     tgrad[0] = 
05965       (dHxijk[0]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
05966        +  dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
05967       + (dHyijk[0]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
05968          +  dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
05969       + (dHzijk[0]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
05970          + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
05971                     tgrad[1] = 
05972       (dHxijk[1]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
05973        +  dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
05974       + (dHyijk[1]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
05975          +  dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
05976       + (dHzijk[1]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
05977          + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
05978                     tgrad[2] = 
05979       (dHxijk[2]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
05980        +  dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
05981       + (dHyijk[2]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
05982          +  dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
05983       + (dHzijk[2]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
05984          + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
05985      dbForce[0] += (dbFmag*tgrad[0]);
05986      dbForce[1] += (dbFmag*tgrad[1]);
05987      dbForce[2] += (dbFmag*tgrad[2]);
05988      
05989     } /* k loop */
05990    } /* j loop */
05991   } /* i loop */
05992         
05993         dbForce[0] = -dbForce[0]*hx*hy*hzed*deps*0.5*izmagic;
05994         dbForce[1] = -dbForce[1]*hx*hy*hzed*deps*0.5*izmagic;
05995         dbForce[2] = -dbForce[2]*hx*hy*hzed*deps*0.5*izmagic;
05996  }
05997 
05998  return 1;
05999 }
06000 
06001 VPUBLIC int Vpmg_qfForce(Vpmg *thee, double *force, int atomID, 
06002   Vchrg_Meth chgm) {
06003 
06004     double tforce[3];
06005 
06006     /* Reset force */
06007     force[0] = 0.0;
06008     force[1] = 0.0;
06009     force[2] = 0.0;
06010 
06011     /* Check surface definition */
06012     if (chgm != VCM_BSPL2) {
06013         Vnm_print(2, "Vpmg_qfForce:  It is recommended that forces be \
06014 calculated with the\n");
06015         Vnm_print(2, "Vpmg_qfForce:  cubic spline charge discretization \
06016 scheme\n");
06017  }
06018 
06019     switch (chgm) {
06020         case VCM_TRIL:
06021             qfForceSpline1(thee, tforce, atomID);
06022             break;
06023         case VCM_BSPL2:
06024             qfForceSpline2(thee, tforce, atomID);
06025             break;
06026   case VCM_BSPL4:
06027    qfForceSpline4(thee, tforce, atomID);
06028    break;
06029         default:
06030             Vnm_print(2, "Vpmg_qfForce:  Undefined charge discretization \
06031 method (%d)!\n", chgm);
06032             Vnm_print(2, "Vpmg_qfForce:  Forces not calculated!\n");
06033             return 0;
06034     }
06035 
06036     /* Assign forces */
06037     force[0] = tforce[0];
06038     force[1] = tforce[1];
06039     force[2] = tforce[2];
06040 
06041     return 1;
06042 }
06043 
06044 
06045 VPRIVATE void qfForceSpline1(Vpmg *thee, double *force, int atomID) {
06046 
06047     Vatom *atom;
06048     
06049     double *apos, position[3], hx, hy, hzed;
06050     double xmin, ymin, zmin, xmax, ymax, zmax;
06051     double dx, dy, dz;
06052     double *u, charge, ifloat, jfloat, kfloat;
06053     int nx, ny, nz, ihi, ilo, jhi, jlo, khi, klo;
06054 
06055     VASSERT(thee != VNULL);
06056 
06057     atom = Valist_getAtom(thee->pbe->alist, atomID);
06058     apos = Vatom_getPosition(atom);
06059     charge = Vatom_getCharge(atom);
06060 
06061     /* Reset force */
06062     force[0] = 0.0;
06063     force[1] = 0.0;
06064     force[2] = 0.0;
06065 
06066     /* If we aren't in the current position, then we're done */
06067     if (atom->partID == 0) return;
06068 
06069     /* Mesh info */
06070     nx = thee->pmgp->nx;
06071     ny = thee->pmgp->ny;
06072     nz = thee->pmgp->nz;
06073     hx = thee->pmgp->hx;
06074     hy = thee->pmgp->hy;
06075     hzed = thee->pmgp->hzed;
06076     xmin = thee->pmgp->xmin;
06077     ymin = thee->pmgp->ymin;
06078     zmin = thee->pmgp->zmin;
06079     xmax = thee->pmgp->xmax;
06080     ymax = thee->pmgp->ymax;
06081     zmax = thee->pmgp->zmax;
06082     u = thee->u;
06083 
06084     /* Make sure we're on the grid */
06085     if ((apos[0]<=xmin) || (apos[0]>=xmax) || (apos[1]<=ymin) || \
06086         (apos[1]>=ymax) || (apos[2]<=zmin) || (apos[2]>=zmax)) {
06087   if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
06088    (thee->pmgp->bcfl != BCFL_MAP)) {
06089             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]);
06090             Vnm_print(2, "Vpmg_qfForce:    xmin = %g, xmax = %g\n", xmin, xmax);
06091             Vnm_print(2, "Vpmg_qfForce:    ymin = %g, ymax = %g\n", ymin, ymax);
06092             Vnm_print(2, "Vpmg_qfForce:    zmin = %g, zmax = %g\n", zmin, zmax);
06093         }
06094         fflush(stderr);
06095     } else {
06096     
06097         /* Convert the atom position to grid coordinates */
06098         position[0] = apos[0] - xmin;
06099         position[1] = apos[1] - ymin;
06100         position[2] = apos[2] - zmin;
06101         ifloat = position[0]/hx;
06102         jfloat = position[1]/hy;
06103         kfloat = position[2]/hzed;
06104         ihi = (int)ceil(ifloat);
06105         ilo = (int)floor(ifloat);
06106         jhi = (int)ceil(jfloat);
06107         jlo = (int)floor(jfloat);
06108         khi = (int)ceil(kfloat);
06109         klo = (int)floor(kfloat);
06110         VASSERT((ihi < nx) && (ihi >=0));
06111         VASSERT((ilo < nx) && (ilo >=0));
06112         VASSERT((jhi < ny) && (jhi >=0));
06113         VASSERT((jlo < ny) && (jlo >=0));
06114         VASSERT((khi < nz) && (khi >=0));
06115         VASSERT((klo < nz) && (klo >=0));
06116         dx = ifloat - (double)(ilo);
06117         dy = jfloat - (double)(jlo);
06118         dz = kfloat - (double)(klo);
06119 
06120 
06121 #if 0
06122         Vnm_print(1, "Vpmg_qfForce: (DEBUG) u ~ %g\n", 
06123           dx    *dy    *dz    *u[IJK(ihi,jhi,khi)]
06124          +dx    *dy    *(1-dz)*u[IJK(ihi,jhi,klo)]
06125          +dx    *(1-dy)*dz    *u[IJK(ihi,jlo,khi)]
06126          +dx    *(1-dy)*(1-dz)*u[IJK(ihi,jlo,klo)]
06127          +(1-dx)*dy    *dz    *u[IJK(ilo,jhi,khi)]
06128          +(1-dx)*dy    *(1-dz)*u[IJK(ilo,jhi,klo)]
06129          +(1-dx)*(1-dy)*dz    *u[IJK(ilo,jlo,khi)]
06130          +(1-dx)*(1-dy)*(1-dz)*u[IJK(ilo,jlo,klo)]);
06131 #endif
06132 
06133 
06134         if ((dx > VPMGSMALL) && (VABS(1.0-dx) > VPMGSMALL)) {
06135             force[0] = 
06136               -charge*(dy    *dz    *u[IJK(ihi,jhi,khi)]
06137                      + dy    *(1-dz)*u[IJK(ihi,jhi,klo)]
06138                      + (1-dy)*dz    *u[IJK(ihi,jlo,khi)]
06139                      + (1-dy)*(1-dz)*u[IJK(ihi,jlo,klo)]
06140                      - dy    *dz    *u[IJK(ilo,jhi,khi)]
06141                      - dy    *(1-dz)*u[IJK(ilo,jhi,klo)]
06142                      - (1-dy)*dz    *u[IJK(ilo,jlo,khi)]
06143                      - (1-dy)*(1-dz)*u[IJK(ilo,jlo,klo)])/hx;
06144         } else {
06145             force[0] = 0;
06146             Vnm_print(0, 
06147               "Vpmg_qfForce:  Atom %d on x gridline; zero x-force\n", atomID);
06148         }
06149         if ((dy > VPMGSMALL) && (VABS(1.0-dy) > VPMGSMALL)) {
06150             force[1] = 
06151               -charge*(dx    *dz    *u[IJK(ihi,jhi,khi)]
06152                      + dx    *(1-dz)*u[IJK(ihi,jhi,klo)]
06153                      - dx    *dz    *u[IJK(ihi,jlo,khi)]
06154                      - dx    *(1-dz)*u[IJK(ihi,jlo,klo)]
06155                      + (1-dx)*dz    *u[IJK(ilo,jhi,khi)]
06156                      + (1-dx)*(1-dz)*u[IJK(ilo,jhi,klo)]
06157                      - (1-dx)*dz    *u[IJK(ilo,jlo,khi)]
06158                      - (1-dx)*(1-dz)*u[IJK(ilo,jlo,klo)])/hy;
06159         } else {
06160             force[1] = 0;
06161             Vnm_print(0, 
06162               "Vpmg_qfForce:  Atom %d on y gridline; zero y-force\n", atomID);
06163         }
06164         if ((dz > VPMGSMALL) && (VABS(1.0-dz) > VPMGSMALL)) {
06165             force[2] = 
06166               -charge*(dy    *dx    *u[IJK(ihi,jhi,khi)]
06167                      - dy    *dx    *u[IJK(ihi,jhi,klo)]
06168                      + (1-dy)*dx    *u[IJK(ihi,jlo,khi)]
06169                      - (1-dy)*dx    *u[IJK(ihi,jlo,klo)]
06170                      + dy    *(1-dx)*u[IJK(ilo,jhi,khi)]
06171                      - dy    *(1-dx)*u[IJK(ilo,jhi,klo)]
06172                      + (1-dy)*(1-dx)*u[IJK(ilo,jlo,khi)]
06173                      - (1-dy)*(1-dx)*u[IJK(ilo,jlo,klo)])/hzed;
06174         } else {
06175             force[2] = 0;
06176             Vnm_print(0, 
06177               "Vpmg_qfForce:  Atom %d on z gridline; zero z-force\n", atomID);
06178         }
06179     }
06180 }
06181 
06182 VPRIVATE void qfForceSpline2(Vpmg *thee, double *force, int atomID) {
06183 
06184     Vatom *atom;
06185     
06186     double *apos, position[3], hx, hy, hzed;
06187     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
06188     double mx, my, mz, dmx, dmy, dmz;
06189     double *u, charge, ifloat, jfloat, kfloat;
06190     int nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1; 
06191     int kp1, kp2, ii, jj, kk;
06192 
06193     VASSERT(thee != VNULL);
06194 
06195     atom = Valist_getAtom(thee->pbe->alist, atomID);
06196     apos = Vatom_getPosition(atom);
06197     charge = Vatom_getCharge(atom);
06198 
06199     /* Reset force */
06200     force[0] = 0.0;
06201     force[1] = 0.0;
06202     force[2] = 0.0;
06203 
06204     /* If we aren't in the current position, then we're done */
06205     if (atom->partID == 0) return;
06206 
06207     /* Mesh info */
06208     nx = thee->pmgp->nx;
06209     ny = thee->pmgp->ny;
06210     nz = thee->pmgp->nz;
06211     hx = thee->pmgp->hx;
06212     hy = thee->pmgp->hy;
06213     hzed = thee->pmgp->hzed;
06214     xlen = thee->pmgp->xlen;
06215     ylen = thee->pmgp->ylen;
06216     zlen = thee->pmgp->zlen;
06217     xmin = thee->pmgp->xmin;
06218     ymin = thee->pmgp->ymin;
06219     zmin = thee->pmgp->zmin;
06220     xmax = thee->pmgp->xmax;
06221     ymax = thee->pmgp->ymax;
06222     zmax = thee->pmgp->zmax;
06223     u = thee->u;
06224 
06225     /* Make sure we're on the grid */
06226     if ((apos[0]<=(xmin+hx))   || (apos[0]>=(xmax-hx)) \
06227      || (apos[1]<=(ymin+hy))   || (apos[1]>=(ymax-hy)) \
06228      || (apos[2]<=(zmin+hzed)) || (apos[2]>=(zmax-hzed))) {
06229   if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
06230    (thee->pmgp->bcfl != BCFL_MAP)) {
06231             Vnm_print(2, "qfForceSpline2:  Atom #%d off the mesh \
06232     (ignoring)\n", atomID);
06233         }
06234         fflush(stderr);
06235 
06236     } else {
06237     
06238         /* Convert the atom position to grid coordinates */
06239         position[0] = apos[0] - xmin;
06240         position[1] = apos[1] - ymin;
06241         position[2] = apos[2] - zmin;
06242         ifloat = position[0]/hx;
06243         jfloat = position[1]/hy;
06244         kfloat = position[2]/hzed;
06245         ip1 = (int)ceil(ifloat);
06246         ip2 = ip1 + 1;
06247         im1 = (int)floor(ifloat);
06248         im2 = im1 - 1;
06249         jp1 = (int)ceil(jfloat);
06250         jp2 = jp1 + 1;
06251         jm1 = (int)floor(jfloat);
06252         jm2 = jm1 - 1;
06253         kp1 = (int)ceil(kfloat);
06254         kp2 = kp1 + 1;
06255         km1 = (int)floor(kfloat);
06256         km2 = km1 - 1;
06257 
06258         /* This step shouldn't be necessary, but it saves nasty debugging
06259          * later on if something goes wrong */
06260         ip2 = VMIN2(ip2,nx-1);
06261         ip1 = VMIN2(ip1,nx-1);
06262         im1 = VMAX2(im1,0);
06263         im2 = VMAX2(im2,0);
06264         jp2 = VMIN2(jp2,ny-1);
06265         jp1 = VMIN2(jp1,ny-1);
06266         jm1 = VMAX2(jm1,0);
06267         jm2 = VMAX2(jm2,0);
06268         kp2 = VMIN2(kp2,nz-1);
06269         kp1 = VMIN2(kp1,nz-1);
06270         km1 = VMAX2(km1,0);
06271         km2 = VMAX2(km2,0);
06272 
06273 
06274         for (ii=im2; ii<=ip2; ii++) {
06275             mx = bspline2(VFCHI(ii,ifloat));
06276             dmx = dbspline2(VFCHI(ii,ifloat));
06277             for (jj=jm2; jj<=jp2; jj++) {
06278                 my = bspline2(VFCHI(jj,jfloat));
06279                 dmy = dbspline2(VFCHI(jj,jfloat));
06280                 for (kk=km2; kk<=kp2; kk++) {
06281                     mz = bspline2(VFCHI(kk,kfloat));
06282                     dmz = dbspline2(VFCHI(kk,kfloat));
06283 
06284                     force[0] += (charge*dmx*my*mz*u[IJK(ii,jj,kk)])/hx;
06285                     force[1] += (charge*mx*dmy*mz*u[IJK(ii,jj,kk)])/hy;
06286                     force[2] += (charge*mx*my*dmz*u[IJK(ii,jj,kk)])/hzed;
06287 
06288                 }
06289             }
06290         }
06291 
06292     }
06293 }
06294 
06295 VPRIVATE void qfForceSpline4(Vpmg *thee, double *force, int atomID) {
06296  
06297     Vatom *atom;
06298     double f, c, *u, *apos, position[3];
06299  
06300     /* Grid variables */
06301     int nx,ny,nz; 
06302     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
06303     double hx, hy, hzed, ifloat, jfloat, kfloat;
06304  
06305     /* B-spline weights */
06306     double mx, my, mz, dmx, dmy, dmz;
06307     double mi, mj, mk;
06308  
06309     /* Loop indeces */
06310     int i, j, k, ii, jj, kk;
06311     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
06312  
06313     /* field */
06314     double e[3];
06315  
06316     VASSERT(thee != VNULL);
06317     VASSERT(thee->filled);
06318  
06319     atom = Valist_getAtom(thee->pbe->alist, atomID);
06320     apos = Vatom_getPosition(atom);
06321     c = Vatom_getCharge(atom);
06322  
06323     for (i=0;i<3;i++){
06324   e[i] = 0.0;
06325     }
06326     
06327     /* Mesh info */
06328     nx = thee->pmgp->nx;
06329     ny = thee->pmgp->ny;
06330     nz = thee->pmgp->nz;
06331     hx = thee->pmgp->hx;
06332     hy = thee->pmgp->hy;
06333     hzed = thee->pmgp->hzed;
06334     xlen = thee->pmgp->xlen;
06335     ylen = thee->pmgp->ylen;
06336     zlen = thee->pmgp->zlen;
06337     xmin = thee->pmgp->xmin;
06338     ymin = thee->pmgp->ymin;
06339     zmin = thee->pmgp->zmin;
06340     xmax = thee->pmgp->xmax;
06341     ymax = thee->pmgp->ymax;
06342     zmax = thee->pmgp->zmax;
06343     u = thee->u;
06344     
06345     /* Make sure we're on the grid */
06346     if ((apos[0]<=(xmin+2*hx))   || (apos[0]>=(xmax-2*hx)) \
06347   || (apos[1]<=(ymin+2*hy))   || (apos[1]>=(ymax-2*hy)) \
06348   || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
06349         Vnm_print(2, "qfForceSpline4:  Atom off the mesh \
06350    (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
06351         fflush(stderr);
06352     } else {
06353   
06354         /* Convert the atom position to grid coordinates */
06355         position[0] = apos[0] - xmin;
06356         position[1] = apos[1] - ymin;
06357         position[2] = apos[2] - zmin;
06358         ifloat = position[0]/hx;
06359         jfloat = position[1]/hy;
06360         kfloat = position[2]/hzed;
06361         ip1 = (int)ceil(ifloat);
06362         ip2 = ip1 + 2;
06363         im1 = (int)floor(ifloat);
06364         im2 = im1 - 2;
06365         jp1 = (int)ceil(jfloat);
06366         jp2 = jp1 + 2;
06367         jm1 = (int)floor(jfloat);
06368         jm2 = jm1 - 2;
06369         kp1 = (int)ceil(kfloat);
06370         kp2 = kp1 + 2;
06371         km1 = (int)floor(kfloat);
06372         km2 = km1 - 2;
06373   
06374         /* This step shouldn't be necessary, but it saves nasty debugging
06375    * later on if something goes wrong */
06376         ip2 = VMIN2(ip2,nx-1);
06377         ip1 = VMIN2(ip1,nx-1);
06378         im1 = VMAX2(im1,0);
06379         im2 = VMAX2(im2,0);
06380         jp2 = VMIN2(jp2,ny-1);
06381         jp1 = VMIN2(jp1,ny-1);
06382         jm1 = VMAX2(jm1,0);
06383         jm2 = VMAX2(jm2,0);
06384         kp2 = VMIN2(kp2,nz-1);
06385         kp1 = VMIN2(kp1,nz-1);
06386         km1 = VMAX2(km1,0);
06387         km2 = VMAX2(km2,0);
06388   
06389         for (ii=im2; ii<=ip2; ii++) {
06390             mi = VFCHI4(ii,ifloat);
06391             mx = bspline4(mi);
06392             dmx = dbspline4(mi);
06393             for (jj=jm2; jj<=jp2; jj++) {
06394                 mj = VFCHI4(jj,jfloat);
06395                 my = bspline4(mj);
06396                 dmy = dbspline4(mj);
06397                 for (kk=km2; kk<=kp2; kk++) {
06398                     mk = VFCHI4(kk,kfloat);
06399                     mz = bspline4(mk);
06400                     dmz = dbspline4(mk);
06401                     f = u[IJK(ii,jj,kk)];
06402                     /* Field */
06403                     e[0] += f*dmx*my*mz/hx;
06404                     e[1] += f*mx*dmy*mz/hy;
06405                     e[2] += f*mx*my*dmz/hzed;
06406                 }
06407             }
06408         }
06409     }
06410  
06411     /* Monopole Force */
06412     force[0] = e[0]*c;
06413     force[1] = e[1]*c;
06414     force[2] = e[2]*c;
06415  
06416 }
06417 
06418 VPRIVATE void markFrac(
06419         double rtot, double *tpos,
06420         int nx, int ny, int nz,
06421         double hx, double hy, double hzed,
06422         double xmin, double ymin, double zmin,
06423         double *xarray, double *yarray, double *zarray) {
06424 
06425     int i, j, k, imin, imax, jmin, jmax, kmin, kmax;
06426     double dx, dx2, dy, dy2, dz, dz2, a000, a001, a010, a100, r2;
06427     double x, xp, xm, y, yp, ym, zp, z, zm, xspan, yspan, zspan;
06428     double rtot2, pos[3];
06429 
06430     /* Convert to grid reference frame */
06431     pos[0] = tpos[0] - xmin;
06432     pos[1] = tpos[1] - ymin;
06433     pos[2] = tpos[2] - zmin;
06434 
06435     rtot2 = VSQR(rtot);
06436 
06437     xspan = rtot + 2*hx;
06438     imin = VMAX2(0, (int)ceil((pos[0] - xspan)/hx));
06439     imax = VMIN2(nx-1, (int)floor((pos[0] + xspan)/hx));
06440     for (i=imin; i<=imax; i++) {
06441         x = hx*i;
06442         dx2 = VSQR(pos[0] - x);
06443         if (rtot2 > dx2) {
06444             yspan = VSQRT(rtot2 - dx2) + 2*hy;
06445         } else {
06446             yspan = 2*hy;
06447         }
06448         jmin = VMAX2(0,(int)ceil((pos[1] - yspan)/hy));
06449         jmax = VMIN2(ny-1,(int)floor((pos[1] + yspan)/hy));
06450         for (j=jmin; j<=jmax; j++) {
06451             y = hy*j;
06452             dy2 = VSQR(pos[1] - y);
06453             if (rtot2 > (dx2+dy2)) { 
06454                 zspan = VSQRT(rtot2-dx2-dy2) + 2*hzed; 
06455             } else {
06456                 zspan = 2*hzed;
06457             }
06458             kmin = VMAX2(0,(int)ceil((pos[2] - zspan)/hzed));
06459             kmax = VMIN2(nz-1,(int)floor((pos[2] + zspan)/hzed));
06460             for (k=kmin; k<=kmax; k++) {
06461                 z = hzed*k;
06462                 dz2 = VSQR(pos[2] - z);
06463 
06464                 r2 = dx2 + dy2 + dz2;
06465 
06466                 /* We need to determine the inclusion value a000 at (i,j,k) */
06467                 if (r2 < rtot2) a000 = 1.0;
06468                 else a000 = 0.0;
06469 
06470                 /* We need to evaluate the values of x which intersect the
06471                  * sphere and determine if these are in the interval 
06472                  * [(i,j,k), (i+1,j,k)] */
06473                 if (r2 < (rtot2 - hx*hx)) a100 = 1.0;
06474                 else if (r2 > (rtot2 + hx*hx)) a100 = 0.0;
06475                 else if (rtot2 > (dy2 + dz2)) {
06476                     dx = VSQRT(rtot2 - dy2 - dz2);
06477                     xm = pos[0] - dx;
06478                     xp = pos[0] + dx;
06479                     if ((xm < x+hx) && (xm > x)) {
06480                         a100 = (xm - x)/hx;
06481                     } else if ((xp < x+hx) && (xp > x)) {
06482                         a100 = (xp - x)/hx;
06483                     }
06484                 } else a100 = 0.0;
06485 
06486                 /* We need to evaluate the values of y which intersect the
06487                  * sphere and determine if these are in the interval 
06488                  * [(i,j,k), (i,j+1,k)] */
06489                 if (r2 < (rtot2 - hy*hy)) a010 = 1.0;
06490                 else if (r2 > (rtot2 + hy*hy)) a010 = 0.0;
06491                 else if (rtot2 > (dx2 + dz2)) {
06492                     dy = VSQRT(rtot2 - dx2 - dz2);
06493                     ym = pos[1] - dy;
06494                     yp = pos[1] + dy;
06495                     if ((ym < y+hy) && (ym > y)) {
06496                         a010 = (ym - y)/hy;
06497                     } else if ((yp < y+hy) && (yp > y)) {
06498                         a010 = (yp - y)/hy;
06499                     }
06500                 } else a010 = 0.0;
06501 
06502                 /* We need to evaluate the values of y which intersect the
06503                  * sphere and determine if these are in the interval 
06504                  * [(i,j,k), (i,j,k+1)] */
06505                 if (r2 < (rtot2 - hzed*hzed)) a001 = 1.0;
06506                 else if (r2 > (rtot2 + hzed*hzed)) a001 = 0.0;
06507                 else if (rtot2 > (dx2 + dy2)) {
06508                     dz = VSQRT(rtot2 - dx2 - dy2);
06509                     zm = pos[2] - dz;
06510                     zp = pos[2] + dz;
06511                     if ((zm < z+hzed) && (zm > z)) {
06512                         a001 = (zm - z)/hzed;
06513                     } else if ((zp < z+hzed) && (zp > z)) {
06514                         a001 = (zp - z)/hzed;
06515                     }
06516                 } else a001 = 0.0;
06517 
06518                 if (a100 < xarray[IJK(i,j,k)]) xarray[IJK(i,j,k)] = a100;
06519                 if (a010 < yarray[IJK(i,j,k)]) yarray[IJK(i,j,k)] = a010;
06520                 if (a001 < zarray[IJK(i,j,k)]) zarray[IJK(i,j,k)] = a001;
06521 
06522             } /* k loop */
06523         } /* j loop */
06524     } /* i loop */
06525 }
06526 
06527 /*
06528  
06529  NOTE: This is the original version of the markSphere function. It's in here
06530  for reference and in case a reversion to the original code is needed.
06531  D. Gohara (2/14/08)
06532  */
06533 /*
06534 VPRIVATE void markSphere(
06535        double rtot, double *tpos,
06536        int nx, int ny, int nz,
06537        double hx, double hy, double hzed,
06538        double xmin, double ymin, double zmin,
06539        double *array, double markVal) {
06540  
06541  int i, j, k, imin, imax, jmin, jmax, kmin, kmax;
06542  double dx, dx2, dy, dy2, dz, dz2;
06543  double rtot2, pos[3];
06544  
06545  // Convert to grid reference frame
06546  pos[0] = tpos[0] - xmin;
06547  pos[1] = tpos[1] - ymin;
06548  pos[2] = tpos[2] - zmin;
06549  
06550  rtot2 = VSQR(rtot);
06551  
06552  dx = rtot + 0.5*hx;
06553  imin = VMAX2(0,(int)ceil((pos[0] - dx)/hx));
06554  imax = VMIN2(nx-1,(int)floor((pos[0] + dx)/hx));
06555  for (i=imin; i<=imax; i++) {
06556   dx2 = VSQR(pos[0] - hx*i);
06557   if (rtot2 > dx2) {
06558    dy = VSQRT(rtot2 - dx2) + 0.5*hy;
06559   } else {
06560    dy = 0.5*hy;
06561   }
06562   jmin = VMAX2(0,(int)ceil((pos[1] - dy)/hy));
06563   jmax = VMIN2(ny-1,(int)floor((pos[1] + dy)/hy));
06564   for (j=jmin; j<=jmax; j++) {
06565    dy2 = VSQR(pos[1] - hy*j);
06566    if (rtot2 > (dx2+dy2)) { 
06567     dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed; 
06568    } else {
06569     dz = 0.5*hzed;
06570    }
06571    kmin = VMAX2(0,(int)ceil((pos[2] - dz)/hzed));
06572    kmax = VMIN2(nz-1,(int)floor((pos[2] + dz)/hzed));
06573    for (k=kmin; k<=kmax; k++) {
06574     dz2 = VSQR(k*hzed - pos[2]);
06575     if ((dz2 + dy2 + dx2) <= rtot2) {
06576      array[IJK(i,j,k)] = markVal;
06577     }
06578    } // k loop
06579   } // j loop
06580  } // i loop
06581 }
06582 */
06583 VPRIVATE void markSphere(double rtot, double *tpos,
06584        int nx, int ny, int nz,
06585        double hx, double hy, double hz,
06586        double xmin, double ymin, double zmin,
06587        double *array, double markVal) {
06588  
06589     int i, j, k;
06590  double fi,fj,fk;
06591  int imin, imax;
06592  int jmin, jmax;
06593  int kmin, kmax;
06594     double dx2, dy2, dz2;
06595  double xrange, yrange, zrange;
06596     double rtot2, posx, posy, posz;
06597  
06598     /* Convert to grid reference frame */
06599     posx = tpos[0] - xmin;
06600     posy = tpos[1] - ymin;
06601     posz = tpos[2] - zmin;
06602  
06603     rtot2 = VSQR(rtot);
06604  
06605  xrange = rtot + 0.5 * hx;
06606  yrange = rtot + 0.5 * hy;
06607  zrange = rtot + 0.5 * hz;
06608  
06609  imin = VMAX2(0, (int)ceil((posx - xrange)/hx));
06610  jmin = VMAX2(0, (int)ceil((posy - yrange)/hy));
06611  kmin = VMAX2(0, (int)ceil((posz - zrange)/hz));
06612  
06613  imax = VMIN2(nx-1, (int)floor((posx + xrange)/hx));
06614  jmax = VMIN2(ny-1, (int)floor((posy + yrange)/hy));
06615  kmax = VMIN2(nz-1, (int)floor((posz + zrange)/hz));
06616  
06617  for (i=imin,fi=imin; i<=imax; i++, fi+=1.) {
06618   dx2 = VSQR(posx - hx*fi);
06619   for (j=jmin,fj=jmin; j<=jmax; j++, fj+=1.) {
06620    dy2 = VSQR(posy - hy*fj);
06621    if((dx2 + dy2) > rtot2) continue;
06622    for (k=kmin, fk=kmin; k<=kmax; k++, fk+=1.) {
06623     dz2 = VSQR(posz - hz*fk);
06624     if ((dz2 + dy2 + dx2) <= rtot2) {
06625      array[IJK(i,j,k)] = markVal;
06626                 }
06627    }
06628   }
06629  }
06630 }
06631 
06632 VPRIVATE void zlapSolve(
06633         Vpmg *thee, 
06634         double **solution,
06635         double **source,
06636         double **work1
06637         ) {
06638 
06639     /* NOTE:  this is an incredibly inefficient algorithm.  The next
06640      * improvement is to focus on only non-zero entries in the source term.
06641      * The best improvement is to use a fast sine transform */
06642 
06643     int n, nx, ny, nz, i, j, k, kx, ky, kz;
06644     double hx, hy, hzed, wx, wy, wz, xlen, ylen, zlen;
06645     double phix, phixp1, phixm1, phiy, phiym1, phiyp1, phiz, phizm1, phizp1;
06646     double norm, coef, proj, eigx, eigy, eigz;
06647     double ihx2, ihy2, ihzed2;
06648     double *u, *f, *phi;
06649 
06650     /* Snarf grid parameters */
06651     nx = thee->pmgp->nx;
06652     ny = thee->pmgp->ny;
06653     nz = thee->pmgp->nz;
06654     n = nx*ny*nz;
06655     hx = thee->pmgp->hx;
06656     ihx2 = 1.0/hx/hx;
06657     hy = thee->pmgp->hy;
06658     ihy2 = 1.0/hy/hy;
06659     hzed = thee->pmgp->hzed;
06660     ihzed2 = 1.0/hzed/hzed;
06661     xlen = thee->pmgp->xlen;
06662     ylen = thee->pmgp->ylen;
06663     zlen = thee->pmgp->zlen;
06664 
06665     /* Set solution and source array pointers */
06666     u = *solution;
06667     f = *source;
06668     phi = *work1;
06669 
06670     /* Zero out the solution vector */
06671     for (i=0; i<n; i++) thee->u[i] = 0.0;
06672 
06673     /* Iterate through the wavenumbers */
06674     for (kx=1; kx<(nx-1); kx++) {
06675 
06676         wx = (VPI*(double)kx)/((double)nx - 1.0);
06677         eigx = 2.0*ihx2*(1.0 - cos(wx));
06678 
06679         for (ky=1; ky<(ny-1); ky++) {
06680 
06681             wy = (VPI*(double)ky)/((double)ny - 1.0);
06682             eigy = 2.0*ihy2*(1.0 - cos(wy));
06683 
06684             for (kz=1; kz<(nz-1); kz++) {
06685 
06686                 wz = (VPI*(double)kz)/((double)nz - 1.0);
06687                 eigz = 2.0*ihzed2*(1.0 - cos(wz));
06688 
06689                 /* Calculate the basis function. 
06690                  * We could calculate each basis function as
06691                  *   phix(i) = sin(wx*i)
06692                  *   phiy(j) = sin(wy*j)
06693                  *   phiz(k) = sin(wz*k)
06694                  * However, this is likely to be very expensive.
06695                  * Therefore, we can use the fact that
06696                  *   phix(i+1) = (2-hx*hx*eigx)*phix(i) - phix(i-1)
06697                  * */
06698                 for (i=1; i<(nx-1); i++) {
06699                     if (i == 1) {
06700                         phix = sin(wx*(double)i);
06701                         phixm1 = 0.0;
06702                     } else {
06703                         phixp1 = (2.0-hx*hx*eigx)*phix - phixm1;
06704                         phixm1 = phix;
06705                         phix = phixp1;
06706                     }
06707                     /* phix = sin(wx*(double)i); */
06708                     for (j=1; j<(ny-1); j++) {
06709                         if (j == 1) {
06710                             phiy = sin(wy*(double)j);
06711                             phiym1 = 0.0;
06712                         } else {
06713                             phiyp1 = (2.0-hy*hy*eigy)*phiy - phiym1;
06714                             phiym1 = phiy;
06715                             phiy = phiyp1;
06716                         }
06717                         /* phiy = sin(wy*(double)j); */
06718                         for (k=1; k<(nz-1); k++) {
06719                             if (k == 1) {
06720                                 phiz = sin(wz*(double)k);
06721                                 phizm1 = 0.0;
06722                             } else {
06723                                 phizp1 = (2.0-hzed*hzed*eigz)*phiz - phizm1;
06724                                 phizm1 = phiz;
06725                                 phiz = phizp1;
06726                             }
06727                             /* phiz = sin(wz*(double)k); */
06728 
06729                             phi[IJK(i,j,k)] = phix*phiy*phiz;
06730 
06731                         }
06732                     }
06733                 }
06734 
06735                 /* Calculate the projection of the source function on this
06736                  * basis function */
06737                 proj = 0.0;
06738                 for (i=1; i<(nx-1); i++) {
06739                     for (j=1; j<(ny-1); j++) {
06740                         for (k=1; k<(nz-1); k++) {
06741 
06742                             proj += f[IJK(i,j,k)]*phi[IJK(i,j,k)];
06743                             
06744                         } /* k loop */
06745                     } /* j loop */
06746                 } /* i loop */
06747 
06748                 /* Assemble the coefficient to weight the contribution of this
06749                  * basis function to the solution */
06750                 /* The first contribution is the projection */
06751                 coef = proj;
06752                 /* The second contribution is the eigenvalue */
06753                 coef = coef/(eigx + eigy + eigz);
06754                 /* The third contribution is the normalization factor */
06755                 coef = (8.0/xlen/ylen/zlen)*coef;
06756                 /* The fourth contribution is from scaling the diagonal */
06757                 /* coef = hx*hy*hzed*coef; */
06758 
06759                 /* Evaluate the basis function at each grid point */
06760                 for (i=1; i<(nx-1); i++) {
06761                     for (j=1; j<(ny-1); j++) {
06762                         for (k=1; k<(nz-1); k++) {
06763 
06764                             u[IJK(i,j,k)] += coef*phi[IJK(i,j,k)];
06765                             
06766                         } /* k loop */
06767                     } /* j loop */
06768                 } /* i loop */
06769 
06770             } /* kz loop */
06771         } /* ky loop */
06772     } /* kx loop */
06773 
06774 }
06775 
06776 VPUBLIC int Vpmg_solveLaplace(Vpmg *thee) {
06777 
06778     int i, j, k, ijk, nx, ny, nz, n, dilo, dihi, djlo, djhi, dklo, dkhi;
06779     double hx, hy, hzed, epsw, iepsw, scal, scalx, scaly, scalz;
06780 
06781     nx = thee->pmgp->nx;
06782     ny = thee->pmgp->ny;
06783     nz = thee->pmgp->nz;
06784     n = nx*ny*nz;
06785     hx = thee->pmgp->hx;
06786     hy = thee->pmgp->hy;
06787     hzed = thee->pmgp->hzed;
06788     epsw = Vpbe_getSolventDiel(thee->pbe);
06789     iepsw = 1.0/epsw;
06790     scal = hx*hy*hzed;
06791     scalx = hx*hy/hzed;
06792     scaly = hx*hzed/hy;
06793     scalz = hx*hy/hzed;
06794 
06795     if (!(thee->filled)) {
06796         Vnm_print(2, "Vpmg_solve:  Need to call Vpmg_fillco()!\n");
06797         return 0;
06798     }
06799 
06800     /* Load boundary conditions into the RHS array */
06801     for (i=1; i<(nx-1); i++) {
06802 
06803         if (i == 1) dilo = 1;
06804         else dilo = 0;
06805         if (i == nx-2) dihi = 1;
06806         else dihi = 0;
06807 
06808         for (j=1; j<(ny-1); j++) {
06809 
06810             if (j == 1) djlo = 1;
06811             else djlo = 0;
06812             if (j == ny-2) djhi = 1;
06813             else djhi = 0;
06814 
06815             for (k=1; k<(nz-1); k++) {
06816 
06817                 if (k == 1) dklo = 1;
06818                 else dklo = 0;
06819                 if (k == nz-2) dkhi = 1;
06820                 else dkhi = 0;
06821 
06822                 thee->fcf[IJK(i,j,k)] = \
06823                       iepsw*scal*thee->charge[IJK(i,j,k)] \
06824                     + dilo*scalx*thee->gxcf[IJKx(j,k,0)] \
06825                     + dihi*scalx*thee->gxcf[IJKx(j,k,1)] \
06826                     + djlo*scaly*thee->gycf[IJKy(i,k,0)] \
06827                     + djhi*scaly*thee->gycf[IJKy(i,k,1)] \
06828                     + dklo*scalz*thee->gzcf[IJKz(i,j,0)] \
06829                     + dkhi*scalz*thee->gzcf[IJKz(i,j,1)] ;
06830 
06831             }
06832         }
06833     }
06834 
06835     /* Solve */ 
06836     zlapSolve( thee, &(thee->u), &(thee->fcf), &(thee->tcf) );
06837 
06838     /* Add boundary conditions to solution */
06839     /* i faces */
06840     for (j=0; j<ny; j++) {
06841         for (k=0; k<nz; k++) {
06842             thee->u[IJK(0,j,k)] = thee->gxcf[IJKx(j,k,0)];
06843             thee->u[IJK(nx-1,j,k)] = thee->gycf[IJKx(j,k,1)];
06844         }
06845     }
06846     /* j faces */
06847     for (i=0; i<nx; i++) {
06848         for (k=0; k<nz; k++) {
06849             thee->u[IJK(i,0,k)] = thee->gycf[IJKy(i,k,0)];
06850             thee->u[IJK(i,ny-1,k)] = thee->gycf[IJKy(i,k,1)];
06851         }
06852     }
06853     /* k faces */
06854     for (i=0; i<nx; i++) {
06855         for (j=0; j<ny; j++) {
06856             thee->u[IJK(i,j,0)] = thee->gzcf[IJKz(i,j,0)];
06857             thee->u[IJK(i,j,nz-1)] = thee->gzcf[IJKz(i,j,1)];
06858         }
06859     }
06860 
06861     return 1;
06862 
06863 }
06864 
06865 VPRIVATE double VFCHI4(int i, double f) {
06866   return (2.5+((double)(i)-(f)));
06867 }
06868 
06869 VPRIVATE double bspline4(double x) {
06870 
06871     double m, m2;
06872     static double one6 = 1.0/6.0;
06873     static double one8 = 1.0/8.0;
06874     static double one24 = 1.0/24.0;
06875     static double thirteen24 = 13.0/24.0;
06876     static double fourtyseven24 = 47.0/24.0;
06877     static double seventeen24 = 17.0/24.0;
06878 
06879     if ((x > 0.0) && (x <= 1.0)){
06880       m = x*x;
06881       return one24*m*m;
06882     } else if ((x > 1.0) && (x <= 2.0)){
06883       m = x - 1.0;
06884       m2 = m*m;
06885       return -one8 + one6*x + m2*(0.25 + one6*m - one6*m2);
06886     } else if ((x > 2.0) && (x <= 3.0)){
06887       m = x - 2.0;
06888       m2 = m*m;
06889       return -thirteen24 + 0.5*x + m2*(-0.25 - 0.5*m + 0.25*m2);
06890     } else if ((x > 3.0) && (x <= 4.0)){
06891       m = x - 3.0;
06892       m2 = m*m;
06893       return fourtyseven24 - 0.5*x + m2*(-0.25 + 0.5*m - one6*m2);
06894     } else if ((x > 4.0) && (x <= 5.0)){
06895       m = x - 4.0;
06896       m2 = m*m;
06897       return seventeen24 - one6*x + m2*(0.25 - one6*m + one24*m2);
06898     } else {
06899       return 0.0;
06900     }
06901 }
06902 
06903 VPUBLIC double dbspline4(double x) {
06904 
06905     double m, m2;
06906     static double one6 = 1.0/6.0;
06907     static double one3 = 1.0/3.0;
06908     static double two3 = 2.0/3.0;
06909     static double thirteen6 = 13.0/6.0;
06910 
06911     if ((x > 0.0) && (x <= 1.0)){
06912       m2 = x*x;
06913       return one6*x*m2;
06914     } else if ((x > 1.0) && (x <= 2.0)){
06915       m = x - 1.0;
06916       m2 = m*m;
06917       return -one3 + 0.5*x + m2*(0.5 - two3*m);
06918     } else if ((x > 2.0) && (x <= 3.0)){
06919       m = x - 2.0;
06920       m2 = m*m;
06921       return 1.5 - 0.5*x + m2*(-1.5 + m);
06922     } else if ((x > 3.0) && (x <= 4.0)){
06923       m = x - 3.0;
06924       m2 = m*m;
06925       return 1.0 - 0.5*x + m2*(1.5 - two3*m);
06926     } else if ((x > 4.0) && (x <= 5.0)){
06927       m = x - 4.0;
06928       m2 = m*m;
06929       return -thirteen6 + 0.5*x + m2*(-0.5 + one6*m);
06930     } else {
06931       return 0.0;
06932     }
06933 }
06934 
06935 VPUBLIC double d2bspline4(double x) {
06936 
06937     double m, m2;
06938 
06939     if ((x > 0.0) && (x <= 1.0)){
06940       return 0.5*x*x;
06941     } else if ((x > 1.0) && (x <= 2.0)){
06942       m = x - 1.0;
06943       m2 = m*m;
06944       return -0.5 + x - 2.0*m2;
06945     } else if ((x > 2.0) && (x <= 3.0)){
06946       m = x - 2.0;
06947       m2 = m*m;
06948       return 5.5 - 3.0*x + 3.0*m2;
06949     } else if ((x > 3.0) && (x <= 4.0)){
06950       m = x - 3.0;
06951       m2 = m*m;
06952       return -9.5 + 3.0*x - 2.0*m2;
06953     } else if ((x > 4.0) && (x <= 5.0)){
06954       m = x - 4.0;
06955       m2 = m*m;
06956       return 4.5 - x + 0.5*m2;
06957     } else {
06958       return 0.0;
06959     }
06960 }
06961 
06962 VPUBLIC double d3bspline4(double x) {
06963 
06964     if      ((x > 0.0) && (x <= 1.0)) return x;
06965     else if ((x > 1.0) && (x <= 2.0)) return 5.0 - 4.0 * x;
06966     else if ((x > 2.0) && (x <= 3.0)) return -15.0 + 6.0 * x;
06967     else if ((x > 3.0) && (x <= 4.0)) return 15.0 - 4.0 * x;
06968     else if ((x > 4.0) && (x <= 5.0)) return x - 5.0;
06969     else                              return 0.0;
06970 
06971 }
06972 
06973 VPUBLIC void fillcoPermanentMultipole(Vpmg *thee) {
06974 
06975     Valist *alist;
06976     Vpbe *pbe;
06977     Vatom *atom;
06978     /* Coversions */
06979     double zmagic, f;
06980     /* Grid */
06981     double xmin, xmax, ymin, ymax, zmin, zmax;
06982     double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
06983     double hx, hy, hzed, *apos;
06984     /* Multipole */
06985     double charge, *dipole,*quad;
06986     double c,ux,uy,uz,qxx,qyx,qyy,qzx,qzy,qzz,qave;
06987     /* B-spline weights */
06988     double mx,my,mz,dmx,dmy,dmz,d2mx,d2my,d2mz;
06989     double mi,mj,mk;
06990     /* Loop variables */
06991     int i, ii, jj, kk, nx, ny, nz, iatom; 
06992     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
06993 
06994     /* sanity check */
06995     double mir,mjr,mkr,mr2;
06996     double debye,mc,mux,muy,muz,mqxx,mqyx,mqyy,mqzx,mqzy,mqzz;
06997 
06998     VASSERT(thee != VNULL);
06999 
07000     /* Get PBE info */
07001     pbe = thee->pbe;
07002     alist = pbe->alist;
07003     zmagic = Vpbe_getZmagic(pbe);
07004 
07005     /* Mesh info */
07006     nx = thee->pmgp->nx;
07007     ny = thee->pmgp->ny;
07008     nz = thee->pmgp->nz;
07009     hx = thee->pmgp->hx;
07010     hy = thee->pmgp->hy;
07011     hzed = thee->pmgp->hzed;
07012 
07013     /* Conversion */
07014     f = zmagic/(hx*hy*hzed);
07015    
07016     /* Define the total domain size */
07017     xlen = thee->pmgp->xlen;
07018     ylen = thee->pmgp->ylen;
07019     zlen = thee->pmgp->zlen;
07020 
07021     /* Define the min/max dimensions */
07022     xmin = thee->pmgp->xcent - (xlen/2.0);
07023     ymin = thee->pmgp->ycent - (ylen/2.0);
07024     zmin = thee->pmgp->zcent - (zlen/2.0);
07025     xmax = thee->pmgp->xcent + (xlen/2.0);
07026     ymax = thee->pmgp->ycent + (ylen/2.0);
07027     zmax = thee->pmgp->zcent + (zlen/2.0);
07028 
07029     /* Fill in the source term (permanent atomic multipoles) */
07030     Vnm_print(0, "fillcoPermanentMultipole:  filling in source term.\n");
07031     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
07032 
07033         atom = Valist_getAtom(alist, iatom);
07034         apos = Vatom_getPosition(atom);
07035 
07036         c = Vatom_getCharge(atom)*f;
07037 
07038 #if defined(WITH_TINKER)
07039         dipole = Vatom_getDipole(atom);
07040         ux = dipole[0]/hx*f;
07041         uy = dipole[1]/hy*f;
07042         uz = dipole[2]/hzed*f;
07043         quad = Vatom_getQuadrupole(atom);
07044         qxx = (1.0/3.0)*quad[0]/(hx*hx)*f;
07045         qyx = (2.0/3.0)*quad[3]/(hx*hy)*f;
07046         qyy = (1.0/3.0)*quad[4]/(hy*hy)*f;
07047         qzx = (2.0/3.0)*quad[6]/(hzed*hx)*f;
07048         qzy = (2.0/3.0)*quad[7]/(hzed*hy)*f;
07049         qzz = (1.0/3.0)*quad[8]/(hzed*hzed)*f;
07050 #else
07051         ux = 0.0;
07052         uy = 0.0;
07053         uz = 0.0;
07054         qxx = 0.0;
07055         qyx = 0.0;
07056         qyy = 0.0;
07057         qzx = 0.0;
07058         qzy = 0.0;
07059         qzz = 0.0;
07060 #endif /* if defined(WITH_TINKER) */
07061         
07062         /* check 
07063         mc = 0.0;
07064         mux = 0.0;
07065         muy = 0.0;
07066         muz = 0.0;
07067         mqxx = 0.0;
07068         mqyx = 0.0;
07069         mqyy = 0.0;
07070         mqzx = 0.0;
07071         mqzy = 0.0;
07072         mqzz = 0.0; */
07073 
07074         /* Make sure we're on the grid */
07075         if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx))  || \
07076             (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy))  || \
07077             (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
07078             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]);
07079             Vnm_print(2, "fillcoPermanentMultipole: xmin = %g, xmax = %g\n", xmin, xmax);
07080             Vnm_print(2, "fillcoPermanentMultipole: ymin = %g, ymax = %g\n", ymin, ymax);
07081             Vnm_print(2, "fillcoPermanentMultipole: zmin = %g, zmax = %g\n", zmin, zmax);
07082             fflush(stderr);
07083         } else {
07084 
07085             /* Convert the atom position to grid reference frame */
07086             position[0] = apos[0] - xmin;
07087             position[1] = apos[1] - ymin;
07088             position[2] = apos[2] - zmin;
07089 
07090             /* Figure out which vertices we're next to */
07091             ifloat = position[0]/hx;
07092             jfloat = position[1]/hy;
07093             kfloat = position[2]/hzed;
07094 
07095             ip1   = (int)ceil(ifloat);
07096             ip2   = ip1 + 2;
07097             im1   = (int)floor(ifloat);
07098             im2   = im1 - 2;
07099             jp1   = (int)ceil(jfloat);
07100             jp2   = jp1 + 2;
07101             jm1   = (int)floor(jfloat);
07102             jm2   = jm1 - 2;
07103             kp1   = (int)ceil(kfloat);
07104             kp2   = kp1 + 2;
07105             km1   = (int)floor(kfloat);
07106             km2   = km1 - 2;
07107 
07108             /* This step shouldn't be necessary, but it saves nasty debugging
07109              * later on if something goes wrong */
07110             ip2 = VMIN2(ip2,nx-1);
07111             ip1 = VMIN2(ip1,nx-1);
07112             im1 = VMAX2(im1,0);
07113             im2 = VMAX2(im2,0);
07114             jp2 = VMIN2(jp2,ny-1);
07115             jp1 = VMIN2(jp1,ny-1);
07116             jm1 = VMAX2(jm1,0);
07117             jm2 = VMAX2(jm2,0);
07118             kp2 = VMIN2(kp2,nz-1);
07119             kp1 = VMIN2(kp1,nz-1);
07120             km1 = VMAX2(km1,0);
07121             km2 = VMAX2(km2,0);
07122 
07123             /* Now assign fractions of the charge to the nearby verts */
07124             for (ii=im2; ii<=ip2; ii++) {
07125                 mi = VFCHI4(ii,ifloat);
07126                 mx = bspline4(mi);
07127                 dmx = dbspline4(mi);
07128                 d2mx = d2bspline4(mi);
07129                 for (jj=jm2; jj<=jp2; jj++) {
07130                     mj = VFCHI4(jj,jfloat);
07131                     my = bspline4(mj);
07132                     dmy = dbspline4(mj);
07133                     d2my = d2bspline4(mj);
07134                     for (kk=km2; kk<=kp2; kk++) {
07135                         mk = VFCHI4(kk,kfloat);
07136                         mz = bspline4(mk);
07137                         dmz = dbspline4(mk);
07138                         d2mz = d2bspline4(mk);
07139                         charge = mx*my*mz*c -
07140                          dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz +
07141                          d2mx*my*mz*qxx + 
07142                          dmx*dmy*mz*qyx + mx*d2my*mz*qyy +
07143                          dmx*my*dmz*qzx + mx*dmy*dmz*qzy + mx*my*d2mz*qzz;
07144                         thee->charge[IJK(ii,jj,kk)] += charge;
07145                          
07146                         /* sanity check - recalculate traceless multipoles 
07147                            from the grid charge distribution for this
07148                            site. 
07149 
07150                         mir = (mi - 2.5) * hx;
07151                         mjr = (mj - 2.5) * hy;
07152                         mkr = (mk - 2.5) * hzed;
07153                         mr2 = mir*mir+mjr*mjr+mkr*mkr;
07154                         mc += charge;
07155                         mux += mir * charge;
07156                         muy += mjr * charge;
07157                         muz += mkr * charge;
07158                         mqxx += (1.5*mir*mir - 0.5*mr2) * charge;
07159                         mqyx += 1.5*mjr*mir * charge;
07160                         mqyy += (1.5*mjr*mjr - 0.5*mr2) * charge;
07161                         mqzx += 1.5*mkr*mir * charge;
07162                         mqzy += 1.5*mkr*mjr * charge;
07163                         mqzz += (1.5*mkr*mkr - 0.5*mr2) * charge;
07164                          */
07165                     }
07166                 }
07167             }
07168         } /* endif (on the mesh) */
07169 
07170         /* print out the Grid vs. Ideal Point Multipole. */
07171         
07172         /*
07173         debye = 4.8033324;
07174         mc = mc/f;
07175         mux = mux/f*debye;
07176         muy = muy/f*debye;
07177         muz = muz/f*debye;
07178         mqxx = mqxx/f*debye;
07179         mqyy = mqyy/f*debye;
07180         mqzz = mqzz/f*debye;
07181         mqyx = mqyx/f*debye;
07182         mqzx = mqzx/f*debye;
07183         mqzy = mqzy/f*debye;
07184 
07185         printf(" Grid v. Actual Permanent Multipole for Site %i\n",iatom);
07186         printf(" G: %10.6f\n",mc);
07187         printf(" A: %10.6f\n\n",c/f);
07188         printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
07189         printf(" A: %10.6f %10.6f %10.6f\n\n",
07190                  (ux * hx / f) * debye,
07191                  (uy * hy / f) * debye,
07192                  (uz * hzed /f) * debye);
07193         printf(" G: %10.6f\n",mqxx);
07194         printf(" A: %10.6f\n",quad[0]*debye);
07195         printf(" G: %10.6f %10.6f\n",mqyx,mqyy);
07196         printf(" A: %10.6f %10.6f\n",quad[3]*debye,quad[4]*debye);
07197         printf(" G: %10.6f %10.6f %10.6f\n",mqzx,mqzy,mqzz);
07198         printf(" A: %10.6f %10.6f %10.6f\n\n",
07199                 quad[6]*debye,quad[7]*debye,quad[8]*debye);  */
07200 
07201     } /* endfor (each atom) */
07202 }
07203 
07204 #if defined(WITH_TINKER)
07205 
07206 VPUBLIC void fillcoInducedDipole(Vpmg *thee) {
07207 
07208     Valist *alist;
07209     Vpbe *pbe;
07210     Vatom *atom;
07211     /* Conversions */
07212     double zmagic, f;
07213     /* Grid */
07214     double xmin, xmax, ymin, ymax, zmin, zmax;
07215     double xlen, ylen, zlen, ifloat, jfloat, kfloat;
07216     double hx, hy, hzed, *apos, position[3];
07217     /* B-spline weights */
07218     double mx, my, mz, dmx, dmy, dmz;
07219     /* Dipole */
07220     double charge, *dipole, ux,uy,uz;
07221     double mi,mj,mk;
07222     /* Loop indeces */
07223     int i, ii, jj, kk, nx, ny, nz, iatom; 
07224     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
07225 
07226     double debye;
07227     double mux,muy,muz;
07228     double mir,mjr,mkr;
07229 
07230     VASSERT(thee != VNULL);
07231 
07232     /* Get PBE info */
07233     pbe = thee->pbe;
07234     alist = pbe->alist;
07235     zmagic = Vpbe_getZmagic(pbe);
07236 
07237     /* Mesh info */
07238     nx = thee->pmgp->nx;
07239     ny = thee->pmgp->ny;
07240     nz = thee->pmgp->nz;
07241     hx = thee->pmgp->hx;
07242     hy = thee->pmgp->hy;
07243     hzed = thee->pmgp->hzed;
07244 
07245     /* Conversion */
07246     f = zmagic/(hx*hy*hzed);
07247    
07248     /* Define the total domain size */
07249     xlen = thee->pmgp->xlen;
07250     ylen = thee->pmgp->ylen;
07251     zlen = thee->pmgp->zlen;
07252 
07253     /* Define the min/max dimensions */
07254     xmin = thee->pmgp->xcent - (xlen/2.0);
07255     ymin = thee->pmgp->ycent - (ylen/2.0);
07256     zmin = thee->pmgp->zcent - (zlen/2.0);
07257     xmax = thee->pmgp->xcent + (xlen/2.0);
07258     ymax = thee->pmgp->ycent + (ylen/2.0);
07259     zmax = thee->pmgp->zcent + (zlen/2.0);
07260 
07261     /* Fill in the source term (induced dipoles) */
07262     Vnm_print(0, "fillcoInducedDipole:  filling in the source term.\n");
07263     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
07264 
07265         atom = Valist_getAtom(alist, iatom);
07266         apos = Vatom_getPosition(atom);
07267 
07268         dipole = Vatom_getInducedDipole(atom);
07269         ux = dipole[0]/hx*f;
07270         uy = dipole[1]/hy*f;
07271         uz = dipole[2]/hzed*f;
07272 
07273         mux = 0.0;
07274         muy = 0.0;
07275         muz = 0.0;
07276 
07277         /* Make sure we're on the grid */
07278         if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx))  || \
07279             (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy))  || \
07280             (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
07281             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]);
07282             Vnm_print(2, "fillcoInducedDipole: xmin = %g, xmax = %g\n", xmin, xmax);
07283             Vnm_print(2, "fillcoInducedDipole: ymin = %g, ymax = %g\n", ymin, ymax);
07284             Vnm_print(2, "fillcoInducedDipole: zmin = %g, zmax = %g\n", zmin, zmax);
07285             fflush(stderr);
07286         } else {
07287 
07288             /* Convert the atom position to grid reference frame */
07289             position[0] = apos[0] - xmin;
07290             position[1] = apos[1] - ymin;
07291             position[2] = apos[2] - zmin;
07292 
07293             /* Figure out which vertices we're next to */
07294             ifloat = position[0]/hx;
07295             jfloat = position[1]/hy;
07296             kfloat = position[2]/hzed;
07297 
07298             ip1   = (int)ceil(ifloat);
07299             ip2   = ip1 + 2;
07300             im1   = (int)floor(ifloat);
07301             im2   = im1 - 2;
07302             jp1   = (int)ceil(jfloat);
07303             jp2   = jp1 + 2;
07304             jm1   = (int)floor(jfloat);
07305             jm2   = jm1 - 2;
07306             kp1   = (int)ceil(kfloat);
07307             kp2   = kp1 + 2;
07308             km1   = (int)floor(kfloat);
07309             km2   = km1 - 2;
07310 
07311             /* This step shouldn't be necessary, but it saves nasty debugging
07312              * later on if something goes wrong */
07313             ip2 = VMIN2(ip2,nx-1);
07314             ip1 = VMIN2(ip1,nx-1);
07315             im1 = VMAX2(im1,0);
07316             im2 = VMAX2(im2,0);
07317             jp2 = VMIN2(jp2,ny-1);
07318             jp1 = VMIN2(jp1,ny-1);
07319             jm1 = VMAX2(jm1,0);
07320             jm2 = VMAX2(jm2,0);
07321             kp2 = VMIN2(kp2,nz-1);
07322             kp1 = VMIN2(kp1,nz-1);
07323             km1 = VMAX2(km1,0);
07324             km2 = VMAX2(km2,0);
07325 
07326             /* Now assign fractions of the dipole to the nearby verts */
07327             for (ii=im2; ii<=ip2; ii++) {
07328                 mi = VFCHI4(ii,ifloat);
07329                 mx = bspline4(mi);
07330                 dmx = dbspline4(mi);
07331                 for (jj=jm2; jj<=jp2; jj++) {
07332                     mj = VFCHI4(jj,jfloat);
07333                     my = bspline4(mj);
07334                     dmy = dbspline4(mj);
07335                     for (kk=km2; kk<=kp2; kk++) {
07336                         mk = VFCHI4(kk,kfloat);
07337                         mz = bspline4(mk);
07338                         dmz = dbspline4(mk);
07339                         charge = -dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz;
07340                         thee->charge[IJK(ii,jj,kk)] += charge;
07341 
07342                         /*
07343                         mir = (mi - 2.5) * hx;
07344                         mjr = (mj - 2.5) * hy;
07345                         mkr = (mk - 2.5) * hzed;
07346                         mux += mir * charge;
07347                         muy += mjr * charge;
07348                         muz += mkr * charge;
07349                         */
07350                     }
07351                 }
07352             }
07353         } /* endif (on the mesh) */
07354 
07355         /* check 
07356         debye = 4.8033324;
07357         mux = mux/f*debye;
07358         muy = muy/f*debye;
07359         muz = muz/f*debye;
07360 
07361         printf(" Grid v. Actual Induced Dipole for Site %i\n",iatom);
07362         printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
07363         printf(" A: %10.6f %10.6f %10.6f\n\n",
07364                  (ux * hx / f) * debye,
07365                  (uy * hy / f) * debye,
07366                  (uz * hzed /f) * debye);
07367          */
07368 
07369     } /* endfor (each atom) */
07370 }
07371 
07372 VPUBLIC void fillcoNLInducedDipole(Vpmg *thee) {
07373 
07374     Valist *alist;
07375     Vpbe *pbe;
07376     Vatom *atom;
07377     /* Conversions */
07378     double zmagic, f;
07379     /* Grid */
07380     double xmin, xmax, ymin, ymax, zmin, zmax;
07381     double xlen, ylen, zlen, ifloat, jfloat, kfloat;
07382     double hx, hy, hzed, *apos, position[3];
07383     /* B-spline weights */
07384     double mx, my, mz, dmx, dmy, dmz;
07385     /* Dipole */
07386     double charge, *dipole, ux,uy,uz;
07387     double mi,mj,mk;
07388     /* Loop indeces */
07389     int i, ii, jj, kk, nx, ny, nz, iatom;
07390     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
07391 
07392     /* sanity check
07393     double debye;
07394     double mux,muy,muz;
07395     double mir,mjr,mkr;
07396      */
07397 
07398     VASSERT(thee != VNULL);
07399 
07400     /* Get PBE info */
07401     pbe = thee->pbe;
07402     alist = pbe->alist;
07403     zmagic = Vpbe_getZmagic(pbe);
07404 
07405     /* Mesh info */
07406     nx = thee->pmgp->nx;
07407     ny = thee->pmgp->ny;
07408     nz = thee->pmgp->nz;
07409     hx = thee->pmgp->hx;
07410     hy = thee->pmgp->hy;
07411     hzed = thee->pmgp->hzed;
07412 
07413     /* Conversion */
07414     f = zmagic/(hx*hy*hzed);
07415    
07416     /* Define the total domain size */
07417     xlen = thee->pmgp->xlen;
07418     ylen = thee->pmgp->ylen;
07419     zlen = thee->pmgp->zlen;
07420 
07421     /* Define the min/max dimensions */
07422     xmin = thee->pmgp->xcent - (xlen/2.0);
07423     ymin = thee->pmgp->ycent - (ylen/2.0);
07424     zmin = thee->pmgp->zcent - (zlen/2.0);
07425     xmax = thee->pmgp->xcent + (xlen/2.0);
07426     ymax = thee->pmgp->ycent + (ylen/2.0);
07427     zmax = thee->pmgp->zcent + (zlen/2.0);
07428 
07429     /* Fill in the source term (non-local induced dipoles) */
07430     Vnm_print(0, "fillcoNLInducedDipole:  filling in source term.\n");
07431     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
07432 
07433         atom = Valist_getAtom(alist, iatom);
07434         apos = Vatom_getPosition(atom);
07435 
07436         dipole = Vatom_getNLInducedDipole(atom);
07437         ux = dipole[0]/hx*f;
07438         uy = dipole[1]/hy*f;
07439         uz = dipole[2]/hzed*f;
07440 
07441         /*
07442         mux = 0.0;
07443         muy = 0.0;
07444         muz = 0.0;
07445          */
07446 
07447         /* Make sure we're on the grid */
07448         if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx))  || \
07449             (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy))  || \
07450             (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
07451             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]);
07452             Vnm_print(2, "fillcoNLInducedDipole: xmin = %g, xmax = %g\n", xmin, xmax);
07453             Vnm_print(2, "fillcoNLInducedDipole: ymin = %g, ymax = %g\n", ymin, ymax);
07454             Vnm_print(2, "fillcoNLInducedDipole: zmin = %g, zmax = %g\n", zmin, zmax);
07455             fflush(stderr);
07456         } else {
07457 
07458             /* Convert the atom position to grid reference frame */
07459             position[0] = apos[0] - xmin;
07460             position[1] = apos[1] - ymin;
07461             position[2] = apos[2] - zmin;
07462 
07463             /* Figure out which vertices we're next to */
07464             ifloat = position[0]/hx;
07465             jfloat = position[1]/hy;
07466             kfloat = position[2]/hzed;
07467 
07468             ip1   = (int)ceil(ifloat);
07469             ip2   = ip1 + 2;
07470             im1   = (int)floor(ifloat);
07471             im2   = im1 - 2;
07472             jp1   = (int)ceil(jfloat);
07473             jp2   = jp1 + 2;
07474             jm1   = (int)floor(jfloat);
07475             jm2   = jm1 - 2;
07476             kp1   = (int)ceil(kfloat);
07477             kp2   = kp1 + 2;
07478             km1   = (int)floor(kfloat);
07479             km2   = km1 - 2;
07480 
07481             /* This step shouldn't be necessary, but it saves nasty debugging
07482              * later on if something goes wrong */
07483             ip2 = VMIN2(ip2,nx-1);
07484             ip1 = VMIN2(ip1,nx-1);
07485             im1 = VMAX2(im1,0);
07486             im2 = VMAX2(im2,0);
07487             jp2 = VMIN2(jp2,ny-1);
07488             jp1 = VMIN2(jp1,ny-1);
07489             jm1 = VMAX2(jm1,0);
07490             jm2 = VMAX2(jm2,0);
07491             kp2 = VMIN2(kp2,nz-1);
07492             kp1 = VMIN2(kp1,nz-1);
07493             km1 = VMAX2(km1,0);
07494             km2 = VMAX2(km2,0);
07495 
07496             /* Now assign fractions of the non local induced dipole
07497                to the nearby verts */
07498             for (ii=im2; ii<=ip2; ii++) {
07499                 mi = VFCHI4(ii,ifloat);
07500                 mx = bspline4(mi);
07501                 dmx = dbspline4(mi);
07502                 for (jj=jm2; jj<=jp2; jj++) {
07503                     mj = VFCHI4(jj,jfloat);
07504                     my = bspline4(mj);
07505                     dmy = dbspline4(mj);
07506                     for (kk=km2; kk<=kp2; kk++) {
07507                         mk = VFCHI4(kk,kfloat);
07508                         mz = bspline4(mk);
07509                         dmz = dbspline4(mk);
07510                         charge = -dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz;
07511                         thee->charge[IJK(ii,jj,kk)] += charge;
07512 
07513                         /*
07514                         mir = (mi - 2.5) * hx;
07515                         mjr = (mj - 2.5) * hy;
07516                         mkr = (mk - 2.5) * hzed;
07517                         mux += mir * charge;
07518                         muy += mjr * charge;
07519                         muz += mkr * charge;
07520                          */
07521                     }
07522                 }
07523             }
07524         } /* endif (on the mesh) */
07525 
07526         /*
07527         debye = 4.8033324;
07528         mux = mux/f*debye;
07529         muy = muy/f*debye;
07530         muz = muz/f*debye;
07531 
07532         printf(" Grid v. Actual Non-Local Induced Dipole for Site %i\n",iatom);
07533         printf(" G: %10.6f %10.6f %10.6f\n",mux,muy,muz);
07534         printf(" A: %10.6f %10.6f %10.6f\n\n",
07535                  (ux * hx / f) * debye,
07536                  (uy * hy / f) * debye,
07537                  (uz * hzed /f) * debye); */
07538 
07539     } /* endfor (each atom) */
07540 }
07541 
07542 VPUBLIC double Vpmg_qfPermanentMultipoleEnergy(Vpmg *thee, int atomID) {
07543 
07544     double *u; 
07545     Vatom *atom;
07546     /* Grid variables */
07547     int nx, ny, nz;
07548     double xmax, xmin, ymax, ymin, zmax, zmin; 
07549     double hx, hy, hzed, ifloat, jfloat, kfloat; 
07550     double mi, mj, mk;
07551     double *position; 
07552     /* B-spline weights */ 
07553     double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz;
07554     /* Loop indeces */
07555     int ip1,ip2,im1,im2,jp1,jp2,jm1,jm2,kp1,kp2,km1,km2;
07556     int i,j,ii,jj,kk;
07557     /* Potential, field, field gradient and multipole components */
07558     double pot, rfe[3], rfde[3][3], energy;
07559     double f, charge, *dipole, *quad;
07560     double qxx, qyx, qyy, qzx, qzy, qzz;
07561     
07562     
07563     VASSERT(thee != VNULL);
07564     VASSERT(thee->filled);
07565 
07566     /* Get the mesh information */
07567     nx = thee->pmgp->nx;
07568     ny = thee->pmgp->ny;
07569     nz = thee->pmgp->nz;
07570     hx = thee->pmgp->hx;
07571     hy = thee->pmgp->hy;
07572     hzed = thee->pmgp->hzed;
07573     xmax = thee->xf[nx-1];
07574     ymax = thee->yf[ny-1];
07575     zmax = thee->zf[nz-1];
07576     xmin = thee->xf[0];
07577     ymin = thee->yf[0];
07578     zmin = thee->zf[0];
07579 
07580     u = thee->u;
07581 
07582     atom = Valist_getAtom(thee->pbe->alist, atomID);
07583 
07584     /* Currently all atoms must be in the same partition. */
07585 
07586     VASSERT(atom->partID != 0);   
07587 
07588     /* Convert the atom position to grid coordinates */
07589 
07590     position = Vatom_getPosition(atom);
07591     ifloat = (position[0] - xmin)/hx;
07592     jfloat = (position[1] - ymin)/hy;
07593     kfloat = (position[2] - zmin)/hzed;
07594     ip1 = (int)ceil(ifloat);
07595     ip2 = ip1 + 2;
07596     im1 = (int)floor(ifloat);
07597     im2 = im1 - 2;
07598     jp1 = (int)ceil(jfloat);
07599     jp2 = jp1 + 2;
07600     jm1 = (int)floor(jfloat);
07601     jm2 = jm1 - 2;
07602     kp1 = (int)ceil(kfloat);
07603     kp2 = kp1 + 2;
07604     km1 = (int)floor(kfloat);
07605     km2 = km1 - 2;
07606 
07607     /* This step shouldn't be necessary, but it saves nasty debugging
07608      * later on if something goes wrong */
07609     ip2 = VMIN2(ip2,nx-1);
07610     ip1 = VMIN2(ip1,nx-1);
07611     im1 = VMAX2(im1,0);
07612     im2 = VMAX2(im2,0);
07613     jp2 = VMIN2(jp2,ny-1);
07614     jp1 = VMIN2(jp1,ny-1);
07615     jm1 = VMAX2(jm1,0);
07616     jm2 = VMAX2(jm2,0);
07617     kp2 = VMIN2(kp2,nz-1);
07618     kp1 = VMIN2(kp1,nz-1);
07619     km1 = VMAX2(km1,0);
07620     km2 = VMAX2(km2,0);
07621 
07622     /* Initialize observables to zero */
07623     energy = 0.0;
07624     pot = 0.0;
07625     for (i=0;i<3;i++){
07626        rfe[i] = 0.0;
07627        for (j=0;j<3;j++){
07628          rfde[i][j] = 0.0;
07629        }
07630     }
07631 
07632     for (ii=im2; ii<=ip2; ii++) {
07633       mi = VFCHI4(ii,ifloat);
07634       mx = bspline4(mi);
07635       dmx = dbspline4(mi);
07636       d2mx = d2bspline4(mi);
07637       for (jj=jm2; jj<=jp2; jj++) {
07638         mj = VFCHI4(jj,jfloat);
07639         my = bspline4(mj);
07640         dmy = dbspline4(mj);
07641         d2my = d2bspline4(mj);
07642         for (kk=km2; kk<=kp2; kk++) { 
07643           mk = VFCHI4(kk,kfloat); 
07644           mz = bspline4(mk);
07645           dmz = dbspline4(mk);
07646           d2mz = d2bspline4(mk);
07647           f = u[IJK(ii,jj,kk)];
07648           /* potential */
07649           pot  += f*mx*my*mz;
07650           /* field */ 
07651           rfe[0] += f*dmx*my*mz/hx;
07652           rfe[1] += f*mx*dmy*mz/hy;
07653           rfe[2] += f*mx*my*dmz/hzed;
07654           /* field gradient */
07655           rfde[0][0] += f*d2mx*my*mz/(hx*hx);
07656           rfde[1][0] += f*dmx*dmy*mz/(hy*hx);
07657           rfde[1][1] += f*mx*d2my*mz/(hy*hy);
07658           rfde[2][0] += f*dmx*my*dmz/(hx*hzed);
07659           rfde[2][1] += f*mx*dmy*dmz/(hy*hzed);
07660           rfde[2][2] += f*mx*my*d2mz/(hzed*hzed);
07661         }
07662       }
07663     }
07664 
07665     charge = Vatom_getCharge(atom);
07666     dipole = Vatom_getDipole(atom);
07667     quad = Vatom_getQuadrupole(atom);
07668     qxx = quad[0]/3.0;
07669     qyx = quad[3]/3.0;
07670     qyy = quad[4]/3.0;
07671     qzx = quad[6]/3.0;
07672     qzy = quad[7]/3.0;
07673     qzz = quad[8]/3.0;
07674 
07675     energy =   pot * charge
07676              - rfe[0] * dipole[0] 
07677              - rfe[1] * dipole[1] 
07678              - rfe[2] * dipole[2]
07679              +     rfde[0][0]*qxx
07680              + 2.0*rfde[1][0]*qyx +     rfde[1][1]*qyy
07681              + 2.0*rfde[2][0]*qzx + 2.0*rfde[2][1]*qzy + rfde[2][2]*qzz;
07682 
07683     return energy;
07684 }
07685 
07686 VPUBLIC void Vpmg_fieldSpline4(Vpmg *thee, int atomID, double field[3]) {
07687 
07688     Vatom *atom;
07689     double *u, f;
07690     /* Grid variables */
07691     int nx, ny, nz;
07692     double xmax, xmin, ymax, ymin, zmax, zmin; 
07693     double hx, hy, hzed, ifloat, jfloat, kfloat;
07694     double *apos, position[3];
07695     /* B-Spline weights */
07696     double mx, my, mz, dmx, dmy, dmz;
07697     double mi, mj, mk; 
07698     /* Loop indeces */
07699     int ip1,ip2,im1,im2,jp1,jp2,jm1,jm2,kp1,kp2,km1,km2;
07700     int i,j,ii,jj,kk;
07701 
07702 
07703     VASSERT (thee != VNULL);
07704 
07705     /* Get the mesh information */
07706     nx = thee->pmgp->nx;
07707     ny = thee->pmgp->ny;
07708     nz = thee->pmgp->nz;
07709     hx = thee->pmgp->hx;
07710     hy = thee->pmgp->hy;
07711     hzed = thee->pmgp->hzed;
07712     xmax = thee->xf[nx-1];
07713     ymax = thee->yf[ny-1];
07714     zmax = thee->zf[nz-1];
07715     xmin = thee->xf[0];
07716     ymin = thee->yf[0];
07717     zmin = thee->zf[0];
07718 
07719     u = thee->u;
07720 
07721     atom = Valist_getAtom(thee->pbe->alist, atomID);
07722     
07723     /* Currently all atoms must be in the same partition. */
07724 
07725     VASSERT (atom->partID != 0);   
07726 
07727     /* Convert the atom position to grid coordinates */
07728 
07729     apos = Vatom_getPosition(atom);
07730     position[0] = apos[0] - xmin;
07731     position[1] = apos[1] - ymin;
07732     position[2] = apos[2] - zmin;
07733     ifloat = position[0]/hx;
07734     jfloat = position[1]/hy;
07735     kfloat = position[2]/hzed;
07736     ip1 = (int)ceil(ifloat);
07737     ip2 = ip1 + 2;
07738     im1 = (int)floor(ifloat);
07739     im2 = im1 - 2;
07740     jp1 = (int)ceil(jfloat);
07741     jp2 = jp1 + 2;
07742     jm1 = (int)floor(jfloat);
07743     jm2 = jm1 - 2;
07744     kp1 = (int)ceil(kfloat);
07745     kp2 = kp1 + 2;
07746     km1 = (int)floor(kfloat);
07747     km2 = km1 - 2;
07748 
07749     /* This step shouldn't be necessary, but it saves nasty debugging
07750      * later on if something goes wrong */
07751     ip2 = VMIN2(ip2,nx-1);
07752     ip1 = VMIN2(ip1,nx-1);
07753     im1 = VMAX2(im1,0);
07754     im2 = VMAX2(im2,0);
07755     jp2 = VMIN2(jp2,ny-1);
07756     jp1 = VMIN2(jp1,ny-1);
07757     jm1 = VMAX2(jm1,0);
07758     jm2 = VMAX2(jm2,0);
07759     kp2 = VMIN2(kp2,nz-1);
07760     kp1 = VMIN2(kp1,nz-1);
07761     km1 = VMAX2(km1,0);
07762     km2 = VMAX2(km2,0);
07763 
07764     for (i=0;i<3;i++){
07765        field[i] = 0.0;
07766     }
07767 
07768     for (ii=im2; ii<=ip2; ii++) {
07769       mi = VFCHI4(ii,ifloat);
07770       mx = bspline4(mi);
07771       dmx = dbspline4(mi);
07772       for (jj=jm2; jj<=jp2; jj++) {
07773         mj = VFCHI4(jj,jfloat);
07774         my = bspline4(mj);
07775         dmy = dbspline4(mj);
07776         for (kk=km2; kk<=kp2; kk++) { 
07777           mk = VFCHI4(kk,kfloat); 
07778           mz = bspline4(mk);
07779           dmz = dbspline4(mk);
07780           f = u[IJK(ii,jj,kk)];
07781 
07782           field[0] += f*dmx*my*mz/hx;
07783           field[1] += f*mx*dmy*mz/hy;
07784           field[2] += f*mx*my*dmz/hzed;
07785         }
07786       }
07787     }
07788 }
07789 
07790 VPUBLIC void Vpmg_qfPermanentMultipoleForce(Vpmg *thee, int atomID,
07791                                            double force[3], double torque[3]) {
07792 
07793     Vatom *atom;
07794     double f, *u, *apos, position[3];
07795 
07796     /* Grid variables */
07797     int nx,ny,nz; 
07798     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
07799     double hx, hy, hzed, ifloat, jfloat, kfloat;
07800 
07801     /* B-spline weights */
07802     double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, d3mx, d3my, d3mz;
07803     double mi, mj, mk;
07804 
07805     /* Loop indeces */
07806     int i, j, k, ii, jj, kk;
07807     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
07808 
07809     /* Potential, field, field gradient and 2nd field gradient */
07810     double pot, e[3], de[3][3], d2e[3][3][3];
07811 
07812     /* Permanent multipole components */
07813     double *dipole, *quad;
07814     double c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
07815 
07816     VASSERT(thee != VNULL);
07817     VASSERT(thee->filled);
07818 
07819     atom = Valist_getAtom(thee->pbe->alist, atomID);
07820 
07821     /* Currently all atoms must be in the same partition. */
07822 
07823     VASSERT(atom->partID != 0);   
07824 
07825     apos = Vatom_getPosition(atom);
07826 
07827     c = Vatom_getCharge(atom);
07828     dipole = Vatom_getDipole(atom);
07829     ux = dipole[0];
07830     uy = dipole[1];
07831     uz = dipole[2];
07832     quad = Vatom_getQuadrupole(atom);
07833     qxx = quad[0]/3.0;
07834     qxy = quad[1]/3.0;
07835     qxz = quad[2]/3.0;
07836     qyx = quad[3]/3.0;
07837     qyy = quad[4]/3.0;
07838     qyz = quad[5]/3.0;
07839     qzx = quad[6]/3.0;
07840     qzy = quad[7]/3.0;
07841     qzz = quad[8]/3.0;
07842 
07843     /* Initialize observables */
07844     pot = 0.0;
07845     for (i=0;i<3;i++){
07846        e[i] = 0.0;
07847        for (j=0;j<3;j++){
07848           de[i][j] = 0.0;
07849           for (k=0;k<3;k++){
07850              d2e[i][j][k] = 0.0;
07851           }
07852        }
07853     }
07854     
07855     /* Mesh info */
07856     nx = thee->pmgp->nx;
07857     ny = thee->pmgp->ny;
07858     nz = thee->pmgp->nz;
07859     hx = thee->pmgp->hx;
07860     hy = thee->pmgp->hy;
07861     hzed = thee->pmgp->hzed;
07862     xlen = thee->pmgp->xlen;
07863     ylen = thee->pmgp->ylen;
07864     zlen = thee->pmgp->zlen;
07865     xmin = thee->pmgp->xmin;
07866     ymin = thee->pmgp->ymin;
07867     zmin = thee->pmgp->zmin;
07868     xmax = thee->pmgp->xmax;
07869     ymax = thee->pmgp->ymax;
07870     zmax = thee->pmgp->zmax;
07871     u = thee->u;
07872     
07873     /* Make sure we're on the grid */
07874     if ((apos[0]<=(xmin+2*hx))   || (apos[0]>=(xmax-2*hx)) \
07875      || (apos[1]<=(ymin+2*hy))   || (apos[1]>=(ymax-2*hy)) \
07876      || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
07877         Vnm_print(2, "qfPermanentMultipoleForce:  Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
07878         fflush(stderr);
07879     } else {
07880     
07881         /* Convert the atom position to grid coordinates */
07882         position[0] = apos[0] - xmin;
07883         position[1] = apos[1] - ymin;
07884         position[2] = apos[2] - zmin;
07885         ifloat = position[0]/hx;
07886         jfloat = position[1]/hy;
07887         kfloat = position[2]/hzed;
07888         ip1 = (int)ceil(ifloat);
07889         ip2 = ip1 + 2;
07890         im1 = (int)floor(ifloat);
07891         im2 = im1 - 2;
07892         jp1 = (int)ceil(jfloat);
07893         jp2 = jp1 + 2;
07894         jm1 = (int)floor(jfloat);
07895         jm2 = jm1 - 2;
07896         kp1 = (int)ceil(kfloat);
07897         kp2 = kp1 + 2;
07898         km1 = (int)floor(kfloat);
07899         km2 = km1 - 2;
07900 
07901         /* This step shouldn't be necessary, but it saves nasty debugging
07902          * later on if something goes wrong */
07903         ip2 = VMIN2(ip2,nx-1);
07904         ip1 = VMIN2(ip1,nx-1);
07905         im1 = VMAX2(im1,0);
07906         im2 = VMAX2(im2,0);
07907         jp2 = VMIN2(jp2,ny-1);
07908         jp1 = VMIN2(jp1,ny-1);
07909         jm1 = VMAX2(jm1,0);
07910         jm2 = VMAX2(jm2,0);
07911         kp2 = VMIN2(kp2,nz-1);
07912         kp1 = VMIN2(kp1,nz-1);
07913         km1 = VMAX2(km1,0);
07914         km2 = VMAX2(km2,0);
07915 
07916         for (ii=im2; ii<=ip2; ii++) {
07917             mi = VFCHI4(ii,ifloat);
07918             mx = bspline4(mi);
07919             dmx = dbspline4(mi);
07920             d2mx = d2bspline4(mi);
07921             d3mx = d3bspline4(mi);
07922             for (jj=jm2; jj<=jp2; jj++) {
07923                 mj = VFCHI4(jj,jfloat);
07924                 my = bspline4(mj);
07925                 dmy = dbspline4(mj);
07926                 d2my = d2bspline4(mj);
07927                 d3my = d3bspline4(mj);
07928                 for (kk=km2; kk<=kp2; kk++) {
07929                     mk = VFCHI4(kk,kfloat);
07930                     mz = bspline4(mk);
07931                     dmz = dbspline4(mk);
07932                     d2mz = d2bspline4(mk);
07933                     d3mz = d3bspline4(mk);
07934                     f = u[IJK(ii,jj,kk)];
07935                     /* Potential */
07936                     pot  += f*mx*my*mz;
07937                     /* Field */
07938                     e[0] += f*dmx*my*mz/hx;
07939                     e[1] += f*mx*dmy*mz/hy;
07940                     e[2] += f*mx*my*dmz/hzed;
07941                     /* Field gradient */
07942                     de[0][0] += f*d2mx*my*mz/(hx*hx);
07943                     de[1][0] += f*dmx*dmy*mz/(hy*hx);
07944                     de[1][1] += f*mx*d2my*mz/(hy*hy);
07945                     de[2][0] += f*dmx*my*dmz/(hx*hzed);
07946                     de[2][1] += f*mx*dmy*dmz/(hy*hzed);
07947                     de[2][2] += f*mx*my*d2mz/(hzed*hzed);
07948                     /* 2nd Field Gradient
07949                        VxVxVa */
07950                     d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
07951                     d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
07952                     d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
07953                     /* VyVxVa */
07954                     d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
07955                     d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
07956                     d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
07957                     /* VyVyVa */
07958                     d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
07959                     d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
07960                     d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
07961                     /* VzVxVa */
07962                     d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
07963                     d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
07964                     d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
07965                     /* VzVyVa */
07966                     d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
07967                     d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
07968                     d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
07969                     /* VzVzVa */
07970                     d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
07971                     d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
07972                     d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
07973                 }
07974             }
07975         }
07976     }
07977 
07978     /* Monopole Force */
07979     force[0] = e[0]*c;
07980     force[1] = e[1]*c;
07981     force[2] = e[2]*c;
07982 
07983     /* Dipole Force */
07984     force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;   
07985     force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;   
07986     force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;   
07987 
07988     /* Quadrupole Force */
07989     force[0] += d2e[0][0][0]*qxx
07990              +  d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
07991              +  d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
07992     force[1] += d2e[0][0][1]*qxx
07993              +  d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
07994              +  d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
07995     force[2] += d2e[0][0][2]*qxx
07996              +  d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
07997              +  d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
07998 
07999     /* Dipole Torque */
08000     torque[0] = uy * e[2] - uz * e[1];
08001     torque[1] = uz * e[0] - ux * e[2];
08002     torque[2] = ux * e[1] - uy * e[0];
08003     /* Quadrupole Torque */
08004     de[0][1] = de[1][0];
08005     de[0][2] = de[2][0];
08006     de[1][2] = de[2][1];
08007     torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
08008                     - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
08009     torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
08010                     - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
08011     torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
08012                     - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
08013     
08014 
08015     /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
08016        printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
08017 }
08018 
08019 VPUBLIC void Vpmg_ibPermanentMultipoleForce(Vpmg *thee, int atomID, 
08020                                             double force[3]) {
08021 
08022     Valist *alist;
08023     Vacc *acc;
08024     Vpbe *pbe;
08025     Vatom *atom;
08026     Vsurf_Meth srfm;
08027 
08028     /* Grid variables */
08029     double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
08030     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
08031     double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
08032     double izmagic;
08033     int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
08034    
08035     VASSERT(thee != VNULL);
08036 
08037     /* Nonlinear PBE is not implemented for AMOEBA */
08038     VASSERT(!thee->pmgp->nonlin); 
08039     
08040     acc = thee->pbe->acc;
08041     srfm = thee->surfMeth;
08042     atom = Valist_getAtom(thee->pbe->alist, atomID);
08043 
08044     /* Currently all atoms must be in the same partition. */
08045 
08046     VASSERT(atom->partID != 0);   
08047     apos = Vatom_getPosition(atom);
08048     arad = Vatom_getRadius(atom);
08049 
08050     /* Reset force */
08051     force[0] = 0.0;
08052     force[1] = 0.0;
08053     force[2] = 0.0;
08054 
08055     /* Get PBE info */
08056     pbe = thee->pbe;
08057     acc = pbe->acc;
08058     alist = pbe->alist;
08059     irad = Vpbe_getMaxIonRadius(pbe);
08060     zkappa2 = Vpbe_getZkappa2(pbe);
08061     izmagic = 1.0/Vpbe_getZmagic(pbe);
08062 
08063     /* Should be a check for this further up. */
08064     VASSERT (zkappa2 > VPMGSMALL); 
08065     
08066     /* Mesh info */
08067     nx = thee->pmgp->nx;
08068     ny = thee->pmgp->ny;
08069     nz = thee->pmgp->nz;
08070     hx = thee->pmgp->hx;
08071     hy = thee->pmgp->hy;
08072     hzed = thee->pmgp->hzed;
08073     xlen = thee->pmgp->xlen;
08074     ylen = thee->pmgp->ylen;
08075     zlen = thee->pmgp->zlen;
08076     xmin = thee->pmgp->xmin;
08077     ymin = thee->pmgp->ymin;
08078     zmin = thee->pmgp->zmin;
08079     xmax = thee->pmgp->xmax;
08080     ymax = thee->pmgp->ymax;
08081     zmax = thee->pmgp->zmax;
08082 
08083     /* Make sure we're on the grid */
08084     if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
08085       (apos[1]<=ymin) || (apos[1]>=ymax)  || \
08086       (apos[2]<=zmin) || (apos[2]>=zmax)) {
08087         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]);
08088         Vnm_print(2, "ibPermanentMultipoleForce:  xmin = %g, xmax = %g\n", xmin, xmax);
08089         Vnm_print(2, "ibPermanentMultipoleForce:  ymin = %g, ymax = %g\n", ymin, ymax);
08090         Vnm_print(2, "ibPermanentMultipoleForce:  zmin = %g, zmax = %g\n", zmin, zmax);
08091         fflush(stderr);
08092     } else {
08093 
08094         /* Convert the atom position to grid reference frame */
08095         position[0] = apos[0] - xmin;
08096         position[1] = apos[1] - ymin;
08097         position[2] = apos[2] - zmin;
08098 
08099         /* Integrate over points within this atom's (inflated) radius */
08100         rtot = (irad + arad + thee->splineWin);
08101         rtot2 = VSQR(rtot);
08102         dx = rtot + 0.5*hx;
08103         imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
08104         imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
08105         for (i=imin; i<=imax; i++) { 
08106             dx2 = VSQR(position[0] - hx*i);
08107             if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
08108             else dy = 0.5*hy;
08109             jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
08110             jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
08111             for (j=jmin; j<=jmax; j++) { 
08112                 dy2 = VSQR(position[1] - hy*j);
08113                 if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
08114                 else dz = 0.5*hzed;
08115                 kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
08116                 kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
08117                 for (k=kmin; k<=kmax; k++) {
08118                     dz2 = VSQR(k*hzed - position[2]);
08119                     /* See if grid point is inside ivdw radius and set ccf
08120                      * accordingly (do spline assignment here) */
08121                     if ((dz2 + dy2 + dx2) <= rtot2) {
08122                         gpos[0] = i*hx + xmin;
08123                         gpos[1] = j*hy + ymin;
08124                         gpos[2] = k*hzed + zmin;
08125                         Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad, atom, tgrad);
08126                         fmag = VSQR(thee->u[IJK(i,j,k)])*thee->kappa[IJK(i,j,k)];
08127                         force[0] += (zkappa2*fmag*tgrad[0]);
08128                         force[1] += (zkappa2*fmag*tgrad[1]);
08129                         force[2] += (zkappa2*fmag*tgrad[2]);
08130                     }
08131                 } /* k loop */
08132             } /* j loop */
08133         } /* i loop */
08134     } 
08135 
08136     force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
08137     force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
08138     force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
08139 
08140 }
08141 
08142 VPUBLIC void Vpmg_dbPermanentMultipoleForce(Vpmg *thee, int atomID, 
08143                                             double force[3]) {
08144 
08145     Vacc *acc;
08146     Vpbe *pbe;
08147     Vatom *atom;
08148     Vsurf_Meth srfm;
08149 
08150     double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
08151     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
08152     double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
08153     double *u, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
08154     double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3]; 
08155     double dHzijkm1[3];
08156     int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
08157 
08158     VASSERT(thee != VNULL);
08159     
08160     acc = thee->pbe->acc;
08161     srfm = thee->surfMeth;
08162     atom = Valist_getAtom(thee->pbe->alist, atomID);
08163 
08164     /* Currently all atoms must be in the same partition. */
08165 
08166     VASSERT(atom->partID != 0);  
08167     arad = Vatom_getRadius(atom);
08168     apos = Vatom_getPosition(atom);
08169 
08170     /* Reset force */
08171     force[0] = 0.0;
08172     force[1] = 0.0;
08173     force[2] = 0.0;
08174 
08175     /* Get PBE info */
08176     pbe = thee->pbe;
08177     acc = pbe->acc;
08178     epsp = Vpbe_getSoluteDiel(pbe);
08179     epsw = Vpbe_getSolventDiel(pbe);
08180     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
08181     izmagic = 1.0/Vpbe_getZmagic(pbe);
08182 
08183 
08184     deps = (epsw - epsp);
08185     depsi = 1.0/deps;
08186 
08187     VASSERT(VABS(deps) > VPMGSMALL);
08188     
08189     /* Mesh info */
08190     nx = thee->pmgp->nx;
08191     ny = thee->pmgp->ny;
08192     nz = thee->pmgp->nz;
08193     hx = thee->pmgp->hx;
08194     hy = thee->pmgp->hy;
08195     hzed = thee->pmgp->hzed;
08196     xlen = thee->pmgp->xlen;
08197     ylen = thee->pmgp->ylen;
08198     zlen = thee->pmgp->zlen;
08199     xmin = thee->pmgp->xmin;
08200     ymin = thee->pmgp->ymin;
08201     zmin = thee->pmgp->zmin;
08202     xmax = thee->pmgp->xmax;
08203     ymax = thee->pmgp->ymax;
08204     zmax = thee->pmgp->zmax;
08205     u = thee->u;
08206 
08207     /* Make sure we're on the grid */
08208     if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
08209       (apos[1]<=ymin) || (apos[1]>=ymax)  || \
08210       (apos[2]<=zmin) || (apos[2]>=zmax)) {
08211         Vnm_print(2, "dbPermanentMultipoleForce:  Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n", apos[0], apos[1], apos[2]);
08212         Vnm_print(2, "dbPermanentMultipoleForce:  xmin = %g, xmax = %g\n", xmin, xmax);
08213         Vnm_print(2, "dbPermanentMultipoleForce:  ymin = %g, ymax = %g\n", ymin, ymax);
08214         Vnm_print(2, "dbPermanentMultipoleForce:  zmin = %g, zmax = %g\n", zmin, zmax);
08215         fflush(stderr);
08216     } else {
08217 
08218         /* Convert the atom position to grid reference frame */
08219         position[0] = apos[0] - xmin;
08220         position[1] = apos[1] - ymin;
08221         position[2] = apos[2] - zmin;
08222 
08223         /* Integrate over points within this atom's (inflated) radius */
08224         rtot = (arad + thee->splineWin);
08225         rtot2 = VSQR(rtot);
08226         dx = rtot/hx;
08227         imin = (int)floor((position[0]-rtot)/hx);
08228         if (imin < 1) {
08229             Vnm_print(2, "dbPermanentMultipoleForce:  Atom off grid!\n"); 
08230             return;
08231         }
08232         imax = (int)ceil((position[0]+rtot)/hx);
08233         if (imax > (nx-2)) {
08234             Vnm_print(2, "dbPermanentMultipoleForce:  Atom off grid!\n"); 
08235             return;
08236         }
08237         jmin = (int)floor((position[1]-rtot)/hy);
08238         if (jmin < 1) {
08239             Vnm_print(2, "dbPermanentMultipoleForce:  Atom off grid!\n"); 
08240             return;
08241         }
08242         jmax = (int)ceil((position[1]+rtot)/hy);
08243         if (jmax > (ny-2)) {
08244             Vnm_print(2, "dbPermanentMultipoleForce:  Atom off grid!\n"); 
08245             return;
08246         }
08247         kmin = (int)floor((position[2]-rtot)/hzed);
08248         if (kmin < 1) {
08249             Vnm_print(2, "dbPermanentMultipoleForce:  Atom off grid!\n"); 
08250             return;
08251         }
08252         kmax = (int)ceil((position[2]+rtot)/hzed);
08253         if (kmax > (nz-2)) {
08254             Vnm_print(2, "dbPermanentMultipoleForce:  Atom off grid!\n"); 
08255             return;
08256         }
08257         for (i=imin; i<=imax; i++) {
08258             for (j=jmin; j<=jmax; j++) {
08259                 for (k=kmin; k<=kmax; k++) {
08260                     /* i,j,k */
08261                     gpos[0] = (i+0.5)*hx + xmin;
08262                     gpos[1] = j*hy + ymin;
08263                     gpos[2] = k*hzed + zmin;
08264                     Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
08265                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
08266                             atom, dHxijk);
08267                     for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
08268                     gpos[0] = i*hx + xmin;
08269                     gpos[1] = (j+0.5)*hy + ymin;
08270                     gpos[2] = k*hzed + zmin;
08271                     Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
08272                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
08273                             atom, dHyijk);
08274                     for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
08275                     gpos[0] = i*hx + xmin;
08276                     gpos[1] = j*hy + ymin;
08277                     gpos[2] = (k+0.5)*hzed + zmin;
08278                     Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
08279                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
08280                             atom, dHzijk);
08281                     for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
08282                     /* i-1,j,k */
08283                     gpos[0] = (i-0.5)*hx + xmin;
08284                     gpos[1] = j*hy + ymin;
08285                     gpos[2] = k*hzed + zmin;
08286                     Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
08287                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
08288                             atom, dHxim1jk);
08289                     for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
08290                     /* i,j-1,k */
08291                     gpos[0] = i*hx + xmin;
08292                     gpos[1] = (j-0.5)*hy + ymin;
08293                     gpos[2] = k*hzed + zmin;
08294                     Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
08295                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
08296                             atom, dHyijm1k);
08297                     for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
08298                     /* i,j,k-1 */
08299                     gpos[0] = i*hx + xmin;
08300                     gpos[1] = j*hy + ymin;
08301                     gpos[2] = (k-0.5)*hzed + zmin;
08302                     Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
08303                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
08304                             atom, dHzijkm1);
08305                     for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
08306                     dbFmag = u[IJK(i,j,k)];
08307                     tgrad[0] = 
08308                        (dHxijk[0]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
08309                      +  dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
08310                      + (dHyijk[0]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
08311                      +  dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
08312                      + (dHzijk[0]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
08313                      + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
08314                     tgrad[1] = 
08315                        (dHxijk[1]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
08316                      +  dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
08317                      + (dHyijk[1]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
08318                      +  dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
08319                      + (dHzijk[1]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
08320                      + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
08321                     tgrad[2] = 
08322                        (dHxijk[2]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
08323                      +  dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
08324                      + (dHyijk[2]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
08325                      +  dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
08326                      + (dHzijk[2]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
08327                      + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
08328                      force[0] += (dbFmag*tgrad[0]);
08329                      force[1] += (dbFmag*tgrad[1]);
08330                      force[2] += (dbFmag*tgrad[2]);
08331                 } /* k loop */
08332             } /* j loop */
08333         } /* i loop */
08334         force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
08335         force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
08336         force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
08337     }
08338 }
08339 
08340 VPUBLIC void Vpmg_qfDirectPolForce(Vpmg *thee, Vgrid* perm, Vgrid *induced,
08341                                    int atomID, double force[3], double torque[3]) {
08342 
08343     Vatom *atom;
08344     Vpbe *pbe;
08345     double f, fp, *u, *up, *apos, position[3];
08346 
08347     /* Grid variables */
08348     int nx,ny,nz;
08349     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
08350     double hx, hy, hzed, ifloat, jfloat, kfloat;
08351 
08352     /* B-spline weights */
08353     double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, d3mx, d3my, d3mz;
08354     double mi, mj, mk;
08355 
08356     /* Loop indeces */
08357     int i, j, k, ii, jj, kk;
08358     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
08359 
08360     /* Permanent potential, field, field gradient and 2nd field gradient */
08361     double pot, e[3], de[3][3], d2e[3][3][3];
08362     /* Induced dipole field */
08363     double dep[3][3];
08364 
08365     /* Permanent multipole components */
08366     double *dipole, *quad;
08367     double c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
08368     double uix, uiy, uiz;
08369 
08370     VASSERT(thee != VNULL);  
08371     VASSERT(induced != VNULL); /* the potential due to permanent multipoles.*/
08372     VASSERT(induced != VNULL); /* the potential due to local induced dipoles.*/
08373     VASSERT(thee->pbe != VNULL);
08374     VASSERT(thee->pbe->alist != VNULL);
08375     
08376     atom = Valist_getAtom(thee->pbe->alist, atomID);
08377     VASSERT(atom->partID != 0); /* all atoms must be in the same partition.*/
08378     apos = Vatom_getPosition(atom);
08379 
08380     c = Vatom_getCharge(atom);
08381     dipole = Vatom_getDipole(atom);
08382     ux = dipole[0];
08383     uy = dipole[1];
08384     uz = dipole[2];
08385     quad = Vatom_getQuadrupole(atom);
08386     qxx = quad[0]/3.0;
08387     qxy = quad[1]/3.0;
08388     qxz = quad[2]/3.0;
08389     qyx = quad[3]/3.0;
08390     qyy = quad[4]/3.0;
08391     qyz = quad[5]/3.0;
08392     qzx = quad[6]/3.0;
08393     qzy = quad[7]/3.0;
08394     qzz = quad[8]/3.0;
08395 
08396     dipole = Vatom_getInducedDipole(atom);
08397     uix = dipole[0];
08398     uiy = dipole[1];
08399     uiz = dipole[2];
08400 
08401     /* Reset Field Gradients */
08402     pot = 0.0;
08403     for (i=0;i<3;i++){
08404        e[i] = 0.0;
08405        for (j=0;j<3;j++){
08406           de[i][j] = 0.0;
08407           dep[i][j] = 0.0;
08408           for (k=0;k<3;k++){
08409              d2e[i][j][k] = 0.0;
08410           }
08411        }
08412     }
08413     
08414     /* Mesh info */
08415     nx = thee->pmgp->nx;
08416     ny = thee->pmgp->ny;
08417     nz = thee->pmgp->nz;
08418     hx = thee->pmgp->hx;
08419     hy = thee->pmgp->hy;
08420     hzed = thee->pmgp->hzed;
08421     xlen = thee->pmgp->xlen;
08422     ylen = thee->pmgp->ylen;
08423     zlen = thee->pmgp->zlen;
08424     xmin = thee->pmgp->xmin;
08425     ymin = thee->pmgp->ymin;
08426     zmin = thee->pmgp->zmin;
08427     xmax = thee->pmgp->xmax;
08428     ymax = thee->pmgp->ymax;
08429     zmax = thee->pmgp->zmax;
08430     u = induced->data;
08431     up = perm->data;
08432     
08433     /* Make sure we're on the grid */
08434     if ((apos[0]<=(xmin+2*hx))   || (apos[0]>=(xmax-2*hx)) \
08435      || (apos[1]<=(ymin+2*hy))   || (apos[1]>=(ymax-2*hy)) \
08436      || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
08437         Vnm_print(2, "qfDirectPolForce:  Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
08438         fflush(stderr);
08439 
08440     } else {
08441     
08442         /* Convert the atom position to grid coordinates */
08443         position[0] = apos[0] - xmin;
08444         position[1] = apos[1] - ymin;
08445         position[2] = apos[2] - zmin;
08446         ifloat = position[0]/hx;
08447         jfloat = position[1]/hy;
08448         kfloat = position[2]/hzed;
08449         ip1 = (int)ceil(ifloat);
08450         ip2 = ip1 + 2;
08451         im1 = (int)floor(ifloat);
08452         im2 = im1 - 2;
08453         jp1 = (int)ceil(jfloat);
08454         jp2 = jp1 + 2;
08455         jm1 = (int)floor(jfloat);
08456         jm2 = jm1 - 2;
08457         kp1 = (int)ceil(kfloat);
08458         kp2 = kp1 + 2;
08459         km1 = (int)floor(kfloat);
08460         km2 = km1 - 2;
08461 
08462         /* This step shouldn't be necessary, but it saves nasty debugging
08463          * later on if something goes wrong */
08464         ip2 = VMIN2(ip2,nx-1);
08465         ip1 = VMIN2(ip1,nx-1);
08466         im1 = VMAX2(im1,0);
08467         im2 = VMAX2(im2,0);
08468         jp2 = VMIN2(jp2,ny-1);
08469         jp1 = VMIN2(jp1,ny-1);
08470         jm1 = VMAX2(jm1,0);
08471         jm2 = VMAX2(jm2,0);
08472         kp2 = VMIN2(kp2,nz-1);
08473         kp1 = VMIN2(kp1,nz-1);
08474         km1 = VMAX2(km1,0);
08475         km2 = VMAX2(km2,0);
08476 
08477         for (ii=im2; ii<=ip2; ii++) {
08478             mi = VFCHI4(ii,ifloat);
08479             mx = bspline4(mi);
08480             dmx = dbspline4(mi);
08481             d2mx = d2bspline4(mi);
08482             d3mx = d3bspline4(mi);
08483             for (jj=jm2; jj<=jp2; jj++) {
08484                 mj = VFCHI4(jj,jfloat);
08485                 my = bspline4(mj);
08486                 dmy = dbspline4(mj);
08487                 d2my = d2bspline4(mj);
08488                 d3my = d3bspline4(mj);
08489                 for (kk=km2; kk<=kp2; kk++) {
08490                     mk = VFCHI4(kk,kfloat);
08491                     mz = bspline4(mk);
08492                     dmz = dbspline4(mk);
08493                     d2mz = d2bspline4(mk);
08494                     d3mz = d3bspline4(mk);
08495                     f = u[IJK(ii,jj,kk)];
08496                     fp = up[IJK(ii,jj,kk)];
08497                     /* The potential */
08498                     pot  += f*mx*my*mz;
08499                     /* The field */
08500                     e[0] += f*dmx*my*mz/hx;
08501                     e[1] += f*mx*dmy*mz/hy;
08502                     e[2] += f*mx*my*dmz/hzed;
08503                     /* The gradient of the field */
08504                     de[0][0] += f*d2mx*my*mz/(hx*hx);
08505                     de[1][0] += f*dmx*dmy*mz/(hy*hx);
08506                     de[1][1] += f*mx*d2my*mz/(hy*hy);
08507                     de[2][0] += f*dmx*my*dmz/(hx*hzed);
08508                     de[2][1] += f*mx*dmy*dmz/(hy*hzed);
08509                     de[2][2] += f*mx*my*d2mz/(hzed*hzed);
08510                     /* The gradient of the (permanent) field */
08511                     dep[0][0] += fp*d2mx*my*mz/(hx*hx);
08512                     dep[1][0] += fp*dmx*dmy*mz/(hy*hx);
08513                     dep[1][1] += fp*mx*d2my*mz/(hy*hy);
08514                     dep[2][0] += fp*dmx*my*dmz/(hx*hzed);
08515                     dep[2][1] += fp*mx*dmy*dmz/(hy*hzed);
08516                     dep[2][2] += fp*mx*my*d2mz/(hzed*hzed);
08517                     /* The 2nd gradient of the field
08518                        VxVxVa */
08519                     d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
08520                     d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
08521                     d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
08522                     /* VyVxVa */
08523                     d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
08524                     d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
08525                     d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
08526                     /* VyVyVa */
08527                     d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
08528                     d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
08529                     d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
08530                     /* VzVxVa */
08531                     d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
08532                     d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
08533                     d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
08534                     /* VzVyVa */
08535                     d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
08536                     d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
08537                     d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
08538                     /* VzVzVa */
08539                     d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
08540                     d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
08541                     d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
08542                 }
08543             }
08544         }
08545     }
08546 
08547     /* force on permanent multipole due to induced reaction field */
08548 
08549     /* Monopole Force */
08550     force[0] = e[0]*c;
08551     force[1] = e[1]*c;
08552     force[2] = e[2]*c;
08553 
08554     /* Dipole Force */
08555     force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;   
08556     force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;   
08557     force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;   
08558 
08559     /* Quadrupole Force */
08560     force[0] += d2e[0][0][0]*qxx
08561              +  d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
08562              +  d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
08563     force[1] += d2e[0][0][1]*qxx
08564              +  d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
08565              +  d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
08566     force[2] += d2e[0][0][2]*qxx
08567              +  d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
08568              +  d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
08569 
08570     /* torque on permanent mulitpole due to induced reaction field */
08571 
08572     /* Dipole Torque */
08573     torque[0] = uy * e[2] - uz * e[1];
08574     torque[1] = uz * e[0] - ux * e[2];
08575     torque[2] = ux * e[1] - uy * e[0];
08576 
08577     /* Quadrupole Torque */
08578     /* Tx = -2.0*(Sum_a (Qya*dEaz) + Sum_b (Qzb*dEby)) 
08579        Ty = -2.0*(Sum_a (Qza*dEax) + Sum_b (Qxb*dEbz)) 
08580        Tz = -2.0*(Sum_a (Qxa*dEay) + Sum_b (Qyb*dEbx))  */
08581     de[0][1] = de[1][0];
08582     de[0][2] = de[2][0];
08583     de[1][2] = de[2][1];
08584     torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
08585                     - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
08586     torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
08587                     - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
08588     torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
08589                     - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
08590 
08591     /* force on induced dipole due to permanent reaction field */
08592 
08593     force[0] -= dep[0][0]*uix+dep[1][0]*uiy+dep[2][0]*uiz;
08594     force[1] -= dep[1][0]*uix+dep[1][1]*uiy+dep[2][1]*uiz;
08595     force[2] -= dep[2][0]*uix+dep[2][1]*uiy+dep[2][2]*uiz;
08596 
08597     force[0] = 0.5 * force[0];
08598     force[1] = 0.5 * force[1];
08599     force[2] = 0.5 * force[2];
08600     torque[0] = 0.5 * torque[0];
08601     torque[1] = 0.5 * torque[1];
08602     torque[2] = 0.5 * torque[2];
08603 
08604     /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
08605       printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
08606 }
08607 
08608 VPUBLIC void Vpmg_qfNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
08609                                      int atomID, double force[3], double torque[3]) {
08610 
08611     Vatom *atom;
08612     double *apos, *dipole, *quad, position[3], hx, hy, hzed;
08613     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
08614     double pot, e[3],de[3][3],dep[3][3],d2e[3][3][3];
08615     double mx, my, mz, dmx, dmy, dmz, mi, mj, mk;
08616     double d2mx, d2my, d2mz, d3mx, d3my, d3mz;
08617     double *u, *up, charge, ifloat, jfloat, kfloat;
08618     double f, fp, c, ux, uy, uz, qxx, qxy, qxz, qyx, qyy, qyz, qzx, qzy, qzz;
08619     double uix, uiy, uiz;
08620     int i,j,k,nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1; 
08621     int kp1, kp2, ii, jj, kk;
08622 
08623     VASSERT(thee != VNULL);      
08624     VASSERT(perm != VNULL);      /* potential due to permanent multipoles. */
08625     VASSERT(nlInduced != VNULL); /* potential due to non-local induced dipoles */ 
08626     VASSERT(!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
08627 
08628     atom = Valist_getAtom(thee->pbe->alist, atomID);
08629     VASSERT(atom->partID != 0);   /* Currently all atoms must be in the same partition. */
08630     apos = Vatom_getPosition(atom);
08631 
08632     c = Vatom_getCharge(atom);
08633     dipole = Vatom_getDipole(atom);
08634     ux = dipole[0];
08635     uy = dipole[1];
08636     uz = dipole[2];
08637     quad = Vatom_getQuadrupole(atom);
08638     qxx = quad[0]/3.0;
08639     qxy = quad[1]/3.0;
08640     qxz = quad[2]/3.0;
08641     qyx = quad[3]/3.0;
08642     qyy = quad[4]/3.0;
08643     qyz = quad[5]/3.0;
08644     qzx = quad[6]/3.0;
08645     qzy = quad[7]/3.0;
08646     qzz = quad[8]/3.0;
08647 
08648     dipole = Vatom_getNLInducedDipole(atom);
08649     uix = dipole[0];
08650     uiy = dipole[1];
08651     uiz = dipole[2];
08652 
08653     /* Reset Field Gradients */
08654     pot = 0.0;
08655     for (i=0;i<3;i++){
08656        e[i] = 0.0;
08657        for (j=0;j<3;j++){
08658           de[i][j] = 0.0;
08659           dep[i][j] = 0.0;
08660           for (k=0;k<3;k++){
08661              d2e[i][j][k] = 0.0;
08662           }
08663        }
08664     }
08665     
08666     /* Mesh info */
08667     nx = thee->pmgp->nx;
08668     ny = thee->pmgp->ny;
08669     nz = thee->pmgp->nz;
08670     hx = thee->pmgp->hx;
08671     hy = thee->pmgp->hy;
08672     hzed = thee->pmgp->hzed;
08673     xlen = thee->pmgp->xlen;
08674     ylen = thee->pmgp->ylen;
08675     zlen = thee->pmgp->zlen;
08676     xmin = thee->pmgp->xmin;
08677     ymin = thee->pmgp->ymin;
08678     zmin = thee->pmgp->zmin;
08679     xmax = thee->pmgp->xmax;
08680     ymax = thee->pmgp->ymax;
08681     zmax = thee->pmgp->zmax;
08682     u = nlInduced->data;
08683     up = perm->data;
08684 
08685     
08686     /* Make sure we're on the grid */
08687     if ((apos[0]<=(xmin+2*hx))   || (apos[0]>=(xmax-2*hx)) \
08688      || (apos[1]<=(ymin+2*hy))   || (apos[1]>=(ymax-2*hy)) \
08689      || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
08690         Vnm_print(2, "qfNLDirectMultipoleForce:  Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
08691     } else {
08692     
08693         /* Convert the atom position to grid coordinates */
08694         position[0] = apos[0] - xmin;
08695         position[1] = apos[1] - ymin;
08696         position[2] = apos[2] - zmin;
08697         ifloat = position[0]/hx;
08698         jfloat = position[1]/hy;
08699         kfloat = position[2]/hzed;
08700         ip1 = (int)ceil(ifloat);
08701         ip2 = ip1 + 2;
08702         im1 = (int)floor(ifloat);
08703         im2 = im1 - 2;
08704         jp1 = (int)ceil(jfloat);
08705         jp2 = jp1 + 2;
08706         jm1 = (int)floor(jfloat);
08707         jm2 = jm1 - 2;
08708         kp1 = (int)ceil(kfloat);
08709         kp2 = kp1 + 2;
08710         km1 = (int)floor(kfloat);
08711         km2 = km1 - 2;
08712 
08713         /* This step shouldn't be necessary, but it saves nasty debugging
08714          * later on if something goes wrong */
08715         ip2 = VMIN2(ip2,nx-1);
08716         ip1 = VMIN2(ip1,nx-1);
08717         im1 = VMAX2(im1,0);
08718         im2 = VMAX2(im2,0);
08719         jp2 = VMIN2(jp2,ny-1);
08720         jp1 = VMIN2(jp1,ny-1);
08721         jm1 = VMAX2(jm1,0);
08722         jm2 = VMAX2(jm2,0);
08723         kp2 = VMIN2(kp2,nz-1);
08724         kp1 = VMIN2(kp1,nz-1);
08725         km1 = VMAX2(km1,0);
08726         km2 = VMAX2(km2,0);
08727 
08728         for (ii=im2; ii<=ip2; ii++) {
08729             mi = VFCHI4(ii,ifloat);
08730             mx = bspline4(mi);
08731             dmx = dbspline4(mi);
08732             d2mx = d2bspline4(mi);
08733             d3mx = d3bspline4(mi);
08734             for (jj=jm2; jj<=jp2; jj++) {
08735                 mj = VFCHI4(jj,jfloat);
08736                 my = bspline4(mj);
08737                 dmy = dbspline4(mj);
08738                 d2my = d2bspline4(mj);
08739                 d3my = d3bspline4(mj);
08740                 for (kk=km2; kk<=kp2; kk++) {
08741                     mk = VFCHI4(kk,kfloat);
08742                     mz = bspline4(mk);
08743                     dmz = dbspline4(mk);
08744                     d2mz = d2bspline4(mk);
08745                     d3mz = d3bspline4(mk);
08746                     f = u[IJK(ii,jj,kk)];
08747                     fp = up[IJK(ii,jj,kk)];
08748                     /* The potential */
08749                     pot  += f*mx*my*mz;
08750                     /* The field */
08751                     e[0] += f*dmx*my*mz/hx;
08752                     e[1] += f*mx*dmy*mz/hy;
08753                     e[2] += f*mx*my*dmz/hzed;
08754                     /* The gradient of the field */
08755                     de[0][0] += f*d2mx*my*mz/(hx*hx);
08756                     de[1][0] += f*dmx*dmy*mz/(hy*hx);
08757                     de[1][1] += f*mx*d2my*mz/(hy*hy);
08758                     de[2][0] += f*dmx*my*dmz/(hx*hzed);
08759                     de[2][1] += f*mx*dmy*dmz/(hy*hzed);
08760                     de[2][2] += f*mx*my*d2mz/(hzed*hzed);
08761                     /* The gradient of the (permanent) field */
08762                     dep[0][0] += fp*d2mx*my*mz/(hx*hx);
08763                     dep[1][0] += fp*dmx*dmy*mz/(hy*hx);
08764                     dep[1][1] += fp*mx*d2my*mz/(hy*hy);
08765                     dep[2][0] += fp*dmx*my*dmz/(hx*hzed);
08766                     dep[2][1] += fp*mx*dmy*dmz/(hy*hzed);
08767                     dep[2][2] += fp*mx*my*d2mz/(hzed*hzed);
08768                     /* The 2nd gradient of the field */
08769                     /* VxVxVa */
08770                     d2e[0][0][0] += f*d3mx*my*mz /(hx*hx*hx);
08771                     d2e[0][0][1] += f*d2mx*dmy*mz/(hx*hy*hx);
08772                     d2e[0][0][2] += f*d2mx*my*dmz/(hx*hx*hzed);
08773                     /* VyVxVa */
08774                     d2e[1][0][0] += f*d2mx*dmy*mz/(hx*hx*hy);
08775                     d2e[1][0][1] += f*dmx*d2my*mz/(hx*hy*hy);
08776                     d2e[1][0][2] += f*dmx*dmy*dmz/(hx*hy*hzed);
08777                     /* VyVyVa */
08778                     d2e[1][1][0] += f*dmx*d2my*mz/(hx*hy*hy);
08779                     d2e[1][1][1] += f*mx*d3my*mz /(hy*hy*hy);
08780                     d2e[1][1][2] += f*mx*d2my*dmz/(hy*hy*hzed);
08781                     /* VzVxVa */
08782                     d2e[2][0][0] += f*d2mx*my*dmz/(hx*hx*hzed);
08783                     d2e[2][0][1] += f*dmx*dmy*dmz/(hx*hy*hzed);
08784                     d2e[2][0][2] += f*dmx*my*d2mz/(hx*hzed*hzed);
08785                     /* VzVyVa */
08786                     d2e[2][1][0] += f*dmx*dmy*dmz/(hx*hy*hzed);
08787                     d2e[2][1][1] += f*mx*d2my*dmz/(hy*hy*hzed);
08788                     d2e[2][1][2] += f*mx*dmy*d2mz/(hy*hzed*hzed);
08789                     /* VzVzVa */
08790                     d2e[2][2][0] += f*dmx*my*d2mz/(hx*hzed*hzed);
08791                     d2e[2][2][1] += f*mx*dmy*d2mz/(hy*hzed*hzed);
08792                     d2e[2][2][2] += f*mx*my*d3mz /(hzed*hzed*hzed);
08793                 }
08794             }
08795         }
08796     }
08797 
08798     /* force on permanent multipole due to non-local induced reaction field */
08799 
08800     /* Monopole Force */
08801     force[0] = e[0]*c;
08802     force[1] = e[1]*c;
08803     force[2] = e[2]*c;
08804 
08805     /* Dipole Force */
08806     force[0] -= de[0][0]*ux+de[1][0]*uy+de[2][0]*uz;   
08807     force[1] -= de[1][0]*ux+de[1][1]*uy+de[2][1]*uz;   
08808     force[2] -= de[2][0]*ux+de[2][1]*uy+de[2][2]*uz;   
08809 
08810     /* Quadrupole Force */
08811     force[0] += d2e[0][0][0]*qxx
08812              +  d2e[1][0][0]*qyx*2.0+d2e[1][1][0]*qyy
08813              +  d2e[2][0][0]*qzx*2.0+d2e[2][1][0]*qzy*2.0+d2e[2][2][0]*qzz;
08814     force[1] += d2e[0][0][1]*qxx
08815              +  d2e[1][0][1]*qyx*2.0+d2e[1][1][1]*qyy
08816              +  d2e[2][0][1]*qzx*2.0+d2e[2][1][1]*qzy*2.0+d2e[2][2][1]*qzz;
08817     force[2] += d2e[0][0][2]*qxx
08818              +  d2e[1][0][2]*qyx*2.0+d2e[1][1][2]*qyy
08819              +  d2e[2][0][2]*qzx*2.0+d2e[2][1][2]*qzy*2.0+d2e[2][2][2]*qzz;
08820 
08821     /* torque on permanent mulitpole due to non-local induced reaction field */
08822 
08823     /* Dipole Torque */
08824     torque[0] = uy * e[2] - uz * e[1];
08825     torque[1] = uz * e[0] - ux * e[2];
08826     torque[2] = ux * e[1] - uy * e[0];
08827 
08828     /* Quadrupole Torque */
08829     /* Tx = -2.0*(Sum_a (Qya*dEaz) + Sum_b (Qzb*dEby)) 
08830        Ty = -2.0*(Sum_a (Qza*dEax) + Sum_b (Qxb*dEbz)) 
08831        Tz = -2.0*(Sum_a (Qxa*dEay) + Sum_b (Qyb*dEbx))  */
08832     de[0][1] = de[1][0];
08833     de[0][2] = de[2][0];
08834     de[1][2] = de[2][1];
08835     torque[0] -= 2.0*(qyx*de[0][2] + qyy*de[1][2] + qyz*de[2][2]
08836                     - qzx*de[0][1] - qzy*de[1][1] - qzz*de[2][1]);
08837     torque[1] -= 2.0*(qzx*de[0][0] + qzy*de[1][0] + qzz*de[2][0]
08838                     - qxx*de[0][2] - qxy*de[1][2] - qxz*de[2][2]);
08839     torque[2] -= 2.0*(qxx*de[0][1] + qxy*de[1][1] + qxz*de[2][1]
08840                     - qyx*de[0][0] - qyy*de[1][0] - qyz*de[2][0]);
08841 
08842     /* force on non-local induced dipole due to permanent reaction field */
08843 
08844     force[0] -= dep[0][0]*uix+dep[1][0]*uiy+dep[2][0]*uiz;
08845     force[1] -= dep[1][0]*uix+dep[1][1]*uiy+dep[2][1]*uiz;
08846     force[2] -= dep[2][0]*uix+dep[2][1]*uiy+dep[2][2]*uiz;
08847 
08848     force[0] = 0.5 * force[0];
08849     force[1] = 0.5 * force[1];
08850     force[2] = 0.5 * force[2];
08851     torque[0] = 0.5 * torque[0];
08852     torque[1] = 0.5 * torque[1];
08853     torque[2] = 0.5 * torque[2];
08854 
08855     /* printf(" qPhi Force %f %f %f\n", force[0], force[1], force[2]);
08856        printf(" qPhi Torque %f %f %f\n", torque[0], torque[1], torque[2]); */
08857 }
08858 
08859 VPUBLIC void Vpmg_ibDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced, 
08860                                    int atomID, double force[3]) {
08861 
08862     Vatom *atom;
08863     Valist *alist;
08864     Vacc *acc;
08865     Vpbe *pbe;
08866     Vsurf_Meth srfm;
08867 
08868     double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
08869     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
08870     double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
08871     double izmagic;
08872     int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
08873    
08874     VASSERT(thee != VNULL);        
08875     VASSERT(perm != VNULL);        /* potential due to permanent multipoles.*/
08876     VASSERT(induced != VNULL);     /* potential due to induced dipoles. */
08877     VASSERT (!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
08878 
08879     acc = thee->pbe->acc;
08880     srfm = thee->surfMeth;
08881     atom = Valist_getAtom(thee->pbe->alist, atomID);
08882     VASSERT(atom->partID != 0);   /* Currently all atoms must be in the same partition. */
08883     apos = Vatom_getPosition(atom);
08884     arad = Vatom_getRadius(atom);
08885 
08886     /* Reset force */
08887     force[0] = 0.0;
08888     force[1] = 0.0;
08889     force[2] = 0.0;
08890 
08891     /* Get PBE info */
08892     pbe = thee->pbe;
08893     acc = pbe->acc;
08894     alist = pbe->alist;
08895     irad = Vpbe_getMaxIonRadius(pbe);
08896     zkappa2 = Vpbe_getZkappa2(pbe);
08897     izmagic = 1.0/Vpbe_getZmagic(pbe);
08898     
08899     VASSERT (zkappa2 > VPMGSMALL); /* It is ok to run AMOEBA with no ions, but this is checked for higher up in the driver. */
08900 
08901     /* Mesh info */
08902     nx = induced->nx;
08903     ny = induced->ny;
08904     nz = induced->nz;
08905     hx = induced->hx;
08906     hy = induced->hy;
08907     hzed = induced->hzed;
08908     xmin = induced->xmin;
08909     ymin = induced->ymin;
08910     zmin = induced->zmin;
08911     xmax = induced->xmax;
08912     ymax = induced->ymax;
08913     zmax = induced->zmax;
08914     xlen = xmax-xmin;
08915     ylen = ymax-ymin;
08916     zlen = zmax-zmin;
08917 
08918     /* Make sure we're on the grid */
08919     if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
08920       (apos[1]<=ymin) || (apos[1]>=ymax)  || \
08921       (apos[2]<=zmin) || (apos[2]>=zmax)) {
08922         Vnm_print(2, "Vpmg_ibForce:  Atom at (%4.3f, %4.3f, %4.3f) is off the mesh (ignoring):\n",
08923             apos[0], apos[1], apos[2]);
08924         Vnm_print(2, "Vpmg_ibForce:    xmin = %g, xmax = %g\n", xmin, xmax);
08925         Vnm_print(2, "Vpmg_ibForce:    ymin = %g, ymax = %g\n", ymin, ymax);
08926         Vnm_print(2, "Vpmg_ibForce:    zmin = %g, zmax = %g\n", zmin, zmax);
08927         fflush(stderr);
08928     } else {
08929 
08930         /* Convert the atom position to grid reference frame */
08931         position[0] = apos[0] - xmin;
08932         position[1] = apos[1] - ymin;
08933         position[2] = apos[2] - zmin;
08934 
08935         /* Integrate over points within this atom's (inflated) radius */
08936         rtot = (irad + arad + thee->splineWin);
08937         rtot2 = VSQR(rtot);
08938         dx = rtot + 0.5*hx;
08939         imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
08940         imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
08941         for (i=imin; i<=imax; i++) { 
08942             dx2 = VSQR(position[0] - hx*i);
08943             if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
08944             else dy = 0.5*hy;
08945             jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
08946             jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
08947             for (j=jmin; j<=jmax; j++) { 
08948                 dy2 = VSQR(position[1] - hy*j);
08949                 if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
08950                 else dz = 0.5*hzed;
08951                 kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
08952                 kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
08953                 for (k=kmin; k<=kmax; k++) {
08954                     dz2 = VSQR(k*hzed - position[2]);
08955                     /* See if grid point is inside ivdw radius and set ccf
08956                      * accordingly (do spline assignment here) */
08957                     if ((dz2 + dy2 + dx2) <= rtot2) {
08958                         gpos[0] = i*hx + xmin;
08959                         gpos[1] = j*hy + ymin;
08960                         gpos[2] = k*hzed + zmin;
08961                         Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad,
08962                           atom, tgrad);
08963                         fmag = induced->data[IJK(i,j,k)];
08964                         fmag *= perm->data[IJK(i,j,k)];
08965                         fmag *= thee->kappa[IJK(i,j,k)];
08966                         force[0] += (zkappa2*fmag*tgrad[0]);
08967                         force[1] += (zkappa2*fmag*tgrad[1]);
08968                         force[2] += (zkappa2*fmag*tgrad[2]);
08969                     }
08970                 } /* k loop */
08971             } /* j loop */
08972         } /* i loop */
08973     } 
08974 
08975     force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
08976     force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
08977     force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
08978 
08979 }
08980 
08981 VPUBLIC void Vpmg_ibNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced,
08982                                      int atomID, double force[3]) {
08983      Vpmg_ibDirectPolForce(thee, perm, nlInduced, atomID, force);
08984 }
08985 
08986 VPUBLIC void Vpmg_dbDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *induced, 
08987                                    int atomID, double force[3]) {
08988 
08989     Vatom *atom;
08990     Vacc *acc;
08991     Vpbe *pbe;
08992     Vsurf_Meth srfm;
08993 
08994     double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
08995     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
08996     double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
08997     double *u, *up, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
08998     double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3]; 
08999     double dHzijkm1[3];
09000     int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
09001 
09002     VASSERT(thee != VNULL);       
09003     VASSERT(perm != VNULL);    /* permanent multipole PMG solution. */
09004     VASSERT(induced != VNULL); /* potential due to induced dipoles. */
09005 
09006     acc = thee->pbe->acc;
09007     atom = Valist_getAtom(thee->pbe->alist, atomID);
09008     VASSERT (atom->partID != 0);   /* Currently all atoms must be in the same partition. */
09009     apos = Vatom_getPosition(atom);
09010     arad = Vatom_getRadius(atom);
09011 
09012     /* Reset force */
09013     force[0] = 0.0;
09014     force[1] = 0.0;
09015     force[2] = 0.0;
09016 
09017     /* Get PBE info */
09018     pbe = thee->pbe;
09019     acc = pbe->acc;
09020     srfm = thee->surfMeth;
09021     epsp = Vpbe_getSoluteDiel(pbe);
09022     epsw = Vpbe_getSolventDiel(pbe);
09023     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
09024     izmagic = 1.0/Vpbe_getZmagic(pbe);
09025 
09026     deps = (epsw - epsp);
09027     depsi = 1.0/deps;
09028     VASSERT(VABS(deps) > VPMGSMALL);
09029 
09030     /* Mesh info */
09031     nx = thee->pmgp->nx;
09032     ny = thee->pmgp->ny;
09033     nz = thee->pmgp->nz;
09034     hx = thee->pmgp->hx;
09035     hy = thee->pmgp->hy;
09036     hzed = thee->pmgp->hzed;
09037     xlen = thee->pmgp->xlen;
09038     ylen = thee->pmgp->ylen;
09039     zlen = thee->pmgp->zlen;
09040     xmin = thee->pmgp->xmin;
09041     ymin = thee->pmgp->ymin;
09042     zmin = thee->pmgp->zmin;
09043     xmax = thee->pmgp->xmax;
09044     ymax = thee->pmgp->ymax;
09045     zmax = thee->pmgp->zmax;
09046     /* If the permanent and induced potentials are flipped the 
09047        results are exactly the same. */
09048     u = induced->data;
09049     up = perm->data;
09050 
09051     /* Make sure we're on the grid */
09052     if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
09053       (apos[1]<=ymin) || (apos[1]>=ymax)  || \
09054       (apos[2]<=zmin) || (apos[2]>=zmax)) {
09055          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]);
09056          Vnm_print(2, "Vpmg_dbDirectPolForce:    xmin = %g, xmax = %g\n", xmin, xmax);
09057          Vnm_print(2, "Vpmg_dbDirectPolForce:    ymin = %g, ymax = %g\n", ymin, ymax); 
09058          Vnm_print(2, "Vpmg_dbDirectPolForce:    zmin = %g, zmax = %g\n", zmin, zmax);
09059          fflush(stderr);
09060     } else {
09061 
09062         /* Convert the atom position to grid reference frame */
09063         position[0] = apos[0] - xmin;
09064         position[1] = apos[1] - ymin;
09065         position[2] = apos[2] - zmin;
09066 
09067         /* Integrate over points within this atom's (inflated) radius */
09068         rtot = (arad + thee->splineWin);
09069         rtot2 = VSQR(rtot);
09070         dx = rtot/hx;
09071         imin = (int)floor((position[0]-rtot)/hx);
09072         if (imin < 1) {
09073             Vnm_print(2, "Vpmg_dbDirectPolForce:  Atom %d off grid!\n", atomID); 
09074             return;
09075         }
09076         imax = (int)ceil((position[0]+rtot)/hx);
09077         if (imax > (nx-2)) {
09078             Vnm_print(2, "Vpmg_dbDirectPolForce:  Atom %d off grid!\n", atomID); 
09079             return;
09080         }
09081         jmin = (int)floor((position[1]-rtot)/hy);
09082         if (jmin < 1) {
09083             Vnm_print(2, "Vpmg_dbDirectPolForce:  Atom %d off grid!\n", atomID); 
09084             return;
09085         }
09086         jmax = (int)ceil((position[1]+rtot)/hy);
09087         if (jmax > (ny-2)) {
09088             Vnm_print(2, "Vpmg_dbDirectPolForce:  Atom %d off grid!\n", atomID); 
09089             return;
09090         }
09091         kmin = (int)floor((position[2]-rtot)/hzed);
09092         if (kmin < 1) {
09093             Vnm_print(2, "Vpmg_dbDirectPolForce:  Atom %d off grid!\n", atomID); 
09094             return;
09095         }
09096         kmax = (int)ceil((position[2]+rtot)/hzed);
09097         if (kmax > (nz-2)) {
09098             Vnm_print(2, "Vpmg_dbDirectPolForce:  Atom %d off grid!\n", atomID); 
09099             return;
09100         }
09101         for (i=imin; i<=imax; i++) {
09102             for (j=jmin; j<=jmax; j++) {
09103                 for (k=kmin; k<=kmax; k++) {
09104                     /* i,j,k */
09105                     gpos[0] = (i+0.5)*hx + xmin;
09106                     gpos[1] = j*hy + ymin;
09107                     gpos[2] = k*hzed + zmin;
09108                     Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
09109                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
09110                             atom, dHxijk);
09111                     for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
09112                     gpos[0] = i*hx + xmin;
09113                     gpos[1] = (j+0.5)*hy + ymin;
09114                     gpos[2] = k*hzed + zmin;
09115                     Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
09116                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
09117                             atom, dHyijk);
09118                     for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
09119                     gpos[0] = i*hx + xmin;
09120                     gpos[1] = j*hy + ymin;
09121                     gpos[2] = (k+0.5)*hzed + zmin;
09122                     Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
09123                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
09124                             atom, dHzijk);
09125                     for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
09126                     /* i-1,j,k */
09127                     gpos[0] = (i-0.5)*hx + xmin;
09128                     gpos[1] = j*hy + ymin;
09129                     gpos[2] = k*hzed + zmin;
09130                     Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
09131                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
09132                             atom, dHxim1jk);
09133                     for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
09134                     /* i,j-1,k */
09135                     gpos[0] = i*hx + xmin;
09136                     gpos[1] = (j-0.5)*hy + ymin;
09137                     gpos[2] = k*hzed + zmin;
09138                     Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
09139                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
09140                             atom, dHyijm1k);
09141                     for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
09142                     /* i,j,k-1 */
09143                     gpos[0] = i*hx + xmin;
09144                     gpos[1] = j*hy + ymin;
09145                     gpos[2] = (k-0.5)*hzed + zmin;
09146                     Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
09147                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
09148                             atom, dHzijkm1);
09149                     for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
09150 
09151                     dbFmag = up[IJK(i,j,k)];
09152                     tgrad[0] = 
09153                        (dHxijk[0]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
09154                      +  dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
09155                      + (dHyijk[0]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
09156                      +  dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
09157                      + (dHzijk[0]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
09158                      + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
09159                     tgrad[1] = 
09160                        (dHxijk[1]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
09161                      +  dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
09162                      + (dHyijk[1]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
09163                      +  dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
09164                      + (dHzijk[1]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
09165                      + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
09166                     tgrad[2] = 
09167                        (dHxijk[2]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
09168                      +  dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
09169                      + (dHyijk[2]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
09170                      +  dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
09171                      + (dHzijk[2]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
09172                      + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
09173                      force[0] += (dbFmag*tgrad[0]);
09174                      force[1] += (dbFmag*tgrad[1]);
09175                      force[2] += (dbFmag*tgrad[2]);
09176 
09177                 } /* k loop */
09178             } /* j loop */
09179         } /* i loop */
09180         
09181         force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
09182         force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
09183         force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
09184         
09185     }
09186 }
09187 
09188 VPUBLIC void Vpmg_dbNLDirectPolForce(Vpmg *thee, Vgrid *perm, Vgrid *nlInduced, 
09189                                      int atomID, double force[3]) {
09190      Vpmg_dbDirectPolForce(thee, perm, nlInduced, atomID, force);
09191 }
09192 
09193 VPUBLIC void Vpmg_qfMutualPolForce(Vpmg *thee, Vgrid *induced, 
09194                               Vgrid *nlinduced, int atomID, double force[3]) {
09195 
09196     Vatom *atom;
09197     double *apos, *dipole, position[3], hx, hy, hzed;
09198     double *u, *unl;
09199     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax;
09200     double de[3][3], denl[3][3];
09201     double mx, my, mz, dmx, dmy, dmz, d2mx, d2my, d2mz, mi, mj, mk;
09202     double ifloat, jfloat, kfloat;
09203     double f, fnl, uix, uiy, uiz, uixnl, uiynl, uiznl;
09204     int i,j,k,nx, ny, nz, im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1; 
09205     int kp1, kp2, ii, jj, kk;
09206 
09207     VASSERT(thee != VNULL);   /* PMG object with PBE info. */
09208     VASSERT(induced != VNULL); /* potential due to induced dipoles. */
09209     VASSERT(nlinduced != VNULL); /* potential due to non-local induced dipoles. */
09210     atom = Valist_getAtom(thee->pbe->alist, atomID);
09211     VASSERT(atom->partID != 0);    /* all atoms must be in the same partition. */
09212     apos = Vatom_getPosition(atom);
09213     dipole = Vatom_getInducedDipole(atom);
09214     uix = dipole[0];
09215     uiy = dipole[1];
09216     uiz = dipole[2];
09217     dipole = Vatom_getNLInducedDipole(atom);
09218     uixnl = dipole[0];
09219     uiynl = dipole[1];
09220     uiznl = dipole[2];
09221     u = induced->data; 
09222     unl = nlinduced->data; 
09223 
09224     for (i=0;i<3;i++){
09225        for (j=0;j<3;j++){
09226           de[i][j] = 0.0;
09227           denl[i][j] = 0.0;
09228        }
09229     }
09230 
09231     /* Mesh info */
09232     nx = induced->nx;
09233     ny = induced->ny;
09234     nz = induced->nz;
09235     hx = induced->hx;
09236     hy = induced->hy;
09237     hzed = induced->hzed;
09238     xmin = induced->xmin;
09239     ymin = induced->ymin;
09240     zmin = induced->zmin;
09241     xmax = induced->xmax;
09242     ymax = induced->ymax;
09243     zmax = induced->zmax;
09244     xlen = xmax-xmin;
09245     ylen = ymax-ymin;
09246     zlen = zmax-zmin;
09247     
09248     /* If we aren't in the current position, then we're done */
09249     if (atom->partID == 0) return;
09250 
09251     /* Make sure we're on the grid */
09252     if ((apos[0]<=(xmin+2*hx))   || (apos[0]>=(xmax-2*hx)) \
09253      || (apos[1]<=(ymin+2*hy))   || (apos[1]>=(ymax-2*hy)) \
09254      || (apos[2]<=(zmin+2*hzed)) || (apos[2]>=(zmax-2*hzed))) {
09255         Vnm_print(2, "qfMutualPolForce:  Atom off the mesh (ignoring) %6.3f %6.3f %6.3f\n", apos[0], apos[1], apos[2]);
09256         fflush(stderr);
09257     } else {
09258     
09259         /* Convert the atom position to grid coordinates */
09260         position[0] = apos[0] - xmin;
09261         position[1] = apos[1] - ymin;
09262         position[2] = apos[2] - zmin;
09263         ifloat = position[0]/hx;
09264         jfloat = position[1]/hy;
09265         kfloat = position[2]/hzed;
09266         ip1 = (int)ceil(ifloat);
09267         ip2 = ip1 + 2;
09268         im1 = (int)floor(ifloat);
09269         im2 = im1 - 2;
09270         jp1 = (int)ceil(jfloat);
09271         jp2 = jp1 + 2;
09272         jm1 = (int)floor(jfloat);
09273         jm2 = jm1 - 2;
09274         kp1 = (int)ceil(kfloat);
09275         kp2 = kp1 + 2;
09276         km1 = (int)floor(kfloat);
09277         km2 = km1 - 2;
09278 
09279         /* This step shouldn't be necessary, but it saves nasty debugging
09280          * later on if something goes wrong */
09281         ip2 = VMIN2(ip2,nx-1);
09282         ip1 = VMIN2(ip1,nx-1);
09283         im1 = VMAX2(im1,0);
09284         im2 = VMAX2(im2,0);
09285         jp2 = VMIN2(jp2,ny-1);
09286         jp1 = VMIN2(jp1,ny-1);
09287         jm1 = VMAX2(jm1,0);
09288         jm2 = VMAX2(jm2,0);
09289         kp2 = VMIN2(kp2,nz-1);
09290         kp1 = VMIN2(kp1,nz-1);
09291         km1 = VMAX2(km1,0);
09292         km2 = VMAX2(km2,0);
09293 
09294         for (ii=im2; ii<=ip2; ii++) {
09295             mi = VFCHI4(ii,ifloat);
09296             mx = bspline4(mi);
09297             dmx = dbspline4(mi);
09298             d2mx = d2bspline4(mi);
09299             for (jj=jm2; jj<=jp2; jj++) {
09300                 mj = VFCHI4(jj,jfloat);
09301                 my = bspline4(mj);
09302                 dmy = dbspline4(mj);
09303                 d2my = d2bspline4(mj);
09304                 for (kk=km2; kk<=kp2; kk++) {
09305                     mk = VFCHI4(kk,kfloat);
09306                     mz = bspline4(mk);
09307                     dmz = dbspline4(mk);
09308                     d2mz = d2bspline4(mk);
09309                     f = u[IJK(ii,jj,kk)];
09310                     fnl = unl[IJK(ii,jj,kk)];
09311 
09312                     /* The gradient of the reaction field
09313                        due to induced dipoles */
09314                     de[0][0] += f*d2mx*my*mz/(hx*hx);
09315                     de[1][0] += f*dmx*dmy*mz/(hy*hx);
09316                     de[1][1] += f*mx*d2my*mz/(hy*hy);
09317                     de[2][0] += f*dmx*my*dmz/(hx*hzed);
09318                     de[2][1] += f*mx*dmy*dmz/(hy*hzed);
09319                     de[2][2] += f*mx*my*d2mz/(hzed*hzed);
09320 
09321                     /* The gradient of the reaction field
09322                        due to non-local induced dipoles */
09323                     denl[0][0] += fnl*d2mx*my*mz/(hx*hx);
09324                     denl[1][0] += fnl*dmx*dmy*mz/(hy*hx);
09325                     denl[1][1] += fnl*mx*d2my*mz/(hy*hy);
09326                     denl[2][0] += fnl*dmx*my*dmz/(hx*hzed);
09327                     denl[2][1] += fnl*mx*dmy*dmz/(hy*hzed);
09328                     denl[2][2] += fnl*mx*my*d2mz/(hzed*hzed);
09329                 }
09330             }
09331         }
09332     }
09333 
09334     /* mutual polarization force */
09335     force[0] = -(de[0][0]*uixnl + de[1][0]*uiynl + de[2][0]*uiznl);   
09336     force[1] = -(de[1][0]*uixnl + de[1][1]*uiynl + de[2][1]*uiznl);   
09337     force[2] = -(de[2][0]*uixnl + de[2][1]*uiynl + de[2][2]*uiznl);   
09338     force[0] -=  denl[0][0]*uix + denl[1][0]*uiy + denl[2][0]*uiz;   
09339     force[1] -=  denl[1][0]*uix + denl[1][1]*uiy + denl[2][1]*uiz;   
09340     force[2] -=  denl[2][0]*uix + denl[2][1]*uiy + denl[2][2]*uiz;   
09341 
09342     force[0] = 0.5 * force[0];
09343     force[1] = 0.5 * force[1];
09344     force[2] = 0.5 * force[2];
09345 
09346 }
09347 
09348 VPUBLIC void Vpmg_ibMutualPolForce(Vpmg *thee, Vgrid *induced, Vgrid *nlinduced,
09349                                    int atomID, double force[3]) {
09350 
09351     Vatom *atom;
09352     Valist *alist;
09353     Vacc *acc;
09354     Vpbe *pbe;
09355     Vsurf_Meth srfm;
09356 
09357     double *apos, position[3], arad, irad, zkappa2, hx, hy, hzed;
09358     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2;
09359     double rtot, dx, dx2, dy, dy2, dz, dz2, gpos[3], tgrad[3], fmag;
09360     double izmagic;
09361     int i, j, k, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
09362    
09363     VASSERT(thee != VNULL);        /* We need a PMG object with PBE info. */
09364     VASSERT(induced != VNULL);     /* We need the potential due to induced dipoles. */
09365     VASSERT(nlinduced != VNULL);   /* We need the potential due to non-local induced dipoles. */
09366     VASSERT (!thee->pmgp->nonlin); /* Nonlinear PBE is not implemented for AMOEBA */
09367    
09368     atom = Valist_getAtom(thee->pbe->alist, atomID);
09369  VASSERT (atom->partID != 0);   /* Currently all atoms must be in the same partition. */
09370  
09371     acc = thee->pbe->acc;
09372     srfm = thee->surfMeth;
09373     apos = Vatom_getPosition(atom);
09374     arad = Vatom_getRadius(atom);
09375 
09376     /* Reset force */
09377     force[0] = 0.0;
09378     force[1] = 0.0;
09379     force[2] = 0.0;
09380 
09381     /* If we aren't in the current position, then we're done */
09382     if (atom->partID == 0) return;
09383 
09384     /* Get PBE info */
09385     pbe = thee->pbe;
09386     acc = pbe->acc;
09387     alist = pbe->alist;
09388     irad = Vpbe_getMaxIonRadius(pbe);
09389     zkappa2 = Vpbe_getZkappa2(pbe);
09390     izmagic = 1.0/Vpbe_getZmagic(pbe);
09391 
09392     VASSERT (zkappa2 > VPMGSMALL); /* Should be a check for this further up.*/
09393     
09394     /* Mesh info */
09395     nx = induced->nx;
09396     ny = induced->ny;
09397     nz = induced->nz;
09398     hx = induced->hx;
09399     hy = induced->hy;
09400     hzed = induced->hzed;
09401     xmin = induced->xmin;
09402     ymin = induced->ymin;
09403     zmin = induced->zmin;
09404     xmax = induced->xmax;
09405     ymax = induced->ymax;
09406     zmax = induced->zmax;
09407     xlen = xmax-xmin;
09408     ylen = ymax-ymin;
09409     zlen = zmax-zmin;
09410 
09411     /* Make sure we're on the grid */
09412     if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
09413       (apos[1]<=ymin) || (apos[1]>=ymax)  || \
09414       (apos[2]<=zmin) || (apos[2]>=zmax)) {
09415         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]);
09416         Vnm_print(2, "Vpmg_ibMutalPolForce:    xmin = %g, xmax = %g\n", xmin, xmax);
09417         Vnm_print(2, "Vpmg_ibMutalPolForce:    ymin = %g, ymax = %g\n", ymin, ymax);
09418         Vnm_print(2, "Vpmg_ibMutalPolForce:    zmin = %g, zmax = %g\n", zmin, zmax);
09419         fflush(stderr);
09420     } else {
09421 
09422         /* Convert the atom position to grid reference frame */
09423         position[0] = apos[0] - xmin;
09424         position[1] = apos[1] - ymin;
09425         position[2] = apos[2] - zmin;
09426 
09427         /* Integrate over points within this atom's (inflated) radius */
09428         rtot = (irad + arad + thee->splineWin);
09429         rtot2 = VSQR(rtot);
09430         dx = rtot + 0.5*hx;
09431         imin = VMAX2(0,(int)ceil((position[0] - dx)/hx));
09432         imax = VMIN2(nx-1,(int)floor((position[0] + dx)/hx));
09433         for (i=imin; i<=imax; i++) { 
09434             dx2 = VSQR(position[0] - hx*i);
09435             if (rtot2 > dx2) dy = VSQRT(rtot2 - dx2) + 0.5*hy;
09436             else dy = 0.5*hy;
09437             jmin = VMAX2(0,(int)ceil((position[1] - dy)/hy));
09438             jmax = VMIN2(ny-1,(int)floor((position[1] + dy)/hy));
09439             for (j=jmin; j<=jmax; j++) { 
09440                 dy2 = VSQR(position[1] - hy*j);
09441                 if (rtot2 > (dx2+dy2)) dz = VSQRT(rtot2-dx2-dy2)+0.5*hzed;
09442                 else dz = 0.5*hzed;
09443                 kmin = VMAX2(0,(int)ceil((position[2] - dz)/hzed));
09444                 kmax = VMIN2(nz-1,(int)floor((position[2] + dz)/hzed));
09445                 for (k=kmin; k<=kmax; k++) {
09446                     dz2 = VSQR(k*hzed - position[2]);
09447                     /* See if grid point is inside ivdw radius and set ccf
09448                      * accordingly (do spline assignment here) */
09449                     if ((dz2 + dy2 + dx2) <= rtot2) {
09450                         gpos[0] = i*hx + xmin;
09451                         gpos[1] = j*hy + ymin;
09452                         gpos[2] = k*hzed + zmin;
09453                         Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, irad,
09454                           atom, tgrad);
09455                         fmag = induced->data[IJK(i,j,k)];
09456                         fmag *= nlinduced->data[IJK(i,j,k)];
09457                         fmag *= thee->kappa[IJK(i,j,k)];
09458                         force[0] += (zkappa2*fmag*tgrad[0]);
09459                         force[1] += (zkappa2*fmag*tgrad[1]);
09460                         force[2] += (zkappa2*fmag*tgrad[2]);
09461                     }
09462                 } /* k loop */
09463             } /* j loop */
09464         } /* i loop */
09465     } 
09466 
09467     force[0] = force[0] * 0.5 * hx * hy * hzed * izmagic;
09468     force[1] = force[1] * 0.5 * hx * hy * hzed * izmagic;
09469     force[2] = force[2] * 0.5 * hx * hy * hzed * izmagic;
09470 }
09471 
09472 VPUBLIC void Vpmg_dbMutualPolForce(Vpmg *thee, Vgrid *induced, 
09473                                    Vgrid *nlinduced, int atomID, 
09474                                    double force[3]) {
09475 
09476     Vatom *atom;
09477     Vacc *acc;
09478     Vpbe *pbe;
09479     Vsurf_Meth srfm;
09480 
09481     double *apos, position[3], arad, hx, hy, hzed, izmagic, deps, depsi;
09482     double xlen, ylen, zlen, xmin, ymin, zmin, xmax, ymax, zmax, rtot2, epsp;
09483     double rtot, dx, gpos[3], tgrad[3], dbFmag, epsw, kT;
09484     double *u, *unl, Hxijk, Hyijk, Hzijk, Hxim1jk, Hyijm1k, Hzijkm1;
09485     double dHxijk[3], dHyijk[3], dHzijk[3], dHxim1jk[3], dHyijm1k[3]; 
09486     double dHzijkm1[3];
09487     int i, j, k, l, nx, ny, nz, imin, imax, jmin, jmax, kmin, kmax;
09488 
09489     VASSERT(thee != VNULL); /* PMG object with PBE info. */
09490     VASSERT(induced != VNULL); /* potential due to induced dipoles.*/
09491     VASSERT(nlinduced != VNULL); /* potential due to non-local induced dipoles.*/
09492     
09493     acc = thee->pbe->acc;
09494     srfm = thee->surfMeth;
09495     atom = Valist_getAtom(thee->pbe->alist, atomID);
09496     VASSERT (atom->partID != 0); /* all atoms must be in the same partition.*/
09497     apos = Vatom_getPosition(atom);
09498     arad = Vatom_getRadius(atom);
09499 
09500     /* Reset force */
09501     force[0] = 0.0;
09502     force[1] = 0.0;
09503     force[2] = 0.0;
09504 
09505     /* Get PBE info */
09506     pbe = thee->pbe;
09507     acc = pbe->acc;
09508     epsp = Vpbe_getSoluteDiel(pbe);
09509     epsw = Vpbe_getSolventDiel(pbe);
09510     kT = Vpbe_getTemperature(pbe)*(1e-3)*Vunit_Na*Vunit_kb;
09511     izmagic = 1.0/Vpbe_getZmagic(pbe);
09512 
09513     deps = (epsw - epsp);
09514     depsi = 1.0/deps;
09515     VASSERT(VABS(deps) > VPMGSMALL);
09516 
09517     /* Mesh info */
09518     nx = thee->pmgp->nx;
09519     ny = thee->pmgp->ny;
09520     nz = thee->pmgp->nz;
09521     hx = thee->pmgp->hx;
09522     hy = thee->pmgp->hy;
09523     hzed = thee->pmgp->hzed;
09524     xlen = thee->pmgp->xlen;
09525     ylen = thee->pmgp->ylen;
09526     zlen = thee->pmgp->zlen;
09527     xmin = thee->pmgp->xmin;
09528     ymin = thee->pmgp->ymin;
09529     zmin = thee->pmgp->zmin;
09530     xmax = thee->pmgp->xmax;
09531     ymax = thee->pmgp->ymax;
09532     zmax = thee->pmgp->zmax;
09533     u = induced->data;
09534     unl = nlinduced->data;
09535 
09536     /* Make sure we're on the grid */
09537     if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
09538       (apos[1]<=ymin) || (apos[1]>=ymax)  || \
09539       (apos[2]<=zmin) || (apos[2]>=zmax)) {
09540         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]);
09541         Vnm_print(2, "Vpmg_dbMutualPolForce:    xmin = %g, xmax = %g\n", xmin, xmax);
09542         Vnm_print(2, "Vpmg_dbMutualPolForce:    ymin = %g, ymax = %g\n", ymin, ymax); 
09543         Vnm_print(2, "Vpmg_dbMutualPolForce:    zmin = %g, zmax = %g\n", zmin, zmax);
09544         fflush(stderr);
09545     } else {
09546 
09547         /* Convert the atom position to grid reference frame */
09548         position[0] = apos[0] - xmin;
09549         position[1] = apos[1] - ymin;
09550         position[2] = apos[2] - zmin;
09551 
09552         /* Integrate over points within this atom's (inflated) radius */
09553         rtot = (arad + thee->splineWin);
09554         rtot2 = VSQR(rtot);
09555         dx = rtot/hx;
09556         imin = (int)floor((position[0]-rtot)/hx);
09557         if (imin < 1) {
09558             Vnm_print(2, "Vpmg_dbMutualPolForce:  Atom %d off grid!\n", atomID); 
09559             return;
09560         }
09561         imax = (int)ceil((position[0]+rtot)/hx);
09562         if (imax > (nx-2)) {
09563             Vnm_print(2, "Vpmg_dbMutualPolForce:  Atom %d off grid!\n", atomID); 
09564             return;
09565         }
09566         jmin = (int)floor((position[1]-rtot)/hy);
09567         if (jmin < 1) {
09568             Vnm_print(2, "Vpmg_dbMutualPolForce:  Atom %d off grid!\n", atomID); 
09569             return;
09570         }
09571         jmax = (int)ceil((position[1]+rtot)/hy);
09572         if (jmax > (ny-2)) {
09573             Vnm_print(2, "Vpmg_dbMutualPolForce:  Atom %d off grid!\n", atomID); 
09574             return;
09575         }
09576         kmin = (int)floor((position[2]-rtot)/hzed);
09577         if (kmin < 1) {
09578             Vnm_print(2, "Vpmg_dbMutualPolForce:  Atom %d off grid!\n", atomID); 
09579             return;
09580         }
09581         kmax = (int)ceil((position[2]+rtot)/hzed);
09582         if (kmax > (nz-2)) {
09583             Vnm_print(2, "Vpmg_dbMutualPolForce:  Atom %d off grid!\n", atomID); 
09584             return;
09585         }
09586         for (i=imin; i<=imax; i++) {
09587             for (j=jmin; j<=jmax; j++) {
09588                 for (k=kmin; k<=kmax; k++) {
09589                     /* i,j,k */
09590                     gpos[0] = (i+0.5)*hx + xmin;
09591                     gpos[1] = j*hy + ymin;
09592                     gpos[2] = k*hzed + zmin;
09593                     Hxijk = (thee->epsx[IJK(i,j,k)] - epsp)*depsi;
09594                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
09595                             atom, dHxijk);
09596                     for (l=0; l<3; l++) dHxijk[l] *= Hxijk;
09597                     gpos[0] = i*hx + xmin;
09598                     gpos[1] = (j+0.5)*hy + ymin;
09599                     gpos[2] = k*hzed + zmin;
09600                     Hyijk = (thee->epsy[IJK(i,j,k)] - epsp)*depsi;
09601                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
09602                             atom, dHyijk);
09603                     for (l=0; l<3; l++) dHyijk[l] *= Hyijk;
09604                     gpos[0] = i*hx + xmin;
09605                     gpos[1] = j*hy + ymin;
09606                     gpos[2] = (k+0.5)*hzed + zmin;
09607                     Hzijk = (thee->epsz[IJK(i,j,k)] - epsp)*depsi;
09608                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0., 
09609                             atom, dHzijk);
09610                     for (l=0; l<3; l++) dHzijk[l] *= Hzijk;
09611                     /* i-1,j,k */
09612                     gpos[0] = (i-0.5)*hx + xmin;
09613                     gpos[1] = j*hy + ymin;
09614                     gpos[2] = k*hzed + zmin;
09615                     Hxim1jk = (thee->epsx[IJK(i-1,j,k)] - epsp)*depsi;
09616                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
09617                             atom, dHxim1jk);
09618                     for (l=0; l<3; l++) dHxim1jk[l] *= Hxim1jk;
09619                     /* i,j-1,k */
09620                     gpos[0] = i*hx + xmin;
09621                     gpos[1] = (j-0.5)*hy + ymin;
09622                     gpos[2] = k*hzed + zmin;
09623                     Hyijm1k = (thee->epsy[IJK(i,j-1,k)] - epsp)*depsi;
09624                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
09625                             atom, dHyijm1k);
09626                     for (l=0; l<3; l++) dHyijm1k[l] *= Hyijm1k;
09627                     /* i,j,k-1 */
09628                     gpos[0] = i*hx + xmin;
09629                     gpos[1] = j*hy + ymin;
09630                     gpos[2] = (k-0.5)*hzed + zmin;
09631                     Hzijkm1 = (thee->epsz[IJK(i,j,k-1)] - epsp)*depsi;
09632                     Vpmg_splineSelect(srfm, acc, gpos, thee->splineWin, 0.,
09633                             atom, dHzijkm1);
09634                     for (l=0; l<3; l++) dHzijkm1[l] *= Hzijkm1;
09635                     dbFmag = unl[IJK(i,j,k)];
09636                     tgrad[0] = 
09637                        (dHxijk[0]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
09638                      +  dHxim1jk[0]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
09639                      + (dHyijk[0]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
09640                      +  dHyijm1k[0]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
09641                      + (dHzijk[0]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
09642                      + dHzijkm1[0]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
09643                     tgrad[1] = 
09644                        (dHxijk[1]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
09645                      +  dHxim1jk[1]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
09646                      + (dHyijk[1]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
09647                      +  dHyijm1k[1]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
09648                      + (dHzijk[1]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
09649                      + dHzijkm1[1]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
09650                     tgrad[2] = 
09651                        (dHxijk[2]  *(u[IJK(i+1,j,k)]-u[IJK(i,j,k)])
09652                      +  dHxim1jk[2]*(u[IJK(i-1,j,k)]-u[IJK(i,j,k)]))/VSQR(hx)
09653                      + (dHyijk[2]  *(u[IJK(i,j+1,k)]-u[IJK(i,j,k)])
09654                      +  dHyijm1k[2]*(u[IJK(i,j-1,k)]-u[IJK(i,j,k)]))/VSQR(hy)
09655                      + (dHzijk[2]  *(u[IJK(i,j,k+1)]-u[IJK(i,j,k)])
09656                      + dHzijkm1[2]*(u[IJK(i,j,k-1)]-u[IJK(i,j,k)]))/VSQR(hzed);
09657                      force[0] += (dbFmag*tgrad[0]);
09658                      force[1] += (dbFmag*tgrad[1]);
09659                      force[2] += (dbFmag*tgrad[2]);
09660                 } /* k loop */
09661             } /* j loop */
09662         } /* i loop */
09663         
09664         force[0] = -force[0]*hx*hy*hzed*deps*0.5*izmagic;
09665         force[1] = -force[1]*hx*hy*hzed*deps*0.5*izmagic;
09666         force[2] = -force[2]*hx*hy*hzed*deps*0.5*izmagic;
09667     }
09668 }
09669 
09670 #endif /* if defined(WITH_TINKER) */
09671 
09672 VPRIVATE void fillcoCoefSpline4(Vpmg *thee) {
09673 
09674     Valist *alist;
09675     Vpbe *pbe;
09676     Vatom *atom;
09677     double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
09678     double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
09679     double irad, dx, dy, dz, epsw, epsp, w2i;
09680     double hx, hy, hzed, *apos, arad, sctot2;
09681     double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin;
09682     double dist, value, denom, sm, sm2, sm3, sm4, sm5, sm6, sm7;
09683     double e, e2, e3, e4, e5, e6, e7;
09684     double b, b2, b3, b4, b5, b6, b7;
09685     double c0, c1, c2, c3, c4, c5, c6, c7;
09686     double ic0, ic1, ic2, ic3, ic4, ic5, ic6, ic7;
09687     int i, j, k, nx, ny, nz, iatom;
09688     int imin, imax, jmin, jmax, kmin, kmax;
09689 
09690     VASSERT(thee != VNULL);
09691     splineWin = thee->splineWin;
09692 
09693     /* Get PBE info */
09694     pbe = thee->pbe;
09695     alist = pbe->alist;
09696     irad = Vpbe_getMaxIonRadius(pbe);
09697     ionstr = Vpbe_getBulkIonicStrength(pbe);
09698     epsw = Vpbe_getSolventDiel(pbe);
09699     epsp = Vpbe_getSoluteDiel(pbe);
09700 
09701     /* Mesh info */
09702     nx = thee->pmgp->nx;
09703     ny = thee->pmgp->ny;
09704     nz = thee->pmgp->nz;
09705     hx = thee->pmgp->hx;
09706     hy = thee->pmgp->hy;
09707     hzed = thee->pmgp->hzed;
09708 
09709     /* Define the total domain size */
09710     xlen = thee->pmgp->xlen;
09711     ylen = thee->pmgp->ylen;
09712     zlen = thee->pmgp->zlen;
09713 
09714     /* Define the min/max dimensions */
09715     xmin = thee->pmgp->xcent - (xlen/2.0);
09716     ymin = thee->pmgp->ycent - (ylen/2.0);
09717     zmin = thee->pmgp->zcent - (zlen/2.0);
09718     xmax = thee->pmgp->xcent + (xlen/2.0);
09719     ymax = thee->pmgp->ycent + (ylen/2.0);
09720     zmax = thee->pmgp->zcent + (zlen/2.0);
09721 
09722     /* This is a floating point parameter related to the non-zero nature of the
09723      * bulk ionic strength.  If the ionic strength is greater than zero; this
09724      * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
09725      * Otherwise, this parameter is set to 0.0 */
09726     if (ionstr > VPMGSMALL) ionmask = 1.0;
09727     else ionmask = 0.0;
09728 
09729     /* Reset the kappa, epsx, epsy, and epsz arrays */
09730     for (i=0; i<(nx*ny*nz); i++) {
09731         thee->kappa[i] = 1.0;
09732         thee->epsx[i] = 1.0;
09733         thee->epsy[i] = 1.0;
09734         thee->epsz[i] = 1.0;
09735     }
09736 
09737     /* Loop through the atoms and do assign the dielectric */
09738     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
09739 
09740         atom = Valist_getAtom(alist, iatom);
09741         apos = Vatom_getPosition(atom);
09742         arad = Vatom_getRadius(atom);
09743 
09744         b = arad - splineWin; 
09745         e = arad + splineWin;
09746         e2 = e * e;
09747         e3 = e2 * e;
09748         e4 = e3 * e;
09749         e5 = e4 * e;
09750         e6 = e5 * e;
09751         e7 = e6 * e;
09752         b2 = b * b;
09753         b3 = b2 * b;
09754         b4 = b3 * b;
09755         b5 = b4 * b;
09756         b6 = b5 * b;
09757         b7 = b6 * b;
09758         denom = e7  - 7.0*b*e6 + 21.0*b2*e5 - 35.0*e4*b3 
09759               + 35.0*e3*b4 - 21.0*b5*e2  + 7.0*e*b6 - b7;
09760         c0 = b4*(35.0*e3 - 21.0*b*e2 + 7*e*b2 - b3)/denom;
09761         c1 = -140.0*b3*e3/denom;
09762         c2 = 210.0*e2*b2*(e + b)/denom;
09763         c3 = -140.0*e*b*(e2 + 3.0*b*e + b2)/denom;
09764         c4 =  35.0*(e3 + 9.0*b*e2 + + 9.0*e*b2 + b3)/denom;
09765         c5 = -84.0*(e2 + 3.0*b*e + b2)/denom;
09766         c6 =  70.0*(e + b)/denom;
09767         c7 = -20.0/denom;
09768 
09769         b = irad + arad - splineWin;
09770         e = irad + arad + splineWin;
09771         e2 = e * e;
09772         e3 = e2 * e;
09773         e4 = e3 * e;
09774         e5 = e4 * e;
09775         e6 = e5 * e;
09776         e7 = e6 * e;
09777         b2 = b * b;
09778         b3 = b2 * b;
09779         b4 = b3 * b;
09780         b5 = b4 * b;
09781         b6 = b5 * b;
09782         b7 = b6 * b;
09783         denom = e7  - 7.0*b*e6 + 21.0*b2*e5 - 35.0*e4*b3
09784               + 35.0*e3*b4 - 21.0*b5*e2  + 7.0*e*b6 - b7;
09785         ic0 = b4*(35.0*e3 - 21.0*b*e2 + 7*e*b2 - b3)/denom;
09786         ic1 = -140.0*b3*e3/denom;
09787         ic2 = 210.0*e2*b2*(e + b)/denom;
09788         ic3 = -140.0*e*b*(e2 + 3.0*b*e + b2)/denom;
09789         ic4 =  35.0*(e3 + 9.0*b*e2 + + 9.0*e*b2 + b3)/denom;
09790         ic5 = -84.0*(e2 + 3.0*b*e + b2)/denom;
09791         ic6 =  70.0*(e + b)/denom;
09792         ic7 = -20.0/denom;
09793 
09794         /* Make sure we're on the grid */
09795         if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
09796             (apos[1]<=ymin) || (apos[1]>=ymax)  || \
09797             (apos[2]<=zmin) || (apos[2]>=zmax)) {
09798             if ((thee->pmgp->bcfl != BCFL_FOCUS) && 
09799     (thee->pmgp->bcfl != BCFL_MAP)) {
09800                 Vnm_print(2, "Vpmg_fillco:  Atom #%d at (%4.3f, %4.3f,\
09801  %4.3f) is off the mesh (ignoring):\n",
09802                   iatom, apos[0], apos[1], apos[2]);
09803                 Vnm_print(2, "Vpmg_fillco:    xmin = %g, xmax = %g\n", 
09804                   xmin, xmax);
09805                 Vnm_print(2, "Vpmg_fillco:    ymin = %g, ymax = %g\n", 
09806                   ymin, ymax);
09807                 Vnm_print(2, "Vpmg_fillco:    zmin = %g, zmax = %g\n", 
09808                   zmin, zmax);
09809             }
09810             fflush(stderr);
09811 
09812         } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
09813 
09814             /* Convert the atom position to grid reference frame */
09815             position[0] = apos[0] - xmin;
09816             position[1] = apos[1] - ymin;
09817             position[2] = apos[2] - zmin;
09818 
09819             /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
09820              * ASSIGNMENT (Steps #1-3) */
09821             itot = irad + arad + splineWin;
09822             itot2 = VSQR(itot);     
09823             ictot = VMAX2(0, (irad + arad - splineWin));
09824             ictot2 = VSQR(ictot);
09825             stot = arad + splineWin;
09826             stot2 = VSQR(stot);
09827             sctot = VMAX2(0, (arad - splineWin));
09828             sctot2 = VSQR(sctot);
09829 
09830            /* We'll search over grid points which are in the greater of
09831              * these two radii */
09832             rtot = VMAX2(itot, stot);
09833             rtot2 = VMAX2(itot2, stot2);
09834             dx = rtot + 0.5*hx;
09835             dy = rtot + 0.5*hy;
09836             dz = rtot + 0.5*hzed;
09837             imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
09838             imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
09839             jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
09840             jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
09841             kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
09842             kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
09843             for (i=imin; i<=imax; i++) {
09844                 dx2 = VSQR(position[0] - hx*i);
09845                 for (j=jmin; j<=jmax; j++) {
09846                     dy2 = VSQR(position[1] - hy*j);
09847                     for (k=kmin; k<=kmax; k++) {
09848                         dz2 = VSQR(position[2] - k*hzed);
09849 
09850                         /* ASSIGN CCF */
09851                         if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
09852                             dist2 = dz2 + dy2 + dx2;
09853                             if (dist2 >= itot2) {
09854                                 ;
09855                             } 
09856                             if (dist2 <= ictot2) {
09857                                 thee->kappa[IJK(i,j,k)] = 0.0;
09858                             }
09859                             if ((dist2 < itot2) && (dist2 > ictot2)) {
09860                                 dist = VSQRT(dist2);
09861                                 sm = dist;
09862                                 sm2 = dist2;
09863                                 sm3 = sm2 * sm;
09864                                 sm4 = sm3 * sm;
09865                                 sm5 = sm4 * sm;
09866                                 sm6 = sm5 * sm;
09867                                 sm7 = sm6 * sm;
09868                                 value = ic0 + ic1*sm + ic2*sm2 + ic3*sm3
09869                                       + ic4*sm4 + ic5*sm5 + ic6*sm6 + ic7*sm7;
09870                                 if (value > 1.0) {
09871                                    value = 1.0;
09872                                 } else if (value < 0.0){
09873                                    value = 0.0; 
09874                                 }
09875                                 thee->kappa[IJK(i,j,k)] *= value;
09876                             }
09877                         }
09878 
09879                         /* ASSIGN A1CF */
09880                         if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
09881                             dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
09882                             if (dist2 >= stot2) {
09883                                 thee->epsx[IJK(i,j,k)] *= 1.0;
09884                             } 
09885                             if (dist2 <= sctot2) {
09886                                 thee->epsx[IJK(i,j,k)] = 0.0;
09887                             } 
09888                             if ((dist2 > sctot2) && (dist2 < stot2)) {
09889                                 dist = VSQRT(dist2);
09890                                 sm = dist;
09891                                 sm2 = VSQR(sm);
09892                                 sm3 = sm2 * sm;
09893                                 sm4 = sm3 * sm;
09894                                 sm5 = sm4 * sm;
09895                                 sm6 = sm5 * sm;
09896                                 sm7 = sm6 * sm;
09897                                 value = c0 + c1*sm + c2*sm2 + c3*sm3
09898                                       + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
09899                                 if (value > 1.0) {
09900                                    value = 1.0;
09901                                 } else if (value < 0.0){
09902                                    value = 0.0; 
09903                                 }
09904                                 thee->epsx[IJK(i,j,k)] *= value;
09905                             } 
09906                         }
09907 
09908                         /* ASSIGN A2CF */
09909                         if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
09910                             dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
09911                             if (dist2 >= stot2) {
09912                                 thee->epsy[IJK(i,j,k)] *= 1.0;
09913                             } 
09914                             if (dist2 <= sctot2) {
09915                                 thee->epsy[IJK(i,j,k)] = 0.0;
09916                             }
09917                             if ((dist2 > sctot2) && (dist2 < stot2)) {
09918                                 dist = VSQRT(dist2);
09919                                 sm = dist;
09920                                 sm2 = VSQR(sm);
09921                                 sm3 = sm2 * sm;
09922                                 sm4 = sm3 * sm;
09923                                 sm5 = sm4 * sm;
09924                                 sm6 = sm5 * sm;
09925                                 sm7 = sm6 * sm;
09926                                 value = c0 + c1*sm + c2*sm2 + c3*sm3
09927                                       + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
09928                                 if (value > 1.0) {
09929                                    value = 1.0;
09930                                 } else if (value < 0.0){
09931                                    value = 0.0; 
09932                                 }
09933                                 thee->epsy[IJK(i,j,k)] *= value;
09934                             }
09935                         }
09936 
09937                         /* ASSIGN A3CF */
09938                         if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
09939                             dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
09940                             if (dist2 >= stot2) {
09941                                 thee->epsz[IJK(i,j,k)] *= 1.0;
09942                             } 
09943                             if (dist2 <= sctot2) {
09944                                 thee->epsz[IJK(i,j,k)] = 0.0;
09945                             } 
09946                             if ((dist2 > sctot2) && (dist2 < stot2)) {
09947                                 dist = VSQRT(dist2);
09948                                 sm = dist;
09949                                 sm2 = dist2;
09950                                 sm3 = sm2 * sm;
09951                                 sm4 = sm3 * sm;
09952                                 sm5 = sm4 * sm;
09953                                 sm6 = sm5 * sm;
09954                                 sm7 = sm6 * sm;
09955                                 value = c0 + c1*sm + c2*sm2 + c3*sm3
09956                                       + c4*sm4 + c5*sm5 + c6*sm6 + c7*sm7;
09957                                 if (value > 1.0) {
09958                                    value = 1.0;
09959                                 } else if (value < 0.0){
09960                                    value = 0.0; 
09961                                 }
09962                                 thee->epsz[IJK(i,j,k)] *= value;
09963                             }
09964                         }
09965 
09966 
09967                     } /* k loop */
09968                 } /* j loop */
09969             } /* i loop */
09970         } /* endif (on the mesh) */
09971     } /* endfor (over all atoms) */
09972 
09973     Vnm_print(0, "Vpmg_fillco:  filling coefficient arrays\n");
09974     /* Interpret markings and fill the coefficient arrays */
09975     for (k=0; k<nz; k++) {
09976         for (j=0; j<ny; j++) {
09977             for (i=0; i<nx; i++) {
09978 
09979                 thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
09980                 thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)] 
09981                   + epsp;
09982                 thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)] 
09983                   + epsp;
09984                 thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)] 
09985                   + epsp;
09986 
09987             } /* i loop */
09988         } /* j loop */
09989     } /* k loop */
09990 
09991 }
09992 
09993 VPUBLIC void fillcoPermanentInduced(Vpmg *thee) {
09994 
09995     Valist *alist;
09996     Vpbe *pbe;
09997     Vatom *atom;
09998     /* Coversions */
09999     double zmagic, f;
10000     /* Grid */
10001     double xmin, xmax, ymin, ymax, zmin, zmax;
10002     double xlen, ylen, zlen, position[3], ifloat, jfloat, kfloat;
10003     double hx, hy, hzed, *apos;
10004     /* Multipole */
10005     double charge, *dipole,*quad;
10006     double c,ux,uy,uz,qxx,qyx,qyy,qzx,qzy,qzz,qave;
10007     /* B-spline weights */
10008     double mx,my,mz,dmx,dmy,dmz,d2mx,d2my,d2mz;
10009     double mi,mj,mk;
10010     /* Loop variables */
10011     int i, ii, jj, kk, nx, ny, nz, iatom;
10012     int im2, im1, ip1, ip2, jm2, jm1, jp1, jp2, km2, km1, kp1, kp2;
10013 
10014     VASSERT(thee != VNULL);
10015 
10016     /* Get PBE info */
10017     pbe = thee->pbe;
10018     alist = pbe->alist;
10019     zmagic = Vpbe_getZmagic(pbe);
10020 
10021     /* Mesh info */
10022     nx = thee->pmgp->nx;
10023     ny = thee->pmgp->ny;
10024     nz = thee->pmgp->nz;
10025     hx = thee->pmgp->hx;
10026     hy = thee->pmgp->hy;
10027     hzed = thee->pmgp->hzed;
10028 
10029     /* Conversion */
10030     f = zmagic/(hx*hy*hzed);
10031 
10032     /* Define the total domain size */
10033     xlen = thee->pmgp->xlen;
10034     ylen = thee->pmgp->ylen;
10035     zlen = thee->pmgp->zlen;
10036 
10037     /* Define the min/max dimensions */
10038     xmin = thee->pmgp->xcent - (xlen/2.0);
10039     ymin = thee->pmgp->ycent - (ylen/2.0);
10040     zmin = thee->pmgp->zcent - (zlen/2.0);
10041     xmax = thee->pmgp->xcent + (xlen/2.0);
10042     ymax = thee->pmgp->ycent + (ylen/2.0);
10043     zmax = thee->pmgp->zcent + (zlen/2.0);
10044 
10045     /* Fill in the source term (permanent atomic multipoles
10046        and induced dipoles) */
10047     Vnm_print(0, "fillcoPermanentInduced:  filling in source term.\n");
10048     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
10049 
10050         atom = Valist_getAtom(alist, iatom);
10051         apos = Vatom_getPosition(atom);
10052 
10053         c = Vatom_getCharge(atom)*f;
10054 
10055 #if defined(WITH_TINKER)
10056         dipole = Vatom_getDipole(atom);
10057         ux = dipole[0]/hx*f;
10058         uy = dipole[1]/hy*f;
10059         uz = dipole[2]/hzed*f;
10060         dipole = Vatom_getInducedDipole(atom);
10061         ux = ux + dipole[0]/hx*f;
10062         uy = uy + dipole[1]/hy*f;
10063         uz = uz + dipole[2]/hzed*f;
10064         quad = Vatom_getQuadrupole(atom);
10065         qxx = (1.0/3.0)*quad[0]/(hx*hx)*f;
10066         qyx = (2.0/3.0)*quad[3]/(hx*hy)*f;
10067         qyy = (1.0/3.0)*quad[4]/(hy*hy)*f;
10068         qzx = (2.0/3.0)*quad[6]/(hzed*hx)*f;
10069         qzy = (2.0/3.0)*quad[7]/(hzed*hy)*f;
10070         qzz = (1.0/3.0)*quad[8]/(hzed*hzed)*f;
10071 #else
10072         ux = 0.0;
10073         uy = 0.0;
10074         uz = 0.0;
10075         qxx = 0.0;
10076         qyx = 0.0;
10077         qyy = 0.0;
10078         qzx = 0.0;
10079         qzy = 0.0;
10080         qzz = 0.0;
10081 #endif /* if defined(WITH_TINKER) */
10082 
10083         /* Make sure we're on the grid */
10084         if ((apos[0]<=(xmin-2*hx)) || (apos[0]>=(xmax+2*hx))  || \
10085             (apos[1]<=(ymin-2*hy)) || (apos[1]>=(ymax+2*hy))  || \
10086             (apos[2]<=(zmin-2*hzed)) || (apos[2]>=(zmax+2*hzed))) {
10087             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]);
10088             Vnm_print(2, "fillcoPermanentMultipole: xmin = %g, xmax = %g\n", xmin, xmax);
10089             Vnm_print(2, "fillcoPermanentMultipole: ymin = %g, ymax = %g\n", ymin, ymax);
10090             Vnm_print(2, "fillcoPermanentMultipole: zmin = %g, zmax = %g\n", zmin, zmax);
10091             fflush(stderr);
10092         } else {
10093 
10094             /* Convert the atom position to grid reference frame */
10095             position[0] = apos[0] - xmin;
10096             position[1] = apos[1] - ymin;
10097             position[2] = apos[2] - zmin;
10098 
10099             /* Figure out which vertices we're next to */
10100             ifloat = position[0]/hx;
10101             jfloat = position[1]/hy;
10102             kfloat = position[2]/hzed;
10103 
10104             ip1   = (int)ceil(ifloat);
10105             ip2   = ip1 + 2;
10106             im1   = (int)floor(ifloat);
10107             im2   = im1 - 2;
10108             jp1   = (int)ceil(jfloat);
10109             jp2   = jp1 + 2;
10110             jm1   = (int)floor(jfloat);
10111             jm2   = jm1 - 2;
10112             kp1   = (int)ceil(kfloat);
10113             kp2   = kp1 + 2;
10114             km1   = (int)floor(kfloat);
10115             km2   = km1 - 2;
10116 
10117             /* This step shouldn't be necessary, but it saves nasty debugging
10118              * later on if something goes wrong */
10119             ip2 = VMIN2(ip2,nx-1);
10120             ip1 = VMIN2(ip1,nx-1);
10121             im1 = VMAX2(im1,0);
10122             im2 = VMAX2(im2,0);
10123             jp2 = VMIN2(jp2,ny-1);
10124             jp1 = VMIN2(jp1,ny-1);
10125             jm1 = VMAX2(jm1,0);
10126             jm2 = VMAX2(jm2,0);
10127             kp2 = VMIN2(kp2,nz-1);
10128             kp1 = VMIN2(kp1,nz-1);
10129             km1 = VMAX2(km1,0);
10130             km2 = VMAX2(km2,0);
10131 
10132             /* Now assign fractions of the charge to the nearby verts */
10133             for (ii=im2; ii<=ip2; ii++) {
10134                 mi = VFCHI4(ii,ifloat);
10135                 mx = bspline4(mi);
10136                 dmx = dbspline4(mi);
10137                 d2mx = d2bspline4(mi);
10138                 for (jj=jm2; jj<=jp2; jj++) {
10139                     mj = VFCHI4(jj,jfloat);
10140                     my = bspline4(mj);
10141                     dmy = dbspline4(mj);
10142                     d2my = d2bspline4(mj);
10143                     for (kk=km2; kk<=kp2; kk++) {
10144                         mk = VFCHI4(kk,kfloat);
10145                         mz = bspline4(mk);
10146                         dmz = dbspline4(mk);
10147                         d2mz = d2bspline4(mk);
10148                         charge = mx*my*mz*c -
10149                          dmx*my*mz*ux - mx*dmy*mz*uy - mx*my*dmz*uz +
10150                          d2mx*my*mz*qxx +
10151                          dmx*dmy*mz*qyx + mx*d2my*mz*qyy +
10152                          dmx*my*dmz*qzx + mx*dmy*dmz*qzy + mx*my*d2mz*qzz;
10153                         thee->charge[IJK(ii,jj,kk)] += charge;
10154 
10155                     }
10156                 }
10157             }
10158         } /* endif (on the mesh) */
10159 
10160     } /* endfor (each atom) */
10161 }
10162 
10163 VPRIVATE void fillcoCoefSpline3(Vpmg *thee) {
10164 
10165     Valist *alist;
10166     Vpbe *pbe;
10167     Vatom *atom;
10168     double xmin, xmax, ymin, ymax, zmin, zmax, ionmask, ionstr, dist2;
10169     double xlen, ylen, zlen, position[3], itot, stot, ictot, ictot2, sctot;
10170     double irad, dx, dy, dz, epsw, epsp, w2i;
10171     double hx, hy, hzed, *apos, arad, sctot2;
10172     double dx2, dy2, dz2, stot2, itot2, rtot, rtot2, splineWin;
10173     double dist, value, denom, sm, sm2, sm3, sm4, sm5;
10174     double e, e2, e3, e4, e5;
10175     double b, b2, b3, b4, b5;
10176     double c0, c1, c2, c3, c4, c5;
10177     double ic0, ic1, ic2, ic3, ic4, ic5;
10178     int i, j, k, nx, ny, nz, iatom;
10179     int imin, imax, jmin, jmax, kmin, kmax;
10180 
10181     VASSERT(thee != VNULL);
10182     splineWin = thee->splineWin;
10183 
10184     /* Get PBE info */
10185     pbe = thee->pbe;
10186     alist = pbe->alist;
10187     irad = Vpbe_getMaxIonRadius(pbe);
10188     ionstr = Vpbe_getBulkIonicStrength(pbe);
10189     epsw = Vpbe_getSolventDiel(pbe);
10190     epsp = Vpbe_getSoluteDiel(pbe);
10191 
10192     /* Mesh info */
10193     nx = thee->pmgp->nx;
10194     ny = thee->pmgp->ny;
10195     nz = thee->pmgp->nz;
10196     hx = thee->pmgp->hx;
10197     hy = thee->pmgp->hy;
10198     hzed = thee->pmgp->hzed;
10199 
10200     /* Define the total domain size */
10201     xlen = thee->pmgp->xlen;
10202     ylen = thee->pmgp->ylen;
10203     zlen = thee->pmgp->zlen;
10204 
10205     /* Define the min/max dimensions */
10206     xmin = thee->pmgp->xcent - (xlen/2.0);
10207     ymin = thee->pmgp->ycent - (ylen/2.0);
10208     zmin = thee->pmgp->zcent - (zlen/2.0);
10209     xmax = thee->pmgp->xcent + (xlen/2.0);
10210     ymax = thee->pmgp->ycent + (ylen/2.0);
10211     zmax = thee->pmgp->zcent + (zlen/2.0);
10212 
10213     /* This is a floating point parameter related to the non-zero nature of the
10214      * bulk ionic strength.  If the ionic strength is greater than zero; this
10215      * parameter is set to 1.0 and later scaled by the appropriate pre-factors.
10216      * Otherwise, this parameter is set to 0.0 */
10217     if (ionstr > VPMGSMALL) ionmask = 1.0;
10218     else ionmask = 0.0;
10219 
10220     /* Reset the kappa, epsx, epsy, and epsz arrays */
10221     for (i=0; i<(nx*ny*nz); i++) {
10222         thee->kappa[i] = 1.0;
10223         thee->epsx[i] = 1.0;
10224         thee->epsy[i] = 1.0;
10225         thee->epsz[i] = 1.0;
10226     }
10227 
10228     /* Loop through the atoms and do assign the dielectric */
10229     for (iatom=0; iatom<Valist_getNumberAtoms(alist); iatom++) {
10230 
10231         atom = Valist_getAtom(alist, iatom);
10232         apos = Vatom_getPosition(atom);
10233         arad = Vatom_getRadius(atom);
10234 
10235         b = arad - splineWin; 
10236         e = arad + splineWin;
10237         e2 = e * e;
10238         e3 = e2 * e;
10239         e4 = e3 * e;
10240         e5 = e4 * e;
10241         b2 = b * b;
10242         b3 = b2 * b;
10243         b4 = b3 * b;
10244         b5 = b4 * b;
10245         denom = pow((e - b), 5.0);
10246         c0 = -10.0*e2*b3 + 5.0*e*b4 - b5;
10247         c1 = 30.0*e2*b2;
10248         c2 = -30.0*(e2*b + e*b2);
10249         c3 = 10.0*(e2 + 4.0*e*b + b2);
10250         c4 = -15.0*(e + b);
10251         c5 = 6;
10252         c0 = c0/denom;
10253         c1 = c1/denom;
10254         c2 = c2/denom;
10255         c3 = c3/denom;
10256         c4 = c4/denom;
10257         c5 = c5/denom;
10258 
10259         b = irad + arad - splineWin;
10260         e = irad + arad + splineWin;
10261         e2 = e * e;
10262         e3 = e2 * e;
10263         e4 = e3 * e;
10264         e5 = e4 * e;
10265         b2 = b * b;
10266         b3 = b2 * b;
10267         b4 = b3 * b;
10268         b5 = b4 * b;
10269         denom = pow((e - b), 5.0);
10270         ic0 = -10.0*e2*b3 + 5.0*e*b4 - b5;
10271         ic1 = 30.0*e2*b2;
10272         ic2 = -30.0*(e2*b + e*b2);
10273         ic3 = 10.0*(e2 + 4.0*e*b + b2);
10274         ic4 = -15.0*(e + b);
10275         ic5 = 6;
10276         ic0 = c0/denom;
10277         ic1 = c1/denom;
10278         ic2 = c2/denom;
10279         ic3 = c3/denom;
10280         ic4 = c4/denom;
10281         ic5 = c5/denom;
10282 
10283         /* Make sure we're on the grid */
10284         if ((apos[0]<=xmin) || (apos[0]>=xmax)  || \
10285             (apos[1]<=ymin) || (apos[1]>=ymax)  || \
10286             (apos[2]<=zmin) || (apos[2]>=zmax)) {
10287    if ((thee->pmgp->bcfl != BCFL_FOCUS) &&
10288     (thee->pmgp->bcfl != BCFL_MAP)) {
10289                 Vnm_print(2, "Vpmg_fillco:  Atom #%d at (%4.3f, %4.3f,\
10290  %4.3f) is off the mesh (ignoring):\n",
10291                   iatom, apos[0], apos[1], apos[2]);
10292                 Vnm_print(2, "Vpmg_fillco:    xmin = %g, xmax = %g\n", 
10293                   xmin, xmax);
10294                 Vnm_print(2, "Vpmg_fillco:    ymin = %g, ymax = %g\n", 
10295                   ymin, ymax);
10296                 Vnm_print(2, "Vpmg_fillco:    zmin = %g, zmax = %g\n", 
10297                   zmin, zmax);
10298             }
10299             fflush(stderr);
10300 
10301         } else if (arad > VPMGSMALL ) { /* if we're on the mesh */
10302 
10303             /* Convert the atom position to grid reference frame */
10304             position[0] = apos[0] - xmin;
10305             position[1] = apos[1] - ymin;
10306             position[2] = apos[2] - zmin;
10307 
10308             /* MARK ION ACCESSIBILITY AND DIELECTRIC VALUES FOR LATER
10309              * ASSIGNMENT (Steps #1-3) */
10310             itot = irad + arad + splineWin;
10311             itot2 = VSQR(itot);     
10312             ictot = VMAX2(0, (irad + arad - splineWin));
10313             ictot2 = VSQR(ictot);
10314             stot = arad + splineWin;
10315             stot2 = VSQR(stot);
10316             sctot = VMAX2(0, (arad - splineWin));
10317             sctot2 = VSQR(sctot);
10318 
10319            /* We'll search over grid points which are in the greater of
10320              * these two radii */
10321             rtot = VMAX2(itot, stot);
10322             rtot2 = VMAX2(itot2, stot2);
10323             dx = rtot + 0.5*hx;
10324             dy = rtot + 0.5*hy;
10325             dz = rtot + 0.5*hzed;
10326             imin = VMAX2(0,(int)floor((position[0] - dx)/hx));
10327             imax = VMIN2(nx-1,(int)ceil((position[0] + dx)/hx));
10328             jmin = VMAX2(0,(int)floor((position[1] - dy)/hy));
10329             jmax = VMIN2(ny-1,(int)ceil((position[1] + dy)/hy));
10330             kmin = VMAX2(0,(int)floor((position[2] - dz)/hzed));
10331             kmax = VMIN2(nz-1,(int)ceil((position[2] + dz)/hzed));
10332             for (i=imin; i<=imax; i++) {
10333                 dx2 = VSQR(position[0] - hx*i);
10334                 for (j=jmin; j<=jmax; j++) {
10335                     dy2 = VSQR(position[1] - hy*j);
10336                     for (k=kmin; k<=kmax; k++) {
10337                         dz2 = VSQR(position[2] - k*hzed);
10338 
10339                         /* ASSIGN CCF */
10340                         if (thee->kappa[IJK(i,j,k)] > VPMGSMALL) {
10341                             dist2 = dz2 + dy2 + dx2;
10342                             if (dist2 >= itot2) {
10343                                 ;
10344                             } 
10345                             if (dist2 <= ictot2) {
10346                                 thee->kappa[IJK(i,j,k)] = 0.0;
10347                             }
10348                             if ((dist2 < itot2) && (dist2 > ictot2)) {
10349                                 dist = VSQRT(dist2);
10350                                 sm = dist;
10351                                 sm2 = dist2;
10352                                 sm3 = sm2 * sm;
10353                                 sm4 = sm3 * sm;
10354                                 sm5 = sm4 * sm;
10355                                 value = ic0 + ic1*sm + ic2*sm2 + ic3*sm3
10356                                       + ic4*sm4 + ic5*sm5;
10357                                 if (value > 1.0) {
10358                                    value = 1.0;
10359                                 } else if (value < 0.0){
10360                                    value = 0.0; 
10361                                 }
10362                                 thee->kappa[IJK(i,j,k)] *= value;
10363                             }
10364                         }
10365 
10366                         /* ASSIGN A1CF */
10367                         if (thee->epsx[IJK(i,j,k)] > VPMGSMALL) {
10368                             dist2 = dz2+dy2+VSQR(position[0]-(i+0.5)*hx);
10369                             if (dist2 >= stot2) {
10370                                 thee->epsx[IJK(i,j,k)] *= 1.0;
10371                             } 
10372                             if (dist2 <= sctot2) {
10373                                 thee->epsx[IJK(i,j,k)] = 0.0;
10374                             } 
10375                             if ((dist2 > sctot2) && (dist2 < stot2)) {
10376                                 dist = VSQRT(dist2);
10377                                 sm = dist;
10378                                 sm2 = VSQR(sm);
10379                                 sm3 = sm2 * sm;
10380                                 sm4 = sm3 * sm;
10381                                 sm5 = sm4 * sm;
10382                                 value = c0 + c1*sm + c2*sm2 + c3*sm3
10383                                       + c4*sm4 + c5*sm5;
10384                                 if (value > 1.0) {
10385                                    value = 1.0;
10386                                 } else if (value < 0.0){
10387                                    value = 0.0; 
10388                                 }
10389                                 thee->epsx[IJK(i,j,k)] *= value;
10390                             } 
10391                         }
10392 
10393                         /* ASSIGN A2CF */
10394                         if (thee->epsy[IJK(i,j,k)] > VPMGSMALL) {
10395                             dist2 = dz2+dx2+VSQR(position[1]-(j+0.5)*hy);
10396                             if (dist2 >= stot2) {
10397                                 thee->epsy[IJK(i,j,k)] *= 1.0;
10398                             } 
10399                             if (dist2 <= sctot2) {
10400                                 thee->epsy[IJK(i,j,k)] = 0.0;
10401                             }
10402                             if ((dist2 > sctot2) && (dist2 < stot2)) {
10403                                 dist = VSQRT(dist2);
10404                                 sm = dist;
10405                                 sm2 = VSQR(sm);
10406                                 sm3 = sm2 * sm;
10407                                 sm4 = sm3 * sm;
10408                                 sm5 = sm4 * sm;
10409                                 value = c0 + c1*sm + c2*sm2 + c3*sm3
10410                                       + c4*sm4 + c5*sm5;
10411                                 if (value > 1.0) {
10412                                    value = 1.0;
10413                                 } else if (value < 0.0){
10414                                    value = 0.0; 
10415                                 }
10416                                 thee->epsy[IJK(i,j,k)] *= value;
10417                             }
10418                         }
10419 
10420                         /* ASSIGN A3CF */
10421                         if (thee->epsz[IJK(i,j,k)] > VPMGSMALL) {
10422                             dist2 = dy2+dx2+VSQR(position[2]-(k+0.5)*hzed);
10423                             if (dist2 >= stot2) {
10424                                 thee->epsz[IJK(i,j,k)] *= 1.0;
10425                             } 
10426                             if (dist2 <= sctot2) {
10427                                 thee->epsz[IJK(i,j,k)] = 0.0;
10428                             } 
10429                             if ((dist2 > sctot2) && (dist2 < stot2)) {
10430                                 dist = VSQRT(dist2);
10431                                 sm = dist;
10432                                 sm2 = dist2;
10433                                 sm3 = sm2 * sm;
10434                                 sm4 = sm3 * sm;
10435                                 sm5 = sm4 * sm;
10436                                 value = c0 + c1*sm + c2*sm2 + c3*sm3
10437                                       + c4*sm4 + c5*sm5;
10438                                 if (value > 1.0) {
10439                                    value = 1.0;
10440                                 } else if (value < 0.0){
10441                                    value = 0.0; 
10442                                 }
10443                                 thee->epsz[IJK(i,j,k)] *= value;
10444                             }
10445                         }
10446 
10447 
10448                     } /* k loop */
10449                 } /* j loop */
10450             } /* i loop */
10451         } /* endif (on the mesh) */
10452     } /* endfor (over all atoms) */
10453 
10454     Vnm_print(0, "Vpmg_fillco:  filling coefficient arrays\n");
10455     /* Interpret markings and fill the coefficient arrays */
10456     for (k=0; k<nz; k++) {
10457         for (j=0; j<ny; j++) {
10458             for (i=0; i<nx; i++) {
10459 
10460                 thee->kappa[IJK(i,j,k)] = ionmask*thee->kappa[IJK(i,j,k)];
10461                 thee->epsx[IJK(i,j,k)] = (epsw-epsp)*thee->epsx[IJK(i,j,k)] 
10462                   + epsp;
10463                 thee->epsy[IJK(i,j,k)] = (epsw-epsp)*thee->epsy[IJK(i,j,k)] 
10464                   + epsp;
10465                 thee->epsz[IJK(i,j,k)] = (epsw-epsp)*thee->epsz[IJK(i,j,k)] 
10466                   + epsp;
10467 
10468             } /* i loop */
10469         } /* j loop */
10470     } /* k loop */
10471 
10472 }
10473 

Generated on Wed Oct 20 2010 12:01:34 for APBS by  doxygen 1.7.2