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

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2015-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 "KDE.h"
      23             : #include "KDEGridTools.h"
      24             : #include "SphericalKDEGridTools.h"
      25             : #include "core/AccelerableShortcut.h"
      26             : #include "core/ActionShortcut.h"
      27             : #include "core/ActionRegister.h"
      28             : 
      29             : //+PLUMEDOC ANALYSIS KDE
      30             : /*
      31             : Create a histogram from the input scalar/vector/matrix using KDE
      32             : 
      33             : This action can be used to construct instantaneous distributions for quantities by using [kernel density esstimation](https://en.wikipedia.org/wiki/Kernel_density_estimation).
      34             : The input arguments must all have the same rank and size but you can use a scalar, vector or matrix in input.  The distribution
      35             : of this quantity on a grid is then computed using kernel density estimation.
      36             : 
      37             : The following example demonstrates how this action can be used with a scalar as input:
      38             : 
      39             : ```plumed
      40             : d1: DISTANCE ATOMS=1,2
      41             : kde: KDE ARG=d1 GRID_MIN=0.0 GRID_MAX=1.0 GRID_BIN=100 BANDWIDTH=0.2
      42             : DUMPGRID ARG=kde STRIDE=1 FILE=kde.grid
      43             : ```
      44             : 
      45             : This input outputs a different file on every time step. These files contain a function stored on a grid.  The function output in this case
      46             : consists of a single Gaussian with $\sigma=0.2$ that is centered on the instantaneous value of the distance between atoms 1 and 2.  Obviously,
      47             : you are unlikely to use an input like the one above. The more usual thing to do would be to accumulate the histogram over the course of a
      48             : few trajectory frames using the [ACCUMULATE](ACCUMULATE.md) command as has been done in the input below, which estimates a histogram as a function
      49             : of two collective variables:
      50             : 
      51             : ```plumed
      52             : d1: DISTANCE ATOMS=1,2
      53             : d2: DISTANCE ATOMS=1,2
      54             : kde: KDE ARG=d1,d2 GRID_MIN=0.0,0.0 GRID_MAX=1.0,1.0 GRID_BIN=100,100 BANDWIDTH=0.2,0.2
      55             : histo: ACCUMULATE ARG=kde STRIDE=1
      56             : DUMPGRID ARG=histo FILE=histo.grid STRIDE=10000
      57             : ```
      58             : 
      59             : Notice, that you can also achieve something similar by using the [HISTOGRAM](HISTOGRAM.md) shortcut.
      60             : 
      61             : ## Controlloing the grid
      62             : 
      63             : If you prefer to specify the grid spacing rather than the number of bins you can do so using the GRID_SPACING keyword as shown below:
      64             : 
      65             : ```plumed
      66             : d1: DISTANCE ATOMS=1,2
      67             : kde: KDE ARG=d1 GRID_MIN=0.0 GRID_MAX=1.0 GRID_SPACING=0.01 BANDWIDTH=0.2
      68             : DUMPGRID ARG=kde STRIDE=1 FILE=kde.grid
      69             : ```
      70             : 
      71             : If $x$ is one of the input arguments to the KDE action and $x<g_{min}$ or $x>g_{max}$, where $g_{min}$ and $g_{max}$ are the minimum
      72             : and maximum values on the grid for that argument that were specified using GRID_MIN and GRID_MAX, then by PLUMED will crash.
      73             : 
      74             : ## Constructing the density
      75             : 
      76             : If you are performing a simulation in the NVT ensemble and wish to look at the density as a function of position in the cell you can use an input like the one shown below:
      77             : 
      78             : ```plumed
      79             : a: FIXEDATOM AT=0,0,0
      80             : dens: DISTANCES ATOMS=1-100 ORIGIN=a COMPONENTS
      81             : kde: KDE ARG=dens.x,dens.y,dens.z GRID_BIN=100,100,100 BANDWIDTH=0.05,0.05,0.05
      82             : DUMPGRID ARG=kde STRIDE=1 FILE=density
      83             : ```
      84             : 
      85             : Notice that you do not need to specify GRID_MIN and GRID_MAX values with this input. In this case PLUMED gets the extent of the grid from the cell vectors during the first
      86             : step of the simulation.
      87             : 
      88             : ## Specifying a non diagonal bandwidth
      89             : 
      90             : If for any reason you want to use a bandwidth that is not diagonal when doing kensity density estimation you can do by using an input similar to the one shown below:
      91             : 
      92             : ```plumed
      93             : d1: DISTANCE ATOMS=1,2
      94             : d2: DISTANCE ATOMS=1,2
      95             : kde: KDE ...
      96             :   ARG=d1,d2 GRID_MIN=0.0,0.0
      97             :   GRID_MAX=1.0,1.0 GRID_BIN=100,100
      98             :   BANDWIDTH=0.2,0.1,0.1,0.2 HEIGHTS=1
      99             : ...
     100             : histo: ACCUMULATE ARG=kde STRIDE=1
     101             : DUMPGRID ARG=histo FILE=histo.grid STRIDE=10000
     102             : ```
     103             : 
     104             : As there are two arguments for this KDE action the four numbers passed in the bandwdith parameter are interepretted as a $2\times 2$ matrix.
     105             : Notice that you can also pass the information for the bandwidth in from another argument as has been done here:
     106             : 
     107             : ```plumed
     108             : m: CONSTANT VALUES=0.2,0.1,0.1,0.2 NROWS=2 NCOLS=2
     109             : 
     110             : d1: DISTANCE ATOMS=1,2
     111             : d2: DISTANCE ATOMS=1,2
     112             : kde: KDE ...
     113             :   ARG=d1,d2 GRID_MIN=0.0,0.0
     114             :   GRID_MAX=1.0,1.0 GRID_BIN=100,100
     115             :   BANDWIDTH=m HEIGHTS=1
     116             : ...
     117             : histo: ACCUMULATE ARG=kde STRIDE=1
     118             : DUMPGRID ARG=histo FILE=histo.grid STRIDE=10000
     119             : ```
     120             : 
     121             : In this case the input is equivalent to the first input above and the bandwidth is a constant.  You could, however, also use a non-constant value as input to the BANDWIDTH keyword.
     122             : 
     123             : ## Working with vectors and scalars
     124             : 
     125             : If the input to your KDE action is a set of scalars it appears odd to separate the process of computing the KDE from the process of accumulating the histogram. However, if
     126             : you are using vectors as in the example below, this division can be helpful.
     127             : 
     128             : ```plumed
     129             : d1: DISTANCE ATOMS1=1,2 ATOMS2=3,4 ATOMS3=5,6 ATOMS4=7,8 ATOMS5=9,10
     130             : kde: KDE ARG=d1 GRID_MIN=0.0 GRID_MAX=1.0 GRID_BIN=100 BANDWIDTH=0.2
     131             : ```
     132             : 
     133             : In the papea cited in the bibliography below, the [KL_ENTROPY](KL_ENTROPY.md) between the instantaneous distribution of CVs and a reference distribution was introduced
     134             : as a collective variable. As is detailed in the documentation for that action, the ability to calculate the instantaneous histogram from an input vector is essential to
     135             : reproducing these calculations.
     136             : 
     137             : Notice that you can also use a one or multiple matrices in the input for a KDE object.  The example below uses the angles between the z axis and set of bonds aroud two
     138             : atoms:
     139             : 
     140             : ```plumed
     141             : d1: DISTANCE_MATRIX GROUPA=1,2 GROUPB=3-10 COMPONENTS
     142             : phi: CUSTOM ARG=d1.z,d1.w FUNC=acos(x/y) PERIODIC=NO
     143             : kde: KDE ARG=phi GRID_MIN=0 GRID_MAX=pi GRID_BIN=200 BANDWIDTH=0.1
     144             : ```
     145             : 
     146             : ## Using different weights
     147             : 
     148             : In all the inputs above the kernels that are added to the grid on each step are Gaussians with that are normalised so that their integral over all space is one. If you want your
     149             : Gaussians to have a particular height you can use the HEIGHT keyword as illustrated below:
     150             : 
     151             : ```plumed
     152             : d1: CONTACT_MATRIX GROUPA=1,2 GROUPB=3-10 SWITCH={RATIONAL R_0=0.1} COMPONENTS
     153             : mag: CUSTOM ARG=d1.x,d1.y,d1.z FUNC=x*x+y*y+z*z PERIODIC=NO
     154             : phi: CUSTOM ARG=d1.z,mag FUNC=acos(x/sqrt(y)) PERIODIC=NO
     155             : kde: KDE ARG=phi GRID_MIN=0 GRID_MAX=pi HEIGHTS=d1.w GRID_BIN=200 BANDWIDTH=0.1
     156             : ```
     157             : 
     158             : As indicated above, the HEIGHTS keyword should be passed a Value that has the same rank and size as the arguments that are passed using the ARG keyword. Each of the Gaussian kernels
     159             : that are added to the grid in this case have a value equal to the weight at the maximum of the function.
     160             : 
     161             : Notice that you can also use the VOLUMES keyword in a similar way as shown below:
     162             : 
     163             : ```plumed
     164             : d1: CONTACT_MATRIX GROUPA=1,2 GROUPB=3-10 SWITCH={RATIONAL R_0=0.1} COMPONENTS
     165             : mag: CUSTOM ARG=d1.x,d1.y,d1.z FUNC=x*x+y*y+z*z PERIODIC=NO
     166             : phi: CUSTOM ARG=d1.z,mag FUNC=acos(x/sqrt(y)) PERIODIC=NO
     167             : kde: KDE ARG=phi GRID_MIN=0 GRID_MAX=pi VOLUMES=d1.w GRID_BIN=200 BANDWIDTH=0.1
     168             : ```
     169             : 
     170             : Now, however, the integral of the Gaussians over all space are equal to the elements of d1.w.
     171             : 
     172             : ## Setting the kernel
     173             : 
     174             : When you run molecular dynamics and calculate $d$ collective variables using PLUMED you generate a time series of $d$ dimensional vectors $\mathbf{x}_t$. The Kernel Density Estimation
     175             : method that is implemented in this action allows you to calculate an estimate of the probabilty density for the $d$ collective variables, which is a function with $d$ arguments.
     176             : This estimate is calculated using:
     177             : 
     178             : $$
     179             : f(\mathbf{x}) = \sum_{t=1}^n h_t K\left[ \sqrt{(\mathbf{x}-\mathbf{x}_t)^T \mathbf{H}^{-1} (\mathbf{x}-\mathbf{x}_t)} \right]
     180             : $$
     181             : 
     182             : This expression assumes that you are calculating the density from $n$ trajectory frames. $\mathbf{H}$ is bandwidths matrix and $h_t$ is the height that you want to use for the $t$th of your observations.
     183             : $K$ is then a non-negative function called the kernel.  This function acts upon the [Mahalanobis distance](https://en.wikipedia.org/wiki/Mahalanobis_distance) between $\mathbf{x}$ and $\mathbf{x}_t$.
     184             : 
     185             : To provide users with flexibility, the functional form for the kernel is specified using a syntax similar to the one described in the documentation for the [LESS_THAN](LESS_THAN.md)
     186             : action. The only difference from the instructions for swtiching functions in [LESS_THAN](LESS_THAN.md) is that PLUMED automatically
     187             : sets the $r_0$ parameter of your switching function to one for you. __You should thus not include R_0 when you set the parameters of your kernel function.__
     188             : 
     189             : By default PLUMED uses a Gaussian kernel function as illustrated in the input below:
     190             : 
     191             : ```plumed
     192             : d1: DISTANCE ATOMS=1,2
     193             : kde: KDE ARG=d1 GRID_MIN=0.0 GRID_MAX=1.0 GRID_BIN=100 BANDWIDTH=0.2 KERNEL={GAUSSIAN D_MAX=3.5355}
     194             : DUMPGRID ARG=kde STRIDE=1 FILE=kde.grid
     195             : ```
     196             : 
     197             : Notice that a stretching and scaling is performed on the gaussian kernel that is employed here so the function is guaranteed to be equal to zero if
     198             : $\sqrt{(\mathbf{x}-\mathbf{x}_t)^T \mathbf{H}^{-1} (\mathbf{x}-\mathbf{x}_t)}>3.5355$.  One consequence of this is that the integral of the $d$ dimensional
     199             : Gaussian will be slightly less than $(2\pi)^{d/2}\det(H)^{1/2}$. This can be a problem if you are assuming that the integral of your Gaussians is equal to this value when
     200             : normalising them.  You can partially avoid this problem by using the NOSTRETCH option as shown below:
     201             : 
     202             : ```plumed
     203             : d1: DISTANCE ATOMS=1,2
     204             : kde: KDE ARG=d1 GRID_MIN=0.0 GRID_MAX=1.0 GRID_BIN=100 BANDWIDTH=0.2 KERNEL={GAUSSIAN NOSTRETCH D_MAX=3.5355}
     205             : DUMPGRID ARG=kde STRIDE=1 FILE=kde.grid
     206             : ```
     207             : 
     208             : This, however, introduces a discontinuity in the kernel at $d_{max}$.
     209             : 
     210             : __We strongly recommend setting a D_MAX parameter for your kernels__.
     211             : If you do not set a D_MAX value for your KERNEL then a cutoff of $d_{max}=\sqrt{2*6.25}\approx 3.5355$ is used. If you are calculating a 1D histogram and D_MAX is unset PLUMED
     212             : then you evaluate a kernel at centered at $a$ on the gridpoint at $b$ if $\frac{|a-b|}{s}<=\left\lceil \frac{d_{max}\sigma}{s}\right\rceil$, where $s$ is the spacing between
     213             : grid points and $\sigma$ is the bandwidth. The ceiling on the right hand side of this inequality obviously makes this s necessary but not sufficient condition for ensuring that
     214             : $\sqrt{(\mathbf{x}-\mathbf{x}_t)^T \mathbf{H}^{-1} (\mathbf{x}-\mathbf{x}_t)}<d_{max}$.  In higher dimensions this becomes even more severe.  In 2D to
     215             : determine if we need to evaluate a kernel centered at $(a,b)$ for the grid point $(c,d)$ we first determine if $\frac{|a-c|}{s}<=\left\lceil \frac{d_{max}\sigma}{s}\right\rceil$ and
     216             : if $\frac{|b-d|}{s}<=\left\lceil \frac{d_{max}\sigma}{s}\right\rceil$. With these tests there are many more points that satisfy these two criteria but that do not satisfy
     217             : $\sqrt{(\mathbf{x}-\mathbf{x}_t)^T \mathbf{H}^{-1} (\mathbf{x}-\mathbf{x}_t)}<d_{max}$. The key thing to recognise is that when D_MAX is set PLUMED sets the value of the kernel equal to zero if
     218             : $\sqrt{(\mathbf{x}-\mathbf{x}_t)^T \mathbf{H}^{-1} (\mathbf{x}-\mathbf{x}_t)}>d_{max}$ even when the NOSTRETCH option is employed. If D_MAX is not set this check is not performed and
     219             : kernels can thus contribute density to grid points that are more than $\sqrt(2*6.25)$ from the center of the kernel.
     220             : 
     221             : Kernel density estimation is a method for estimating a histogram but it is probably not the first method that you learned for estimating histograms. Instead you probably learned to assume that the
     222             : the density is constant between grid points. In 1D we thus express the density between grid point $x_k$ and $x_{k+1}$ using:
     223             : 
     224             : $$
     225             : F(x_{k+1}) - F(x_k) = \int_{x_k}^{x_{k+1}} f(x) \textrm{d}x = \int_{x_k}^{x_{k+1}} \sum_{t=1}^n h_t K\left[ \frac{|x - x_t|}{\sigma} \right] \textrm{d}x
     226             : $$
     227             : 
     228             : In the typical method for calculating histograms $\sigma=x_{k+1}-x_k$ and $K$ is then:
     229             : 
     230             : $$
     231             : K(x) = \begin{cases}
     232             : 1 & \textrm{if} x<1 \\
     233             : 0 & \textrm{otherwise}
     234             : \end{cases}
     235             : $$
     236             : 
     237             : This is the method used for estimating the histogram when you use KERNEL=DISCRETE. Other (continuous) choices for $K$ are possible and are described in the documentation for [BETWEEN](BETWEEN.md).
     238             : For example, in the following input $K$ is is a Gaussian:
     239             : 
     240             : ```
     241             : d1: DISTANCE ATOMS=1,2
     242             : kde: KDE ARG=d1 GRID_MIN=0.0 GRID_MAX=1.0 GRID_BIN=100 BANDWIDTH=0.2 KERNEL=gaussian-bin
     243             : DUMPGRID ARG=kde STRIDE=1 FILE=kde.grid
     244             : ```
     245             : 
     246             : Multidimensional analogues of this approach can also be used. For example, in a 2D analogue the density in the square with corners at $(x_k,y_j)$, $(x_{k+1},y_j)$, $(x_k,y_{j+1})$ and $(x_{k+1},y_{j+1})$ is computed using:
     247             : 
     248             : $$
     249             : [ F(x_{k+1}) - F(x_k) ][ F(y_{j+1} - F(y_j) ] = \int_{x_k}^{x_{k+1}} f(x) \textrm{d}x = \sum_{t=1}^n h_t \int_{x_k}^{x_{k+1}} K\left[ \frac{|x - x_t|}{\sigma} \right] \textrm{d}x \int_{y_j}^{y_{j+1}} K\left[ \frac{|y - y_t|}{\sigma} \right] \textrm{d}y
     250             : $$
     251             : 
     252             : */
     253             : //+ENDPLUMEDOC
     254             : 
     255             : //+PLUMEDOC ANALYSIS SPHERICAL_KDE
     256             : /*
     257             : Create a histogram from the input scalar/vector/matrix using SPHERICAL_KDE
     258             : 
     259             : This action operates similarly to [KDE](KDE.md) but it is designed to be used for investigating [directional statistics]().
     260             : It is particularly useful if you are looking at the distribution of bond vectors as illustrated in the input below:
     261             : 
     262             : ```plumed
     263             : # Calculate all the bond vectors
     264             : d1: CONTACT_MATRIX GROUP=1-100 SWITCH={RATIONAL R_0=0.1} COMPONENTS
     265             : # Normalise the bond vectors
     266             : mag: CUSTOM ARG=d1.x,d1.y,d1.z FUNC=sqrt(x*x+y*y+z*z) PERIODIC=NO
     267             : d1x: CUSTOM ARG=d1.x,mag FUNC=x/y PERIODIC=NO
     268             : d1y: CUSTOM ARG=d1.y,mag FUNC=x/y PERIODIC=NO
     269             : d1z: CUSTOM ARG=d1.z,mag FUNC=x/y PERIODIC=NO
     270             : # And construct the KDE
     271             : kde: SPHERICAL_KDE ARG=d1x,d1y,d1z HEIGHTS=d1.w CONCENTRATION=100 GRID_BIN=144
     272             : ```
     273             : 
     274             : Each bond vector here contributes a [Fisher von-Mises kernel](https://en.wikipedia.org/wiki/Von_Mises–Fisher_distribution) to the spherical grid.  This spherical grid is constructed
     275             : using a [Fibonnacci sphere algorithm](https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere) so the number of specified using the GRID_BIN keyword must be a Fibonacci number.
     276             : 
     277             : */
     278             : //+ENDPLUMEDOC
     279             : 
     280             : namespace PLMD {
     281             : namespace gridtools {
     282             : 
     283             : typedef KDE<DiagonalKernelParams,DiscreteKernel,KDEGridTools<DiagonalKernelParams,DiscreteKernel>> discretekde;
     284             : PLUMED_REGISTER_ACTION(discretekde,"KDE_DISCRETE")
     285             : typedef KDE<DiagonalKernelParams,HistogramBeadKernel,KDEGridTools<DiagonalKernelParams,HistogramBeadKernel>> beadkde;
     286             : PLUMED_REGISTER_ACTION(beadkde,"KDE_BEADS")
     287             : typedef KDE<DiagonalKernelParams,RegularKernel<DiagonalKernelParams>,KDEGridTools<DiagonalKernelParams,RegularKernel<DiagonalKernelParams>>> flatkde;
     288             : PLUMED_REGISTER_ACTION(flatkde,"KDE_KERNELS")
     289             : typedef KDE<VonMissesKernelParams,UniversalVonMisses,SphericalKDEGridTools> sphericalkde;
     290             : PLUMED_REGISTER_ACTION(sphericalkde,"SPHERICAL_KDE_CPU")
     291             : typedef AccelerableShortcut<sphericalkde> sphshortcut;
     292             : PLUMED_REGISTER_ACTION(sphshortcut,"SPHERICAL_KDE")
     293             : typedef KDE<NonDiagonalKernelParams,RegularKernel<NonDiagonalKernelParams>,KDEGridTools<NonDiagonalKernelParams,RegularKernel<NonDiagonalKernelParams>>> flatfkde;
     294             : PLUMED_REGISTER_ACTION(flatfkde,"KDE_FULLCOVAR")
     295             : 
     296             : 
     297             : class KDEShortcut : public ActionShortcut {
     298             : public:
     299             :   static void registerKeywords(Keywords& keys);
     300             :   explicit KDEShortcut(const ActionOptions&);
     301             : };
     302             : 
     303             : PLUMED_REGISTER_ACTION(KDEShortcut,"KDE")
     304             : 
     305         181 : void KDEShortcut::registerKeywords(Keywords& keys) {
     306         181 :   KDE<DiagonalKernelParams,RegularKernel<DiagonalKernelParams>,KDEGridTools<DiagonalKernelParams,RegularKernel<DiagonalKernelParams>>>::registerKeywords( keys );
     307         181 :   keys.addActionNameSuffix("_DISCRETE");
     308         181 :   keys.addActionNameSuffix("_KERNELS");
     309         181 :   keys.addActionNameSuffix("_BEADS");
     310         181 :   keys.addActionNameSuffix("_FULLCOVAR");
     311         181 :   keys.addActionNameSuffix("_DISCRETEACC");
     312         181 :   keys.addActionNameSuffix("_KERNELSACC");
     313         181 :   keys.addActionNameSuffix("_BEADSACC");
     314         181 :   keys.addActionNameSuffix("_FULLCOVARACC");
     315         181 : }
     316             : 
     317         100 : KDEShortcut::KDEShortcut(const ActionOptions&ao):
     318             :   Action(ao),
     319         100 :   ActionShortcut(ao) {
     320         100 :   bool usegpuFLAG=false;
     321         200 :   parseFlag("USEGPU",usegpuFLAG);
     322             :   std::string kerneltype;
     323         100 :   parse("KERNEL",kerneltype);
     324         100 :   if( kerneltype=="DISCRETE" ) {
     325          62 :     readInputLine( getShortcutLabel() + ": KDE_DISCRETE" + (usegpuFLAG ? "ACC ":" ") + convertInputLineToString() );
     326             :     return;
     327             :   }
     328          85 :   std::vector<std::string> args;
     329          85 :   parseVector("ARG", args );
     330          85 :   std::vector<Value*> argvals;
     331          85 :   ActionWithArguments::interpretArgumentList( args, plumed.getActionSet(), this, argvals );
     332          85 :   if( argvals.empty() ) {
     333           2 :     error("no arguments were specified with ARG");
     334             :   }
     335          85 :   std::string argstr = " ARG=" + argvals[0]->getName();
     336         118 :   for(unsigned i=1; i<argvals.size(); ++i) {
     337          68 :     argstr += "," + argvals[i]->getName();
     338             :   }
     339          85 :   std::vector<std::string> bw;
     340         170 :   parseVector("BANDWIDTH",bw);
     341          85 :   if( bw.empty() ) {
     342           1 :     error("BANDWIDTH is required unless KERNEL=DISCRETE");
     343             :   }
     344          84 :   std::string bwstr = " BANDWIDTH=" + bw[0];
     345         119 :   for(unsigned i=1; i<bw.size(); ++i) {
     346          70 :     bwstr += "," + bw[i];
     347             :   }
     348          84 :   if( bw.size() == 1 && argvals.size()>1 ) {
     349           0 :     std::vector<Value*> bwargs;
     350           0 :     ActionWithArguments::interpretArgumentList( bw, plumed.getActionSet(), this, bwargs );
     351           0 :     if( bwargs.size()!=1 ) {
     352           0 :       error("invalid input for bandwidth parameter");
     353           0 :     } else if( bwargs[0]->getRank()<=1 ) {
     354           0 :       if( kerneltype.find("bin")==std::string::npos ) {
     355           0 :         readInputLine( getShortcutLabel() + ": KDE_KERNELS" + (usegpuFLAG ? "ACC ":" ") + argstr + " " + bwstr + " KERNEL={" + kerneltype + "} " + convertInputLineToString() );
     356             :       } else {
     357           0 :         std::size_t dd = kerneltype.find("-bin");
     358           0 :         readInputLine( getShortcutLabel() + ": KDE_BEADS" + (usegpuFLAG ? "ACC ":" ") + argstr + " " + bwstr + " KERNEL=" + kerneltype.substr(0,dd) + " " + convertInputLineToString() );
     359             :       }
     360           0 :     } else if( bwargs[0]->getRank()==2 ) {
     361           0 :       readInputLine( getShortcutLabel() + ": KDE_FULLCOVAR" + (usegpuFLAG ? "ACC ":" ") + argstr + " " + bwstr + " KERNEL={" + kerneltype + "} " + convertInputLineToString() );
     362             :     } else {
     363           0 :       error("found strange rank for bandwidth parameter");
     364             :     }
     365          84 :   } else if( bw.size()==argvals.size() ) {
     366          83 :     if( kerneltype.find("bin")==std::string::npos ) {
     367         455 :       readInputLine( getShortcutLabel() + ": KDE_KERNELS" + (usegpuFLAG ? "ACC ":" ") + argstr + " " + bwstr + " KERNEL={" + kerneltype + "} " + convertInputLineToString() );
     368             :     } else {
     369          18 :       std::size_t dd = kerneltype.find("-bin");
     370         126 :       readInputLine( getShortcutLabel() + ": KDE_BEADS" + (usegpuFLAG ? "ACC ":" ") + argstr + " " + bwstr + " KERNEL=" + kerneltype.substr(0,dd) + " " + convertInputLineToString() );
     371             :     }
     372           1 :   } else if( bw.size()==argvals.size()*argvals.size() ) {
     373           7 :     readInputLine( getShortcutLabel() + ": KDE_FULLCOVAR" + (usegpuFLAG ? "ACC ":" ")+ argstr + " " + bwstr + " KERNEL={" + kerneltype + "} " + convertInputLineToString() );
     374             :   } else {
     375           1 :     error("invalid input for bandwidth");
     376             :   }
     377         174 : }
     378             : 
     379             : }
     380             : }

Generated by: LCOV version 1.16