Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2015-2020 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/ActionWithValue.h" 23 : #include "core/ActionWithArguments.h" 24 : #include "core/ActionRegister.h" 25 : #include "core/ActionShortcut.h" 26 : #include "core/PlumedMain.h" 27 : #include "multicolvar/MultiColvarShortcuts.h" 28 : #include "ClusteringBase.h" 29 : 30 : //+PLUMEDOC CONCOMP CLUSTER_DISTRIBUTION 31 : /* 32 : Calculate functions of the distribution of properties in your connected components. 33 : 34 : __This shortcut action is present to ensure that inputs for older PLUMED versions remain compatible. We STRONGLY 35 : encourage you to use the newer (and simpler) syntax for clustering calculations__ Look at the documentation for 36 : [CLUSTER_WEIGHTS](CLUSTER_WEIGHTS.md) and the expanded version of the input below to see how this new input syntax operates. 37 : 38 : The input provided below calculates the local q6 Steinhardt parameter on each atom. The coordination number 39 : that atoms with a high value for the local q6 Steinhardt parameter have with other atoms that have a high 40 : value for the local q6 Steinhardt parameter is then computed. A contact matrix is then computed that measures 41 : whether atoms atoms $i$ and $j$ have a high value for this coordination number and if they are within 42 : 3.6 nm of each other. The connected components of this matrix are then found using a depth first clustering 43 : algorithm on the corresponding graph. The number of components in this graph that contain more than 27 atoms is then computed. 44 : An input similar to this one was used to analyze the formation of a polycrystal of GeTe from amorphous GeTe in the paper cited below 45 : 46 : ```plumed 47 : q6: Q6 SPECIES=1-300 SWITCH={GAUSSIAN D_0=5.29 R_0=0.01 D_MAX=5.3} 48 : lq6: LOCAL_Q6 SPECIES=q6 SWITCH={GAUSSIAN D_0=5.29 R_0=0.01 D_MAX=5.3} 49 : flq6: MORE_THAN ARG=lq6 SWITCH={GAUSSIAN D_0=0.19 R_0=0.01 D_MAX=0.2} 50 : cc: COORDINATIONNUMBER SPECIES=1-300 SWITCH={GAUSSIAN D_0=3.59 R_0=0.01 D_MAX=3.6} 51 : fcc: MORE_THAN ARG=cc SWITCH={GAUSSIAN D_0=5.99 R_0=0.01 D_MAX=6.0} 52 : mat: CONTACT_MATRIX GROUP=1-300 SWITCH={GAUSSIAN D_0=3.59 R_0=0.01 D_MAX=3.6} 53 : dfs: DFSCLUSTERING ARG=mat 54 : nclust: CLUSTER_DISTRIBUTION ... 55 : CLUSTERS=dfs WEIGHTS=fcc 56 : MORE_THAN={GAUSSIAN D_0=26.99 R_0=0.01 D_MAX=27} 57 : ... 58 : PRINT ARG=nclust.* FILE=colvar 59 : ``` 60 : 61 : */ 62 : //+ENDPLUMEDOC 63 : 64 : //+PLUMEDOC CONCOMP CLUSTER_DISTRIBUTION_CALC 65 : /* 66 : Calculate functions of the distribution of properties in your connected components. 67 : 68 : See \ref CLUSTER_DISTRIBUTION 69 : 70 : \par Examples 71 : 72 : */ 73 : //+ENDPLUMEDOC 74 : 75 : 76 : namespace PLMD { 77 : namespace clusters { 78 : 79 : class ClusterDistribution : 80 : public ActionWithArguments, 81 : public ActionWithValue { 82 : public: 83 : /// Create manual 84 : static void registerKeywords( Keywords& keys ); 85 : /// Constructor 86 : explicit ClusterDistribution(const ActionOptions&); 87 : /// The number of derivatives 88 : unsigned getNumberOfDerivatives() override ; 89 : /// Do the calculation 90 : void calculate() override; 91 : /// We can use ActionWithVessel to run all the calculation 92 2 : void apply() override {} 93 : }; 94 : 95 : PLUMED_REGISTER_ACTION(ClusterDistribution,"CLUSTER_DISTRIBUTION_CALC") 96 : 97 6 : void ClusterDistribution::registerKeywords( Keywords& keys ) { 98 6 : Action::registerKeywords( keys ); 99 6 : ActionWithArguments::registerKeywords( keys ); 100 6 : ActionWithValue::registerKeywords( keys ); 101 6 : keys.remove("NUMERICAL_DERIVATIVES"); 102 12 : keys.addInputKeyword("compulsory","CLUSTERS","vector","the label of the action that does the clustering"); 103 6 : keys.setDisplayName("CLUSTER_DISTRIBUTION"); 104 12 : keys.addInputKeyword("optional","WEIGHTS","vector","use the vector of values calculated by this action as weights rather than giving each atom a unit weight"); 105 12 : keys.setValueDescription("vector","a vector containing the sum of a atomic-cv that is calculated for each of the identified clusters"); 106 6 : keys.addDOI("https://doi.org/10.1021/acs.jctc.6b01073"); 107 6 : } 108 : 109 2 : ClusterDistribution::ClusterDistribution(const ActionOptions&ao): 110 : Action(ao), 111 : ActionWithArguments(ao), 112 2 : ActionWithValue(ao) { 113 : // Read in the clustering object 114 : std::vector<Value*> clusters; 115 4 : parseArgumentList("CLUSTERS",clusters); 116 2 : if( clusters.size()!=1 ) { 117 0 : error("should pass only one matrix to clustering base"); 118 : } 119 2 : ClusteringBase* cc = dynamic_cast<ClusteringBase*>( clusters[0]->getPntrToAction() ); 120 2 : if( !cc ) { 121 0 : error("input to CLUSTERS keyword should be a clustering action"); 122 : } 123 : std::vector<Value*> weights; 124 4 : parseArgumentList("WEIGHTS",weights); 125 2 : if( weights.size()==0 ) { 126 1 : log.printf(" using unit weights in cluster distribution \n"); 127 1 : } else if( weights.size()==1 ) { 128 1 : if( weights[0]->getRank()!=1 ) { 129 0 : error("input weights has wrong shape"); 130 : } 131 1 : if( weights[0]->getShape()[0]!=clusters[0]->getShape()[0] ) { 132 0 : error("mismatch between number of weights and number of atoms"); 133 : } 134 1 : log.printf(" using weights from action with label %s in cluster distribution \n", weights[0]->getName().c_str() ); 135 1 : clusters.push_back( weights[0] ); 136 : } else { 137 0 : error("should have only one argument for weights \n"); 138 : } 139 : // Request the arguments 140 2 : requestArguments( clusters ); 141 2 : getPntrToArgument(0)->buildDataStore(); 142 2 : if( getNumberOfArguments()>1 ) { 143 1 : getPntrToArgument(1)->buildDataStore(); 144 : } 145 : // Now create the value 146 2 : std::vector<unsigned> shape(1); 147 2 : shape[0]=clusters[0]->getShape()[0]; 148 2 : addValue( shape ); 149 2 : setNotPeriodic(); 150 2 : getPntrToValue()->buildDataStore(); 151 2 : } 152 : 153 0 : unsigned ClusterDistribution::getNumberOfDerivatives() { 154 0 : return 0; 155 : } 156 : 157 2 : void ClusterDistribution::calculate() { 158 2 : plumed_assert( getPntrToArgument(0)->valueHasBeenSet() ); 159 2 : if( getNumberOfArguments()>1 ) { 160 1 : plumed_assert( getPntrToArgument(1)->valueHasBeenSet() ); 161 : } 162 2 : double csize = getPntrToArgument(0)->get(0); 163 12 : for(unsigned i=1; i<getPntrToArgument(0)->getShape()[0]; ++i) { 164 10 : if( getPntrToArgument(0)->get(i)>csize ) { 165 4 : csize = getPntrToArgument(0)->get(i); 166 : } 167 : } 168 2 : unsigned ntasks = static_cast<unsigned>( csize ); 169 8 : for(unsigned i=0; i<ntasks; ++i) { 170 42 : for(unsigned j=0; j<getPntrToArgument(0)->getShape()[0]; ++j) { 171 36 : if( fabs(getPntrToArgument(0)->get(j)-i)<epsilon ) { 172 10 : if( getNumberOfArguments()==2 ) { 173 5 : getPntrToValue()->add( i, getPntrToArgument(1)->get(j) ); 174 : } else { 175 5 : getPntrToValue()->add( i, 1.0 ); 176 : } 177 : } 178 : } 179 : } 180 2 : } 181 : 182 : class ClusterDistributionShortcut : public ActionShortcut { 183 : public: 184 : static void registerKeywords( Keywords& keys ); 185 : ClusterDistributionShortcut(const ActionOptions&); 186 : }; 187 : 188 : PLUMED_REGISTER_ACTION(ClusterDistributionShortcut,"CLUSTER_DISTRIBUTION") 189 : 190 6 : void ClusterDistributionShortcut::registerKeywords( Keywords& keys ) { 191 6 : ActionShortcut::registerKeywords( keys ); 192 6 : keys.add("compulsory","CLUSTERS","the label of the action that does the clustering"); 193 6 : keys.add("optional","WEIGHTS","use the vector of values calculated by this action as weights rather than giving each atom a unit weight"); 194 6 : multicolvar::MultiColvarShortcuts::shortcutKeywords( keys ); 195 6 : keys.addActionNameSuffix("_CALC"); 196 6 : } 197 : 198 2 : ClusterDistributionShortcut::ClusterDistributionShortcut(const ActionOptions&ao): 199 : Action(ao), 200 2 : ActionShortcut(ao) { 201 : std::map<std::string,std::string> keymap; 202 2 : multicolvar::MultiColvarShortcuts::readShortcutKeywords( keymap, this ); 203 4 : readInputLine( getShortcutLabel() + ": CLUSTER_DISTRIBUTION_CALC " + convertInputLineToString() ); 204 4 : multicolvar::MultiColvarShortcuts::expandFunctions( getShortcutLabel(), getShortcutLabel(), "", keymap, this ); 205 2 : } 206 : 207 : } 208 : }