From 2c6faabdfd4804088cdfeeebf3bde02564839cc2 Mon Sep 17 00:00:00 2001 From: Gareth Aneurin Tribello Date: Sun, 20 Oct 2024 13:26:29 +0100 Subject: [PATCH] Added new method for links to switching function documentation This commit addresses something that has bothered me for a while; namely, how we should provide the links to the parts of the manual on swiching function when you encounter a keyword like switch. I would ideally like to provide the link to this page both in the tooltip that is generated for the annotated inputs and in the tables that contain the syntax. This commit addresses this problem. You can now use the command keys.linkActionInDocs when you register the keyword in registerKeywords. Links to the manual pages for relevant actions will then be included in the manual and in tooltips. As part of this I have also moved the documentation for switching function to the action LESS_THAN and the documentation for HistogramBead to BETWEEN as this is where these classes are used in their most basic form. --- src/adjmat/BridgeMatrix.cpp | 3 + src/adjmat/ContactMatrix.cpp | 1 + src/adjmat/ContactMatrixShortcut.cpp | 1 + src/adjmat/DistanceMatrix.cpp | 1 - src/adjmat/HbondMatrix.cpp | 3 + src/adjmat/TopologyMatrix.cpp | 4 + src/cltools/GenJson.cpp | 15 +- src/colvar/ContactMap.cpp | 2 +- src/colvar/Coordination.cpp | 1 + src/function/Bessel.cpp | 1 - src/function/Between.cpp | 51 +++++++ src/function/LessThan.cpp | 137 +++++++++++++++++ src/multicolvar/MultiColvarShortcuts.cpp | 3 + .../SecondaryStructureRMSD.cpp | 1 + src/symfunc/CoordinationNumbers.cpp | 1 + src/symfunc/SMAC.cpp | 2 + src/tools/HistogramBead.cpp | 57 ------- src/tools/Keywords.cpp | 96 +++--------- src/tools/Keywords.h | 17 +-- src/tools/SwitchingFunction.cpp | 144 ------------------ src/volumes/VolumeInCylinder.cpp | 2 +- src/volumes/VolumeInSphere.cpp | 2 +- 22 files changed, 254 insertions(+), 291 deletions(-) diff --git a/src/adjmat/BridgeMatrix.cpp b/src/adjmat/BridgeMatrix.cpp index 7d049a8874..50968fedd8 100644 --- a/src/adjmat/BridgeMatrix.cpp +++ b/src/adjmat/BridgeMatrix.cpp @@ -75,10 +75,13 @@ void BridgeMatrix::registerKeywords( Keywords& keys ) { keys.add("atoms","BRIDGING_ATOMS","The list of atoms that can form the bridge between the two interesting parts " "of the structure."); keys.add("optional","SWITCH","The parameters of the two switchingfunction in the above formula"); + keys.linkActionInDocs("SWITCH","LESS_THAN"); keys.add("optional","SWITCHA","The switchingfunction on the distance between bridging atoms and the atoms in " "group A"); + keys.linkActionInDocs("SWITCHA","LESS_THAN"); keys.add("optional","SWITCHB","The switchingfunction on the distance between the bridging atoms and the atoms in " "group B"); + keys.linkActionInDocs("SWITCHB","LESS_THAN"); } BridgeMatrix::BridgeMatrix(const ActionOptions&ao): diff --git a/src/adjmat/ContactMatrix.cpp b/src/adjmat/ContactMatrix.cpp index 1f6b8c0df3..cb6fe0bf7d 100644 --- a/src/adjmat/ContactMatrix.cpp +++ b/src/adjmat/ContactMatrix.cpp @@ -86,6 +86,7 @@ void ContactMatrix::registerKeywords( Keywords& keys ) { keys.add("optional","SWITCH","This keyword is used if you want to employ an alternative to the continuous swiching function defined above. " "The following provides information on the \\ref switchingfunction that are available. " "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords."); + keys.linkActionInDocs("SWITCH","LESS_THAN"); } ContactMatrix::ContactMatrix( const ActionOptions& ao ): diff --git a/src/adjmat/ContactMatrixShortcut.cpp b/src/adjmat/ContactMatrixShortcut.cpp index 70682bdb68..419d2527b2 100644 --- a/src/adjmat/ContactMatrixShortcut.cpp +++ b/src/adjmat/ContactMatrixShortcut.cpp @@ -77,6 +77,7 @@ void ContactMatrixShortcut::registerKeywords(Keywords& keys) { keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function"); keys.add("compulsory","R_0","The r_0 parameter of the switching function"); keys.add("numbered","SWITCH","specify the switching function to use between two sets of indistinguishable atoms"); + keys.linkActionInDocs("SWITCH","LESS_THAN"); keys.addActionNameSuffix("_PROPER"); keys.needsAction("TRANSPOSE"); keys.needsAction("CONCATENATE"); } diff --git a/src/adjmat/DistanceMatrix.cpp b/src/adjmat/DistanceMatrix.cpp index e224f2ee84..5b42275123 100644 --- a/src/adjmat/DistanceMatrix.cpp +++ b/src/adjmat/DistanceMatrix.cpp @@ -21,7 +21,6 @@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ #include "AdjacencyMatrixBase.h" #include "core/ActionRegister.h" -#include "tools/SwitchingFunction.h" #include "tools/Matrix.h" //+PLUMEDOC MATRIX DISTANCE_MATRIX diff --git a/src/adjmat/HbondMatrix.cpp b/src/adjmat/HbondMatrix.cpp index acb779720a..2d0997d097 100644 --- a/src/adjmat/HbondMatrix.cpp +++ b/src/adjmat/HbondMatrix.cpp @@ -101,10 +101,13 @@ void HbondMatrix::registerKeywords( Keywords& keys ) { keys.add("atoms","HYDROGENS","The list of atoms that can form the bridge between the two interesting parts " "of the structure."); keys.add("numbered","SWITCH","The switchingfunction that specifies how close a pair of atoms must be together for there to be a hydrogen bond between them"); + keys.linkActionInDocs("SWITCH","LESS_THAN"); keys.add("numbered","HSWITCH","The switchingfunction that specifies how close the hydrogen must be to the donor atom of the hydrogen bond for it to be " "considered a hydrogen bond"); + keys.linkActionInDocs("HSWITCH","LESS_THAN"); keys.add("numbered","ASWITCH","A switchingfunction that is used to specify what the angle between the vector connecting the donor atom to the acceptor atom and " "the vector connecting the donor atom to the hydrogen must be in order for it considered to be a hydrogen bond"); + keys.linkActionInDocs("ASWITCH","LESS_THAN"); } HbondMatrix::HbondMatrix(const ActionOptions&ao): diff --git a/src/adjmat/TopologyMatrix.cpp b/src/adjmat/TopologyMatrix.cpp index 4234c24ddb..d315689a34 100644 --- a/src/adjmat/TopologyMatrix.cpp +++ b/src/adjmat/TopologyMatrix.cpp @@ -72,10 +72,14 @@ void TopologyMatrix::registerKeywords( Keywords& keys ) { keys.add("compulsory","SWITCH","This keyword is used if you want to employ an alternative to the continuous swiching function defined above. " "The following provides information on the \\ref switchingfunction that are available. " "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords."); + keys.linkActionInDocs("SWITCH","LESS_THAN"); keys.add("compulsory","RADIUS",""); + keys.linkActionInDocs("RADIUS","LESS_THAN"); keys.add("compulsory","CYLINDER_SWITCH","a switching function on ( r_ij . r_ik - 1 )/r_ij"); + keys.linkActionInDocs("CYLINDER_SWITCH","LESS_THAN"); keys.add("compulsory","BIN_SIZE","the size to use for the bins"); keys.add("compulsory","DENSITY_THRESHOLD",""); + keys.linkActionInDocs("DENSITY_THRESHOLD","LESS_THAN"); keys.add("compulsory","SIGMA","the width of the function to be used for kernel density estimation"); keys.add("compulsory","KERNEL","gaussian","the type of kernel function to be used"); } diff --git a/src/cltools/GenJson.cpp b/src/cltools/GenJson.cpp index 8c9adec6ce..b30b16f2a9 100644 --- a/src/cltools/GenJson.cpp +++ b/src/cltools/GenJson.cpp @@ -52,6 +52,7 @@ class GenJson : public CLTool { private: std::string version; void printHyperlink(std::string action ); + void printKeywordDocs( const std::string& k, const std::string& mydescrip, const Keywords& keys ); public: static void registerKeywords( Keywords& keys ); explicit GenJson(const CLToolOptions& co ); @@ -90,6 +91,10 @@ void GenJson::printHyperlink( std::string action ) { std::cout<<".html\","<0 ) std::cout<<" \""<0 ) std::cout<<" \""<0 ) { + printKeywordDocs( keys.getKeyword(j), mydescrip, keys ); std::cout<<", \"argtype\": \""<0 ) { + printKeywordDocs( keys.getKeyword(j), mydescrip, keys ); std::cout<<", \"default\": \""< #include diff --git a/src/function/Between.cpp b/src/function/Between.cpp index 533f778851..ae3dfe9a43 100644 --- a/src/function/Between.cpp +++ b/src/function/Between.cpp @@ -34,6 +34,57 @@ namespace function { /* Use a switching function to determine how many of the input variables are within a certain range. +If we have multiple instances of a variable we can estimate the probability density function +for that variable using a process called kernel density estimation: + +\f[ +P(s) = \sum_i K\left( \frac{s - s_i}{w} \right) +\f] + +In this equation \f$K\f$ is a symmetric function that must integrate to one that is often +called a kernel function and \f$w\f$ is a smearing parameter. From a probability density function calculated using +kernel density estimation we can calculate the number/fraction of values between an upper and lower +bound using: + +\f[ +w(s) = \int_a^b \sum_i K\left( \frac{s - s_i}{w} \right) +\f] + +All the input to calculate a quantity like \f$w(s)\f$ is generally provided through a single +keyword that will have the following form: + +KEYWORD={TYPE UPPER=\f$a\f$ LOWER=\f$b\f$ SMEAR=\f$\frac{w}{b-a}\f$} + +This will calculate the number of values between \f$a\f$ and \f$b\f$. To calculate +the fraction of values you add the word NORM to the input specification. If the +function keyword SMEAR is not present \f$w\f$ is set equal to \f$0.5(b-a)\f$. Finally, +type should specify one of the kernel types that is present in plumed. These are listed +in the table below: + + + + + + + + + +
TYPE FUNCTION
GAUSSIAN \f$\frac{1}{\sqrt{2\pi}w} \exp\left( -\frac{(s-s_i)^2}{2w^2} \right)\f$
TRIANGULAR \f$ \frac{1}{2w} \left( 1. - \left| \frac{s-s_i}{w} \right| \right) \quad \frac{s-s_i}{w}<1 \f$
+ +Some keywords can also be used to calculate a discrete version of the histogram. That +is to say the number of values between \f$a\f$ and \f$b\f$, the number of values between +\f$b\f$ and \f$c\f$ and so on. A keyword that specifies this sort of calculation would look +something like + +KEYWORD={TYPE UPPER=\f$a\f$ LOWER=\f$b\f$ NBINS=\f$n\f$ SMEAR=\f$\frac{w}{n(b-a)}\f$} + +This specification would calculate the following vector of quantities: + +\f[ +w_j(s) = \int_{a + \frac{j-1}{n}(b-a)}^{a + \frac{j}{n}(b-a)} \sum_i K\left( \frac{s - s_i}{w} \right) +\f] + + \par Examples */ diff --git a/src/function/LessThan.cpp b/src/function/LessThan.cpp index 844cec1cbc..0a8c42301a 100644 --- a/src/function/LessThan.cpp +++ b/src/function/LessThan.cpp @@ -34,6 +34,143 @@ namespace function { /* Use a switching function to determine how many of the input variables are less than a certain cutoff. +Switching functions \f$s(r)\f$ take a minimum of one input parameter \f$r_0\f$. +For \f$r \le d_0 \quad s(r)=1.0\f$ while for \f$r > d_0\f$ the function decays smoothly to 0. +The various switching functions available in PLUMED differ in terms of how this decay is performed. + +Where there is an accepted convention in the literature (e.g. \ref COORDINATION) on the form of the +switching function we use the convention as the default. However, the flexibility to use different +switching functions is always present generally through a single keyword. This keyword generally +takes an input with the following form: + +\verbatim +KEYWORD={TYPE } +\endverbatim + +The following table contains a list of the various switching functions that are available in PLUMED 2 +together with an example input. + + + + + + + + + + + + + + + + + + + + + + +
TYPE FUNCTION EXAMPLE INPUT DEFAULT PARAMETERS
RATIONAL +\f$ +s(r)=\frac{ 1 - \left(\frac{ r - d_0 }{ r_0 }\right)^{n} }{ 1 - \left(\frac{ r - d_0 }{ r_0 }\right)^{m} } +\f$ + +{RATIONAL R_0=\f$r_0\f$ D_0=\f$d_0\f$ NN=\f$n\f$ MM=\f$m\f$} + \f$d_0=0.0\f$, \f$n=6\f$, \f$m=2n\f$
EXP +\f$ +s(r)=\exp\left(-\frac{ r - d_0 }{ r_0 }\right) +\f$ + +{EXP R_0=\f$r_0\f$ D_0=\f$d_0\f$} + \f$d_0=0.0\f$
GAUSSIAN +\f$ +s(r)=\exp\left(-\frac{ (r - d_0)^2 }{ 2r_0^2 }\right) +\f$ + +{GAUSSIAN R_0=\f$r_0\f$ D_0=\f$d_0\f$} + \f$d_0=0.0\f$
SMAP +\f$ +s(r) = \left[ 1 + ( 2^{a/b} -1 )\left( \frac{r-d_0}{r_0} \right)^a \right]^{-b/a} +\f$ + +{SMAP R_0=\f$r_0\f$ D_0=\f$d_0\f$ A=\f$a\f$ B=\f$b\f$} + \f$d_0=0.0\f$
Q +\f$ +s(r) = \frac{1}{1 + \exp(\beta(r_{ij} - \lambda r_{ij}^0))} +\f$ + +{Q REF=\f$r_{ij}^0\f$ BETA=\f$\beta\f$ LAMBDA=\f$\lambda\f$ } + \f$\lambda=1.8\f$, \f$\beta=50 nm^-1\f$ (all-atom)
\f$\lambda=1.5\f$, \f$\beta=50 nm^-1\f$ (coarse-grained)
CUBIC +\f$ +s(r) = (y-1)^2(1+2y) \qquad \textrm{where} \quad y = \frac{r - r_1}{r_0-r_1} +\f$ + +{CUBIC D_0=\f$r_1\f$ D_MAX=\f$r_0\f$} +
TANH +\f$ +s(r) = 1 - \tanh\left( \frac{ r - d_0 }{ r_0 } \right) +\f$ + +{TANH R_0=\f$r_0\f$ D_0=\f$d_0\f$} +
COSINUS +\f$s(r) =\left\{\begin{array}{ll} + 1 & \mathrm{if } r \leq d_0 \\ + 0.5 \left( \cos ( \frac{ r - d_0 }{ r_0 } \pi ) + 1 \right) & \mathrm{if } d_0 < r\leq d_0 + r_0 \\ + 0 & \mathrm{if } r > d_0 + r_0 + \end{array}\right. +\f$ + +{COSINUS R_0=\f$r_0\f$ D_0=\f$d_0\f$} +
CUSTOM +\f$ +s(r) = FUNC +\f$ + +{CUSTOM FUNC=1/(1+x^6) R_0=\f$r_0\f$ D_0=\f$d_0\f$} +
+ +Notice that most commonly used rational functions are better optimized and might run faster. + +Notice that for backward compatibility we allow using `MATHEVAL` instead of `CUSTOM`. +Also notice that if the a `CUSTOM` switching function only depends on even powers of `x` it can be +made faster by using `x2` as a variable. For instance +\verbatim +{CUSTOM FUNC=1/(1+x2^3) R_0=0.3} +\endverbatim +is equivalent to +\verbatim +{CUSTOM FUNC=1/(1+x^6) R_0=0.3} +\endverbatim +but runs faster. The reason is that there is an expensive square root calculation that can be optimized out. + + +\attention +With the default implementation CUSTOM is slower than other functions +(e.g., it is slower than an equivalent RATIONAL function by approximately a factor 2). +Checkout page \ref Lepton to see how to improve its performance. + +For all the switching functions in the above table one can also specify a further (optional) parameter using the parameter +keyword D_MAX to assert that for \f$r>d_{\textrm{max}}\f$ the switching function can be assumed equal to zero. +In this case the function is brought smoothly to zero by stretching and shifting it. +\verbatim +KEYWORD={RATIONAL R_0=1 D_MAX=3} +\endverbatim +the resulting switching function will be +\f$ +s(r) = \frac{s'(r)-s'(d_{max})}{s'(0)-s'(d_{max})} +\f$ +where +\f$ +s'(r)=\frac{1-r^6}{1-r^{12}} +\f$ +Since PLUMED 2.2 this is the default. The old behavior (no stretching) can be obtained with the +NOSTRETCH flag. The NOSTRETCH keyword is only provided for backward compatibility and might be +removed in the future. Similarly, the STRETCH keyword is still allowed but has no effect. + +Notice that switching functions defined with the simplified syntax are never stretched +for backward compatibility. This might change in the future. + \par Examples */ diff --git a/src/multicolvar/MultiColvarShortcuts.cpp b/src/multicolvar/MultiColvarShortcuts.cpp index ac8514999d..75da8b296b 100644 --- a/src/multicolvar/MultiColvarShortcuts.cpp +++ b/src/multicolvar/MultiColvarShortcuts.cpp @@ -31,10 +31,12 @@ void MultiColvarShortcuts::shortcutKeywords( Keywords& keys ) { keys.add("numbered","LESS_THAN","calculate the number of variables that are less than a certain target value. " "This quantity is calculated using \\f$\\sum_i \\sigma(s_i)\\f$, where \\f$\\sigma(s)\\f$ " "is a \\ref switchingfunction."); + keys.linkActionInDocs("LESS_THAN","LESS_THAN"); keys.addOutputComponent("lessthan","LESS_THAN","scalar","the number of colvars that have a value less than a threshold"); keys.add("numbered","MORE_THAN","calculate the number of variables that are more than a certain target value. " "This quantity is calculated using \\f$\\sum_i 1 - \\sigma(s_i)\\f$, where \\f$\\sigma(s)\\f$ " "is a \\ref switchingfunction."); + keys.linkActionInDocs("MORE_THAN","MORE_THAN"); keys.addOutputComponent("morethan","MORE_THAN","scalar","the number of colvars that have a value more than a threshold"); keys.add("optional","ALT_MIN","calculate the minimum value. " "To make this quantity continuous the minimum is calculated using " @@ -54,6 +56,7 @@ void MultiColvarShortcuts::shortcutKeywords( Keywords& keys ) { keys.add("numbered","BETWEEN","calculate the number of values that are within a certain range. " "These quantities are calculated using kernel density estimation as described on " "\\ref histogrambead."); + keys.linkActionInDocs("BETWEEN","BETWEEN"); keys.addOutputComponent("between","BETWEEN","scalar","the number of colvars that have a value that lies in a particular interval"); keys.addFlag("HIGHEST",false,"this flag allows you to recover the highest of these variables."); keys.addOutputComponent("highest","HIGHEST","scalar","the largest of the colvars"); diff --git a/src/secondarystructure/SecondaryStructureRMSD.cpp b/src/secondarystructure/SecondaryStructureRMSD.cpp index e360c1ce3b..d66d9bd161 100644 --- a/src/secondarystructure/SecondaryStructureRMSD.cpp +++ b/src/secondarystructure/SecondaryStructureRMSD.cpp @@ -83,6 +83,7 @@ void SecondaryStructureRMSD::registerKeywords( Keywords& keys ) { keys.addFlag("VERBOSE",false,"write a more detailed output"); keys.add("optional","LESS_THAN","calculate the number of a residue segments that are within a certain target distance of this secondary structure type. " "This quantity is calculated using \\f$\\sum_i \\sigma(s_i)\\f$, where \\f$\\sigma(s)\\f$ is a \\ref switchingfunction."); + keys.linkActionInDocs("LESS_THAN","LESS_THAN"); keys.add("optional","R_0","The r_0 parameter of the switching function."); keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function"); keys.add("compulsory","NN","8","The n parameter of the switching function"); diff --git a/src/symfunc/CoordinationNumbers.cpp b/src/symfunc/CoordinationNumbers.cpp index 9e54c9ef08..ef664f9603 100644 --- a/src/symfunc/CoordinationNumbers.cpp +++ b/src/symfunc/CoordinationNumbers.cpp @@ -110,6 +110,7 @@ void CoordinationNumbers::shortcutKeywords( Keywords& keys ) { keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function"); keys.add("compulsory","R_0","The r_0 parameter of the switching function"); keys.add("optional","SWITCH","the switching function that it used in the construction of the contact matrix"); + keys.linkActionInDocs("SWITCH","LESS_THAN"); multicolvar::MultiColvarShortcuts::shortcutKeywords( keys ); keys.needsAction("CONTACT_MATRIX"); keys.needsAction("GROUP"); } diff --git a/src/symfunc/SMAC.cpp b/src/symfunc/SMAC.cpp index 1fd6632a12..989207333a 100644 --- a/src/symfunc/SMAC.cpp +++ b/src/symfunc/SMAC.cpp @@ -55,8 +55,10 @@ void SMAC::registerKeywords(Keywords& keys) { keys.add("optional","SWITCH","This keyword is used if you want to employ an alternative to the continuous swiching function defined above. " "The following provides information on the \\ref switchingfunction that are available. " "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords."); + keys.linkActionInDocs("SWITCH","LESS_THAN"); keys.add("numbered","KERNEL","The kernels used in the function of the angle"); keys.add("optional","SWITCH_COORD","This keyword is used to define the coordination switching function."); + keys.linkActionInDocs("SWITCH_COORD","LESS_THAN"); keys.reset_style("KERNEL","optional"); keys.setValueDescription("vector","the value of the smac parameter for each of the input molecules"); multicolvar::MultiColvarShortcuts::shortcutKeywords( keys ); diff --git a/src/tools/HistogramBead.cpp b/src/tools/HistogramBead.cpp index eec7d7a312..b55d29e9a8 100644 --- a/src/tools/HistogramBead.cpp +++ b/src/tools/HistogramBead.cpp @@ -27,63 +27,6 @@ namespace PLMD { -//+PLUMEDOC INTERNAL histogrambead -/* -A function that can be used to calculate whether quantities are between fixed upper and lower bounds. - -If we have multiple instances of a variable we can estimate the probability density function -for that variable using a process called kernel density estimation: - -\f[ -P(s) = \sum_i K\left( \frac{s - s_i}{w} \right) -\f] - -In this equation \f$K\f$ is a symmetric function that must integrate to one that is often -called a kernel function and \f$w\f$ is a smearing parameter. From a probability density function calculated using -kernel density estimation we can calculate the number/fraction of values between an upper and lower -bound using: - -\f[ -w(s) = \int_a^b \sum_i K\left( \frac{s - s_i}{w} \right) -\f] - -All the input to calculate a quantity like \f$w(s)\f$ is generally provided through a single -keyword that will have the following form: - -KEYWORD={TYPE UPPER=\f$a\f$ LOWER=\f$b\f$ SMEAR=\f$\frac{w}{b-a}\f$} - -This will calculate the number of values between \f$a\f$ and \f$b\f$. To calculate -the fraction of values you add the word NORM to the input specification. If the -function keyword SMEAR is not present \f$w\f$ is set equal to \f$0.5(b-a)\f$. Finally, -type should specify one of the kernel types that is present in plumed. These are listed -in the table below: - - - - - - - - - -
TYPE FUNCTION
GAUSSIAN \f$\frac{1}{\sqrt{2\pi}w} \exp\left( -\frac{(s-s_i)^2}{2w^2} \right)\f$
TRIANGULAR \f$ \frac{1}{2w} \left( 1. - \left| \frac{s-s_i}{w} \right| \right) \quad \frac{s-s_i}{w}<1 \f$
- -Some keywords can also be used to calculate a discrete version of the histogram. That -is to say the number of values between \f$a\f$ and \f$b\f$, the number of values between -\f$b\f$ and \f$c\f$ and so on. A keyword that specifies this sort of calculation would look -something like - -KEYWORD={TYPE UPPER=\f$a\f$ LOWER=\f$b\f$ NBINS=\f$n\f$ SMEAR=\f$\frac{w}{n(b-a)}\f$} - -This specification would calculate the following vector of quantities: - -\f[ -w_j(s) = \int_{a + \frac{j-1}{n}(b-a)}^{a + \frac{j}{n}(b-a)} \sum_i K\left( \frac{s - s_i}{w} \right) -\f] - -*/ -//+ENDPLUMEDOC - void HistogramBead::registerKeywords( Keywords& keys ) { keys.add("compulsory","LOWER","the lower boundary for this particular bin"); keys.add("compulsory","UPPER","the upper boundary for this particular bin"); diff --git a/src/tools/Keywords.cpp b/src/tools/Keywords.cpp index ca9af95d6e..92e7e643da 100644 --- a/src/tools/Keywords.cpp +++ b/src/tools/Keywords.cpp @@ -38,8 +38,6 @@ Keywords::KeyType::KeyType( const std::string& type ) { style=atoms; } else if( type=="hidden" ) { style=hidden; - } else if( type=="vessel" ) { - style=vessel; } else { plumed_massert(false,"invalid keyword specifier " + type); } @@ -56,8 +54,6 @@ void Keywords::KeyType::setStyle( const std::string& type ) { style=atoms; } else if( type=="hidden" ) { style=hidden; - } else if( type=="vessel" ) { - style=vessel; } else { plumed_massert(false,"invalid keyword specifier " + type); } @@ -68,55 +64,6 @@ std::string Keywords::getStyle( const std::string & k ) const { return (types.find(k)->second).toString(); } -void Keywords::add( const Keywords& newkeys ) { - newkeys.copyData( keys, reserved_keys, types, allowmultiple, documentation, booldefs, numdefs, atomtags, cnames, ckey, cdocs ); -} - -void Keywords::copyData( std::vector& kk, std::vector& rk, std::map& tt, std::map& am, - std::map& docs, std::map& bools, std::map& nums, - std::map& atags, std::vector& cnam, std::map& ck, - std::map& cd ) const { - for(unsigned i=0; i( thiskey,types.find(thiskey)->second) ); - if( (types.find(thiskey)->second).isAtomList() ) atags.insert( std::pair( thiskey,atomtags.find(thiskey)->second) ); - plumed_massert( allowmultiple.count( thiskey ), "no numbered data on keyword " + thiskey + " to copy" ); - am.insert( std::pair(thiskey,allowmultiple.find(thiskey)->second) ); - plumed_massert( documentation.count( thiskey ), "no documentation for keyword " + thiskey + " to copy" ); - docs.insert( std::pair(thiskey,documentation.find(thiskey)->second) ); - if( booldefs.count( thiskey ) ) bools.insert( std::pair( thiskey,booldefs.find(thiskey)->second) ); - if( numdefs.count( thiskey ) ) nums.insert( std::pair( thiskey,numdefs.find(thiskey)->second) ); - } - for(unsigned i=0; i( thiskey,types.find(thiskey)->second) ); - if( (types.find(thiskey)->second).isAtomList() ) atags.insert( std::pair( thiskey,atomtags.find(thiskey)->second) ); - plumed_massert( allowmultiple.count( thiskey ), "no numbered data on keyword " + thiskey + " to copy" ); - am.insert( std::pair(thiskey,allowmultiple.find(thiskey)->second) ); - plumed_massert( documentation.count( thiskey ), "no documentation for keyword " + thiskey + " to copy" ); - docs.insert( std::pair(thiskey,documentation.find(thiskey)->second) ); - if( booldefs.count( thiskey ) ) bools.insert( std::pair( thiskey,booldefs.find(thiskey)->second) ); - if( numdefs.count( thiskey ) ) nums.insert( std::pair( thiskey,numdefs.find(thiskey)->second) ); - } - for(unsigned i=0; i( thisnam, ckey.find(thisnam)->second) ); - plumed_massert( cdocs.count( thisnam ), "no documentation on component " + thisnam + " to copy" ); - cd.insert( std::pair( thisnam, cdocs.find(thisnam)->second) ); - } -} - void Keywords::reserve( const std::string & t, const std::string & k, const std::string & d ) { plumed_assert( !exists(k) && !reserved(k) ); std::string fd, lowkey=k; @@ -128,15 +75,7 @@ void Keywords::reserve( const std::string & t, const std::string & k, const std: if( num==std::string::npos ) break; lowkey.erase( lowkey.begin() + num, lowkey.begin() + num + 1 ); } - if( t=="vessel" ) { - fd = d + " The final value can be referenced using label." + lowkey; - if(d.find("flag")==std::string::npos) fd += ". You can use multiple instances of this keyword i.e. " + - k +"1, " + k + "2, " + k + "3... The corresponding values are then " - "referenced using label."+ lowkey +"-1, label." + lowkey + - "-2, label." + lowkey + "-3..."; - allowmultiple.insert( std::pair(k,true) ); - types.insert( std::pair(k,KeyType("vessel")) ); - } else if( t=="numbered" ) { + if( t=="numbered" ) { fd = d + ". You can use multiple instances of this keyword i.e. " + k +"1, " + k + "2, " + k + "3..."; allowmultiple.insert( std::pair(k,true) ); types.insert( std::pair(k,KeyType("optional")) ); @@ -148,6 +87,7 @@ void Keywords::reserve( const std::string & t, const std::string & k, const std: if( (types.find(k)->second).isAtomList() ) atomtags.insert( std::pair(k,t) ); } documentation.insert( std::pair(k,fd) ); + linkaction.insert( std::pair(k,"none") ); reserved_keys.push_back(k); } @@ -159,6 +99,7 @@ void Keywords::reserveFlag( const std::string & k, const bool def, const std::st std::string fd,lowkey=k; std::transform(lowkey.begin(),lowkey.end(),lowkey.begin(),[](unsigned char c) { return std::tolower(c); }); fd=defstr + d; documentation.insert( std::pair(k,fd) ); + linkaction.insert( std::pair(k,"none") ); allowmultiple.insert( std::pair(k,false) ); booldefs.insert( std::pair(k,def) ); reserved_keys.push_back(k); @@ -175,12 +116,16 @@ void Keywords::reset_style( const std::string & k, const std::string & style ) { plumed_massert( exists(k) || reserved(k), "no " + k + " keyword" ); if( style=="numbered" ) { allowmultiple[k]=true; return; } (types.find(k)->second).setStyle(style); - if( (types.find(k)->second).isVessel() ) allowmultiple[k]=true; if( (types.find(k)->second).isAtomList() ) atomtags.insert( std::pair(k,style) ); } +void Keywords::linkActionInDocs( const std::string& k, const std::string& action ) { + plumed_massert( exists(k), "no " + k + " keyword" ); + (linkaction.find(k)->second)=action; +} + void Keywords::add( const std::string & t, const std::string & k, const std::string & d ) { - plumed_massert( !exists(k) && t!="flag" && !reserved(k) && t!="vessel", "keyword " + k + " has already been registered"); + plumed_massert( !exists(k) && t!="flag" && !reserved(k), "keyword " + k + " has already been registered"); std::string fd; if( t=="numbered" ) { fd=d + ". You can use multiple instances of this keyword i.e. " + k +"1, " + k + "2, " + k + "3..."; @@ -194,6 +139,7 @@ void Keywords::add( const std::string & t, const std::string & k, const std::str } if( t=="atoms" && isaction ) fd = d + ". For more information on how to specify lists of atoms see \\ref Group"; documentation.insert( std::pair(k,fd) ); + linkaction.insert( std::pair(k,"none") ); keys.push_back(k); } @@ -208,9 +154,10 @@ void Keywords::addInputKeyword( const std::string & t, const std::string & k, co } void Keywords::add( const std::string & t, const std::string & k, const std::string & def, const std::string & d ) { - plumed_massert( !exists(k) && !reserved(k) && (t=="compulsory" || t=="hidden" ), "failing on keyword " + k ); // An optional keyword can't have a default + plumed_massert( !exists(k) && !reserved(k) && (t=="compulsory" || t=="hidden" ), "failing in action " + thisactname + " on keyword " + k ); // An optional keyword can't have a default types.insert( std::pair(k, KeyType(t)) ); documentation.insert( std::pair(k,"( default=" + def + " ) " + d) ); + linkaction.insert( std::pair(k,"none") ); allowmultiple.insert( std::pair(k,false) ); numdefs.insert( std::pair(k,def) ); keys.push_back(k); @@ -222,6 +169,7 @@ void Keywords::addFlag( const std::string & k, const bool def, const std::string defstr="( default=off ) "; types.insert( std::pair(k,KeyType("flag")) ); documentation.insert( std::pair(k,defstr + d) ); + linkaction.insert( std::pair(k,"none") ); allowmultiple.insert( std::pair(k,false) ); booldefs.insert( std::pair(k,def) ); keys.push_back(k); @@ -242,7 +190,7 @@ void Keywords::remove( const std::string & k ) { } else break; } // Delete documentation, type and so on from the description - types.erase(k); documentation.erase(k); allowmultiple.erase(k); booldefs.erase(k); numdefs.erase(k); + types.erase(k); documentation.erase(k); linkaction.erase(k); allowmultiple.erase(k); booldefs.erase(k); numdefs.erase(k); // Remove any output comonents that this keyword creates for(const auto& dkey : ckey ) { if( dkey.second==k ) removeOutputComponent( dkey.first ); @@ -443,7 +391,7 @@ void Keywords::print_html() const { } nkeys=0; for(unsigned i=0; isecond).isFlag() || (types.find(keys[i])->second).isOptional() || (types.find(keys[i])->second).isVessel() ) nkeys++; + if ( (types.find(keys[i])->second).isFlag() || (types.find(keys[i])->second).isOptional() ) nkeys++; } if( nkeys>0 ) { if(isaction) std::cout<<"\\par Options\n\n"; @@ -456,11 +404,11 @@ void Keywords::print_html() const { } nkeys=0; for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) nkeys++; + if ( (types.find(keys[i])->second).isOptional() ) nkeys++; } if( nkeys>0 ) { for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) print_html_item( keys[i] ); + if ( (types.find(keys[i])->second).isOptional() ) print_html_item( keys[i] ); } } std::cout<<"\n\n"; @@ -486,6 +434,10 @@ std::string Keywords::getKeywordDocs( const std::string& key ) const { sstr<<"\n"; return sstr.str(); } +std::string Keywords::getLinkedActions( const std::string& key ) const { + plumed_assert( exists( key ) ); return linkaction.find(key)->second; +} + std::string Keywords::getHelpString() const { std::string helpstr; unsigned nkeys=0; for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) nkeys++; + if ( (types.find(keys[i])->second).isOptional() ) nkeys++; } if( nkeys>0 ) { for(unsigned i=0; isecond).isOptional() || (types.find(keys[i])->second).isVessel() ) helpstr += getKeywordDocs( keys[i] ); + if ( (types.find(keys[i])->second).isOptional() ) helpstr += getKeywordDocs( keys[i] ); } helpstr += "\n"; } @@ -595,7 +547,7 @@ bool Keywords::getDefaultValue(const std::string & key, std::string& def ) const void Keywords::destroyData() { keys.clear(); reserved_keys.clear(); types.clear(); - allowmultiple.clear(); documentation.clear(); + allowmultiple.clear(); documentation.clear(); linkaction.clear(); booldefs.clear(); numdefs.clear(); atomtags.clear(); ckey.clear(); cdocs.clear(); ckey.clear(); } diff --git a/src/tools/Keywords.h b/src/tools/Keywords.h index efce8b38a0..09bc79d6b1 100644 --- a/src/tools/Keywords.h +++ b/src/tools/Keywords.h @@ -37,21 +37,19 @@ class Keywords { /// This class lets me pass keyword types easily class KeyType { public: - enum {hidden,compulsory,flag,optional,atoms,vessel} style; + enum {hidden,compulsory,flag,optional,atoms} style; explicit KeyType( const std::string& type ); void setStyle( const std::string& type ); bool isCompulsory() const { return (style==compulsory); } bool isFlag() const { return (style==flag); } bool isOptional() const { return (style==optional); } bool isAtomList() const { return (style==atoms); } - bool isVessel() const { return (style==vessel); } std::string toString() const { if(style==compulsory) return "compulsory"; else if(style==optional) return "optional"; else if(style==atoms) return "atoms"; else if(style==flag) return "flag"; else if(style==hidden) return "hidden"; - else if(style==vessel) return "vessel"; else plumed_assert(0); return ""; } @@ -76,6 +74,8 @@ class Keywords { std::map allowmultiple; /// The documentation for the keywords std::map documentation; +/// This stores any action documentation that we should link to + std::map linkaction; /// The type for the arguments in this action std::map argument_types; /// The default values for the flags (are they on or of) @@ -119,6 +119,8 @@ class Keywords { std::string getKeyword( const unsigned i ) const ; /// Get the documentation for a particular keyword std::string getKeywordDocs( const std::string& key ) const ; +/// Get any actions that are linked to this keyword + std::string getLinkedActions( const std::string& key ) const ; /// Print the documentation to the log file (used by PLMD::Action::error) void print( Log& log ) const ; /// Print the documentation to a file (use by PLUMED::CLTool::readCommandLineArgs) @@ -141,6 +143,8 @@ class Keywords { void add( const std::string & t, const std::string & k, const std::string & def, const std::string & d ); /// Add a falg with name k that is by default on if def is true and off if def is false. d should provide a description of the flag void addFlag( const std::string & k, const bool def, const std::string & d ); +/// Create a link to this action in the documentation for it + void linkActionInDocs( const std::string& k, const std::string& action ); /// Remove the keyword with name k void remove( const std::string & k ); /// Check if there is a keyword with name k @@ -159,13 +163,6 @@ class Keywords { void print_template( const std::string& actionname, bool include_optional) const ; /// Change the style of a keyword void reset_style( const std::string & k, const std::string & style ); -/// Add keywords from one keyword object to another - void add( const Keywords& keys ); -/// Copy the keywords data - void copyData( std::vector& kk, std::vector& rk, std::map& tt, std::map& am, - std::map& docs, std::map& bools, std::map& nums, - std::map& atags, std::vector& cnam, std::map& ck, - std::map& cd ) const ; /// Clear everything from the keywords object. /// Not actually needed if your Keywords object is going out of scope. void destroyData(); diff --git a/src/tools/SwitchingFunction.cpp b/src/tools/SwitchingFunction.cpp index 3339c2ec3b..a20e29d57e 100644 --- a/src/tools/SwitchingFunction.cpp +++ b/src/tools/SwitchingFunction.cpp @@ -30,150 +30,6 @@ namespace PLMD { -//+PLUMEDOC INTERNAL switchingfunction -/* -Functions that measure whether values are less than a certain quantity. - -Switching functions \f$s(r)\f$ take a minimum of one input parameter \f$r_0\f$. -For \f$r \le d_0 \quad s(r)=1.0\f$ while for \f$r > d_0\f$ the function decays smoothly to 0. -The various switching functions available in PLUMED differ in terms of how this decay is performed. - -Where there is an accepted convention in the literature (e.g. \ref COORDINATION) on the form of the -switching function we use the convention as the default. However, the flexibility to use different -switching functions is always present generally through a single keyword. This keyword generally -takes an input with the following form: - -\verbatim -KEYWORD={TYPE } -\endverbatim - -The following table contains a list of the various switching functions that are available in PLUMED 2 -together with an example input. - - - - - - - - - - - - - - - - - - - - - - -
TYPE FUNCTION EXAMPLE INPUT DEFAULT PARAMETERS
RATIONAL -\f$ -s(r)=\frac{ 1 - \left(\frac{ r - d_0 }{ r_0 }\right)^{n} }{ 1 - \left(\frac{ r - d_0 }{ r_0 }\right)^{m} } -\f$ - -{RATIONAL R_0=\f$r_0\f$ D_0=\f$d_0\f$ NN=\f$n\f$ MM=\f$m\f$} - \f$d_0=0.0\f$, \f$n=6\f$, \f$m=2n\f$
EXP -\f$ -s(r)=\exp\left(-\frac{ r - d_0 }{ r_0 }\right) -\f$ - -{EXP R_0=\f$r_0\f$ D_0=\f$d_0\f$} - \f$d_0=0.0\f$
GAUSSIAN -\f$ -s(r)=\exp\left(-\frac{ (r - d_0)^2 }{ 2r_0^2 }\right) -\f$ - -{GAUSSIAN R_0=\f$r_0\f$ D_0=\f$d_0\f$} - \f$d_0=0.0\f$
SMAP -\f$ -s(r) = \left[ 1 + ( 2^{a/b} -1 )\left( \frac{r-d_0}{r_0} \right)^a \right]^{-b/a} -\f$ - -{SMAP R_0=\f$r_0\f$ D_0=\f$d_0\f$ A=\f$a\f$ B=\f$b\f$} - \f$d_0=0.0\f$
Q -\f$ -s(r) = \frac{1}{1 + \exp(\beta(r_{ij} - \lambda r_{ij}^0))} -\f$ - -{Q REF=\f$r_{ij}^0\f$ BETA=\f$\beta\f$ LAMBDA=\f$\lambda\f$ } - \f$\lambda=1.8\f$, \f$\beta=50 nm^-1\f$ (all-atom)
\f$\lambda=1.5\f$, \f$\beta=50 nm^-1\f$ (coarse-grained)
CUBIC -\f$ -s(r) = (y-1)^2(1+2y) \qquad \textrm{where} \quad y = \frac{r - r_1}{r_0-r_1} -\f$ - -{CUBIC D_0=\f$r_1\f$ D_MAX=\f$r_0\f$} -
TANH -\f$ -s(r) = 1 - \tanh\left( \frac{ r - d_0 }{ r_0 } \right) -\f$ - -{TANH R_0=\f$r_0\f$ D_0=\f$d_0\f$} -
COSINUS -\f$s(r) =\left\{\begin{array}{ll} - 1 & \mathrm{if } r \leq d_0 \\ - 0.5 \left( \cos ( \frac{ r - d_0 }{ r_0 } \pi ) + 1 \right) & \mathrm{if } d_0 < r\leq d_0 + r_0 \\ - 0 & \mathrm{if } r > d_0 + r_0 - \end{array}\right. -\f$ - -{COSINUS R_0=\f$r_0\f$ D_0=\f$d_0\f$} -
CUSTOM -\f$ -s(r) = FUNC -\f$ - -{CUSTOM FUNC=1/(1+x^6) R_0=\f$r_0\f$ D_0=\f$d_0\f$} -
- -Notice that most commonly used rational functions are better optimized and might run faster. - -Notice that for backward compatibility we allow using `MATHEVAL` instead of `CUSTOM`. -Also notice that if the a `CUSTOM` switching function only depends on even powers of `x` it can be -made faster by using `x2` as a variable. For instance -\verbatim -{CUSTOM FUNC=1/(1+x2^3) R_0=0.3} -\endverbatim -is equivalent to -\verbatim -{CUSTOM FUNC=1/(1+x^6) R_0=0.3} -\endverbatim -but runs faster. The reason is that there is an expensive square root calculation that can be optimized out. - - -\attention -With the default implementation CUSTOM is slower than other functions -(e.g., it is slower than an equivalent RATIONAL function by approximately a factor 2). -Checkout page \ref Lepton to see how to improve its performance. - -For all the switching functions in the above table one can also specify a further (optional) parameter using the parameter -keyword D_MAX to assert that for \f$r>d_{\textrm{max}}\f$ the switching function can be assumed equal to zero. -In this case the function is brought smoothly to zero by stretching and shifting it. -\verbatim -KEYWORD={RATIONAL R_0=1 D_MAX=3} -\endverbatim -the resulting switching function will be -\f$ -s(r) = \frac{s'(r)-s'(d_{max})}{s'(0)-s'(d_{max})} -\f$ -where -\f$ -s'(r)=\frac{1-r^6}{1-r^{12}} -\f$ -Since PLUMED 2.2 this is the default. The old behavior (no stretching) can be obtained with the -NOSTRETCH flag. The NOSTRETCH keyword is only provided for backward compatibility and might be -removed in the future. Similarly, the STRETCH keyword is still allowed but has no effect. - -Notice that switching functions defined with the simplified syntax are never stretched -for backward compatibility. This might change in the future. - -*/ -//+ENDPLUMEDOC - namespace switchContainers { baseSwitch::baseSwitch(double D0,double DMAX, double R0, std::string_view name) diff --git a/src/volumes/VolumeInCylinder.cpp b/src/volumes/VolumeInCylinder.cpp index 1253007546..aeb854031e 100644 --- a/src/volumes/VolumeInCylinder.cpp +++ b/src/volumes/VolumeInCylinder.cpp @@ -106,7 +106,7 @@ void VolumeInCylinder::registerKeywords( Keywords& keys ) { keys.add("compulsory","RADIUS","a switching function that gives the extent of the cylinder in the plane perpendicular to the direction"); keys.add("compulsory","LOWER","0.0","the lower boundary on the direction parallel to the long axis of the cylinder"); keys.add("compulsory","UPPER","0.0","the upper boundary on the direction parallel to the long axis of the cylinder"); - keys.reset_style("SIGMA","optional"); + keys.reset_style("SIGMA","optional"); keys.linkActionInDocs("RADIUS","LESS_THAN"); } VolumeInCylinder::VolumeInCylinder(const ActionOptions& ao): diff --git a/src/volumes/VolumeInSphere.cpp b/src/volumes/VolumeInSphere.cpp index 141ad4fbe7..3709bf09f8 100644 --- a/src/volumes/VolumeInSphere.cpp +++ b/src/volumes/VolumeInSphere.cpp @@ -100,7 +100,7 @@ void VolumeInSphere::registerKeywords( Keywords& keys ) { keys.add("atoms","CENTER","the atom whose vicinity we are interested in examining"); keys.add("atoms-2","ATOM","the atom whose vicinity we are interested in examining"); keys.add("compulsory","RADIUS","the switching function that tells us the extent of the sphereical region of interest"); - keys.remove("SIGMA"); + keys.remove("SIGMA"); keys.linkActionInDocs("RADIUS","LESS_THAN"); } VolumeInSphere::VolumeInSphere(const ActionOptions& ao):