LCOV - code coverage report
Current view: top level - crystdistrib - Quaternion.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 163 211 77.3 %
Date: 2025-12-04 11:19:34 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) crystdistrib 2023-2023 The code team
       3             :    (see the PEOPLE-crystdistrib file at the root of this folder for a list of names)
       4             : 
       5             :    This file is part of crystdistrib code module.
       6             : 
       7             :    The crystdistrib code module is free software: you can redistribute it and/or modify
       8             :    it under the terms of the GNU Lesser General Public License as published by
       9             :    the Free Software Foundation, either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    The crystdistrib code module is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU Lesser General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU Lesser General Public License
      18             :    along with the crystdistrib code module.  If not, see <http://www.gnu.org/licenses/>.
      19             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      20             : #include "core/Colvar.h"
      21             : #include "colvar/ColvarShortcut.h"
      22             : #include "colvar/MultiColvarTemplate.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "tools/Pbc.h"
      25             : 
      26             : namespace PLMD {
      27             : namespace crystdistrib {
      28             : 
      29             : //+PLUMEDOC COLVAR QUATERNION
      30             : /*
      31             : Calculate unit quaternions for molecules.
      32             : 
      33             : This action calculates a unit quaternion to define an internal coordinate frame for a molecule. The reference frame for the molecule is user-defined using the positions of three (non-collinear) atoms.
      34             : The vectors which will define the frame are calculated as follows from atomic coordinates, all vectors in  $\mathbb{R}^3$, $x_1, x_2, x_3$:
      35             : 
      36             : The first axis is the normalized difference of $x_1$ and $x_2$:
      37             : 
      38             : $$
      39             : \mathbf{\hat{v_1}} = x_2 - x_1 / \|x_2 - x_1\|
      40             : $$
      41             : 
      42             : In general, the vector $\mathbf{v'_2} = x_3 - x_1$ will not be orthogonal to $\mathbf{\hat{v_1}}$. This is fixed by taking the difference between the projection of $\mathbf{v'_2}$
      43             : onto $\mathbf{\hat{v_1}}$ and $\mathbf{\hat{v_1}}$.
      44             : 
      45             : $$
      46             : \mathbf{v_2} = \mathbf{v'_2} - Proj_{\mathbf{\hat{v}_1}}(\mathbf{v'_2}) = \mathbf{v'_2} -  \mathbf{\hat{v}_1} \cdot  \mathbf{v'_2}
      47             : $$
      48             : 
      49             : This is then normalized to form the second axis.
      50             : 
      51             : $$
      52             : \mathbf{\hat{v_2}} = \mathbf{v_2} / \|\mathbf{v_2}\|
      53             : $$
      54             : 
      55             : Finally, the third axis is the cross product between the first two.
      56             : 
      57             : $$
      58             : \mathbf{\hat{v_3}} = \mathbf{\hat{v_1}} \times \mathbf{\hat{v_2}}
      59             : $$
      60             : 
      61             : The above 9 components form an orthonormal basis, centered on the molecule provided. The rotation matrix is generally the inverse of this matrix, and
      62             : in this case since the matrix is orthogonal and its determinant is 1, the inverse is simply the transpose. The rotation matrix is then converted to a quaternion.
      63             : The resulting quaternion has 4 real numbers attached to it, and they can be called as w, i , j ,and k. Note the quaternions are not unique e.g. q and -q perform the same rotation,
      64             : so take care when using the results.  Take care that the components are simply 4 real numbers, and the usual non-commutativity of quaternions, and any other algebraic difference
      65             : will need to be accounted for manually in later usage. No checks are made for co-linearity, or if the atoms are a part of the same molecule.
      66             : 
      67             : An example input file, in a system of 12 atoms. It calculates four molecular frames, then uses them in an order parameter.
      68             : 
      69             : ```plumed
      70             : q1: QUATERNION ATOMS1=1,3,2 ATOMS2=4,6,5
      71             : q2: QUATERNION ATOMS1=7,9,8 ATOMS2=10,12,11
      72             : #There are no checks to make sure the atoms belong to the same molecule
      73             : 
      74             : fake: CUSTOM ARG=q1.w,q1.i,q1.j,q1.k,q2.w,q2.i,q2.j,q2.k VAR=w1,i1,j1,k1,w2,i2,j2,k2 FUNC=w1*w2+i1*i2+j1*j2+k1*k2 PERIODIC=NO
      75             : #this isn’t a real order parameter, for the record
      76             : PRINT ARG=fake FILE=fakeout
      77             : ```
      78             : 
      79             : The last thing to note is that by default a procedure akin to that used in [WHOLEMOLECULES](WHOLEMOLECULES.md)
      80             : is used to ensure that the sets of atoms that are specified to each ATOMS keyword are not broken by the periodic
      81             : boundary conditions.  If you would like to turn this off for any reason you add the NOPBC in your input file as shown
      82             : below:
      83             : 
      84             : ```plumed
      85             : d: QUATERNION ATOMS=1,2,3 NOPBC
      86             : ```
      87             : 
      88             : */
      89             : //+ENDPLUMEDOC
      90             : 
      91             : //simple hamilton/quaternion product, which is just expanding the two quats as expected, then applying the rules for i j k
      92             : //passing 12 references might be a bit silly
      93             : //void QuatProduct(double &a1, double &b1, double &c1, double &d1, double &a2, double &b2, double &c2, double &d2, double &w, double &i, double &j, double &k) {
      94             : //
      95             : //  w = a1*a2 - b1*b2 - c1*c2 - d1*d2;
      96             : //  i = a1*b2 + b1*a2 + c1*d2 - d1*c2;
      97             : //  j = a1*c2 - b1*d2 + c1*a2 + d1*b2;
      98             : //  k = a1*d2 + b1*c2 - c1*b2 + d1*a2;
      99             : //}
     100             : //
     101             : //
     102             : //
     103             : 
     104             : class Quaternion : public Colvar {
     105             : private:
     106             :   bool pbc;
     107             :   std::vector<double> value;
     108             :   std::vector<double> derivs;
     109             : public:
     110             :   static void registerKeywords( Keywords& keys );
     111             :   explicit Quaternion(const ActionOptions&);
     112             :   static void parseAtomList( const int& num, std::vector<AtomNumber>& t, ActionAtomistic* aa );
     113             :   static unsigned getModeAndSetupValues( ActionWithValue* av );
     114             : // active methods:
     115             :   void calculate() override;
     116             :   static void calculateCV( const colvar::ColvarInput& cvin, ColvarOutput& cvout );
     117             : };
     118             : 
     119             : typedef colvar::ColvarShortcut<Quaternion> QuaternionShortcut;
     120             : PLUMED_REGISTER_ACTION(QuaternionShortcut,"QUATERNION")
     121             : PLUMED_REGISTER_ACTION(Quaternion,"QUATERNION_SCALAR")
     122             : typedef colvar::MultiColvarTemplate<Quaternion> QuaternionMulti;
     123             : PLUMED_REGISTER_ACTION(QuaternionMulti,"QUATERNION_VECTOR")
     124             : 
     125          90 : void Quaternion::registerKeywords( Keywords& keys ) {
     126          90 :   Colvar::registerKeywords( keys );
     127         180 :   keys.setDisplayName("QUATERNION");
     128          90 :   keys.add("atoms","ATOMS","the three atom that we are using to calculate the quaternion");
     129         180 :   keys.addOutputComponent("w","default","scalar/vector","the real component of quaternion");
     130         180 :   keys.addOutputComponent("i","default","scalar/vector","the i component of the quaternion");
     131         180 :   keys.addOutputComponent("j","default","scalar/vector","the j component of the quaternion");
     132         180 :   keys.addOutputComponent("k","default","scalar/vector","the k component of the quaternion");
     133          90 :   keys.add("hidden","NO_ACTION_LOG","suppresses printing from action on the log");
     134         180 :   keys.reset_style("NUMERICAL_DERIVATIVES","hidden");
     135          90 : }
     136             : 
     137           5 : Quaternion::Quaternion(const ActionOptions&ao):
     138             :   PLUMED_COLVAR_INIT(ao),
     139           5 :   pbc(true),
     140           5 :   value(4) {
     141             :   std::vector<AtomNumber> atoms;
     142           5 :   parseAtomList(-1,atoms,this);
     143           5 :   if(atoms.size()!=3) {
     144           0 :     error("Number of specified atoms should be 3");
     145             :   }
     146             :   // please note, I do NO checking if these atoms are in the same molecule at all, so be careful in your input
     147             : 
     148           5 :   bool nopbc=!pbc;
     149           5 :   parseFlag("NOPBC",nopbc);
     150           5 :   pbc=!nopbc;
     151             : 
     152           5 :   unsigned mode = getModeAndSetupValues( this );
     153           5 :   requestAtoms(atoms);
     154           5 : }
     155             : 
     156        1322 : void Quaternion::parseAtomList( const int& num, std::vector<AtomNumber>& t, ActionAtomistic* aa ) {
     157        2644 :   aa->parseAtomList("ATOMS",num,t);
     158        1322 :   if( t.size()==3 ) {
     159        1309 :     aa->log.printf("  involving atoms %d %d %d\n",t[0].serial(),t[1].serial(),t[0].serial());
     160             :   }
     161        1322 : }
     162             : 
     163          18 : unsigned Quaternion::getModeAndSetupValues( ActionWithValue* av ) {
     164             :   // This sets up values that we can pass around in PLUMED
     165          36 :   av->addComponentWithDerivatives("w");
     166          18 :   av->componentIsNotPeriodic("w");
     167          36 :   av->addComponentWithDerivatives("i");
     168          18 :   av->componentIsNotPeriodic("i");
     169          36 :   av->addComponentWithDerivatives("j");
     170          18 :   av->componentIsNotPeriodic("j");
     171          36 :   av->addComponentWithDerivatives("k");
     172          18 :   av->componentIsNotPeriodic("k");
     173          18 :   return 0;
     174             : }
     175             : 
     176          45 : void Quaternion::calculate() {
     177          45 :   if(pbc) {
     178          45 :     makeWhole();
     179             :   }
     180             : 
     181          45 :   ColvarOutput cvout = ColvarOutput::createColvarOutput(value,derivs,this);
     182          45 :   calculateCV( colvar::ColvarInput::createColvarInput( 0, getPositions(), this ), cvout );
     183         225 :   for(unsigned j=0; j<4; ++j) {
     184         180 :     Value* valuej=getPntrToComponent(j);
     185         720 :     for(unsigned i=0; i<3; ++i) {
     186         540 :       setAtomsDerivatives(valuej,i,cvout.getAtomDerivatives(j,i) );
     187             :     }
     188         180 :     setBoxDerivatives(valuej,cvout.virial[j]);
     189         180 :     valuej->set(value[j]);
     190             :   }
     191          45 : }
     192             : 
     193             : // calculator
     194        7203 : void Quaternion::calculateCV( const colvar::ColvarInput& cvin, ColvarOutput& cvout ) {
     195             :   //declarations
     196        7203 :   Vector vec1_comp = delta( cvin.pos[0], cvin.pos[1] ); //components between atom 1 and 2
     197        7203 :   Vector vec2_comp = delta( cvin.pos[0], cvin.pos[2] ); //components between atom 1 and 3
     198             : 
     199             : ////////x-vector calculations///////
     200        7203 :   double magx = vec1_comp.modulo();
     201             :   Vector xt = vec1_comp / magx;
     202        7203 :   std::vector<Tensor> dx(3);
     203        7203 :   double magx3= magx*magx*magx;
     204             : //dx[i] - derivatives of atom i's coordinates
     205        7203 :   dx[0](0,0) = ( -(vec1_comp[1]*vec1_comp[1]+vec1_comp[2]*vec1_comp[2])/magx3 ); //dx[0]/dx0
     206        7203 :   dx[0](0,1) = (  vec1_comp[0]*vec1_comp[1]/magx3 );                 // dx[0]/dy0
     207        7203 :   dx[0](0,2) = (  vec1_comp[0]*vec1_comp[2]/magx3 );                 // dx[0]/dz0
     208        7203 :   dx[0](1,0) = (  vec1_comp[1]*vec1_comp[0]/magx3 );                 // dx[1]/dx0
     209        7203 :   dx[0](1,1) = ( -(vec1_comp[0]*vec1_comp[0]+vec1_comp[2]*vec1_comp[2])/magx3 );   // dx[1]/dy0
     210        7203 :   dx[0](1,2) = (  vec1_comp[1]*vec1_comp[2]/magx3 ); //dx[1]/dz0
     211        7203 :   dx[0](2,0) = (  vec1_comp[2]*vec1_comp[0]/magx3 );//etc
     212        7203 :   dx[0](2,1) = (  vec1_comp[2]*vec1_comp[1]/magx3 );
     213        7203 :   dx[0](2,2) = ( -(vec1_comp[1]*vec1_comp[1]+vec1_comp[0]*vec1_comp[0])/magx3 );
     214             : 
     215        7203 :   dx[1](0,0) = ( (vec1_comp[1]*vec1_comp[1]+vec1_comp[2]*vec1_comp[2])/magx3 );//dx[0]/dx1
     216        7203 :   dx[1](0,1) = ( -vec1_comp[0]*vec1_comp[1]/magx3 );//dx[0]/dy1
     217        7203 :   dx[1](0,2) = ( -vec1_comp[0]*vec1_comp[2]/magx3 );
     218        7203 :   dx[1](1,0) = ( -vec1_comp[1]*vec1_comp[0]/magx3 );
     219        7203 :   dx[1](1,1) = ( (vec1_comp[0]*vec1_comp[0]+vec1_comp[2]*vec1_comp[2])/magx3 );
     220        7203 :   dx[1](1,2) = ( -vec1_comp[1]*vec1_comp[2]/magx3 );
     221        7203 :   dx[1](2,0) = ( -vec1_comp[2]*vec1_comp[0]/magx3 );
     222        7203 :   dx[1](2,1) = ( -vec1_comp[2]*vec1_comp[1]/magx3 );
     223        7203 :   dx[1](2,2) = ( (vec1_comp[1]*vec1_comp[1]+vec1_comp[0]*vec1_comp[0])/magx3 );
     224             :   dx[2].zero();//not atom[2] terms present
     225             : 
     226             : ////////y-vector calculations////////
     227             :   //project vec2_comp on to vec1_comp
     228             :   //first do dot product of unormalized x and unormed y, divided by magnitude of x^2
     229             :   double dp = dotProduct( vec1_comp, vec2_comp );
     230             :   double magx2=magx*magx;
     231        7203 :   std::vector<Vector> fac_derivs(3);
     232        7203 :   double magx4=magx2*magx2, fac = dp/magx2; //fac meaning factor on front
     233        7203 :   fac_derivs[0] = (-vec2_comp - vec1_comp)/magx2 + 2*dp*vec1_comp / magx4;
     234        7203 :   fac_derivs[1] = (vec2_comp)/(magx2) - 2*dp*vec1_comp / magx4;
     235        7203 :   fac_derivs[2] = (vec1_comp)/(magx2);   //atom 1, components x2,y2,z2
     236             :   //now multiply fac by unormed x, and subtract it from unormed y, then normalize
     237             :   Vector yt = vec2_comp - fac*vec1_comp;
     238        7203 :   std::vector<Tensor> dy(3);
     239        7203 :   dy[0](0,0) = -1 - fac_derivs[0][0]*vec1_comp[0] + fac;   // dy[0]/dx0
     240        7203 :   dy[0](0,1) = -fac_derivs[0][1]*vec1_comp[0];             // dy[0]/dy0
     241        7203 :   dy[0](0,2) = -fac_derivs[0][2]*vec1_comp[0];
     242        7203 :   dy[0](1,0) = -fac_derivs[0][0]*vec1_comp[1];
     243        7203 :   dy[0](1,1) = -1 - fac_derivs[0][1]*vec1_comp[1] + fac;
     244        7203 :   dy[0](1,2) = -fac_derivs[0][2]*vec1_comp[1];
     245        7203 :   dy[0](2,0) = -fac_derivs[0][0]*vec1_comp[2];
     246        7203 :   dy[0](2,1) = -fac_derivs[0][1]*vec1_comp[2];
     247        7203 :   dy[0](2,2) = -1 - fac_derivs[0][2]*vec1_comp[2] + fac;
     248             : 
     249        7203 :   dy[1](0,0) = -fac_derivs[1][0]*vec1_comp[0] - fac; //dy[0]/dx0
     250        7203 :   dy[1](0,1) = -fac_derivs[1][1]*vec1_comp[0];
     251        7203 :   dy[1](0,2) = -fac_derivs[1][2]*vec1_comp[0];
     252        7203 :   dy[1](1,0) = -fac_derivs[1][0]*vec1_comp[1];
     253        7203 :   dy[1](1,1) = -fac_derivs[1][1]*vec1_comp[1] - fac;
     254        7203 :   dy[1](1,2) = -fac_derivs[1][2]*vec1_comp[1];
     255        7203 :   dy[1](2,0) = -fac_derivs[1][0]*vec1_comp[2];
     256        7203 :   dy[1](2,1) = -fac_derivs[1][1]*vec1_comp[2];
     257        7203 :   dy[1](2,2) = -fac_derivs[1][2]*vec1_comp[2] - fac;
     258             : 
     259        7203 :   dy[2](0,0) = 1 - fac_derivs[2][0]*vec1_comp[0];//dy[0]/dx2
     260        7203 :   dy[2](0,1) = -fac_derivs[2][1]*vec1_comp[0];
     261        7203 :   dy[2](0,2) = -fac_derivs[2][2]*vec1_comp[0];
     262        7203 :   dy[2](1,0) = -fac_derivs[2][0]*vec1_comp[1];
     263        7203 :   dy[2](1,1) = 1 - fac_derivs[2][1]*vec1_comp[1];
     264        7203 :   dy[2](1,2) = -fac_derivs[2][2]*vec1_comp[1];
     265        7203 :   dy[2](2,0) = -fac_derivs[2][0]*vec1_comp[2];
     266        7203 :   dy[2](2,1) = -fac_derivs[2][1]*vec1_comp[2];
     267        7203 :   dy[2](2,2) = 1 - fac_derivs[2][2]*vec1_comp[2];
     268             :   //now normalize, and we have our y vector
     269        7203 :   double magy = yt.modulo();
     270        7203 :   double imagy = 1/magy, magy3 = magy*magy*magy;
     271             :   Tensor abc;
     272       28812 :   for(unsigned i=0; i<3; ++i) {
     273             :     abc.setRow(i, yt);
     274             :   }
     275             :   Tensor abc_diag;
     276        7203 :   abc_diag.setRow(0, Vector(yt[0], 0, 0));
     277        7203 :   abc_diag.setRow(1, Vector(0, yt[1], 0));
     278        7203 :   abc_diag.setRow(2, Vector(0, 0, yt[2]));
     279        7203 :   Tensor abc_prod = matmul(abc_diag, abc);
     280       28812 :   for(unsigned i=0; i<3; ++i) {
     281       43218 :     dy[i] = dy[i]/magy - matmul(abc_prod, dy[i])/magy3;
     282             :   }
     283             :   //normalize now, derivatives are with respect to un-normalized y vector
     284        7203 :   yt = yt / magy;
     285             : 
     286             : ///////z-vector calculations/////////
     287             : //comparatively simple
     288        7203 :   Vector zt = crossProduct(xt,yt);
     289        7203 :   std::vector<Tensor> dz(3);
     290       21609 :   dz[0].setCol( 0, crossProduct( dx[0].getCol(0), yt ) + crossProduct( xt, dy[0].getCol(0) ) );
     291       21609 :   dz[0].setCol( 1, crossProduct( dx[0].getCol(1), yt ) + crossProduct( xt, dy[0].getCol(1) ) );
     292       21609 :   dz[0].setCol( 2, crossProduct( dx[0].getCol(2), yt ) + crossProduct( xt, dy[0].getCol(2) ) );
     293             : 
     294       21609 :   dz[1].setCol( 0, crossProduct( dx[1].getCol(0), yt ) + crossProduct( xt, dy[1].getCol(0) ) );
     295       21609 :   dz[1].setCol( 1, crossProduct( dx[1].getCol(1), yt ) + crossProduct( xt, dy[1].getCol(1) ) );
     296       21609 :   dz[1].setCol( 2, crossProduct( dx[1].getCol(2), yt ) + crossProduct( xt, dy[1].getCol(2) ) );
     297             : 
     298       14406 :   dz[2].setCol( 0, crossProduct( xt, dy[2].getCol(0) ) );
     299       14406 :   dz[2].setCol( 1, crossProduct( xt, dy[2].getCol(1) ) );
     300        7203 :   dz[2].setCol( 2, crossProduct( xt, dy[2].getCol(2) ) );
     301             : 
     302             : //for debugging frame values
     303             : //aa->log.printf("%8.6f %8.6f %8.6f\n%8.6f %8.6f %8.6f\n%8.6f %8.6f %8.6f\n",xt[0],xt[1],xt[2],yt[0],yt[1],yt[2],zt[0],zt[1],zt[2]);
     304             : 
     305             : //for bebuffing derivatives
     306             : //aa->log.printf("x1 x2 x3 y1 y2 y3 z1 z2 z3\n");
     307             : //for (int i=0; i<3; i++){
     308             : //for (int j=0;j<3;j++){
     309             : //aa->log.printf("%8.4f %8.4f %8.4f\n%8.4f %8.4f %8.4f\n%8.4f %8.4f %8.4f\n",dx[i](0,j), dx[i](1,j), dx[i](2,j), dy[i](0,j), dy[i](1,j), dy[i](2,j), dz[i](0,j), dz[i](1,j), dz[i](2,j));
     310             : //}
     311             : //}
     312             : //
     313             : 
     314             : //the above 9 components form an orthonormal basis, centered on the molecule in question
     315             : //the rotation matrix is generally the inverse of this matrix, and in this case since it is 1) orthogonal and 2) its determinant is 1
     316             : //the inverse is simply the transpose
     317             : 
     318             : 
     319             : //[x[0] x[1] x[2]]
     320             : //[y[0] y[1] y[2]]
     321             : //[z[0] z[1] z[2]]
     322             : //QUICKFIX to transpose basis
     323        7203 :   Vector x(xt[0],yt[0],zt[0]);
     324        7203 :   Vector y(xt[1],yt[1],zt[1]);
     325        7203 :   Vector z(xt[2],yt[2],zt[2]);
     326             : 
     327             : //likewise transposing the tensors into proper form
     328        7203 :   std::vector<Tensor> tdx(3);
     329        7203 :   std::vector<Tensor> tdy(3);
     330        7203 :   std::vector<Tensor> tdz(3);
     331       28812 :   for (int i=0; i<3; ++i) {
     332       43218 :     tdx[i].setRow(0, dx[i].getRow(0));
     333       21609 :     tdx[i].setRow(1, dy[i].getRow(0));
     334       21609 :     tdx[i].setRow(2, dz[i].getRow(0));
     335             : 
     336       21609 :     tdy[i].setRow(0, dx[i].getRow(1));
     337       21609 :     tdy[i].setRow(1, dy[i].getRow(1));
     338       21609 :     tdy[i].setRow(2, dz[i].getRow(1));
     339             : 
     340       21609 :     tdz[i].setRow(0, dx[i].getRow(2));
     341       21609 :     tdz[i].setRow(1, dy[i].getRow(2));
     342       21609 :     tdz[i].setRow(2, dz[i].getRow(2));
     343             :   }
     344             : 
     345             : //convert to quaternion
     346        7203 :   double tr = x[0] + y[1] + z[2] + 1; //trace of the rotation matrix + 1
     347        7203 :   std::vector<Vector> dS(3);
     348        7203 :   if (tr > 1.0E-8) { //to avoid numerical instability
     349        7203 :     double S = 1/(sqrt(tr) * 2); // S=4*qw
     350       28812 :     for(unsigned i=0; i<3; ++i) {
     351       43218 :       dS[i] = (-2*S*S*S)*(tdx[i].getRow(0) + tdy[i].getRow(1) + tdz[i].getRow(2));
     352             :     }
     353             : 
     354        7203 :     cvout.values[0] = 0.25 / S;
     355       28812 :     for(unsigned i=0; i<3; ++i) {
     356       21609 :       cvout.derivs[0][i] =-0.25*dS[i]/(S*S);
     357             :     }
     358             : 
     359        7203 :     cvout.values[1] = (z[1] - y[2]) * S;
     360       28812 :     for(unsigned i=0; i<3; ++i) {
     361       43218 :       cvout.derivs[1][i] = (S)*(tdz[i].getRow(1) - tdy[i].getRow(2)) + (z[1]-y[2])*dS[i];
     362             :     }
     363             : 
     364        7203 :     cvout.values[2] = (x[2] - z[0]) * S;
     365       28812 :     for(unsigned i=0; i<3; ++i) {
     366       43218 :       cvout.derivs[2][i] = (S)*(tdx[i].getRow(2) - tdz[i].getRow(0)) + (x[2]-z[0])*dS[i];
     367             :     }
     368             : 
     369        7203 :     cvout.values[3] = (y[0] - x[1]) * S;
     370       28812 :     for(unsigned i=0; i<3; ++i) {
     371       43218 :       cvout.derivs[3][i] = (S)*(tdy[i].getRow(0) - tdx[i].getRow(1)) + (y[0]-x[1])*dS[i];
     372             :     }
     373           0 :   } else if ((x[0] > y[1])&(x[0] > z[2])) {
     374           0 :     double S = sqrt(1.0 + x[0] - y[1] - z[2]) * 2; // S=4*qx
     375           0 :     for(unsigned i=0; i<3; ++i) {
     376           0 :       dS[i] = (2/S)*(tdx[i].getRow(0) - tdy[i].getRow(1) - tdz[i].getRow(2));
     377             :     }
     378             : 
     379           0 :     cvout.values[0] = (z[1] - y[2]) / S;
     380           0 :     for(unsigned i=0; i<3; ++i) {
     381           0 :       cvout.derivs[0][i] = (1/S)*(tdz[i].getRow(1) - tdy[i].getRow(2)) - (cvout.values[0]/S)*dS[i];
     382             :     }
     383             : 
     384           0 :     cvout.values[1] = 0.25 * S;
     385           0 :     for(unsigned i=0; i<3; ++i) {
     386           0 :       cvout.derivs[1][i] =0.25*dS[i];
     387             :     }
     388             : 
     389           0 :     cvout.values[2] = (x[1] + y[0]) / S;
     390           0 :     for(unsigned i=0; i<3; ++i) {
     391           0 :       cvout.derivs[2][i] = (1/S)*(tdx[i].getRow(1) + tdy[i].getRow(0)) - (cvout.values[2]/S)*dS[i];
     392             :     }
     393             : 
     394           0 :     cvout.values[3] = (x[2] + z[0]) / S;
     395           0 :     for(unsigned i=0; i<3; ++i) {
     396           0 :       cvout.derivs[3][i] = (1/S)*(tdx[i].getRow(2) + tdz[i].getRow(0)) - (cvout.values[3]/S)*dS[i];
     397             :     }
     398           0 :   } else if (y[1] > z[2]) {
     399           0 :     double S = sqrt(1.0 + y[1] - x[0] - z[2]) * 2; // S=4*qy
     400           0 :     for(unsigned i=0; i<3; ++i) {
     401           0 :       dS[i] = (2/S)*( -tdx[i].getRow(0) + tdy[i].getRow(1) - tdz[i].getRow(2));
     402             :     }
     403             : 
     404             : 
     405           0 :     cvout.values[0] = (x[2] - z[0]) / S;
     406           0 :     for(unsigned i=0; i<3; ++i) {
     407           0 :       cvout.derivs[0][i] = (1/S)*(tdx[i].getRow(2) - tdz[i].getRow(0)) - (cvout.values[0]/S)*dS[i];
     408             :     }
     409             : 
     410           0 :     cvout.values[1] = (x[1] + y[0]) / S;
     411           0 :     for(unsigned i=0; i<3; ++i) {
     412           0 :       cvout.derivs[1][i] = (1/S)*(tdx[i].getRow(1) + tdy[i].getRow(0)) - (cvout.values[1]/S)*dS[i];
     413             :     }
     414             : 
     415           0 :     cvout.values[2] = 0.25 * S;
     416           0 :     for(unsigned i=0; i<3; ++i) {
     417           0 :       cvout.derivs[2][i] =0.25*dS[i];
     418             :     }
     419             : 
     420           0 :     cvout.values[3] = (y[2] + z[1]) / S;
     421           0 :     for(unsigned i=0; i<3; ++i) {
     422           0 :       cvout.derivs[3][i] = (1/S)*(tdy[i].getRow(2) + tdz[i].getRow(1)) - (cvout.values[3]/S)*dS[i];
     423             :     }
     424             :   } else {
     425           0 :     double S = sqrt(1.0 + z[2] - x[0] - y[1]) * 2; // S=4*qz
     426           0 :     for(unsigned i=0; i<3; ++i) {
     427           0 :       dS[i] = (2/S)*(-tdx[i].getRow(0) - tdy[i].getRow(1) + tdz[i].getRow(2));
     428             :     }
     429             : 
     430             : 
     431           0 :     cvout.values[0] = (y[0] - x[1]) / S;
     432           0 :     for(unsigned i=0; i<3; ++i) {
     433           0 :       cvout.derivs[0][i] = (1/S)*(tdy[i].getRow(0) - tdx[i].getRow(1)) - (cvout.values[0]/S)*dS[i];
     434             :     }
     435             : 
     436           0 :     cvout.values[1] = (x[2] + z[0]) / S;
     437           0 :     for(unsigned i=0; i<3; ++i) {
     438           0 :       cvout.derivs[1][i] = (1/S)*(tdx[i].getRow(2) + tdz[i].getRow(0)) - (cvout.values[1]/S)*dS[i];
     439             :     }
     440             : 
     441           0 :     cvout.values[2] = (y[2] + z[1]) / S;
     442           0 :     for(unsigned i=0; i<3; ++i) {
     443           0 :       cvout.derivs[2][i] = (1/S)*(tdy[i].getRow(2) + tdz[i].getRow(1)) - (cvout.values[2]/S)*dS[i];
     444             :     }
     445             : 
     446           0 :     cvout.values[3] = 0.25 * S;
     447           0 :     for(unsigned i=0; i<3; ++i) {
     448           0 :       cvout.derivs[3][i] =0.25*dS[i];
     449             :     }
     450             :   }
     451        7203 :   colvar::ColvarInput::setBoxDerivativesNoPbc( cvin, cvout );
     452             : 
     453        7203 : }
     454             : 
     455             : }
     456             : }
     457             : 
     458             : 
     459             : 

Generated by: LCOV version 1.16