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 13893 : PLUMED_REGISTER_ACTION(Dipole,"DIPOLE") 72 : 73 58 : void Dipole::registerKeywords(Keywords& keys) { 74 58 : Colvar::registerKeywords(keys); 75 116 : keys.add("atoms","GROUP","the group of atoms we are calculating the dipole moment for"); 76 116 : 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 116 : keys.addOutputComponent("x","COMPONENTS","the x-component of the dipole"); 78 116 : keys.addOutputComponent("y","COMPONENTS","the y-component of the dipole"); 79 116 : keys.addOutputComponent("z","COMPONENTS","the z-component of the dipole"); 80 58 : } 81 : 82 54 : Dipole::Dipole(const ActionOptions&ao): 83 : PLUMED_COLVAR_INIT(ao), 84 54 : components(false) { 85 54 : parseAtomList("GROUP",ga_lista); 86 54 : parseFlag("COMPONENTS",components); 87 54 : parseFlag("NOPBC",nopbc); 88 54 : checkRead(); 89 54 : if(components) { 90 2 : addComponentWithDerivatives("x"); 91 2 : componentIsNotPeriodic("x"); 92 2 : addComponentWithDerivatives("y"); 93 2 : componentIsNotPeriodic("y"); 94 2 : addComponentWithDerivatives("z"); 95 4 : componentIsNotPeriodic("z"); 96 : } else { 97 52 : addValueWithDerivatives(); 98 52 : setNotPeriodic(); 99 : } 100 : 101 54 : log.printf(" of %u atoms\n",static_cast<unsigned>(ga_lista.size())); 102 379 : for(unsigned int i=0; i<ga_lista.size(); ++i) { 103 325 : log.printf(" %d", ga_lista[i].serial()); 104 : } 105 54 : log.printf(" \n"); 106 54 : if(nopbc) { 107 38 : log.printf(" without periodic boundary conditions\n"); 108 : } else { 109 16 : log.printf(" using periodic boundary conditions\n"); 110 : } 111 : 112 54 : requestAtoms(ga_lista); 113 54 : } 114 : 115 : // calculator 116 923 : void Dipole::calculate() { 117 923 : if(!nopbc) { 118 541 : makeWhole(); 119 : } 120 : double ctot=0.; 121 : unsigned N=getNumberOfAtoms(); 122 923 : std::vector<double> charges(N); 123 923 : Vector dipje; 124 : 125 7258 : for(unsigned i=0; i<N; ++i) { 126 6335 : charges[i]=getCharge(i); 127 6335 : ctot+=charges[i]; 128 : } 129 923 : ctot/=(double)N; 130 : 131 7258 : for(unsigned i=0; i<N; ++i) { 132 6335 : charges[i]-=ctot; 133 6335 : dipje += charges[i]*getPosition(i); 134 : } 135 : 136 923 : if(!components) { 137 778 : double dipole = dipje.modulo(); 138 778 : double idip = 1./dipole; 139 : 140 6243 : for(unsigned i=0; i<N; i++) { 141 5465 : double dfunc=charges[i]*idip; 142 5465 : setAtomsDerivatives(i,dfunc*dipje); 143 : } 144 778 : setBoxDerivativesNoPbc(); 145 778 : setValue(dipole); 146 : } else { 147 145 : Value* valuex=getPntrToComponent("x"); 148 145 : Value* valuey=getPntrToComponent("y"); 149 145 : Value* valuez=getPntrToComponent("z"); 150 1015 : for(unsigned i=0; i<N; i++) { 151 870 : setAtomsDerivatives(valuex,i,charges[i]*Vector(1.0,0.0,0.0)); 152 870 : setAtomsDerivatives(valuey,i,charges[i]*Vector(0.0,1.0,0.0)); 153 870 : setAtomsDerivatives(valuez,i,charges[i]*Vector(0.0,0.0,1.0)); 154 : } 155 145 : setBoxDerivativesNoPbc(valuex); 156 145 : setBoxDerivativesNoPbc(valuey); 157 145 : setBoxDerivativesNoPbc(valuez); 158 145 : valuex->set(dipje[0]); 159 145 : valuey->set(dipje[1]); 160 145 : valuez->set(dipje[2]); 161 : } 162 923 : } 163 : 164 : } 165 : }