Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2015-2020 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 "tools/SwitchingFunction.h" 25 : #include "core/ActionRegister.h" 26 : #include "vesselbase/LessThan.h" 27 : #include "vesselbase/Between.h" 28 : #include "tools/Angle.h" 29 : 30 : #include <string> 31 : #include <cmath> 32 : 33 : using namespace std; 34 : 35 : namespace PLMD { 36 : namespace multicolvar { 37 : 38 : //+PLUMEDOC MCOLVAR INPLANEDISTANCES 39 : /* 40 : Calculate distances in the plane perpendicular to an axis 41 : 42 : Each quantity calculated in this CV uses the positions of two atoms, this indices of which are specified using the VECTORSTART and VECTOREND keywords, to specify the 43 : orientation of a vector, \f$\mathbf{n}\f$. The perpendicular distance between this vector and the position of some third atom is then computed using: 44 : \f[ 45 : x_j = |\mathbf{r}_{j}| \sin (\theta_j) 46 : \f] 47 : where \f$\mathbf{r}_j\f$ is the distance between one of the two atoms that define the vector \f$\mathbf{n}\f$ and a third atom (atom \f$j\f$) and where \f$\theta_j\f$ 48 : is the angle between the vector \f$\mathbf{n}\f$ and the vector \f$\mathbf{r}_{j}\f$. The \f$x_j\f$ values for each of the atoms specified using the GROUP keyword are calculated. 49 : Keywords such as MORE_THAN and LESS_THAN can then be used to calculate the number of these quantities that are more or less than a given cutoff. 50 : 51 : \par Examples 52 : 53 : The following input can be used to calculate the number of atoms that have indices greater than 3 and less than 101 that 54 : are within a cylinder with a radius of 0.3 nm that has its long axis aligned with the vector connecting atoms 1 and 2. 55 : 56 : \plumedfile 57 : d1: INPLANEDISTANCES VECTORSTART=1 VECTOREND=2 GROUP=3-100 LESS_THAN={RATIONAL D_0=0.2 R_0=0.1} 58 : PRINT ARG=d1.lessthan FILE=colvar 59 : \endplumedfile 60 : 61 : 62 : */ 63 : //+ENDPLUMEDOC 64 : 65 0 : class InPlaneDistances : public MultiColvarBase { 66 : public: 67 : static void registerKeywords( Keywords& keys ); 68 : explicit InPlaneDistances(const ActionOptions&); 69 : // active methods: 70 : virtual double compute(const unsigned& tindex, AtomValuePack& myatoms ) const ; 71 0 : bool isPeriodic() { return false; } 72 : }; 73 : 74 7356 : PLUMED_REGISTER_ACTION(InPlaneDistances,"INPLANEDISTANCES") 75 : 76 1 : void InPlaneDistances::registerKeywords( Keywords& keys ) { 77 1 : MultiColvarBase::registerKeywords( keys ); 78 4 : keys.use("ALT_MIN"); keys.use("LOWEST"); keys.use("HIGHEST"); 79 5 : keys.use("MEAN"); keys.use("MIN"); keys.use("MAX"); keys.use("LESS_THAN"); 80 5 : keys.use("MORE_THAN"); keys.use("BETWEEN"); keys.use("HISTOGRAM"); keys.use("MOMENTS"); 81 4 : keys.add("atoms","VECTORSTART","The first atom position that is used to define the normal to the plane of interest"); 82 4 : keys.add("atoms","VECTOREND","The second atom position that is used to define the normal to the plane of interest"); 83 4 : keys.add("atoms-2","GROUP","The set of atoms for which you wish to calculate the in plane distance "); 84 1 : } 85 : 86 0 : InPlaneDistances::InPlaneDistances(const ActionOptions&ao): 87 : Action(ao), 88 0 : MultiColvarBase(ao) 89 : { 90 : // Read in the atoms 91 : std::vector<AtomNumber> all_atoms; 92 0 : readThreeGroups("GROUP","VECTORSTART","VECTOREND",false,false,all_atoms); 93 0 : setupMultiColvarBase( all_atoms ); 94 : 95 : // Setup the multicolvar base 96 0 : setupMultiColvarBase( all_atoms ); readVesselKeywords(); 97 : // Check atoms are OK 98 0 : if( getFullNumberOfTasks()!=getNumberOfAtoms()-2 ) error("you should specify one atom for VECTORSTART and one atom for VECTOREND only"); 99 : // And check everything has been read in correctly 100 0 : checkRead(); 101 : 102 : // Now check if we can use link cells 103 0 : if( getNumberOfVessels()>0 ) { 104 : bool use_link=false; double rcut; 105 0 : vesselbase::LessThan* lt=dynamic_cast<vesselbase::LessThan*>( getPntrToVessel(0) ); 106 0 : if( lt ) { 107 0 : use_link=true; rcut=lt->getCutoff(); 108 : } else { 109 0 : vesselbase::Between* bt=dynamic_cast<vesselbase::Between*>( getPntrToVessel(0) ); 110 0 : if( bt ) { 111 0 : use_link=true; rcut=bt->getCutoff(); 112 : } 113 : } 114 : if( use_link ) { 115 0 : for(unsigned i=1; i<getNumberOfVessels(); ++i) { 116 0 : vesselbase::LessThan* lt2=dynamic_cast<vesselbase::LessThan*>( getPntrToVessel(i) ); 117 0 : vesselbase::Between* bt=dynamic_cast<vesselbase::Between*>( getPntrToVessel(i) ); 118 0 : if( lt2 ) { 119 0 : double tcut=lt2->getCutoff(); 120 0 : if( tcut>rcut ) rcut=tcut; 121 0 : } else if( bt ) { 122 0 : double tcut=bt->getCutoff(); 123 0 : if( tcut>rcut ) rcut=tcut; 124 : } else { 125 : use_link=false; 126 : } 127 : } 128 : } 129 0 : if( use_link ) setLinkCellCutoff( rcut ); 130 : } 131 0 : } 132 : 133 0 : double InPlaneDistances::compute( const unsigned& tindex, AtomValuePack& myatoms ) const { 134 0 : Vector normal=getSeparation( myatoms.getPosition(1), myatoms.getPosition(2) ); 135 0 : Vector dir=getSeparation( myatoms.getPosition(1), myatoms.getPosition(0) ); 136 0 : PLMD::Angle a; Vector ddij, ddik; double angle=a.compute(normal,dir,ddij,ddik); 137 0 : double sangle=sin(angle), cangle=cos(angle); 138 0 : double dd=dir.modulo(), invdd=1.0/dd, val=dd*sangle; 139 : 140 0 : addAtomDerivatives( 1, 0, dd*cangle*ddik + sangle*invdd*dir, myatoms ); 141 0 : addAtomDerivatives( 1, 1, -dd*cangle*(ddik+ddij) - sangle*invdd*dir, myatoms ); 142 0 : addAtomDerivatives( 1, 2, dd*cangle*ddij, myatoms ); 143 0 : myatoms.addBoxDerivatives( 1, -dd*cangle*(Tensor(normal,ddij)+Tensor(dir,ddik)) - sangle*invdd*Tensor(dir,dir) ); 144 : 145 0 : return val; 146 : } 147 : 148 : } 149 5517 : }