LCOV - code coverage report
Current view: top level - crystallization - InterMolecularTorsions.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 14 76 18.4 %
Date: 2026-03-30 13:16:06 Functions: 3 10 30.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-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 "multicolvar/MultiColvarBase.h"
      23             : #include "multicolvar/AtomValuePack.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "tools/SwitchingFunction.h"
      26             : #include "tools/Torsion.h"
      27             : 
      28             : #include <string>
      29             : #include <cmath>
      30             : 
      31             : //+PLUMEDOC MCOLVARF INTERMOLECULARTORSIONS
      32             : /*
      33             : Calculate torsion angles between vectors on adjacent molecules
      34             : 
      35             : This variable can be used to calculate the average torsional angles between vectors.  In other words,
      36             : it can be used to compute quantities like this:
      37             : 
      38             : \f[
      39             : s = \frac{ \sum_{i \ne j} \sigma(r_{ij}) \theta_{ij} }{ \sum_{i \ne j} \sigma(r_{ij}) }
      40             : \f]
      41             : 
      42             : Here the sums run over all pairs of molecules. \f$\sigma(r_{ij})\f$ is a \ref switchingfunction that
      43             : action on the distance between the centers of molecules \f$i\f$ and \f$j\f$.  \f$\theta_{ij}\f$ is then
      44             : the torsional angle between an orientation vector for molecule \f$i\f$ and molecule \f$j\f$.
      45             : 
      46             : This command can be used to calculate the intermolecular torsional angles between the orientations of nearby molecules.  The orientation of a
      47             : molecule can be calculated by using either the \ref MOLECULES or the \ref PLANES commands.  These two commands calculate the orientation of a
      48             : bond in the molecule or the orientation of a plane containing three of the molecule's atoms.  Furthermore, when we use these commands we think of
      49             : molecules as objects that lie at a point in space and that have an orientation.   This command calculates the torsional angles between the orientations
      50             : of these objects.  We can then calculates functions of a large number of these torsional angles that measures things such as the number of torsional
      51             : angles that are within a particular range.  Because it is often useful to only consider the torsional angles between objects that are within a certain
      52             : distance of each other we can, when calculating these sums, perform a weighted sum and use a \ref switchingfunction to ensure that we focus on molecules
      53             : that are close together.
      54             : 
      55             : \par Examples
      56             : 
      57             : The example input below is necessarily but gives you an idea of what can be achieved using this action.
      58             : The orientations and positions of four molecules are defined using the \ref MOLECULES action as the position of the
      59             : centers of mass of the two atoms specified and the direction of the vector connecting the two atoms that were specified.
      60             : The torsional angles between the molecules are then calculated by the \ref INTERMOLECULARTORSIONS command labelled torsion_p.
      61             : We then compute a \ref HISTOGRAM that shows the distribution that these torsional angles take in the structure.  The weight
      62             : a given torsional angle contributes to this \ref HISTOGRAM is determined using a \ref switchingfunction that acts on the distance
      63             : between the two molecules.  As such the torsional angles between molecules that are close together contribute a high weight to the
      64             : histogram while the torsional angles between molecules that are far apart does not contribute to the histogram.  The histogram is
      65             : averaged over the whole trajectory and output once all the trajectory frames have been read.
      66             : 
      67             : \plumedfile
      68             : m1: MOLECULES MOL1=1,2 MOL2=3,4 MOL3=5,6 MOL4=7,8
      69             : torsion_p: INTERMOLECULARTORSIONS MOLS=m1 SWITCH={RATIONAL R_0=0.25 D_0=2.0 D_MAX=3.0}
      70             : htt_p: HISTOGRAM DATA=torsion_p GRID_MIN=-pi GRID_MAX=pi BANDWIDTH=0.1 GRID_BIN=200 STRIDE=1
      71             : DUMPGRID GRID=htt_p FILE=myhist.out
      72             : \endplumedfile
      73             : 
      74             : */
      75             : //+ENDPLUMEDOC
      76             : 
      77             : namespace PLMD {
      78             : namespace crystallization {
      79             : 
      80             : class InterMolecularTorsions : public multicolvar::MultiColvarBase {
      81             : private:
      82             : /// The switching function that tells us if atoms are close enough together
      83             :   SwitchingFunction switchingFunction;
      84             : public:
      85             :   static void registerKeywords( Keywords& keys );
      86             :   explicit InterMolecularTorsions(const ActionOptions&);
      87             : /// Do the stuff with the switching functions
      88             :   double calculateWeight( const unsigned& taskCode, const double& weight, multicolvar::AtomValuePack& myatoms ) const ;
      89             : /// Actually do the calculation
      90             :   double compute( const unsigned& tindex, multicolvar::AtomValuePack& myatoms ) const ;
      91             : /// Is the variable periodic
      92           0 :   bool isPeriodic() {
      93           0 :     return true;
      94             :   }
      95           0 :   void retrieveDomain( std::string& min, std::string& max ) {
      96             :     min="-pi";
      97             :     max="+pi";
      98           0 :   }
      99             : };
     100             : 
     101       13785 : PLUMED_REGISTER_ACTION(InterMolecularTorsions,"INTERMOLECULARTORSIONS")
     102             : 
     103           4 : void InterMolecularTorsions::registerKeywords( Keywords& keys ) {
     104           4 :   MultiColvarBase::registerKeywords( keys );
     105           8 :   keys.add("atoms","MOLS","The molecules you would like to calculate the torsional angles between. This should be the label/s of \\ref MOLECULES or \\ref PLANES actions");
     106           8 :   keys.add("atoms-1","MOLSA","In this version of the input the torsional angles between all pairs of atoms including one atom from MOLSA one atom from MOLSB will be computed. "
     107             :            "This should be the label/s of \\ref MOLECULES or \\ref PLANES actions");
     108           8 :   keys.add("atoms-1","MOLSB","In this version of the input the torsional angles between all pairs of atoms including one atom from MOLSA one atom from MOLSB will be computed. "
     109             :            "This should be the label/s of \\ref MOLECULES or \\ref PLANES actions");
     110           8 :   keys.add("compulsory","NN","6","The n parameter of the switching function ");
     111           8 :   keys.add("compulsory","MM","0","The m parameter of the switching function; 0 implies 2*NN");
     112           8 :   keys.add("compulsory","D_0","0.0","The d_0 parameter of the switching function");
     113           8 :   keys.add("compulsory","R_0","The r_0 parameter of the switching function");
     114           8 :   keys.add("optional","SWITCH","This keyword is used if you want to employ an alternative to the continuous switching function defined above. "
     115             :            "The following provides information on the \\ref switchingfunction that are available. "
     116             :            "When this keyword is present you no longer need the NN, MM, D_0 and R_0 keywords.");
     117             :   // Use actionWithDistributionKeywords
     118           4 :   keys.remove("LOWMEM");
     119           8 :   keys.addFlag("LOWMEM",false,"lower the memory requirements");
     120           4 : }
     121             : 
     122           0 : InterMolecularTorsions::InterMolecularTorsions(const ActionOptions& ao):
     123             :   Action(ao),
     124           0 :   MultiColvarBase(ao) {
     125           0 :   for(unsigned i=0; i<getNumberOfBaseMultiColvars(); ++i) {
     126           0 :     if( getBaseMultiColvar(i)->getNumberOfQuantities()!=5 ) {
     127           0 :       error("input multicolvar does not calculate molecular orientations");
     128             :     }
     129             :   }
     130             :   // The weight of this does have derivatives
     131           0 :   weightHasDerivatives=true;
     132             : 
     133             :   // Read in the switching function
     134             :   std::string sw, errors;
     135           0 :   parse("SWITCH",sw);
     136           0 :   if(sw.length()>0) {
     137           0 :     switchingFunction.set(sw,errors);
     138             :   } else {
     139           0 :     double r_0=-1.0, d_0;
     140             :     int nn, mm;
     141           0 :     parse("NN",nn);
     142           0 :     parse("MM",mm);
     143           0 :     parse("R_0",r_0);
     144           0 :     parse("D_0",d_0);
     145           0 :     if( r_0<0.0 ) {
     146           0 :       error("you must set a value for R_0");
     147             :     }
     148           0 :     switchingFunction.set(nn,mm,r_0,d_0);
     149             :   }
     150           0 :   log.printf("  calculating number of links with atoms separation of %s\n",( switchingFunction.description() ).c_str() );
     151             :   std::vector<AtomNumber> all_atoms;
     152           0 :   readTwoGroups( "MOLS", "MOLSA", "MOLSB", all_atoms );
     153           0 :   setupMultiColvarBase( all_atoms );
     154           0 :   setLinkCellCutoff( switchingFunction.get_dmax() );
     155             : 
     156           0 :   for(unsigned i=0; i<getNumberOfBaseMultiColvars(); ++i) {
     157           0 :     if( !getBaseMultiColvar(i)->hasDifferentiableOrientation() ) {
     158           0 :       error("cannot use multicolvar of type " + getBaseMultiColvar(i)->getName() );
     159             :     }
     160             :   }
     161             : 
     162             :   // Create holders for the collective variable
     163           0 :   readVesselKeywords();
     164           0 :   plumed_assert( getNumberOfVessels()==0 );
     165             :   std::string input;
     166           0 :   addVessel( "SUM", input, -1 );
     167           0 :   readVesselKeywords();
     168           0 : }
     169             : 
     170           0 : double InterMolecularTorsions::calculateWeight( const unsigned& taskCode, const double& weight, multicolvar::AtomValuePack& myatoms ) const {
     171           0 :   Vector distance = getSeparation( myatoms.getPosition(0), myatoms.getPosition(1) );
     172           0 :   double dfunc, sw = switchingFunction.calculateSqr( distance.modulo2(), dfunc );
     173             : 
     174           0 :   if( !doNotCalculateDerivatives() ) {
     175           0 :     addAtomDerivatives( 0, 0, (-dfunc)*weight*distance, myatoms );
     176           0 :     addAtomDerivatives( 0, 1, (dfunc)*weight*distance, myatoms );
     177           0 :     myatoms.addBoxDerivatives( 0, (-dfunc)*weight*Tensor(distance,distance) );
     178             :   }
     179           0 :   return sw;
     180             : }
     181             : 
     182           0 : double InterMolecularTorsions::compute( const unsigned& tindex, multicolvar::AtomValuePack& myatoms ) const {
     183           0 :   Vector v1, v2, dv1, dv2, dconn, conn = getSeparation( myatoms.getPosition(0), myatoms.getPosition(1) );
     184             : 
     185             :   // Retrieve vectors
     186           0 :   std::vector<double> orient0( 5 ), orient1( 5 );
     187           0 :   getInputData( 0, true, myatoms, orient0 );
     188           0 :   getInputData( 1, true, myatoms, orient1 );
     189           0 :   for(unsigned i=0; i<3; ++i) {
     190           0 :     v1[i]=orient0[2+i];
     191           0 :     v2[i]=orient1[2+i];
     192             :   }
     193           0 :   if( getBaseMultiColvar(0)->getNumberOfQuantities()<3 ) {
     194             :     return 1.0;
     195             :   }
     196             : 
     197             :   // Evaluate angle
     198             :   Torsion t;
     199           0 :   double angle = t.compute( v1, conn, v2, dv1, dconn, dv2 );
     200           0 :   for(unsigned i=0; i<3; ++i) {
     201           0 :     orient0[i+2]=dv1[i];
     202           0 :     orient1[i+2]=dv2[i];
     203             :   }
     204             : 
     205             :   // And accumulate derivatives
     206           0 :   if( !doNotCalculateDerivatives() ) {
     207           0 :     MultiValue& myder0=getInputDerivatives( 0, true, myatoms );
     208           0 :     mergeInputDerivatives( 1, 2, orient1.size(), 0, orient0, myder0, myatoms );
     209           0 :     MultiValue& myder1=getInputDerivatives( 1, true, myatoms );
     210           0 :     mergeInputDerivatives( 1, 2, orient0.size(), 1, orient1, myder1, myatoms );
     211           0 :     addAtomDerivatives( 1, 0, -dconn, myatoms );
     212           0 :     addAtomDerivatives( 1, 1, dconn, myatoms );
     213           0 :     myatoms.addBoxDerivatives( 1, -extProduct( conn, dconn ) );
     214             :   }
     215             : 
     216             :   return angle;
     217             : }
     218             : 
     219             : }
     220             : }

Generated by: LCOV version 1.16