LCOV - code coverage report
Current view: top level - multicolvar - DistanceFromContour.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 96 102 94.1 %
Date: 2018-12-19 07:49:13 Functions: 15 17 88.2 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-2018 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "MultiColvarBase.h"
      23             : #include "AtomValuePack.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "tools/KernelFunctions.h"
      26             : #include "tools/RootFindingBase.h"
      27             : #include "vesselbase/ValueVessel.h"
      28             : 
      29             : //+PLUMEDOC COLVAR DISTANCE_FROM_CONTOUR
      30             : /*
      31             : Calculate the perpendicular distance from a Willard-Chandler dividing surface.
      32             : 
      33             : Suppose that you have calculated a multicolvar.  By doing so you have calculated a
      34             : set of colvars, \f$s_i\f$, and each of these colvars has a well defined position in
      35             : space \f$(x_i,y_i,z_i)\f$.  You can use this information to calculate a phase-field
      36             : model of the colvar density using:
      37             : 
      38             : \f[
      39             : p(x,y,x) = \sum_{i} s_i K\left[\frac{x-x_i}{\sigma_x},\frac{y-y_i}{\sigma_y},\frac{z-z_i}{\sigma_z} \right]
      40             : \f]
      41             : 
      42             : In this expression \f$\sigma_x, \sigma_y\f$ and \f$\sigma_z\f$ are bandwidth parameters and
      43             : \f$K\f$ is one of the \ref kernelfunctions.  This is what is done within \ref MULTICOLVARDENS
      44             : 
      45             : The Willard-Chandler surface is a surface of constant density in the above phase field \f$p(x,y,z)\f$.
      46             : In other words, it is a set of points, \f$(x',y',z')\f$, in your box which have:
      47             : 
      48             : \f[
      49             : p(x',y',z') = \rho
      50             : \f]
      51             : 
      52             : where \f$\rho\f$ is some target density.  This action caculates the distance projected on the \f$x, y\f$ or
      53             : \f$z\f$ axis between the position of some test particle and this surface of constant field density.
      54             : 
      55             : \par Examples
      56             : 
      57             : In this example atoms 2-100 are assumed to be concentraed along some part of the \f$z\f$ axis so that you
      58             : an interface between a liquid/solid and the vapour.  The quantity dc measures the distance between the
      59             : surface at which the density of 2-100 atoms is equal to 0.2 and the position of the test particle atom 1.
      60             : 
      61             : \verbatim
      62             : dens: DENSITY SPECIES=2-100
      63             : dc: DISTANCE_FROM_CONTOUR DATA=dens ATOM=1 BANDWIDTH=0.5,0.5,0.5 DIR=z CONTOUR=0.2
      64             : \endverbatim
      65             : 
      66             : */
      67             : //+ENDPLUMEDOC
      68             : 
      69             : namespace PLMD {
      70             : namespace multicolvar {
      71             : 
      72           2 : class DistanceFromContour : public MultiColvarBase {
      73             : private:
      74             :   unsigned dir;
      75             :   bool derivTime;
      76             :   double rcut2;
      77             :   double contour;
      78             :   std::string kerneltype;
      79             :   std::vector<Value*> pval;
      80             :   std::vector<double> bw, pos, dirv;
      81             :   std::vector<double> forces;
      82             :   std::vector<unsigned> perp_dirs;
      83             :   vesselbase::ValueVessel* myvalue_vessel;
      84             :   vesselbase::ValueVessel* myderiv_vessel;
      85             :   RootFindingBase<DistanceFromContour> mymin;
      86             : public:
      87             :   static void registerKeywords( Keywords& keys );
      88             :   explicit DistanceFromContour( const ActionOptions& );
      89        1293 :   bool isDensity() const { return true; }
      90             :   void calculate();
      91             :   unsigned getNumberOfQuantities() const ;
      92           0 :   bool isPeriodic() { return false; }
      93             :   double compute( const unsigned& tindex, AtomValuePack& myatoms ) const ;
      94             :   double getDifferenceFromContour( const std::vector<double>& x, std::vector<double>& der );
      95             : // We need an apply action as we are using an independent value
      96             :   void apply();
      97             : };
      98             : 
      99        2524 : PLUMED_REGISTER_ACTION(DistanceFromContour,"DISTANCE_FROM_CONTOUR")
     100             : 
     101           2 : void DistanceFromContour::registerKeywords( Keywords& keys ) {
     102           2 :   MultiColvarBase::registerKeywords( keys );
     103           2 :   keys.add("compulsory","DATA","The input base multicolvar which is being used to calculate the contour");
     104           2 :   keys.add("atoms","ATOM","The atom whose perpendicular distance we are calculating from the contour");
     105           2 :   keys.add("compulsory","BANDWIDTH","the bandwidths for kernel density esimtation");
     106             :   keys.add("compulsory","KERNEL","gaussian","the kernel function you are using.  More details on  the kernels available "
     107           2 :            "in plumed plumed can be found in \\ref kernelfunctions.");
     108           2 :   keys.add("compulsory","DIR","the direction perpendicular to the contour that you are looking for");
     109           2 :   keys.add("compulsory","CONTOUR","the value we would like for the contour");
     110           2 : }
     111             : 
     112           1 : DistanceFromContour::DistanceFromContour( const ActionOptions& ao ):
     113             :   Action(ao),
     114             :   MultiColvarBase(ao),
     115             :   derivTime(false),
     116             :   bw(3),
     117             :   pos(3,0.0),
     118             :   dirv(3,0.0),
     119             :   perp_dirs(2),
     120           1 :   mymin(this)
     121             : {
     122             :   // Read in the multicolvar/atoms
     123           1 :   std::vector<AtomNumber> all_atoms;
     124           1 :   bool read2 = parseMultiColvarAtomList("DATA", -1, all_atoms);
     125           1 :   if( !read2 ) error("missing DATA keyword");
     126           1 :   bool read1 = parseMultiColvarAtomList("ATOM", -1, all_atoms);
     127           1 :   if( !read1 ) error("missing ATOM keyword");
     128           1 :   if( all_atoms.size()!=1 ) error("should only be one atom specified");
     129             :   // Read in the center of the binding object
     130           1 :   log.printf("  computing distance of atom %d from contour \n",all_atoms[0].serial() );
     131           1 :   setupMultiColvarBase( all_atoms ); forces.resize( 3*getNumberOfAtoms() + 9 );
     132           1 :   if( getNumberOfBaseMultiColvars()!=1 ) error("should only be one input multicolvar");
     133             : 
     134             :   // Get the direction
     135           2 :   std::string ldir; parse("DIR",ldir );
     136           1 :   if( ldir=="x" ) { dir=0; perp_dirs[0]=1; perp_dirs[1]=2; dirv[0]=1; }
     137           1 :   else if( ldir=="y" ) { dir=1; perp_dirs[0]=0; perp_dirs[1]=2; dirv[1]=1; }
     138           1 :   else if( ldir=="z" ) { dir=2; perp_dirs[0]=0; perp_dirs[1]=1; dirv[2]=1; }
     139           0 :   else error(ldir + " is not a valid direction use x, y or z");
     140             : 
     141             :   // Read in details of phase field construction
     142           1 :   parseVector("BANDWIDTH",bw); parse("KERNEL",kerneltype); parse("CONTOUR",contour);
     143           1 :   log.printf("  searching for contour in %s direction at %f in phase field for multicolvar %s \n",ldir.c_str(), contour, mybasemulticolvars[0]->getLabel().c_str() );
     144           1 :   log.printf("  constructing phase field using %s kernels with bandwidth (%f, %f, %f) \n",kerneltype.c_str(), bw[0], bw[1], bw[2] );
     145             : 
     146             :   // Now create a task list
     147           1 :   for(unsigned i=0; i<mybasemulticolvars[0]->getFullNumberOfTasks(); ++i) addTaskToList(i);
     148             :   // And a cutoff
     149           2 :   std::vector<double> pp( bw.size(),0 );
     150           2 :   KernelFunctions kernel( pp, bw, kerneltype, false, 1.0, true );
     151           1 :   double rcut = kernel.getCutoff( bw[0] );
     152           3 :   for(unsigned j=1; j<bw.size(); ++j) {
     153           2 :     if( kernel.getCutoff(bw[j])>rcut ) rcut=kernel.getCutoff(bw[j]);
     154             :   }
     155           1 :   rcut2=rcut*rcut;
     156             :   // Create the value
     157           1 :   addValueWithDerivatives(); setNotPeriodic();
     158             :   // Create sum vessels
     159           2 :   std::string fake_input; std::string deriv_input="COMPONENT=2";
     160           1 :   if( mybasemulticolvars[0]->isDensity() ) {
     161           1 :     addVessel( "SUM", fake_input, -1 ); addVessel( "SUM", deriv_input, -1 );
     162             :   } else {
     163           0 :     addVessel( "MEAN", fake_input, -1 ); addVessel( "MEAN", deriv_input, -1 );
     164             :   }
     165             :   // And convert to a value vessel so we can get the final value
     166           1 :   myvalue_vessel = dynamic_cast<vesselbase::ValueVessel*>( getPntrToVessel(0) );
     167           1 :   myderiv_vessel = dynamic_cast<vesselbase::ValueVessel*>( getPntrToVessel(1) );
     168           1 :   plumed_assert( myvalue_vessel && myderiv_vessel ); resizeFunctions();
     169             : 
     170             :   // Create the vector of values that holds the position
     171           2 :   for(unsigned i=0; i<3; ++i) pval.push_back( new Value() );
     172           1 : }
     173             : 
     174        2586 : unsigned DistanceFromContour::getNumberOfQuantities() const {
     175        2586 :   return 3;
     176             : }
     177             : 
     178         137 : void DistanceFromContour::calculate() {
     179             :   // Check box is orthorhombic
     180         137 :   if( !getPbc().isOrthorombic() ) error("cell box must be orthorhombic");
     181             : 
     182             :   // The nanoparticle is at the origin of our coordinate system
     183         137 :   pos[0]=pos[1]=pos[2]=0.0;
     184             : 
     185             :   // Set bracket as center of mass of membrane in active region
     186         137 :   double d2, norm=0.0; dirv[dir]=0; deactivateAllTasks();
     187         274 :   for(unsigned j=0; j<getNumberOfAtoms()-1; ++j) {
     188         137 :     Vector distance=getSeparation( getPosition(getNumberOfAtoms()-1), getPosition(j) );
     189         411 :     if( (d2=distance[perp_dirs[0]]*distance[perp_dirs[0]])<rcut2 &&
     190         274 :         (d2+=distance[perp_dirs[1]]*distance[perp_dirs[1]])<rcut2 ) {
     191         137 :       dirv[dir]+=distance[dir]; taskFlags[j]=1; norm+=1.0;
     192             :     }
     193             :   }
     194         137 :   dirv[dir] = dirv[dir] / norm;
     195         137 :   lockContributors(); derivTime=false;
     196             : 
     197             :   // Now do a search for the contour
     198         137 :   mymin.lsearch( dirv, pos, &DistanceFromContour::getDifferenceFromContour );
     199             : 
     200             :   // Set the final value
     201         137 :   setValue( pval[dir]->get() );
     202             :   // Now calculate the derivatives
     203         137 :   if( !doNotCalculateDerivatives() ) {
     204         137 :     Value* ival=myvalue_vessel->getFinalValue(); ival->clearDerivatives();
     205         137 :     derivTime=true; double prefactor; std::vector<double> der(3); getDifferenceFromContour( pos, der );
     206         137 :     if( mybasemulticolvars[0]->isDensity() ) prefactor = 1.0 / myderiv_vessel->getOutputValue(); else plumed_error();
     207         137 :     Value* val=getPntrToValue();
     208         137 :     for(unsigned i=0; i<val->getNumberOfDerivatives(); ++i) val->setDerivative( i, -prefactor*ival->getDerivative(i) );
     209             :   }
     210         137 : }
     211             : 
     212        1293 : double DistanceFromContour::getDifferenceFromContour( const std::vector<double>& x, std::vector<double>& der ) {
     213        2586 :   std::string min, max;
     214        5172 :   for(unsigned j=0; j<3; ++j) {
     215        3879 :     Tools::convert( -0.5*getBox()(j,j), min );
     216        3879 :     Tools::convert( +0.5*getBox()(j,j), max );
     217        3879 :     pval[j]->setDomain( min, max ); pval[j]->set( x[j] );
     218             :   }
     219        1293 :   runAllTasks();
     220        2586 :   return myvalue_vessel->getOutputValue() - contour;
     221             : }
     222             : 
     223        1293 : double DistanceFromContour::compute( const unsigned& tindex, AtomValuePack& myatoms ) const {
     224        1293 :   Vector distance = getSeparation( getPosition(getNumberOfAtoms()-1), myatoms.getPosition(0) );
     225        2586 :   std::vector<double> pp(3), der(3,0); for(unsigned j=0; j<3; ++j) pp[j] = distance[j];
     226             : 
     227             :   // Now create the kernel and evaluate
     228        2586 :   KernelFunctions kernel( pp, bw, kerneltype, false, 1.0, true );
     229        1293 :   double newval = kernel.evaluate( pval, der, true );
     230             : 
     231        1293 :   if( mybasemulticolvars[0]->isDensity() ) {
     232        1293 :     if( !doNotCalculateDerivatives() && derivTime ) {
     233         137 :       MultiValue& myvals=myatoms.getUnderlyingMultiValue();
     234         137 :       Vector vder; unsigned basen=myvals.getNumberOfDerivatives() - 12;
     235         548 :       for(unsigned i=0; i<3; ++i) {
     236         411 :         vder[i]=der[i]; myvals.addDerivative( 1, basen+i, vder[i] );
     237             :       }
     238         137 :       myatoms.setValue( 2, der[dir] );
     239         137 :       addAtomDerivatives( 1, 0, -vder, myatoms );
     240         137 :       myatoms.addBoxDerivatives( 1, Tensor(vder,distance) );
     241             :     }
     242        1293 :     myatoms.setValue( 0, 1.0 ); return newval;
     243             :   }
     244             : 
     245             :   // This does the stuff for averaging
     246           0 :   myatoms.setValue( 0, newval );
     247             : 
     248             :   // This gets the average if we are using a phase field
     249           0 :   std::vector<double> cvals( mybasemulticolvars[0]->getNumberOfQuantities() );
     250           0 :   mybasedata[0]->retrieveValueWithIndex( tindex, false, cvals );
     251        1293 :   return newval*cvals[0]*cvals[1];
     252             : }
     253             : 
     254         137 : void DistanceFromContour::apply() {
     255         137 :   if( getPntrToValue()->applyForce( forces ) ) setForcesOnAtoms( forces );
     256         137 : }
     257             : 
     258             : }
     259        2523 : }

Generated by: LCOV version 1.13