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