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 "core/ActionRegister.h" 23 : #include "core/ActionShortcut.h" 24 : 25 : namespace PLMD { 26 : namespace gridtools { 27 : 28 : //+PLUMEDOC GRIDCALC MULTICOLVARDENS 29 : /* 30 : Evaluate the average value of a multicolvar on a grid. 31 : 32 : This keyword allows one to construct a phase field representation for a symmetry function from 33 : an atomistic description. If each atom has an associated order parameter, \f$\phi_i\f$ then a 34 : smooth phase field function \f$\phi(r)\f$ can be computed using: 35 : 36 : \f[ 37 : \phi(\mathbf{r}) = \frac{\sum_i K(\mathbf{r}-\mathbf{r}_i) \phi_i }{ \sum_i K(\mathbf{r} - \mathbf{r}_i )} 38 : \f] 39 : 40 : where \f$\mathbf{r}_i\f$ is the position of atom \f$i\f$, the sums run over all the atoms input 41 : and \f$K(\mathbf{r} - \mathbf{r}_i)\f$ is one of the \ref kernelfunctions implemented in plumed. 42 : This action calculates the above function on a grid, which can then be used in the input to further 43 : actions. 44 : 45 : \par Examples 46 : 47 : The following example shows perhaps the simplest way in which this action can be used. The following 48 : input computes the density of atoms at each point on the grid and outputs this quantity to a file. In 49 : other words this input instructs plumed to calculate \f$\rho(\mathbf{r}) = \sum_i K(\mathbf{r} - \mathbf{r}_i )\f$ 50 : 51 : \plumedfile 52 : dens: DENSITY SPECIES=1-100 53 : grid: MULTICOLVARDENS DATA=dens ORIGIN=1 DIR=xyz NBINS=100,100,100 BANDWIDTH=0.05,0.05,0.05 STRIDE=1 54 : DUMPGRID GRID=grid STRIDE=500 FILE=density 55 : \endplumedfile 56 : 57 : In the above example density is added to the grid on every step. The PRINT_GRID instruction thus tells PLUMED to 58 : output the average density at each point on the grid every 500 steps of simulation. Notice that the that grid output 59 : on step 1000 is an average over all 1000 frames of the trajectory. If you would like to analyze these two blocks 60 : of data separately you must use the CLEAR flag. 61 : 62 : This second example computes an order parameter (in this case \ref FCCUBIC) and constructs a phase field model 63 : for this order parameter using the equation above. 64 : 65 : \plumedfile 66 : fcc: FCCUBIC SPECIES=1-5184 SWITCH={CUBIC D_0=1.2 D_MAX=1.5} ALPHA=27 67 : dens: MULTICOLVARDENS DATA=fcc ORIGIN=1 DIR=xyz NBINS=14,14,28 BANDWIDTH=1.0,1.0,1.0 STRIDE=1 CLEAR=1 68 : DUMPCUBE GRID=dens STRIDE=1 FILE=dens.cube 69 : \endplumedfile 70 : 71 : In this example the phase field model is computed and output to a file on every step of the simulation. Furthermore, 72 : because the CLEAR=1 keyword is set on the MULTICOLVARDENS line each Gaussian cube file output is a phase field 73 : model for a particular trajectory frame. The average value accumulated thus far is cleared at the start of every single 74 : timestep and there is no averaging over trajectory frames in this case. 75 : 76 : */ 77 : //+ENDPLUMEDOC 78 : 79 : class MultiColvarDensity : public ActionShortcut { 80 : public: 81 : explicit MultiColvarDensity(const ActionOptions&); 82 : static void registerKeywords( Keywords& keys ); 83 : }; 84 : 85 : PLUMED_REGISTER_ACTION(MultiColvarDensity,"MULTICOLVARDENS") 86 : 87 10 : void MultiColvarDensity::registerKeywords( Keywords& keys ) { 88 10 : ActionShortcut::registerKeywords( keys ); 89 20 : keys.add("compulsory","STRIDE","1","the frequency with which to accumulate the densities"); 90 20 : keys.add("compulsory","CLEAR","0","the frequency with which to clear the density"); 91 20 : keys.add("compulsory","ORIGIN","we will use the position of this atom as the origin"); 92 20 : keys.add("compulsory","DIR","the direction in which to calculate the density profile"); 93 20 : keys.add("optional","BANDWIDTH","the bandwidths for kernel density esimtation"); 94 20 : keys.add("compulsory","KERNEL","GAUSSIAN","the kernel function you are using. More details on the kernels available " 95 : "in plumed plumed can be found in \\ref kernelfunctions."); 96 20 : keys.add("optional","NBINS","the number of bins to use in each direction (alternative to GRID_NBIN)"); 97 20 : keys.add("optional","GRID_MIN","the lower bounds for the grid (default boxlengths)"); 98 20 : keys.add("optional","GRID_MAX","the upper bounds for the grid (default boxlengths)"); 99 20 : keys.add("optional","DATA","the multicolvar which you would like to calculate the density profile for"); 100 20 : keys.add("optional","ATOMS","if you are calculating a atomic density you use this keyword to specify the atoms that are involved"); 101 20 : keys.addFlag("UNORMALIZED",false,"do not divide by the density"); 102 20 : keys.add("optional","NORMALIZATION","set true/false to determine how to the data is normalised"); 103 20 : keys.setValueDescription("grid","the average value of the order parameters at each point on the grid"); 104 30 : keys.needsAction("DISTANCES"); keys.needsAction("KDE"); keys.needsAction("ACCUMULATE"); 105 30 : keys.needsAction("CUSTOM"); keys.needsAction("ONES"); keys.needsAction("CUSTOM"); 106 10 : } 107 : 108 8 : MultiColvarDensity::MultiColvarDensity(const ActionOptions&ao): 109 : Action(ao), 110 8 : ActionShortcut(ao) 111 : { 112 : // Read in the position of the origin 113 16 : std::string origin_str; parse("ORIGIN",origin_str); 114 : // Read in the quantity we are calculating the density for 115 24 : std::string atoms_str, data_str; parse("ATOMS",atoms_str); parse("DATA",data_str); 116 8 : if( atoms_str.length()==0 && data_str.length()==0 ) error("quantity to calculate the density for was not specified used DATA/ATOMS"); 117 : // Get the information on the direction for the density 118 24 : std::string dir, direction_string; parse("DIR",dir); std::string nbins=""; parse("NBINS",nbins); if(nbins.length()>0) nbins=" GRID_BIN=" + nbins; 119 16 : if( dir=="x" ) direction_string = "ARG=" + getShortcutLabel() + "_dist.x " + nbins; 120 0 : else if( dir=="y" ) direction_string = "ARG=" + getShortcutLabel() + "_dist.y " + nbins; 121 0 : else if( dir=="z" ) direction_string = "ARG=" + getShortcutLabel() + "_dist.z " + nbins; 122 0 : else if( dir=="xy" ) direction_string = "ARG=" + getShortcutLabel() + "_dist.x," + getShortcutLabel() + "_dist.y " + nbins; 123 0 : else if( dir=="xz" ) direction_string = "ARG=" + getShortcutLabel() + "_dist.x," + getShortcutLabel() + "_dist.z " + nbins; 124 0 : else if( dir=="yz" ) direction_string = "ARG=" + getShortcutLabel() + "_dist.y," + getShortcutLabel() + "_dist.z " + nbins; 125 0 : else if( dir=="xyz" ) direction_string = "ARG=" + getShortcutLabel() + "_dist.x," + getShortcutLabel() + "_dist.y," + getShortcutLabel() + "_dist.z " + nbins; 126 0 : else error( dir + " is invalid dir specification use x/y/z/xy/xz/yz/xyz"); 127 : 128 : // Parse the keymap for this averaging stuff 129 24 : std::string stride, clear; parse("STRIDE",stride); parse("CLEAR",clear); bool unorm; parseFlag("UNORMALIZED",unorm); 130 14 : if( !unorm ) { std::string normstr; parse("NORMALIZATION",normstr); if( normstr=="false" ) unorm=true; } 131 : // Create distance action 132 16 : bool hasheights; std::string dist_words = getShortcutLabel() + "_dist: DISTANCES COMPONENTS ORIGIN=" + origin_str; 133 11 : if( atoms_str.length()>0 ) { hasheights=false; dist_words += " ATOMS=" + atoms_str; } 134 10 : else { hasheights=true; dist_words += " ATOMS=" + data_str; } 135 : // plumed_massert( keys.count("ORIGIN"), "you must specify the position of the origin" ); 136 8 : readInputLine( dist_words ); 137 : 138 8 : std::string inputLine = convertInputLineToString(); 139 : // Make the kde object for the numerator if needed 140 8 : if( hasheights ) { 141 10 : readInputLine( getShortcutLabel() + "_inumer: KDE VOLUMES=" + data_str + " " + direction_string + " " + inputLine ); 142 7 : if( unorm ) { readInputLine( getShortcutLabel() + ": ACCUMULATE ARG=" + getShortcutLabel() + "_inumer STRIDE=" + stride + " CLEAR=" + clear ); return; } 143 6 : else readInputLine( getShortcutLabel() + "_numer: ACCUMULATE ARG=" + getShortcutLabel() + "_inumer STRIDE=" + stride + " CLEAR=" + clear ); 144 : } 145 : // Make the kde object 146 12 : readInputLine( getShortcutLabel() + "_kde: KDE " + inputLine + " " + direction_string ); 147 : // Make the division object if it is required 148 6 : if( hasheights && !unorm ) { 149 6 : readInputLine( getShortcutLabel() + "_denom: ACCUMULATE ARG=" + getShortcutLabel() + "_kde STRIDE=" + stride + " CLEAR=" + clear ); 150 6 : readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_numer," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO"); 151 3 : } else if( !hasheights ) { 152 3 : readInputLine( getShortcutLabel() + "_weight: ONES SIZE=1" ); 153 6 : readInputLine( getShortcutLabel() + "_numer: ACCUMULATE ARG=" + getShortcutLabel() + "_kde STRIDE=" + stride + " CLEAR=" + clear ); 154 6 : readInputLine( getShortcutLabel() + "_denom: ACCUMULATE ARG=" + getShortcutLabel() + "_weight STRIDE=" + stride + " CLEAR=" + clear ); 155 6 : readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_numer," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO"); 156 : } 157 0 : } 158 : 159 : } 160 : }