int gmx_potential(int argc, char *argv[])
{
    const char        *desc[] = {
        "[THISMODULE] computes the electrostatical potential across the box. The potential is",
        "calculated by first summing the charges per slice and then integrating",
        "twice of this charge distribution. Periodic boundaries are not taken",
        "into account. Reference of potential is taken to be the left side of",
        "the box. It is also possible to calculate the potential in spherical",
        "coordinates as function of r by calculating a charge distribution in",
        "spherical slices and twice integrating them. epsilon_r is taken as 1,",
        "but 2 is more appropriate in many cases."
    };
    gmx_output_env_t  *oenv;
    static int         axis       = 2;       /* normal to memb. default z  */
    static const char *axtitle    = "Z";
    static int         nslices    = 10;      /* nr of slices defined       */
    static int         ngrps      = 1;
    static gmx_bool    bSpherical = FALSE;   /* default is bilayer types   */
    static real        fudge_z    = 0;       /* translate coordinates      */
    static gmx_bool    bCorrect   = 0;
    t_pargs            pa []      = {
        { "-d",   FALSE, etSTR, {&axtitle},
          "Take the normal on the membrane in direction X, Y or Z." },
        { "-sl",  FALSE, etINT, {&nslices},
          "Calculate potential as function of boxlength, dividing the box"
          " in this number of slices." },
        { "-cb",  FALSE, etINT, {&cb},
          "Discard this number of  first slices of box for integration" },
        { "-ce",  FALSE, etINT, {&ce},
          "Discard this number of last slices of box for integration" },
        { "-tz",  FALSE, etREAL, {&fudge_z},
          "Translate all coordinates by this distance in the direction of the box" },
        { "-spherical", FALSE, etBOOL, {&bSpherical},
          "Calculate spherical thingie" },
        { "-ng",       FALSE, etINT, {&ngrps},
          "Number of groups to consider" },
        { "-correct",  FALSE, etBOOL, {&bCorrect},
          "Assume net zero charge of groups to improve accuracy" }
    };
    const char        *bugs[] = {
        "Discarding slices for integration should not be necessary."
    };

    double           **potential,              /* potential per slice        */
    **charge,                                  /* total charge per slice     */
    **field,                                   /* field per slice            */
                       slWidth;                /* width of one slice         */
    char      **grpname;                       /* groupnames                 */
    int        *ngx;                           /* sizes of groups            */
    t_topology *top;                           /* topology        */
    int         ePBC;
    int       **index;                         /* indices for all groups     */
    t_filenm    fnm[] = {                      /* files for g_order       */
        { efTRX, "-f", NULL,  ffREAD },        /* trajectory file             */
        { efNDX, NULL, NULL,  ffREAD },        /* index file          */
        { efTPR, NULL, NULL,  ffREAD },        /* topology file               */
        { efXVG, "-o", "potential", ffWRITE }, /* xvgr output file    */
        { efXVG, "-oc", "charge", ffWRITE },   /* xvgr output file    */
        { efXVG, "-of", "field", ffWRITE },    /* xvgr output file    */
    };

#define NFILE asize(fnm)

    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME,
                           NFILE, fnm, asize(pa), pa, asize(desc), desc, asize(bugs), bugs,
                           &oenv))
    {
        return 0;
    }

    /* Calculate axis */
    axis = toupper(axtitle[0]) - 'X';

    top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC); /* read topology file */

    snew(grpname, ngrps);
    snew(index, ngrps);
    snew(ngx, ngrps);

    rd_index(ftp2fn(efNDX, NFILE, fnm), ngrps, ngx, index, grpname);


    calc_potential(ftp2fn(efTRX, NFILE, fnm), index, ngx,
                   &potential, &charge, &field,
                   &nslices, top, ePBC, axis, ngrps, &slWidth, fudge_z,
                   bSpherical, bCorrect, oenv);

    plot_potential(potential, charge, field, opt2fn("-o", NFILE, fnm),
                   opt2fn("-oc", NFILE, fnm), opt2fn("-of", NFILE, fnm),
                   nslices, ngrps, (const char**)grpname, slWidth, oenv);

    do_view(oenv, opt2fn("-o", NFILE, fnm), NULL);  /* view xvgr file */
    do_view(oenv, opt2fn("-oc", NFILE, fnm), NULL); /* view xvgr file */
    do_view(oenv, opt2fn("-of", NFILE, fnm), NULL); /* view xvgr file */

    return 0;
}
Exemple #2
0
/* volume of a domain */
real volume (struct potpars *pp, struct pdb_ATOM model[], struct domain *dom, 
	      struct pprofile *prf) {
  real vol, Rmax, zmin, zmax;
  real min;         /* global (hopefully) minimum in the total potential */
  int i,first,last;

  /* take all atoms of this and neighboring domains: "Take them
     out. All of them!" (Senator Palpartine aka Darth Sidious) */
  first = (dom->prev) ? dom->prev->first_site : dom->first_site;
  last  = (dom->next) ? dom->next->last_site  : dom->last_site;

  pp->ncenters=last-first + 1;

  mesg(SUB1,"volume(): Using %d atoms (%d -> %d), centered on domain '%s'.",
       pp->ncenters,first,last,dom->description);

  Rmax = (prf->bSet) ? prf->Rmax :  dom->rho1/10.0;
  zmin=(dom->z1 - dom->dz/2.0)/10.0;
  zmax=(dom->z2 + dom->dz/2.0)/10.0;

  /* setup the function to be integrated */
  /* (1) find the minimum
     - requires a function in cartesian coordinates
     -> setup the centers in cartesian
     - use cartesian vlj()
  */
  { 
    Cartesian pos;
    pp->xyzcenters=grid2_alloc(pp->ncenters,3);
    for(i=0;i<pp->ncenters;i++) {
      pos = cyl2cart(model[i+first].cpos);
      pp->xyzcenters[i][XX]=pos.x/10.0;
      pp->xyzcenters[i][YY]=pos.y/10.0;
      pp->xyzcenters[i][ZZ]=pos.z/10.0;
    }
  
    init_vljcyl(pp);
    mesg(VERBOSE,"Potential: %s with ffgmx OW-CH4 interaction parameters at T=%g K", 
	 "Lennard-Jones 12-6 V_LJ(x,y,z)", pp->Temp);

    pp->min=find_min(vljvec,Rmax,zmin,zmax,NULL);
    mesg(VERBOSE,"Minimum: %f\n",pp->min);
  }
  
  /* (2) calculate the volume
     - this is better done in cylindrical coordinates
     --> setup again (and use cylindrical LJ)
  */
  /* These cylindrical coordinates are buried in structures; I rather
     have them as simple arrays: AND Length has to be in nm (not
     Angstrom) because this is the length unit in the Lennard-Jones
     parameters (currently CH4-OW hardcoded) */
  pp->centers=grid2_alloc(pp->ncenters,3);
  for(i=0;i<pp->ncenters;i++) {
    pp->centers[i][RAD]=model[i+first].cpos.rho/10.0;
    pp->centers[i][PHI]=model[i+first].cpos.phi;
    pp->centers[i][ZZZ]=model[i+first].cpos.z/10.0;
  }
  init_vljcyl(pp);   /* now with the minimum found and with the
                        LJ-centers in cylindrical coordinates! */
  mesg(VERBOSE,"Potential: Shifted Lennard-Jones 12-6 V_LJ(r,phi,z) - %g kT "
       "with ffgmx OW-CH4 interaction parameters at T=%g K", pp->min, pp->Temp);

  init_gaussleg(pp->ngaussleg);
  mesg(VERBOSE,"Using %d-point Gauss-Legendre quadrature for the volume integrals.",
       pp->ngaussleg);

  if (prf->bPlot)  plot_potential(vljcyl,Rmax,zmin,zmax,prf->nzplot);


  /*
  mesg(VERBOSE,"Potential: %s with ffgmx OW-CH4 interaction parameters", 
       (prf->pot == vRljcyl) ? "WCA repulsive V_R,LJ(r)" : "Lennard-Jones 12-6 V_LJ(r)");
  */

#ifdef TESTCASE
  { 
    real vexact;
    /* test case: this should give the exact volume */
    mesg(SUB1,"-------> Internal test of volume integration <-------");

    vexact=PI*Rmax*Rmax*(zmax-zmin);
    mesg(SUB1,"VOLUME_TEST: R[nm] <%f> R_c[nm] <%f> L[nm] <%f> V=¶·R_c²·L[nm³] <%f>", 
	 dom->r1/10.0, Rmax, zmax-zmin, vexact); 

    vol=cylint(cylconst, 0,Rmax, 0, 2*PI, zmin,zmax); 
    mesg(SUB1,"VOLUME_TEST: f=1: Volume <%f> V_exact <%f>",  vol,vexact); 

    vol=cylint(cylcos, 0,Rmax, 0, 2*PI, zmin,zmax); 
    vexact=0;
    mesg(SUB1,"VOLUME_TEST: f=cos(phi): Volume <%f> V_exact <%f>",  vol,vexact); 

    vol=cylint(cyl3, 0,Rmax, 0, 2*PI, 0,zmax-zmin); 
    vexact=(1-exp(-Rmax))*PI*pow(zmax-zmin,3)/3.0;
    mesg(SUB1,"VOLUME_TEST: f=1/r exp(-r)*cos(phi)*cos(phi)*z: Volume <%f> V_exact <%f>",  
	 vol,vexact); 

    vexact=1.0; /* PI*Rmax*Rmax*(zmax-zmin); */
    vol = tdavg(Qvol,Zero, Rmax,zmin,zmax);
    mesg(SUB1,"VOLUME_TEST: <Qvol>, V(r)=0: Volume <%f> V_exact <%f>",  
	 vol,vexact); 

    /* this analytical soln is alraedy specialised for FHG=6 */
    vexact=(1.0-4.0*exp(-3.0))/(1-exp(-6.0*Rmax)*(1.0+6.0*Rmax));
    vol = tdavg(Qvol,LinCheck, Rmax,zmin,zmax);
    mesg(SUB1,"VOLUME_TEST: <Qvol>, V(r)=6r: Volume <%f> V_exact <%f>",  
	 vol,vexact); 

    /* correct one, f = 6, gives the same as above */
#define FHG 6.0
    vexact= -(exp(FHG*(-0.5 + Rmax))*
	      (-2.0 + 2.0*exp(FHG/2.) - FHG))/ (2.*(1.0 - exp(FHG*Rmax) + FHG*Rmax));
    vol = tdavg(Qvol,LinCheck, Rmax,zmin,zmax);
    mesg(SUB1,"VOLUME_TEST: <Qvol>, V(r)=f/beta r nm^-1: Volume <%f> V_exact <%f>",  
	 vol,vexact); 

    /* g(E) * exp(-bE) * f() 
       f(r) = K1/2 r^2        (integral from Mathematica)
     */
    vexact=2.0*PI*(zmax-zmin)*
           (K1*pow(Rmax,2) - (3.0*sqrt(2.0/PI)*sqrt(K1*pow(Rmax,2)))/
	    exp((K1*pow(Rmax,2))/2.) + 
	    (3.0 - K1*pow(Rmax,2))*
	    erf(sqrt(K1*pow(Rmax,2))/sqrt(2)))/(2.*K1);
    vol = xV(harmonic,Rmax,zmin,zmax);
    mesg(SUB1,"VOLUME_TEST: xV: V(r)= k/2b r² nm^-1: Volume <%f> V_exact <%f>",  
	 vol,vexact); 
      
    mesg(SUB1,"------> End of testcases <-------\n");
  }
#endif /* TESTCASE */ 

  /* total volume */

  /* simple & inefficient (two integrations):
     V = <Q> = Tr Qexp(-beta H) / Tr exp(-beta H)
  */
  mesg(INPUT,"VOLUME_INPUT: R[nm] <%f> R_c[nm] <%f> L[nm] <%f> V=¶·R_c²·L[nm³] <%f>",
       dom->r1/10.0, Rmax, zmax-zmin, PI*Rmax*Rmax*(zmax-zmin));
  mesg(INPUT,"VOLUME_PARAMETERS: Rmax[nm] <%f>  z1 <%f>  z2 <%f>",Rmax,zmin, zmax);

  /* accessible volume, Labbook II, p84 */
  vol = xV(vljshiftcyl,Rmax,zmin,zmax);
  mesg(INPUT,"VOLUME_DATA: xV Volume[nm³] <%f> R*[nm] <%f>\n", 
       vol,sqrt(vol/(PI*(zmax-zmin))));
  
  /* normalised configurational volume, Labbok II, p79 (rubbish!) */
  vol = Zsum(vljshiftcyl,Rmax,zmin,zmax);
  mesg(INPUT,"VOLUME_DATA: v1/Z Volume[nm³] <%f> R*[nm] <%f>\n", 
       vol,sqrt(vol/(PI*(zmax-zmin))));


  /* Thermodynamic average at fixed particle energy, Labbook II, p78 */
/*    vol = tdavg(Qvol,prf->pp->u1, Rmax,zmin,zmax); */
/*    mesg(INPUT,"VOLUME_DATA: <Qvol> Volume[nm³] <%f> R*[nm] <%f>\n",  */
/*         vol,sqrt(vol/(PI*(zmax-zmin)))); */

  /* Pure configurational volume (unnormalised) */
/*    vol = cylint(Z_V, 0,Rmax, 0,2*PI, zmin,zmax); */
/*    mesg(INPUT,"VOLUME_DATA: v1 Volume[nm³] <%f> R*[nm] <%f>\n",  */
/*         vol,sqrt(vol/(PI*(zmax-zmin)))); */

  if (prf->bSet)  calc_profile(prf);
  
  free_gaussleg();
  free(pp->centers);
  free(pp->xyzcenters);
  return vol;
}