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 { 70 0 : return false; 71 : } 72 : }; 73 : 74 13785 : PLUMED_REGISTER_ACTION(InPlaneDistances,"INPLANEDISTANCES") 75 : 76 4 : void InPlaneDistances::registerKeywords( Keywords& keys ) { 77 4 : MultiColvarBase::registerKeywords( keys ); 78 4 : keys.use("ALT_MIN"); 79 4 : keys.use("LOWEST"); 80 4 : keys.use("HIGHEST"); 81 4 : keys.use("MEAN"); 82 4 : keys.use("MIN"); 83 4 : keys.use("MAX"); 84 4 : keys.use("LESS_THAN"); 85 4 : keys.use("MORE_THAN"); 86 4 : keys.use("BETWEEN"); 87 4 : keys.use("HISTOGRAM"); 88 4 : keys.use("MOMENTS"); 89 8 : keys.add("atoms","VECTORSTART","The first atom position that is used to define the normal to the plane of interest"); 90 8 : keys.add("atoms","VECTOREND","The second atom position that is used to define the normal to the plane of interest"); 91 8 : keys.add("atoms-2","GROUP","The set of atoms for which you wish to calculate the in plane distance "); 92 4 : } 93 : 94 0 : InPlaneDistances::InPlaneDistances(const ActionOptions&ao): 95 : Action(ao), 96 0 : MultiColvarBase(ao) { 97 : // Read in the atoms 98 : std::vector<AtomNumber> all_atoms; 99 0 : readThreeGroups("GROUP","VECTORSTART","VECTOREND",false,false,all_atoms); 100 0 : setupMultiColvarBase( all_atoms ); 101 : 102 : // Setup the multicolvar base 103 0 : setupMultiColvarBase( all_atoms ); 104 0 : readVesselKeywords(); 105 : // Check atoms are OK 106 0 : if( getFullNumberOfTasks()!=getNumberOfAtoms()-2 ) { 107 0 : error("you should specify one atom for VECTORSTART and one atom for VECTOREND only"); 108 : } 109 : // And check everything has been read in correctly 110 0 : checkRead(); 111 : 112 : // Now check if we can use link cells 113 0 : if( getNumberOfVessels()>0 ) { 114 : bool use_link=false; 115 : double rcut; 116 0 : vesselbase::LessThan* lt=dynamic_cast<vesselbase::LessThan*>( getPntrToVessel(0) ); 117 0 : if( lt ) { 118 : use_link=true; 119 0 : rcut=lt->getCutoff(); 120 : } else { 121 0 : vesselbase::Between* bt=dynamic_cast<vesselbase::Between*>( getPntrToVessel(0) ); 122 0 : if( bt ) { 123 : use_link=true; 124 0 : rcut=bt->getCutoff(); 125 : } 126 : } 127 : if( use_link ) { 128 0 : for(unsigned i=1; i<getNumberOfVessels(); ++i) { 129 0 : vesselbase::LessThan* lt2=dynamic_cast<vesselbase::LessThan*>( getPntrToVessel(i) ); 130 0 : vesselbase::Between* bt=dynamic_cast<vesselbase::Between*>( getPntrToVessel(i) ); 131 0 : if( lt2 ) { 132 0 : double tcut=lt2->getCutoff(); 133 0 : if( tcut>rcut ) { 134 0 : rcut=tcut; 135 : } 136 0 : } else if( bt ) { 137 0 : double tcut=bt->getCutoff(); 138 0 : if( tcut>rcut ) { 139 0 : rcut=tcut; 140 : } 141 : } else { 142 : use_link=false; 143 : } 144 : } 145 : } 146 0 : if( use_link ) { 147 0 : setLinkCellCutoff( rcut ); 148 : } 149 : } 150 0 : } 151 : 152 0 : double InPlaneDistances::compute( const unsigned& tindex, AtomValuePack& myatoms ) const { 153 0 : Vector normal=getSeparation( myatoms.getPosition(1), myatoms.getPosition(2) ); 154 0 : Vector dir=getSeparation( myatoms.getPosition(1), myatoms.getPosition(0) ); 155 : PLMD::Angle a; 156 0 : Vector ddij, ddik; 157 0 : double angle=a.compute(normal,dir,ddij,ddik); 158 0 : double sangle=std::sin(angle), cangle=std::cos(angle); 159 0 : double dd=dir.modulo(), invdd=1.0/dd, val=dd*sangle; 160 : 161 0 : addAtomDerivatives( 1, 0, dd*cangle*ddik + sangle*invdd*dir, myatoms ); 162 0 : addAtomDerivatives( 1, 1, -dd*cangle*(ddik+ddij) - sangle*invdd*dir, myatoms ); 163 0 : addAtomDerivatives( 1, 2, dd*cangle*ddij, myatoms ); 164 0 : myatoms.addBoxDerivatives( 1, -dd*cangle*(Tensor(normal,ddij)+Tensor(dir,ddik)) - sangle*invdd*Tensor(dir,dir) ); 165 : 166 0 : return val; 167 : } 168 : 169 : } 170 : }