Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2017-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 "core/ActionRegister.h" 23 : #include "tools/Pbc.h" 24 : #include "tools/KernelFunctions.h" 25 : #include "tools/SwitchingFunction.h" 26 : #include "ActionVolume.h" 27 : #include <memory> 28 : 29 : //+PLUMEDOC VOLUMES INENVELOPE 30 : /* 31 : This quantity can be used to calculate functions of the distribution of collective variables for the atoms that lie in a region where the density of a certain type of atom is high. 32 : 33 : This collective variable can be used to determine whether colvars are within region where the density 34 : of a particular atom is high. This is achieved by calculating the following function at the point where 35 : the atom is located \f$(x,y,z)\f$: 36 : 37 : \f[ 38 : w_j = 1 - \sigma\left[ \sum_{i=1}^N K\left( \frac{x-x_i}{\sigma_x},\frac{y-y_i}{\sigma_y},\frac{z-z_i}{\sigma_z} \right) \right] 39 : \f] 40 : 41 : Here \f$\sigma\f$ is a \ref switchingfunction and \f$K\f$ is a \ref kernelfunctions. The sum runs over the atoms 42 : specified using the ATOMS keyword and a \f$w_j\f$ value is calculated for each of the central atoms of the input 43 : multicolvar. 44 : 45 : \par Examples 46 : 47 : The input below calculates a density field from the positions of atoms 1-14400. The number of the atoms 48 : that are specified in the DENSITY action that are within a region where the density field is greater than 49 : 2.0 is then calculated. 50 : 51 : \plumedfile 52 : d1: DENSITY SPECIES=14401-74134:3 LOWMEM 53 : fi: INENVELOPE DATA=d1 ATOMS=1-14400 CONTOUR={RATIONAL D_0=2.0 R_0=1.0} BANDWIDTH=0.1,0.1,0.1 LOWMEM 54 : PRINT ARG=fi FILE=colvar 55 : \endplumedfile 56 : 57 : */ 58 : //+ENDPLUMEDOC 59 : 60 : namespace PLMD { 61 : namespace multicolvar { 62 : 63 : class VolumeInEnvelope : public ActionVolume { 64 : private: 65 : LinkCells mylinks; 66 : std::unique_ptr<KernelFunctions> kernel; 67 : std::vector<std::unique_ptr<Value>> pos; 68 : std::vector<Vector> ltmp_pos; 69 : std::vector<unsigned> ltmp_ind; 70 : SwitchingFunction sfunc; 71 : public: 72 : static void registerKeywords( Keywords& keys ); 73 : explicit VolumeInEnvelope(const ActionOptions& ao); 74 : void setupRegions() override; 75 : double calculateNumberInside( const Vector& cpos, Vector& derivatives, Tensor& vir, std::vector<Vector>& refders ) const override; 76 : }; 77 : 78 10419 : PLUMED_REGISTER_ACTION(VolumeInEnvelope,"INENVELOPE") 79 : 80 1 : void VolumeInEnvelope::registerKeywords( Keywords& keys ) { 81 1 : ActionVolume::registerKeywords( keys ); keys.remove("SIGMA"); 82 2 : keys.add("atoms","ATOMS","the atom whose positions we are constructing a field from"); 83 2 : keys.add("compulsory","BANDWIDTH","the bandwidths for kernel density estimation"); 84 2 : keys.add("compulsory","CONTOUR","a switching function that tells PLUMED how large the density should be"); 85 1 : } 86 : 87 0 : VolumeInEnvelope::VolumeInEnvelope(const ActionOptions& ao): 88 : Action(ao), 89 : ActionVolume(ao), 90 0 : mylinks(comm) 91 : { 92 0 : std::vector<AtomNumber> atoms; parseAtomList("ATOMS",atoms); 93 0 : log.printf(" creating density field from atoms : "); 94 0 : for(unsigned i=0; i<atoms.size(); ++i) log.printf("%d ",atoms[i].serial() ); 95 0 : log.printf("\n"); ltmp_ind.resize( atoms.size() ); ltmp_pos.resize( atoms.size() ); 96 0 : for(unsigned i=0; i<atoms.size(); ++i) ltmp_ind[i]=i; 97 : 98 0 : std::string sw, errors; parse("CONTOUR",sw); 99 0 : if(sw.length()==0) error("missing CONTOURkeyword"); 100 0 : sfunc.set(sw,errors); 101 0 : if( errors.length()!=0 ) error("problem reading RADIUS keyword : " + errors ); 102 0 : log.printf(" density at atom must be larger than %s \n", ( sfunc.description() ).c_str() ); 103 : 104 0 : std::vector<double> pp(3,0.0), bandwidth(3); parseVector("BANDWIDTH",bandwidth); 105 0 : log.printf(" using %s kernel with bandwidths %f %f %f \n",getKernelType().c_str(),bandwidth[0],bandwidth[1],bandwidth[2] ); 106 0 : kernel=Tools::make_unique<KernelFunctions>( pp, bandwidth, getKernelType(), "DIAGONAL", 1.0 ); 107 0 : for(unsigned i=0; i<3; ++i) { pos.emplace_back(Tools::make_unique<Value>()); pos[i]->setNotPeriodic(); } 108 0 : std::vector<double> csupport( kernel->getContinuousSupport() ); 109 0 : double maxs = csupport[0]; 110 0 : for(unsigned i=1; i<csupport.size(); ++i) { 111 0 : if( csupport[i]>maxs ) maxs = csupport[i]; 112 : } 113 0 : checkRead(); requestAtoms(atoms); mylinks.setCutoff( maxs ); 114 0 : } 115 : 116 0 : void VolumeInEnvelope::setupRegions() { 117 0 : for(unsigned i=0; i<ltmp_ind.size(); ++i) { ltmp_pos[i]=getPosition(i); } 118 0 : mylinks.buildCellLists( ltmp_pos, ltmp_ind, getPbc() ); 119 0 : } 120 : 121 0 : double VolumeInEnvelope::calculateNumberInside( const Vector& cpos, Vector& derivatives, Tensor& vir, std::vector<Vector>& refders ) const { 122 0 : unsigned ncells_required=0, natoms=1; std::vector<unsigned> cells_required( mylinks.getNumberOfCells() ), indices( 1 + getNumberOfAtoms() ); 123 0 : mylinks.addRequiredCells( mylinks.findMyCell( cpos ), ncells_required, cells_required ); 124 0 : indices[0]=getNumberOfAtoms(); mylinks.retrieveAtomsInCells( ncells_required, cells_required, natoms, indices ); 125 0 : double value=0; std::vector<double> der(3); Vector tder; 126 : 127 : // convert pointer once 128 0 : auto pos_ptr=Tools::unique2raw(pos); 129 : 130 0 : for(unsigned i=1; i<natoms; ++i) { 131 0 : Vector dist = getSeparation( cpos, getPosition( indices[i] ) ); 132 0 : for(unsigned j=0; j<3; ++j) pos[j]->set( dist[j] ); 133 0 : value += kernel->evaluate( pos_ptr, der, true ); 134 0 : for(unsigned j=0; j<3; ++j) { 135 0 : derivatives[j] -= der[j]; refders[ indices[i] ][j] += der[j]; tder[j]=der[j]; 136 : } 137 0 : vir -= Tensor( tder, dist ); 138 : } 139 0 : double deriv, fval = sfunc.calculate( value, deriv ); 140 0 : derivatives *= -deriv*value; vir *= -deriv*value; 141 0 : for(unsigned i=1; i<natoms; ++i) refders[ indices[i] ] *= -deriv*value; 142 0 : return 1.0 - fval; 143 : } 144 : 145 : } 146 : }