LCOV - code coverage report
Current view: top level - dimred - ProjectPoints.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 133 149 89.3 %
Date: 2026-11-05 10:19:04 Functions: 7 9 77.8 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-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/ActionWithVector.h"
      23             : #include "core/ParallelTaskManager.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "tools/ConjugateGradient.h"
      26             : #include "tools/SwitchingFunction.h"
      27             : #include "tools/OpenMP.h"
      28             : #include "tools/Random.h"
      29             : 
      30             : namespace PLMD {
      31             : namespace dimred {
      32             : 
      33             : //+PLUMEDOC DIMRED PROJECT_POINTS
      34             : /*
      35             : Find the projection of a point in a low dimensional space by matching the (transformed) distance between it and a series of reference configurations that were input
      36             : 
      37             : This action and [ARRANGE_POINTS](ARRANGE_POINTS.md) are the workhorses for the implementation of [SKETCHMAP](SKETCHMAP.md) that is provided within PLUMED.
      38             : PROJECT_POINTS allows you to provide the low dimensional coordinate $y_\textrm{min}$ at which the following stress function is minimised:
      39             : 
      40             : $$
      41             : \chi(y) = \sum_{i=1}^N w_i [ D(X_i, Y) - d(x_i,y) ]^2
      42             : $$
      43             : 
      44             : where $Y$ is a set of coordinates in some high dimensional space, $X_i$ is a set of coordinates for one of $N$ landmark points in this high-dimensional space,
      45             : $x_i$ is a projection for $X_i$ that we have found in some lower dimensional space and $w_i$ is a weight.  The $D$ indicates that we are calculating the dissimilarity between the point $X_i$ and
      46             : $Y$, while $d$ represents the distance between the point $x_i$ and $y$.  In minimising the expression above we are thus finding the point $y$ at which the distances between $y$ and
      47             : each of the projections, $x_i$, of the $N$ landmark points most closely resembles the dissimiarities between $Y$ and the $N$ landmark points in the high-dimensional points.
      48             : 
      49             : The example input below illustrates how you can use PROJECT_POINTS to find the projection of a high-dimensional point in practice.
      50             : 
      51             : ```plumed
      52             : # The coordinates of the landmarks in the high dimensional space
      53             : d1_ref: CONSTANT VALUES=1.0,2.0,1.5,2.1
      54             : d2_ref: CONSTANT VALUES=0.5,0.7,0.2,1.3
      55             : d3_ref: CONSTANT VALUES=3.1,2.0,1.5,0.5
      56             : 
      57             : # The weights of the landmark
      58             : weights: CONSTANT VALUES=1,1,1,1
      59             : 
      60             : # The projections of the landmarks in the low dimensional space
      61             : proj1_ref: CONSTANT VALUES=0.5,0.8,0.2,0.4
      62             : proj2_ref: CONSTANT VALUES=0.2,0.9,0.3,0.7
      63             : 
      64             : # Calculate the instantaneous values of the three distances
      65             : d1: DISTANCE ATOMS=1,2
      66             : d2: DISTANCE ATOMS=3,4
      67             : d3: DISTANCE ATOMS=5,6
      68             : 
      69             : # Calculate the distances between the instananeous points and the current positions
      70             : ed: EUCLIDEAN_DISTANCE SQUARED ARG2=d1,d2,d3 ARG1=d1_ref,d2_ref,d3_ref
      71             : 
      72             : # And generate the projection
      73             : proj: PROJECT_POINTS ARG=proj1_ref,proj2_ref TARGET1=ed WEIGHTS1=weights
      74             : 
      75             : # And output the projection to a file
      76             : PRINT ARG=proj.* FILE=colvar
      77             : ```
      78             : 
      79             : In this example, we use three distances to define the high dimensional coordinates and have four landmarks points.
      80             : 
      81             : ## Projecting multiple coordinates at once
      82             : 
      83             : The input to the [EUCLIDEAN_DISTANCE](EUCLIDEAN_DISTANCE.md) shortcut in the example in the previous section consisted of three
      84             : scalar-valued quantities.  The two components output by project points are thus also scalars. By contrast, in the input below four
      85             : three-dimenional vectors are input to the [EUCLIDEAN_DISTANCE](EUCLIDEAN_DISTANCE.md) shortcut. The components output by proj and thus
      86             : four-dimensional vectors.
      87             : 
      88             : ```plumed
      89             : # The coordinates of the landmarks in the high dimensional space
      90             : d1_ref: CONSTANT VALUES=1.0,2.0,1.5,2.1
      91             : d2_ref: CONSTANT VALUES=0.5,0.7,0.2,1.3
      92             : d3_ref: CONSTANT VALUES=3.1,2.0,1.5,0.5
      93             : 
      94             : # The weights of the landmark
      95             : weights: CONSTANT VALUES=1,1,1,1
      96             : 
      97             : # The projections of the landmarks in the low dimensional space
      98             : proj1_ref: CONSTANT VALUES=0.5,0.8,0.2,0.4
      99             : proj2_ref: CONSTANT VALUES=0.2,0.9,0.3,0.7
     100             : 
     101             : # Calculate the instantaneous values of the distances
     102             : d1: DISTANCE ATOMS1=1,2 ATOMS2=7,8   ATOMS3=13,14 ATOMS4=19,20
     103             : d2: DISTANCE ATOMS1=3,4 ATOMS2=9,10  ATOMS3=15,16 ATOMS4=21,22
     104             : d3: DISTANCE ATOMS1=5,6 ATOMS2=11,12 ATOMS3=17,18 ATOMS4=23,24
     105             : 
     106             : # Calculate the distances between the instananeous points and the current positions
     107             : ed: EUCLIDEAN_DISTANCE SQUARED ARG1=d1,d2,d3 ARG2=d1_ref,d2_ref,d3_ref
     108             : 
     109             : # And generate the projection
     110             : proj: PROJECT_POINTS ARG=proj1_ref,proj2_ref TARGET1=ed WEIGHTS1=weights
     111             : 
     112             : # And output the projection to a file
     113             : PRINT ARG=proj.* FILE=colvar
     114             : ```
     115             : 
     116             : ## Using RMSD distances
     117             : 
     118             : One can use [RMSD](RMSD.md) distances as the dissimilarities rather than distances in some space of arguments as is illustrated below:
     119             : 
     120             : ```plumed
     121             : #SETTINGS INPUTFILES=regtest/trajectories/path_msd/allv.pdb
     122             : 
     123             : # This action reads in the landmarks in the high dimensional space and calculates the
     124             : # distances from the instantaneous configuration
     125             : rmsd: RMSD SQUARED TYPE=OPTIMAL REFERENCE=regtest/trajectories/path_msd/allv.pdb
     126             : 
     127             : # The weights of the landmarks
     128             : weights: ONES SIZE=42
     129             : 
     130             : # The projections of the landmarks in the low dimensional space
     131             : X: PDB2CONSTANT ARG=X NOARGS REFERENCE=regtest/trajectories/path_msd/allv.pdb
     132             : Y: PDB2CONSTANT ARG=Y NOARGS REFERENCE=regtest/trajectories/path_msd/allv.pdb
     133             : 
     134             : # Generate the projection of the instantaneous coordinates
     135             : proj: PROJECT_POINTS ARG=X,Y TARGET1=rmsd WEIGHTS1=weights
     136             : 
     137             : # And output the projection to a file
     138             : PRINT ARG=proj.* FILE=colvar
     139             : ```
     140             : 
     141             : For ths input there are 42 landmark points and dissimilarities are computed by computing the RMSD distance between the 13 atoms in
     142             : each of landmark coordinates and the instantaneous positions of those 13 atoms.
     143             : 
     144             : ## Using transformed distances
     145             : 
     146             : In [SKETCHMAP](SKETCHMAP.md) the stress function that is minimised is not the one given above.  Instead of seeking to generate a projection,
     147             : $y_\textrm{min}$, which is at a point where the distances between it and each projection the landmarks is the same as the dissimilarities between
     148             : the high-dimensional coordinate of the point and the high-dimensional landmarks, the dissimilarities and distances are transformed by functions as illustrated below:
     149             : 
     150             : $$
     151             : \chi(y) = \sum_{i=1}^N w_i [ F[D(X_i, Y)] - f[d(x_i,y)] ]^2
     152             : $$
     153             : 
     154             : The two functions $F$ and $f$ in this expression are usually different as you can see in the input below:
     155             : 
     156             : ```plumed
     157             : # The coordinates of the landmarks in the high dimensional space
     158             : d1_ref: CONSTANT VALUES=1.0,2.0,1.5,2.1
     159             : d2_ref: CONSTANT VALUES=0.5,0.7,0.2,1.3
     160             : d3_ref: CONSTANT VALUES=3.1,2.0,1.5,0.5
     161             : 
     162             : # The weights of the landmark
     163             : weights: CONSTANT VALUES=1,1,1,1
     164             : 
     165             : # The projections of the landmarks in the low dimensional space
     166             : proj1_ref: CONSTANT VALUES=0.5,0.8,0.2,0.4
     167             : proj2_ref: CONSTANT VALUES=0.2,0.9,0.3,0.7
     168             : 
     169             : # Calculate the instantaneous values of the distances
     170             : d1: DISTANCE ATOMS1=1,2 ATOMS2=7,8   ATOMS3=13,14 ATOMS4=19,20
     171             : d2: DISTANCE ATOMS1=3,4 ATOMS2=9,10  ATOMS3=15,16 ATOMS4=21,22
     172             : d3: DISTANCE ATOMS1=5,6 ATOMS2=11,12 ATOMS3=17,18 ATOMS4=23,24
     173             : 
     174             : # Calculate the distances between the instananeous points and the current positions
     175             : ed: EUCLIDEAN_DISTANCE SQUARED ARG1=d1,d2,d3 ARG2=d1_ref,d2_ref,d3_ref
     176             : 
     177             : # Transform the dissimilarities by applying the function F
     178             : fed: MORE_THAN ARG=ed SQUARED SWITCH={SMAP R_0=4 A=3 B=2}
     179             : 
     180             : # And generate the projection
     181             : proj: PROJECT_POINTS ARG=proj1_ref,proj2_ref TARGET1=fed FUNC1={SMAP R_0=4 A=1 B=2} WEIGHTS1=weights
     182             : 
     183             : # And output the projection to a file
     184             : PRINT ARG=proj.* FILE=colvar
     185             : ```
     186             : 
     187             : In the input above the function, $F$, that is applied on the dissimilarities is implemented using a [MORE_THAN](MORE_THAN.md) action. The function, $f$,
     188             : that is applied on the distances in the low-dimensional space is specified using the `FUNC` keyword that is input to PROJECT_POINTS.
     189             : 
     190             : ## Using multiple targets
     191             : 
     192             : At its most complex this action allows you to minimise a stress function such as the one below:
     193             : 
     194             : $$
     195             : \chi(y) = \sum_{i=1}^N \sum_{j=1}^M w_{ij} [ F_j[D(X_i, Y)] - f_j[d(x_i,y)] ]^2
     196             : $$
     197             : 
     198             : The input below shows how this can be implemented within PLUMED:
     199             : 
     200             : ```plumed
     201             : # The coordinates of the landmarks in the high dimensional space
     202             : d1_ref: CONSTANT VALUES=1.0,2.0,1.5,2.1
     203             : d2_ref: CONSTANT VALUES=0.5,0.7,0.2,1.3
     204             : d3_ref: CONSTANT VALUES=3.1,2.0,1.5,0.5
     205             : 
     206             : # The weights of the landmark
     207             : weights: CONSTANT VALUES=1,1,1,1
     208             : w1: CUSTOM ARG=weights FUNC=0.3*x PERIODIC=NO
     209             : w2: CUSTOM ARG=weights FUNC=(1-0.3)*x PERIODIC=NO
     210             : 
     211             : # The projections of the landmarks in the low dimensional space
     212             : proj1_ref: CONSTANT VALUES=0.5,0.8,0.2,0.4
     213             : proj2_ref: CONSTANT VALUES=0.2,0.9,0.3,0.7
     214             : 
     215             : # Calculate the instantaneous values of the distances
     216             : d1: DISTANCE ATOMS1=1,2 ATOMS2=7,8   ATOMS3=13,14 ATOMS4=19,20
     217             : d2: DISTANCE ATOMS1=3,4 ATOMS2=9,10  ATOMS3=15,16 ATOMS4=21,22
     218             : d3: DISTANCE ATOMS1=5,6 ATOMS2=11,12 ATOMS3=17,18 ATOMS4=23,24
     219             : 
     220             : # Calculate the distances between the instananeous points and the current positions
     221             : ed: EUCLIDEAN_DISTANCE SQUARED ARG1=d1,d2,d3 ARG2=d1_ref,d2_ref,d3_ref
     222             : # Transform the dissimilarities by applying the function F
     223             : fed: MORE_THAN ARG=ed SQUARED SWITCH={SMAP R_0=4 A=3 B=2}
     224             : 
     225             : # And generate the projection
     226             : proj: PROJECT_POINTS ...
     227             :   ARG=proj1_ref,proj2_ref
     228             :   TARGET1=ed WEIGHTS1=w1 FUNC1={CUSTOM FUNC=1-sqrt(x2) R_0=1.0}
     229             :   TARGET2=fed WEIGHTS2=w2 FUNC2={SMAP R_0=4 A=1 B=2}
     230             : ...
     231             : 
     232             : # And output the projection to a file
     233             : PRINT ARG=proj.* FILE=colvar
     234             : ```
     235             : 
     236             : Here the sum over $M$ in the expression above has two terms. In the first of these terms $F_1$ is the identity so the
     237             : input for `TARGET1` is the output from [EUCLIDEAN_DISTANCE](EUCLIDEAN_DISTANCE.md). $f_1$ is similarly the identity.  To
     238             : implement the identity here we use the input to `FUNC1` shown above.  The input to this function is the input for one of
     239             : the switching functions described in the documentation for [LESS_THAN](LESS_THAN.md). What we compute for the transformed
     240             : distance is $1-s(d)$ where $s(d)$ is the switching function that is specified in input.  Consequently, applying the
     241             : function `1-sqrt(x2)` returns the distance.
     242             : 
     243             : The second term in our sum over $M$ in the input above has the dissimilarities and distances transformed by the functions that
     244             : we introduced in the previous section.
     245             : 
     246             : */
     247             : //+ENDPLUMEDOC
     248             : 
     249             : class ProjectPoints;
     250             : 
     251             : class ProjectPointsInput {
     252             : public:
     253             :   double cgtol;
     254             :   ProjectPoints* action;
     255             : };
     256             : 
     257             : class ProjectPoints : public ActionWithVector {
     258             : public:
     259             :   using input_type = ProjectPointsInput;
     260             :   using PTM = ParallelTaskManager<ProjectPoints>;
     261             :   typedef typename PTM::ParallelActionsInput ParallelActionsInput;
     262             :   typedef typename PTM::ParallelActionsOutput ParallelActionsOutput;
     263             : private:
     264             :   unsigned dimout;
     265             :   mutable std::vector<unsigned> rowstart;
     266             :   std::vector<SwitchingFunction> switchingFunction;
     267             :   ConjugateGradient<ProjectPoints> myminimiser;
     268             :   PTM taskmanager;
     269             : public:
     270             :   static void registerKeywords( Keywords& keys );
     271             :   ProjectPoints( const ActionOptions& );
     272           0 :   unsigned getNumberOfDerivatives() override {
     273           0 :     return 0;
     274             :   }
     275             :   void prepare() override ;
     276             :   static void performTask( std::size_t task_index, const ProjectPointsInput& actiondata, ParallelActionsInput& input, ParallelActionsOutput& output );
     277             :   double calculateStress( const std::vector<double>& pp, std::vector<double>& der );
     278             :   void calculate() override ;
     279         168 :   void apply() override {}
     280             : };
     281             : 
     282             : PLUMED_REGISTER_ACTION(ProjectPoints,"PROJECT_POINTS")
     283             : 
     284           8 : void ProjectPoints::registerKeywords( Keywords& keys ) {
     285           8 :   ActionWithVector::registerKeywords( keys );
     286          16 :   keys.addInputKeyword("compulsory","ARG","vector","the projections of the landmark points");
     287          16 :   keys.addInputKeyword("numbered","TARGET","vector/matrix","the matrix of target quantities that you would like to match");
     288           8 :   keys.add("numbered","FUNC","a function that is applied on the distances between the points in the low dimensional space");
     289          16 :   keys.addInputKeyword("numbered","WEIGHTS","vector","the matrix with the weights of the target quantities");
     290           8 :   keys.add("compulsory","CGTOL","1E-6","the tolerance for the conjugate gradient minimization");
     291          16 :   keys.addOutputComponent("coord","default","scalar/vector","the coordinates of the points in the low dimensional space");
     292           8 :   PTM::registerKeywords( keys );
     293           8 : }
     294             : 
     295             : 
     296           2 : ProjectPoints::ProjectPoints( const ActionOptions& ao ) :
     297             :   Action(ao),
     298             :   ActionWithVector(ao),
     299           2 :   rowstart(OpenMP::getNumThreads()),
     300             :   myminimiser(this),
     301           4 :   taskmanager(this) {
     302           2 :   dimout = getNumberOfArguments();
     303           2 :   unsigned nvals=getPntrToArgument(0)->getNumberOfValues();
     304           6 :   for(unsigned i=0; i<getNumberOfArguments(); ++i) {
     305           4 :     if( nvals!=getPntrToArgument(i)->getNumberOfValues() ) {
     306           0 :       error("mismatch between numbers of projections");
     307             :     }
     308             :   }
     309           2 :   std::vector<Value*> args( getArguments() ), target, weights;
     310             :   std::string sfd, errors;
     311             :   unsigned ntoproj=0;
     312             :   // Read in target "distances" and target weights
     313           2 :   for(unsigned i=1;; ++i) {
     314           4 :     target.resize(0);
     315           8 :     if( !parseArgumentList("TARGET",i,target) ) {
     316             :       break;
     317             :     }
     318             :     std::string inum;
     319           2 :     Tools::convert( i, inum );
     320           2 :     if( target.size()!=1 ) {
     321           0 :       error("should only be one value in input to TARGET" + inum );
     322             :     }
     323           2 :     if( (target[0]->getRank()!=1 && target[0]->getRank()!=2) || target[0]->hasDerivatives() ) {
     324           0 :       error("input to TARGET" + inum + " keyword should be a vector/matrix");
     325             :     }
     326           2 :     if( target[0]->getShape()[0]!=nvals ) {
     327           0 :       error("number of rows in target matrix should match number of input coordinates");
     328             :     }
     329           2 :     if( i==1 && target[0]->getRank()==1 ) {
     330             :       ntoproj = 1;
     331           1 :     } else if( ntoproj==1 && target[0]->getRank()!=1 ) {
     332           0 :       error("mismatch between numbers of target distances");
     333           1 :     } else if( i==1 ) {
     334           1 :       ntoproj = target[0]->getShape()[1];
     335           0 :     } else if( target[0]->getRank()>1 && ntoproj!=target[0]->getShape()[1] ) {
     336           0 :       error("mismatch between numbers of target distances");
     337             :     }
     338           4 :     if( !parseArgumentList("WEIGHTS",i,weights) ) {
     339           0 :       error("missing WEIGHTS" + inum + " keyword in input");
     340             :     }
     341           2 :     if( weights.size()!=1 ) {
     342           0 :       error("should only be one value in input to WEIGHTS" + inum );
     343             :     }
     344           2 :     if( weights[0]->getRank()!=1 || weights[0]->hasDerivatives() ) {
     345           0 :       error("input to WEIGHTS" + inum + " keyword should be a vector");
     346             :     }
     347           2 :     if( weights[0]->getShape()[0]!=nvals ) {
     348           0 :       error("number of weights should match number of input coordinates");
     349             :     }
     350           2 :     args.push_back( target[0] );
     351           2 :     args.push_back( weights[0] );
     352           2 :     bool has_sf = parseNumbered("FUNC",i,sfd);
     353           4 :     switchingFunction.push_back( SwitchingFunction() );
     354           2 :     if( !has_sf ) {
     355           0 :       switchingFunction[i-1].set( "CUSTOM FUNC=1-sqrt(x2) R_0=1.0", errors );
     356             :     } else {
     357           2 :       switchingFunction[i-1].set( sfd, errors );
     358           2 :       if( errors.length()!=0 ) {
     359           0 :         error("problem reading switching function description " + errors);
     360             :       }
     361             :     }
     362           2 :     log.printf("  %sth term seeks to match transformed distances with those in matrix %s \n", inum.c_str(), target[0]->getName().c_str() );
     363           2 :     log.printf("  in %sth term distances are transformed by 1-switching function with r_0=%s \n", inum.c_str(), switchingFunction[i-1].description().c_str() );
     364           2 :     log.printf("  in %sth term weights of matrix elements in stress function are given by %s \n", inum.c_str(), weights[0]->getName().c_str() );
     365           2 :   }
     366           2 :   std::vector<std::size_t> shape(1);
     367           2 :   shape[0]=ntoproj;
     368           2 :   if( ntoproj==1 ) {
     369           1 :     shape.resize(0);
     370             :   }
     371           6 :   for(unsigned i=0; i<dimout; ++i) {
     372             :     std::string num;
     373           4 :     Tools::convert( i+1, num );
     374           4 :     addComponent( "coord-" + num, shape );
     375           8 :     componentIsNotPeriodic( "coord-" + num );
     376             :   }
     377             :   // Create a list of tasks to perform
     378             :   double cgtol;
     379           2 :   parse("CGTOL",cgtol);
     380           2 :   log.printf("  tolerance for conjugate gradient algorithm equals %f \n",cgtol);
     381           2 :   requestArguments( args );
     382           2 :   checkRead();
     383             : 
     384             :   // Setup parallel task manager
     385             :   ProjectPointsInput input;
     386           2 :   input.cgtol=cgtol;
     387           2 :   input.action=this;
     388           2 :   if( ntoproj!=1 ) {
     389           1 :     taskmanager.setupParallelTaskManager( 0, 0 );
     390             :   }
     391           2 :   taskmanager.setActionInput( input );
     392           4 : }
     393             : 
     394         169 : void ProjectPoints::prepare() {
     395         169 :   if( getPntrToComponent(0)->getRank()==0 ) {
     396         168 :     return;
     397             :   }
     398             : 
     399           1 :   std::vector<std::size_t> shape(1);
     400           1 :   shape[0] = getPntrToArgument(dimout)->getShape()[0];
     401           3 :   for(unsigned i=0; i<dimout; ++i) {
     402           2 :     if( getPntrToComponent(i)->getShape()[0]!=shape[0] ) {
     403           2 :       getPntrToComponent(i)->setShape(shape);
     404             :     }
     405             :   }
     406           1 : }
     407             : 
     408       24180 : double ProjectPoints::calculateStress( const std::vector<double>& pp, std::vector<double>& der ) {
     409       24180 :   unsigned nmatrices = ( getNumberOfArguments() - dimout ) / 2;
     410             :   double stress=0;
     411             : 
     412       24180 :   unsigned t=OpenMP::getThreadNum();
     413       24180 :   std::vector<double> dtmp( pp.size() );
     414       24180 :   unsigned nland = getPntrToArgument(0)->getShape()[0];
     415     4889418 :   for(unsigned i=0; i<nland; ++i) {
     416             :     // Calculate distance in low dimensional space
     417             :     double dd2 = 0;
     418    14595714 :     for(unsigned k=0; k<pp.size(); ++k) {
     419     9730476 :       dtmp[k] = pp[k] - getPntrToArgument(k)->get(i);
     420     9730476 :       dd2 += dtmp[k]*dtmp[k];
     421             :     }
     422             : 
     423     9730476 :     for(unsigned k=0; k<nmatrices; ++k ) {
     424             :       // Now do transformations and calculate differences
     425     4865238 :       double df, fd = 1. - switchingFunction[k].calculateSqr( dd2, df );
     426             :       // Get the weight for this connection
     427     4865238 :       double weight = getPntrToArgument( dimout + 2*k + 1 )->get( i );
     428             :       // Get the difference for the connection
     429     4865238 :       double fdiff = fd - getPntrToArgument( dimout + 2*k )->get( rowstart[t]+i );
     430             :       // Calculate derivatives
     431     4865238 :       double pref = -2.*weight*fdiff*df;
     432    14595714 :       for(unsigned n=0; n<pp.size(); ++n) {
     433     9730476 :         der[n]+=pref*dtmp[n];
     434             :       }
     435             :       // Accumulate the total stress
     436     4865238 :       stress += weight*fdiff*fdiff;
     437             :     }
     438             :   }
     439       24180 :   return stress;
     440             : }
     441             : 
     442         668 : void ProjectPoints::performTask( std::size_t task_index, const ProjectPointsInput& actiondata, ParallelActionsInput& input, ParallelActionsOutput& output ) {
     443             :   // I doubt we are ever going to implement this on the GPU so I think we can leave this declaration here
     444         668 :   std::vector<double> point( input.ncomponents );
     445         668 :   std::size_t nland = input.shapedata[0];
     446             :   std::size_t base = task_index;
     447         668 :   if( input.ranks[input.ncomponents]==2 ) {
     448         500 :     auto myargh = ArgumentBookeepingHolder::create( input.ncomponents, input );
     449         500 :     base = task_index*myargh.shape[1];
     450             :   }
     451             :   unsigned closest=0;
     452         668 :   double mindist = input.inputdata[input.argstarts[input.ncomponents] + base];
     453      139112 :   for(unsigned i=1; i<nland; ++i) {
     454      138444 :     double dist = input.inputdata[input.argstarts[input.ncomponents] + base+i];
     455      138444 :     if( dist<mindist ) {
     456             :       mindist=dist;
     457             :       closest=i;
     458             :     }
     459             :   }
     460             :   // Put the initial guess near to the closest landmark  -- may wish to use grid here again Sandip??
     461         668 :   Random random;
     462         668 :   random.setSeed(-1234);
     463        2004 :   for(unsigned j=0; j<input.ncomponents; ++j) {
     464        1336 :     point[j] = input.inputdata[input.argstarts[j] + closest] + (random.RandU01() - 0.5)*0.01;
     465             :   }
     466             :   // And do the optimisation
     467         668 :   actiondata.action->rowstart[OpenMP::getThreadNum()]=task_index;
     468         668 :   if( input.ranks[input.ncomponents]==2 ) {
     469         500 :     auto myargh=ArgumentBookeepingHolder::create( input.ncomponents, input );
     470         500 :     actiondata.action->rowstart[OpenMP::getThreadNum()] = task_index*myargh.shape[1];
     471             :   }
     472             : 
     473         668 :   int code = actiondata.action->myminimiser.minimise( actiondata.cgtol, point, &ProjectPoints::calculateStress );
     474         668 :   if( code>0 ) {
     475           0 :     plumed_merror("failure in conjugate gradient minimisation");
     476             :   }
     477        2004 :   for(unsigned i=0; i<input.ncomponents; ++i) {
     478        1336 :     output.values[i] = point[i];
     479             :   }
     480         668 : }
     481             : 
     482         169 : void ProjectPoints::calculate() {
     483         169 :   if( getPntrToComponent(0)->getRank()==0 ) {
     484             :     auto myinput =ParallelActionsInput::create( getPbc() );
     485         168 :     myinput.noderiv = true;
     486         168 :     myinput.ncomponents = getNumberOfComponents();
     487         168 :     std::vector<double> input_buffer;
     488         168 :     getArgumentInputData( input_buffer );
     489         168 :     myinput.dataSize = input_buffer.size();
     490         168 :     myinput.inputdata = input_buffer.data();
     491         168 :     ArgumentsBookkeeping abk;
     492         168 :     copyArgumentBookeeping( abk );
     493         168 :     myinput.setupArguments( abk );
     494         168 :     std::vector<double> buffer;
     495         168 :     std::vector<double> derivatives, point( getNumberOfComponents() );
     496         168 :     auto output = ParallelActionsOutput::create( myinput.ncomponents, point.data(), 0, derivatives.data(), 0, buffer.data() );
     497         168 :     performTask( 0, taskmanager.getActionInput(), myinput, output );
     498         504 :     for(unsigned i=0; i<point.size(); ++i) {
     499         336 :       getPntrToComponent(i)->set(point[i]);
     500             :     }
     501         168 :   } else {
     502           1 :     taskmanager.runAllTasks();
     503             :   }
     504         169 : }
     505             : 
     506             : }
     507             : }

Generated by: LCOV version 1.16