LCOV - code coverage report
Current view: top level - gridtools - MultiColvarDensity.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 63 80 78.8 %
Date: 2026-11-05 10:19:04 Functions: 2 3 66.7 %

          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             : __As you can see if you expand the inputs below, you can achieve what this shortcut action does by using [KDE](KDE.md)
      33             : and [ACCUMULATE](ACCUMULATE.md).  This new syntax provides you with much greater control over what is being calculated
      34             : and is also easier to understand.  If you are using this shortcut we strongly encourage you to change to using this new
      35             : syntax.__
      36             : 
      37             : This shortcut allows one to construct a phase field representation for a symmetry function from
      38             : an atomistic description.  If each atom has an associated order parameter, $\phi_i$ then a
      39             : smooth phase field function $\phi(r)$ can be computed using:
      40             : 
      41             : $$
      42             : \phi(\mathbf{r}) = \frac{\sum_i K(\mathbf{r}-\mathbf{r}_i) \phi_i }{ \sum_i K(\mathbf{r} - \mathbf{r}_i )}
      43             : $$
      44             : 
      45             : where $\mathbf{r}_i$ is the position of atom $i$, the sums run over all the atoms input
      46             : and $K(\mathbf{r} - \mathbf{r}_i)$ is a Gaussian function.
      47             : This action calculates the above function on a grid, which can then be used in the input to further
      48             : actions.
      49             : 
      50             : ## Examples
      51             : 
      52             : The following example shows perhaps the simplest way that a phase field can be computed.  The following
      53             : input computes the density of atoms at each point on the grid and outputs this quantity to a file.  In
      54             : other words this input instructs plumed to calculate $\rho(\mathbf{r}) = \sum_i K(\mathbf{r} - \mathbf{r}_i )$
      55             : 
      56             : ```plumed
      57             : dens: DISTANCES ATOMS=1-100 ORIGIN=1 COMPONENTS
      58             : kde: KDE ARG=dens.x,dens.y,dens.z GRID_BIN=100,100,100 BANDWIDTH=0.05,0.05,0.05
      59             : grid: ACCUMULATE ARG=kde STRIDE=10
      60             : DUMPGRID ARG=grid STRIDE=500 FILE=density
      61             : ```
      62             : 
      63             : In the above example density is added to the grid on every step.  The DUMPGRID instruction thus tells PLUMED to
      64             : output the average density at each point on the grid every 500 steps of simulation.
      65             : 
      66             : Understanding the commands in the input above provides a window into understand how MULTICOLVARDENS works.
      67             : This second example computes an order parameter (in this case [FCCUBIC](FCCUBIC.md)) and constructs a phase field model
      68             : for this order parameter using the equation above.
      69             : 
      70             : ```plumed
      71             : fcc: FCCUBIC SPECIES=1-5184 SWITCH={CUBIC D_0=1.2 D_MAX=1.5} ALPHA=27
      72             : dens: MULTICOLVARDENS DATA=fcc ORIGIN=1 DIR=xyz NBINS=14,14,28 BANDWIDTH=1.0,1.0,1.0 STRIDE=1 CLEAR=1
      73             : DUMPCUBE ARG=dens STRIDE=1 FILE=dens.cube
      74             : ```
      75             : 
      76             : In this example the phase field model is computed and output to a file on every step of the simulation.  Furthermore,
      77             : because the CLEAR=1 keyword is set on the MULTICOLVARDENS line each Gaussian cube file output is a phase field
      78             : model for a particular trajectory frame. The average value accumulated thus far is cleared at the start of every single
      79             : timestep and there is no averaging over trajectory frames in this case.
      80             : 
      81             : If you expand the shortcuts in the input above you can see that the average phase field is calculated using [CUSTOM](CUSTOM.md)
      82             : commands.  We hope that this makes it easy to understand the meaning of the phase field that is computed.
      83             : 
      84             : */
      85             : //+ENDPLUMEDOC
      86             : 
      87             : class MultiColvarDensity : public ActionShortcut {
      88             : public:
      89             :   explicit MultiColvarDensity(const ActionOptions&);
      90             :   static void registerKeywords( Keywords& keys );
      91             : };
      92             : 
      93             : PLUMED_REGISTER_ACTION(MultiColvarDensity,"MULTICOLVARDENS")
      94             : 
      95          12 : void MultiColvarDensity::registerKeywords( Keywords& keys ) {
      96          12 :   ActionShortcut::registerKeywords( keys );
      97          12 :   keys.add("compulsory","STRIDE","1","the frequency with which to accumulate the densities");
      98          12 :   keys.add("compulsory","CLEAR","0","the frequency with which to clear the density");
      99          12 :   keys.add("compulsory","ORIGIN","we will use the position of this atom as the origin");
     100          12 :   keys.add("compulsory","DIR","the direction in which to calculate the density profile");
     101          12 :   keys.add("optional","BANDWIDTH","the bandwidths for kernel density esimtation");
     102             :   std::string strcut;
     103          12 :   Tools::convert( sqrt(2*dp2cutoff), strcut );
     104          12 :   keys.add("compulsory","KERNEL","GAUSSIAN DMAX=" + strcut,"the switching function which is used to transform the distance from the kernel center");
     105          24 :   keys.linkActionInDocs("KERNEL","LESS_THAN");
     106          12 :   keys.add("optional","NBINS","the number of bins to use in each direction (alternative to GRID_NBIN)");
     107          12 :   keys.add("optional","GRID_MIN","the lower bounds for the grid (default boxlengths)");
     108          12 :   keys.add("optional","GRID_MAX","the upper bounds for the grid (default boxlengths)");
     109          12 :   keys.add("optional","DATA","the multicolvar which you would like to calculate the density profile for");
     110          12 :   keys.add("optional","ATOMS","if you are calculating a atomic density you use this keyword to specify the atoms that are involved");
     111          12 :   keys.addFlag("UNORMALIZED",false,"do not divide by the density");
     112          12 :   keys.add("optional","NORMALIZATION","set true/false to determine how to the data is normalised");
     113          24 :   keys.setValueDescription("grid","the average value of the order parameters at each point on the grid");
     114          12 :   keys.needsAction("DISTANCES");
     115          12 :   keys.needsAction("KDE");
     116          12 :   keys.needsAction("ACCUMULATE");
     117          12 :   keys.needsAction("CUSTOM");
     118          12 :   keys.needsAction("ONES");
     119          12 :   keys.needsAction("CUSTOM");
     120          24 :   keys.setDeprecated("KDE");
     121          24 : }
     122             : 
     123           8 : MultiColvarDensity::MultiColvarDensity(const ActionOptions&ao):
     124             :   Action(ao),
     125           8 :   ActionShortcut(ao) {
     126             :   // Read in the position of the origin
     127             :   std::string origin_str;
     128          16 :   parse("ORIGIN",origin_str);
     129             :   // Read in the quantity we are calculating the density for
     130             :   std::string atoms_str, data_str;
     131           8 :   parse("ATOMS",atoms_str);
     132          16 :   parse("DATA",data_str);
     133           8 :   if( atoms_str.length()==0 && data_str.length()==0 ) {
     134           0 :     error("quantity to calculate the density for was not specified used DATA/ATOMS");
     135             :   }
     136             :   // Get the information on the direction for the density
     137             :   std::string dir, direction_string;
     138           8 :   parse("DIR",dir);
     139           8 :   std::string nbins="";
     140          16 :   parse("NBINS",nbins);
     141           8 :   if(nbins.length()>0) {
     142           0 :     nbins=" GRID_BIN=" + nbins;
     143             :   }
     144           8 :   if( dir=="x" ) {
     145          24 :     direction_string = "ARG=" + getShortcutLabel() + "_dist.x " + nbins;
     146           0 :   } else if( dir=="y" ) {
     147           0 :     direction_string = "ARG=" + getShortcutLabel() + "_dist.y " + nbins;
     148           0 :   } else if( dir=="z" ) {
     149           0 :     direction_string = "ARG=" + getShortcutLabel() + "_dist.z " + nbins;
     150           0 :   } else if( dir=="xy" ) {
     151           0 :     direction_string = "ARG=" + getShortcutLabel() + "_dist.x," + getShortcutLabel() + "_dist.y " + nbins;
     152           0 :   } else if( dir=="xz" ) {
     153           0 :     direction_string = "ARG=" + getShortcutLabel() + "_dist.x," + getShortcutLabel() + "_dist.z " + nbins;
     154           0 :   } else if( dir=="yz" ) {
     155           0 :     direction_string = "ARG=" + getShortcutLabel() + "_dist.y," + getShortcutLabel() + "_dist.z " + nbins;
     156           0 :   } else if( dir=="xyz" ) {
     157           0 :     direction_string = "ARG=" + getShortcutLabel() + "_dist.x," + getShortcutLabel() + "_dist.y," + getShortcutLabel() + "_dist.z " + nbins;
     158             :   } else {
     159           0 :     error( dir + " is invalid dir specification use x/y/z/xy/xz/yz/xyz");
     160             :   }
     161             : 
     162             :   // Parse the keymap for this averaging stuff
     163             :   std::string stride, clear;
     164           8 :   parse("STRIDE",stride);
     165           8 :   parse("CLEAR",clear);
     166             :   bool unorm;
     167           8 :   parseFlag("UNORMALIZED",unorm);
     168           8 :   if( !unorm ) {
     169             :     std::string normstr;
     170           6 :     parse("NORMALIZATION",normstr);
     171           6 :     if( normstr=="false" ) {
     172           0 :       unorm=true;
     173             :     }
     174             :   }
     175             :   // Create distance action
     176             :   bool hasheights;
     177          16 :   std::string dist_words = getShortcutLabel() + "_dist: DISTANCES COMPONENTS ORIGIN=" + origin_str;
     178           8 :   if( atoms_str.length()>0 ) {
     179             :     hasheights=false;
     180           6 :     dist_words += " ATOMS=" + atoms_str;
     181             :   } else {
     182             :     hasheights=true;
     183          10 :     dist_words += " ATOMS=" + data_str;
     184             :   }
     185           8 :   readInputLine( dist_words );
     186             : 
     187           8 :   std::string inputLine = convertInputLineToString();
     188             :   // Make the kde object for the numerator if needed
     189           8 :   if( hasheights ) {
     190          15 :     readInputLine( getShortcutLabel() + "_inumer: KDE VOLUMES=" + data_str + " " + direction_string + " " + inputLine );
     191           5 :     if( unorm ) {
     192           8 :       readInputLine( getShortcutLabel() + ": ACCUMULATE ARG=" + getShortcutLabel() + "_inumer STRIDE=" + stride + " CLEAR=" + clear );
     193             :       return;
     194             :     } else {
     195          12 :       readInputLine( getShortcutLabel() + "_numer: ACCUMULATE ARG=" + getShortcutLabel() + "_inumer STRIDE=" + stride + " CLEAR=" + clear );
     196             :     }
     197             :   }
     198             :   // Make the kde object
     199          12 :   readInputLine( getShortcutLabel() + "_kde: KDE " + inputLine  + " " + direction_string );
     200             :   // Make the division object if it is required
     201           6 :   if( hasheights && !unorm ) {
     202           9 :     readInputLine( getShortcutLabel() + "_denom: ACCUMULATE ARG=" + getShortcutLabel() + "_kde STRIDE=" + stride + " CLEAR=" + clear );
     203          12 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_numer," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO");
     204             :   } else if( !hasheights ) {
     205           3 :     readInputLine( getShortcutLabel() + "_weight: ONES SIZE=1" );
     206           9 :     readInputLine( getShortcutLabel() + "_numer: ACCUMULATE ARG=" + getShortcutLabel() + "_kde STRIDE=" + stride + " CLEAR=" + clear );
     207           9 :     readInputLine( getShortcutLabel() + "_denom: ACCUMULATE ARG=" + getShortcutLabel() + "_weight STRIDE=" + stride + " CLEAR=" + clear );
     208          12 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_numer," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO");
     209             :   }
     210           0 : }
     211             : 
     212             : }
     213             : }

Generated by: LCOV version 1.16