Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2012-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 "Colvar.h" 23 : #include "ActionRegister.h" 24 : 25 : namespace PLMD { 26 : namespace colvar { 27 : 28 : //+PLUMEDOC COLVAR DIPOLE 29 : /* 30 : Calculate the dipole moment for a group of atoms. 31 : 32 : When running with periodic boundary conditions, the atoms should be 33 : in the proper periodic image. This is done automatically since PLUMED 2.5, 34 : by considering the ordered list of atoms and rebuilding the molecule with a procedure 35 : that is equivalent to that done in \ref WHOLEMOLECULES . Notice that 36 : rebuilding is local to this action. This is different from \ref WHOLEMOLECULES 37 : which actually modifies the coordinates stored in PLUMED. 38 : 39 : In case you want to recover the old behavior you should use the NOPBC flag. 40 : In that case you need to take care that atoms are in the correct 41 : periodic image. 42 : 43 : \par Examples 44 : 45 : The following tells plumed to calculate the dipole of the group of atoms containing 46 : the atoms from 1-10 and print it every 5 steps 47 : \plumedfile 48 : d: DIPOLE GROUP=1-10 49 : PRINT FILE=output STRIDE=5 ARG=d 50 : \endplumedfile 51 : 52 : \attention 53 : If the total charge Q of the group in non zero, then a charge Q/N will be subtracted to every atom, 54 : where N is the number of atoms. This implies that the dipole (which for a charged system depends 55 : on the position) is computed on the geometric center of the group. 56 : 57 : 58 : */ 59 : //+ENDPLUMEDOC 60 : 61 : class Dipole : public Colvar { 62 : std::vector<AtomNumber> ga_lista; 63 : bool components; 64 : bool nopbc; 65 : public: 66 : explicit Dipole(const ActionOptions&); 67 : void calculate() override; 68 : static void registerKeywords(Keywords& keys); 69 : }; 70 : 71 10525 : PLUMED_REGISTER_ACTION(Dipole,"DIPOLE") 72 : 73 54 : void Dipole::registerKeywords(Keywords& keys) { 74 54 : Colvar::registerKeywords(keys); 75 108 : keys.add("atoms","GROUP","the group of atoms we are calculating the dipole moment for"); 76 108 : keys.addFlag("COMPONENTS",false,"calculate the x, y and z components of the dipole separately and store them as label.x, label.y and label.z"); 77 108 : keys.addOutputComponent("x","COMPONENTS","the x-component of the dipole"); 78 108 : keys.addOutputComponent("y","COMPONENTS","the y-component of the dipole"); 79 108 : keys.addOutputComponent("z","COMPONENTS","the z-component of the dipole"); 80 54 : } 81 : 82 53 : Dipole::Dipole(const ActionOptions&ao): 83 : PLUMED_COLVAR_INIT(ao), 84 53 : components(false) 85 : { 86 53 : parseAtomList("GROUP",ga_lista); 87 53 : parseFlag("COMPONENTS",components); 88 53 : parseFlag("NOPBC",nopbc); 89 53 : checkRead(); 90 53 : if(components) { 91 4 : addComponentWithDerivatives("x"); componentIsNotPeriodic("x"); 92 4 : addComponentWithDerivatives("y"); componentIsNotPeriodic("y"); 93 6 : addComponentWithDerivatives("z"); componentIsNotPeriodic("z"); 94 : } else { 95 51 : addValueWithDerivatives(); setNotPeriodic(); 96 : } 97 : 98 53 : log.printf(" of %u atoms\n",static_cast<unsigned>(ga_lista.size())); 99 376 : for(unsigned int i=0; i<ga_lista.size(); ++i) { 100 323 : log.printf(" %d", ga_lista[i].serial()); 101 : } 102 53 : log.printf(" \n"); 103 53 : if(nopbc) log.printf(" without periodic boundary conditions\n"); 104 15 : else log.printf(" using periodic boundary conditions\n"); 105 : 106 53 : requestAtoms(ga_lista); 107 53 : } 108 : 109 : // calculator 110 918 : void Dipole::calculate() 111 : { 112 918 : if(!nopbc) makeWhole(); 113 : double ctot=0.; 114 : unsigned N=getNumberOfAtoms(); 115 918 : std::vector<double> charges(N); 116 918 : Vector dipje; 117 : 118 7243 : for(unsigned i=0; i<N; ++i) { 119 6325 : charges[i]=getCharge(i); 120 6325 : ctot+=charges[i]; 121 : } 122 918 : ctot/=(double)N; 123 : 124 7243 : for(unsigned i=0; i<N; ++i) { 125 6325 : charges[i]-=ctot; 126 6325 : dipje += charges[i]*getPosition(i); 127 : } 128 : 129 918 : if(!components) { 130 773 : double dipole = dipje.modulo(); 131 773 : double idip = 1./dipole; 132 : 133 6228 : for(unsigned i=0; i<N; i++) { 134 5455 : double dfunc=charges[i]*idip; 135 5455 : setAtomsDerivatives(i,dfunc*dipje); 136 : } 137 773 : setBoxDerivativesNoPbc(); 138 773 : setValue(dipole); 139 : } else { 140 145 : Value* valuex=getPntrToComponent("x"); 141 145 : Value* valuey=getPntrToComponent("y"); 142 145 : Value* valuez=getPntrToComponent("z"); 143 1015 : for(unsigned i=0; i<N; i++) { 144 870 : setAtomsDerivatives(valuex,i,charges[i]*Vector(1.0,0.0,0.0)); 145 870 : setAtomsDerivatives(valuey,i,charges[i]*Vector(0.0,1.0,0.0)); 146 870 : setAtomsDerivatives(valuez,i,charges[i]*Vector(0.0,0.0,1.0)); 147 : } 148 145 : setBoxDerivativesNoPbc(valuex); 149 145 : setBoxDerivativesNoPbc(valuey); 150 145 : setBoxDerivativesNoPbc(valuez); 151 145 : valuex->set(dipje[0]); 152 145 : valuey->set(dipje[1]); 153 145 : valuez->set(dipje[2]); 154 : } 155 918 : } 156 : 157 : } 158 : }