1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2014-2021 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 "tools/SwitchingFunction.h"
24 #include "MultiColvarFilter.h"
25 
26 //+PLUMEDOC MTRANSFORMS MTRANSFORM_LESS
27 /*
28 This action can be used to transform the colvar values calculated by a \ref mcolv using a \ref switchingfunction
29 
30 In this action each colvar, \f$s_i\f$, calculated by \ref mcolv is transformed by a \ref switchingfunction function that
31 is equal to one if the colvar is less than a certain target value and which is equal to zero otherwise.
32 It is important to understand the distinction between what is done here and what is done by \ref MFILTER_LESS.
33 In \ref MFILTER_LESS a weight, \f$w_i\f$ for the colvar is calculated using the \ref switchingfunction.  If one calculates the
34 MEAN for \ref MFILTER_LESS one is thus calculating:
35 
36 \f[
37 \mu = \frac{ \sum_i \sigma(s_i) s_i }{\sum_i \simga(s_i) }
38 \f]
39 
40 where \f$\sigma\f$ is the \ref switchingfunction.  In this action by contrast the colvar is being transformed by
41 the \ref switchingfunction.  If one thus calculates a MEAN for this action one computes:
42 
43 \f[
44 \mu = \frac{ \sum_{i=1}^N \simga(s_i) }{ N }
45 \f]
46 
47 In other words, you are calculating the mean for the transformed colvar.
48 
49 \par Examples
50 
51 The following input gives an example of how a MTRANSFORM_LESS action can be used to duplicate
52 functionality that is elsewhere in PLUMED.
53 
54 \plumedfile
55 DISTANCES ...
56  GROUPA=1-10 GROUPB=11-20
57  LABEL=d1
58 ... DISTANCES
59 MTRANSFORM_LESS DATA=d1 SWITCH={GAUSSIAN D_0=1.5 R_0=0.00001}
60 \endplumedfile
61 
62 In this case you can achieve the same result by using:
63 
64 \plumedfile
65 DISTANCES ...
66  GROUPA=1-10 GROUPB=11-20
67  LESS_THAN={GAUSSIAN D_0=1.5 R_0=0.00001}
68 ... DISTANCES
69 \endplumedfile
70 (see \ref DISTANCES)
71 
72 The advantage of MTRANSFORM_LESS comes, however, if you want to use transformed colvars as input
73 for \ref MULTICOLVARDENS
74 
75 */
76 //+ENDPLUMEDOC
77 
78 //+PLUMEDOC MFILTERS MFILTER_LESS
79 /*
80 This action can be used to filter the distribution of colvar values in a \ref mcolv
81 so that one can compute the mean and so on for only those multicolvars less than a tolerance.
82 
83 This action can be used to create a dynamic group of atom based on the value of a multicolvar.
84 In this action a multicolvar is within the dynamic group if its value is less than a target.
85 In actuality a weight, \f$w_i\f$ is ascribed to each colvar, \f$s_i\f$ calculated by a multicolvar
86 and this weight measures the degree to which a colvar is a member of the group.  This weight is a number
87 between 0 and 1 that is calculated using a \ref switchingfunction , \f$\sigma\f$.
88 If one calculates a function of the set of multicolvars
89 these weights are included in the calculation.  As such if one calculates the MEAN, \f$\mu\f$ of a filtered
90 multicolvar what is computed is the following:
91 
92 \f[
93 \mu = \frac{ \sum_i w_i s_i }{ \sum_i w_i}
94 \f]
95 
96 One is thus calculating the mean for those colvars that are less than the target.
97 
98 \par Examples
99 
100 The example shown below calculates the mean for those distances that less than 1.5 nm in length
101 
102 \plumedfile
103 DISTANCES GROUPA=1 GROUPB=2-50 MEAN LABEL=d1
104 MFILTER_LESS DATA=d1 SWITCH={GAUSSIAN D_0=1.5 R_0=0.00001} MEAN LABEL=d4
105 \endplumedfile
106 
107 */
108 //+ENDPLUMEDOC
109 
110 namespace PLMD {
111 namespace multicolvar {
112 
113 class FilterLess : public MultiColvarFilter {
114 private:
115   SwitchingFunction sf;
116 public:
117   static void registerKeywords( Keywords& keys );
118   explicit FilterLess(const ActionOptions& ao);
119   double applyFilter( const double& val, double& df ) const override;
120 };
121 
122 PLUMED_REGISTER_ACTION(FilterLess,"MFILTER_LESS")
123 PLUMED_REGISTER_ACTION(FilterLess,"MTRANSFORM_LESS")
124 
registerKeywords(Keywords & keys)125 void FilterLess::registerKeywords( Keywords& keys ) {
126   MultiColvarFilter::registerKeywords( keys );
127   keys.add("compulsory","NN","6","The n parameter of the switching function ");
128   keys.add("compulsory","MM","0","The m parameter of the switching function ");
129   keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
130   keys.add("compulsory","R_0","The r_0 parameter of the switching function");
131   keys.add("optional","SWITCH","This keyword is used if you want to employ an alternative to the continuous switching function defined above. "
132            "The following provides information on the \\ref switchingfunction that are available. "
133            "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords.");
134 }
135 
FilterLess(const ActionOptions & ao)136 FilterLess::FilterLess(const ActionOptions& ao):
137   Action(ao),
138   MultiColvarFilter(ao)
139 {
140   // Read in the switching function
141   std::string sw, errors; parse("SWITCH",sw);
142   if(sw.length()>0) {
143     sf.set(sw,errors);
144     if( errors.length()!=0 ) error("problem reading SWITCH keyword : " + errors );
145   } else {
146     double r_0=-1.0, d_0; int nn, mm;
147     parse("NN",nn); parse("MM",mm);
148     parse("R_0",r_0); parse("D_0",d_0);
149     if( r_0<0.0 ) error("you must set a value for R_0");
150     sf.set(nn,mm,r_0,d_0);
151   }
152   log.printf("  filtering colvar values and focussing only on those less than %s\n",( sf.description() ).c_str() );
153 
154   checkRead();
155 }
156 
applyFilter(const double & val,double & df) const157 double FilterLess::applyFilter( const double& val, double& df ) const {
158   double f = sf.calculate( val, df ); df*=val;
159   return f;
160 }
161 
162 }
163 }
164