LCOV - code coverage report
Current view: top level - volumes - VolumeTetrapore.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 208 224 92.9 %
Date: 2025-12-04 11:19:34 Functions: 7 7 100.0 %

          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/ActionRegister.h"
      23             : #include "core/PlumedMain.h"
      24             : #include "tools/Units.h"
      25             : #include "tools/Pbc.h"
      26             : #include "ActionVolume.h"
      27             : #include "VolumeShortcut.h"
      28             : #include "tools/HistogramBead.h"
      29             : 
      30             : //+PLUMEDOC VOLUMES TETRAHEDRALPORE
      31             : /*
      32             : This quantity can be used to calculate functions of the distribution of collective variables for the atoms lie that lie in a box defined by the positions of four atoms at the corners of a tetrahedron.
      33             : 
      34             : This action can be used similarly to the way [AROUND](AROUND.md) is used.  Like [AROUND](AROUND.md) this action returns a vector
      35             : with elements that tell you whether an input atom is within a part of the box that is of particular interest or not. However, for this action
      36             : the elements of this vector are calculated using:
      37             : 
      38             : $$
      39             : w(u_i,v_i,w_i) = \int_{0}^{u'} \int_{0}^{v'} \int_{0}^{w'} \textrm{d}u\textrm{d}v\textrm{d}w
      40             :    K\left( \frac{u - u_i}{\sigma} \right)K\left( \frac{v - v_i}{\sigma} \right)K\left( \frac{w - w_i}{\sigma} \right)
      41             : $$
      42             : 
      43             : with $u_i,v_i,w_i$ being calculated from the positon of the $i$th atom, $(x_i,y_i,z_i)$, by performing the following transformation.
      44             : 
      45             : $$
      46             : \left(
      47             : \begin{matrix}
      48             :  u_i \\
      49             :  v_i \\
      50             :  w_i
      51             : \end{matrix}
      52             : \right) = R
      53             : \left(
      54             : \begin{matrix}
      55             :  x_i - x_o \\
      56             :  y_i - y_o \\
      57             :  z_i - z_o
      58             : \end{matrix}
      59             : \right)
      60             : $$
      61             : 
      62             : where $\mathbf{R}$ is a rotation matrix that is calculated by constructing a set of three orthonormal vectors from the
      63             : reference positions specified by the user.  Initially unit vectors are found by calculating the bisector, $\mathbf{b}$, and
      64             : cross product, $\mathbf{c}$, of the vectors connecting the first and second and first and third of the atoms that were specified
      65             : using the `BOX` keyword.  A third unit vector, $\mathbf{p}$ is then found by taking the cross
      66             : product between the cross product calculated during the first step, $\mathbf{c}$ and the bisector, $\mathbf{b}$.  From this
      67             : second cross product $\mathbf{p}$ and the bisector $\mathbf{b}$ two new vectors are calculated using:
      68             : 
      69             : $$
      70             : v_1 = \cos\left(\frac{\pi}{4}\right)\mathbf{b} + \sin\left(\frac{\pi}{4}\right)\mathbf{p} \qquad \textrm{and} \qquad
      71             : v_2 = \cos\left(\frac{\pi}{4}\right)\mathbf{b} - \sin\left(\frac{\pi}{4}\right)\mathbf{p}
      72             : $$
      73             : 
      74             : In the first expression above $K$ is one of the kernel functions described in the documentation for the action [BETWEEN](BETWEEN.md)
      75             : and $\sigma$ is a bandwidth parameter.  Furthermore, The vector connecting atom first and fourth atoms that were specified using the `BOX` keyword is used to define the extent of the box in
      76             : each of the $u$, $v$ and $w$ directions.  This vector connecting atom 1 to atom 4 is projected onto the three unit vectors
      77             : described above and the resulting projections determine the $u'$, $v'$ and $w'$ parameters in the above expression.
      78             : 
      79             : The following commands illustrate how this works in practise.  We are using PLUMED here to calculate the number of atoms from the group specified using the ATOMS keyword below are
      80             : in a tetrahedral pore.  The extent of the pore is calculated from the positions of atoms 1, 4, 5 and 11.
      81             : 
      82             : ```plumed
      83             : cav: TETRAHEDRALPORE ...
      84             :    ATOMS=20-500 BOX=1,4,5,11
      85             :    SIGMA=0.1 KERNEL=gaussian
      86             : ...
      87             : s: SUM ARG=cav PERIODIC=NO
      88             : PRINT ARG=s FILE=colvar
      89             : ```
      90             : 
      91             : If you want to calculate the number of atoms that are not in the pore you can use the `OUTSIDE` flag as shown below:
      92             : 
      93             : ```plumed
      94             : cav: TETRAHEDRALPORE ...
      95             :    ATOMS=20-500 BOX=1,4,5,11
      96             :    SIGMA=0.1 KERNEL=gaussian
      97             :    OUTSIDE
      98             : ...
      99             : s: SUM ARG=cav PERIODIC=NO
     100             : PRINT ARG=s FILE=colvar
     101             : ```
     102             : 
     103             : By contrst the following command tells plumed to calculate the coordination numbers for the atoms
     104             : in the pre described above. The average coordination number and the number of coordination
     105             : numbers more than 4 is then calculated for those molecules that are in the region of interest.
     106             : 
     107             : ```plumed
     108             : # Calculate the atoms that are in the pore
     109             : cav: TETRAHEDRALPORE ATOMS=20-500 BOX=1,4,5,11 SIGMA=0.1
     110             : # Calculate the coordination numbers of all the atoms
     111             : d1: COORDINATIONNUMBER SPECIES=20-500 SWITCH={RATIONAL R_0=0.1}
     112             : # Do atoms have a coordination number that is greater than 4
     113             : fd1: MORE_THAN ARG=d1 SWITCH={RATIONAL R_0=4}
     114             : # Calculate the mean coordination number in the pore
     115             : nnn: CUSTOM ARG=cav,d1 FUNC=x*y PERIODIC=NO
     116             : numer: SUM ARG=nnn PERIODIC=NO
     117             : denom: SUM ARG=cav PERIODIC=NO
     118             : mean: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     119             : # Calculate the number of atoms that are in the pore and that have a coordination number that is greater than 4
     120             : sss: CUSTOM ARG=fd1,cav FUNC=x*y PERIODIC=NO
     121             : mt: SUM ARG=sss PERIODIC=NO
     122             : # And print these two quantities to a file
     123             : PRINT ARG=mean,mt FILE=colvar
     124             : ```
     125             : 
     126             : !!! note ""
     127             : 
     128             :     As with [AROUND](AROUND.md) earlier version of PLUMED used a different syntax for doing these types of calculations, which can
     129             :     still be used with this new version of the command.  We strongly recommend using the newer syntax but if you are interested in the
     130             :     old syntax you can find more information in the old syntax section of the documentation for [AROUND](AROUND.md).
     131             : 
     132             : */
     133             : //+ENDPLUMEDOC
     134             : 
     135             : namespace PLMD {
     136             : namespace volumes {
     137             : 
     138             : class VolumeTetrapore {
     139             : public:
     140             :   double jacob_det{0.0};
     141             :   double len_bi{0.0}, len_cross{0.0}, len_perp{0.0}, sigma{0.0};
     142             :   Vector bi, cross, perp;
     143             :   HistogramBead::KernelType kerneltype{HistogramBead::KernelType::gaussian};
     144             :   std::vector<Vector> dlbi, dlcross, dlperp;
     145             :   std::vector<Tensor> dbi, dcross, dperp;
     146             :   static void registerKeywords( Keywords& keys );
     147           2 :   VolumeTetrapore() : dlbi(4), dlcross(4), dlperp(4), dbi(3), dcross(3), dperp(3) {}
     148             :   void setupRegions( ActionVolume<VolumeTetrapore>* action,
     149             :                      const Pbc& pbc,
     150             :                      const std::vector<Vector>& positions );
     151             :   void parseInput( ActionVolume<VolumeTetrapore>* action );
     152             :   static void parseAtoms( ActionVolume<VolumeTetrapore>* action,
     153             :                           std::vector<AtomNumber>& atoms );
     154           1 :   VolumeTetrapore& operator=( const VolumeTetrapore& m ) {
     155           1 :     jacob_det=m.jacob_det;
     156           1 :     len_bi=m.len_bi;
     157           1 :     len_cross=m.len_cross;
     158           1 :     len_perp=m.len_perp;
     159           1 :     sigma=m.sigma;
     160           1 :     dlbi.resize(4);
     161           1 :     dlcross.resize(4);
     162           1 :     dlperp.resize(4);
     163           1 :     dbi.resize(3);
     164           1 :     dcross.resize(3);
     165           1 :     dperp.resize(3);
     166           1 :     kerneltype=m.kerneltype;
     167           1 :     return *this;
     168             :   }
     169             :   static void calculateNumberInside( const VolumeInput& input, const VolumeTetrapore& actioninput, VolumeOutput& output );
     170             : };
     171             : 
     172           7 : void VolumeTetrapore::registerKeywords( Keywords& keys ) {
     173          14 :   keys.setDisplayName("TETRAHEDRALPORE");
     174           7 :   keys.add("compulsory","SIGMA","the width of the function to be used for kernel density estimation");
     175           7 :   keys.add("compulsory","KERNEL","gaussian","the type of kernel function to be used");
     176           7 :   keys.add("atoms","BOX","the positions of four atoms that define spatial extent of the cavity");
     177           7 : }
     178             : 
     179           1 : void VolumeTetrapore::parseInput( ActionVolume<VolumeTetrapore>* action ) {
     180           2 :   action->parse("SIGMA",sigma);
     181             :   std::string mykerneltype;
     182           1 :   action->parse("KERNEL",mykerneltype);
     183           1 :   kerneltype=HistogramBead::getKernelType(mykerneltype);
     184           1 : }
     185             : 
     186           1 : void VolumeTetrapore::parseAtoms( ActionVolume<VolumeTetrapore>* action, std::vector<AtomNumber>& atoms ) {
     187           2 :   action->parseAtomList("BOX",atoms);
     188           1 :   if( atoms.size()!=4 ) {
     189           0 :     action->error("number of atoms should be equal to four");
     190             :   }
     191             : 
     192           1 :   action->log.printf("  boundaries for region are calculated based on positions of atoms : ");
     193           5 :   for(unsigned i=0; i<atoms.size(); ++i) {
     194           4 :     action->log.printf("%d ",atoms[i].serial() );
     195             :   }
     196           1 :   action->log.printf("\n");
     197           1 : }
     198             : 
     199             : typedef ActionVolume<VolumeTetrapore> VolTet;
     200             : PLUMED_REGISTER_ACTION(VolTet,"TETRAHEDRALPORE_CALC")
     201             : char glob_tetrapore[] = "TETRAHEDRALPORE";
     202             : typedef VolumeShortcut<glob_tetrapore> VolumeTetraporeShortcut;
     203             : PLUMED_REGISTER_ACTION(VolumeTetraporeShortcut,"TETRAHEDRALPORE")
     204             : 
     205        1560 : void VolumeTetrapore::setupRegions( ActionVolume<VolumeTetrapore>* action, const Pbc& pbc, const std::vector<Vector>& positions ) {
     206             :   // Make some space for things
     207             :   Vector d1, d2, d3;
     208             : 
     209             :   // Set the position of the origin
     210        1560 :   Vector origin=positions[0];
     211             : 
     212             :   // Get two vectors
     213        1560 :   d1 = pbc.distance(origin,positions[1]);
     214        1560 :   d2 = pbc.distance(origin,positions[2]);
     215             : 
     216             :   // Find the vector connecting the origin to the top corner of
     217             :   // the subregion
     218        1560 :   d3 = pbc.distance(origin,positions[3]);
     219             : 
     220             :   // Create a set of unit vectors
     221             :   Vector bisector = d1 + d2;
     222        1560 :   double bmod=bisector.modulo();
     223        1560 :   bisector=bisector/bmod;
     224             : 
     225             :   // bi = d1 / d1l; len_bi=dotProduct( d3, bi );
     226        1560 :   cross = crossProduct( d1, d2 );
     227        1560 :   double crossmod=cross.modulo();
     228        1560 :   cross = cross / crossmod;
     229        1560 :   len_cross=dotProduct( d3, cross );
     230        1560 :   Vector truep = crossProduct( cross, bisector );
     231             : 
     232             :   // These are our true vectors 45 degrees from bisector
     233        1560 :   bi = cos(pi/4.0)*bisector + sin(pi/4.0)*truep;
     234        1560 :   perp = cos(pi/4.0)*bisector - sin(pi/4.0)*truep;
     235             : 
     236             :   // And the lengths of the various parts average distance to opposite corners of tetetrahedron
     237        1560 :   len_bi = dotProduct( d1, bi );
     238             :   double len_bi2 = dotProduct( d2, bi );
     239             :   unsigned lbi=1;
     240        1560 :   if( len_bi2>len_bi ) {
     241        1560 :     len_bi=len_bi2;
     242             :     lbi=2;
     243             :   }
     244        1560 :   len_perp = dotProduct( d1, perp );
     245             :   double len_perp2 = dotProduct( d2, perp );
     246             :   unsigned lpi=1;
     247        1560 :   if( len_perp2>len_perp ) {
     248           0 :     len_perp=len_perp2;
     249             :     lpi=2;
     250             :   }
     251        1560 :   plumed_assert( lbi!=lpi );
     252             : 
     253             :   Tensor tcderiv;
     254        1560 :   double cmod3=crossmod*crossmod*crossmod;
     255             :   Vector ucross=crossmod*cross;
     256        3120 :   tcderiv.setCol( 0, crossProduct( d1, Vector(-1.0,0.0,0.0) ) + crossProduct( Vector(-1.0,0.0,0.0), d2 ) );
     257        3120 :   tcderiv.setCol( 1, crossProduct( d1, Vector(0.0,-1.0,0.0) ) + crossProduct( Vector(0.0,-1.0,0.0), d2 ) );
     258        1560 :   tcderiv.setCol( 2, crossProduct( d1, Vector(0.0,0.0,-1.0) ) + crossProduct( Vector(0.0,0.0,-1.0), d2 ) );
     259        1560 :   dcross[0](0,0)=( tcderiv(0,0)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dx/dx
     260        1560 :   dcross[0](0,1)=( tcderiv(0,1)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dx/dy
     261        1560 :   dcross[0](0,2)=( tcderiv(0,2)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dx/dz
     262        1560 :   dcross[0](1,0)=( tcderiv(1,0)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dy/dx
     263        1560 :   dcross[0](1,1)=( tcderiv(1,1)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dy/dy
     264        1560 :   dcross[0](1,2)=( tcderiv(1,2)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dy/dz
     265        1560 :   dcross[0](2,0)=( tcderiv(2,0)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dz/dx
     266        1560 :   dcross[0](2,1)=( tcderiv(2,1)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dz/dy
     267        1560 :   dcross[0](2,2)=( tcderiv(2,2)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dz/dz
     268             : 
     269        3120 :   tcderiv.setCol( 0, crossProduct( Vector(1.0,0.0,0.0), d2 ) );
     270        3120 :   tcderiv.setCol( 1, crossProduct( Vector(0.0,1.0,0.0), d2 ) );
     271        1560 :   tcderiv.setCol( 2, crossProduct( Vector(0.0,0.0,1.0), d2 ) );
     272        1560 :   dcross[1](0,0)=( tcderiv(0,0)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dx/dx
     273        1560 :   dcross[1](0,1)=( tcderiv(0,1)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dx/dy
     274        1560 :   dcross[1](0,2)=( tcderiv(0,2)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dx/dz
     275        1560 :   dcross[1](1,0)=( tcderiv(1,0)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dy/dx
     276        1560 :   dcross[1](1,1)=( tcderiv(1,1)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dy/dy
     277        1560 :   dcross[1](1,2)=( tcderiv(1,2)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dy/dz
     278        1560 :   dcross[1](2,0)=( tcderiv(2,0)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dz/dx
     279        1560 :   dcross[1](2,1)=( tcderiv(2,1)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dz/dy
     280        1560 :   dcross[1](2,2)=( tcderiv(2,2)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dz/dz
     281             : 
     282        3120 :   tcderiv.setCol( 0, crossProduct( d1, Vector(1.0,0.0,0.0) ) );
     283        3120 :   tcderiv.setCol( 1, crossProduct( d1, Vector(0.0,1.0,0.0) ) );
     284        1560 :   tcderiv.setCol( 2, crossProduct( d1, Vector(0.0,0.0,1.0) ) );
     285        1560 :   dcross[2](0,0)=( tcderiv(0,0)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dx/dx
     286        1560 :   dcross[2](0,1)=( tcderiv(0,1)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dx/dy
     287        1560 :   dcross[2](0,2)=( tcderiv(0,2)/crossmod - ucross[0]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dx/dz
     288        1560 :   dcross[2](1,0)=( tcderiv(1,0)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dy/dx
     289        1560 :   dcross[2](1,1)=( tcderiv(1,1)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dy/dy
     290        1560 :   dcross[2](1,2)=( tcderiv(1,2)/crossmod - ucross[1]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dy/dz
     291        1560 :   dcross[2](2,0)=( tcderiv(2,0)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,0) + ucross[1]*tcderiv(1,0) + ucross[2]*tcderiv(2,0))/cmod3 );    // dz/dx
     292        1560 :   dcross[2](2,1)=( tcderiv(2,1)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,1) + ucross[1]*tcderiv(1,1) + ucross[2]*tcderiv(2,1))/cmod3 );    // dz/dy
     293        1560 :   dcross[2](2,2)=( tcderiv(2,2)/crossmod - ucross[2]*(ucross[0]*tcderiv(0,2) + ucross[1]*tcderiv(1,2) + ucross[2]*tcderiv(2,2))/cmod3 );    // dz/dz
     294             : 
     295        1560 :   std::vector<Tensor> dbisector(3);
     296        1560 :   double bmod3=bmod*bmod*bmod;
     297             :   Vector ubisector=bmod*bisector;
     298        1560 :   dbisector[0](0,0)= -2.0/bmod + 2*ubisector[0]*ubisector[0]/bmod3;
     299        1560 :   dbisector[0](0,1)= 2*ubisector[0]*ubisector[1]/bmod3;
     300        1560 :   dbisector[0](0,2)= 2*ubisector[0]*ubisector[2]/bmod3;
     301        1560 :   dbisector[0](1,0)= 2*ubisector[1]*ubisector[0]/bmod3;
     302        1560 :   dbisector[0](1,1)= -2.0/bmod + 2*ubisector[1]*ubisector[1]/bmod3;
     303        1560 :   dbisector[0](1,2)= 2*ubisector[1]*ubisector[2]/bmod3;
     304        1560 :   dbisector[0](2,0)= 2*ubisector[2]*ubisector[0]/bmod3;
     305        1560 :   dbisector[0](2,1)= 2*ubisector[2]*ubisector[1]/bmod3;
     306        1560 :   dbisector[0](2,2)= -2.0/bmod + 2*ubisector[2]*ubisector[2]/bmod3;
     307             : 
     308        1560 :   dbisector[1](0,0)= 1.0/bmod - ubisector[0]*ubisector[0]/bmod3;
     309        1560 :   dbisector[1](0,1)= -ubisector[0]*ubisector[1]/bmod3;
     310        1560 :   dbisector[1](0,2)= -ubisector[0]*ubisector[2]/bmod3;
     311        1560 :   dbisector[1](1,0)= -ubisector[1]*ubisector[0]/bmod3;
     312        1560 :   dbisector[1](1,1)= 1.0/bmod - ubisector[1]*ubisector[1]/bmod3;
     313        1560 :   dbisector[1](1,2)= -ubisector[1]*ubisector[2]/bmod3;
     314        1560 :   dbisector[1](2,0)= -ubisector[2]*ubisector[0]/bmod3;
     315        1560 :   dbisector[1](2,1)= -ubisector[2]*ubisector[1]/bmod3;
     316        1560 :   dbisector[1](2,2)=1.0/bmod - ubisector[2]*ubisector[2]/bmod3;
     317             : 
     318        1560 :   dbisector[2](0,0)=1.0/bmod - ubisector[0]*ubisector[0]/bmod3;
     319        1560 :   dbisector[2](0,1)= -ubisector[0]*ubisector[1]/bmod3;
     320        1560 :   dbisector[2](0,2)= -ubisector[0]*ubisector[2]/bmod3;
     321        1560 :   dbisector[2](1,0)= -ubisector[1]*ubisector[0]/bmod3;
     322        1560 :   dbisector[2](1,1)=1.0/bmod - ubisector[1]*ubisector[1]/bmod3;
     323        1560 :   dbisector[2](1,2)= -ubisector[1]*ubisector[2]/bmod3;
     324        1560 :   dbisector[2](2,0)= -ubisector[2]*ubisector[0]/bmod3;
     325        1560 :   dbisector[2](2,1)= -ubisector[2]*ubisector[1]/bmod3;
     326        1560 :   dbisector[2](2,2)=1.0/bmod - ubisector[2]*ubisector[2]/bmod3;
     327             : 
     328        1560 :   std::vector<Tensor> dtruep(3);
     329        4680 :   dtruep[0].setCol( 0, ( crossProduct( dcross[0].getCol(0), bisector ) + crossProduct( cross, dbisector[0].getCol(0) ) ) );
     330        4680 :   dtruep[0].setCol( 1, ( crossProduct( dcross[0].getCol(1), bisector ) + crossProduct( cross, dbisector[0].getCol(1) ) ) );
     331        4680 :   dtruep[0].setCol( 2, ( crossProduct( dcross[0].getCol(2), bisector ) + crossProduct( cross, dbisector[0].getCol(2) ) ) );
     332             : 
     333        4680 :   dtruep[1].setCol( 0, ( crossProduct( dcross[1].getCol(0), bisector ) + crossProduct( cross, dbisector[1].getCol(0) ) ) );
     334        4680 :   dtruep[1].setCol( 1, ( crossProduct( dcross[1].getCol(1), bisector ) + crossProduct( cross, dbisector[1].getCol(1) ) ) );
     335        4680 :   dtruep[1].setCol( 2, ( crossProduct( dcross[1].getCol(2), bisector ) + crossProduct( cross, dbisector[1].getCol(2) ) ) );
     336             : 
     337        4680 :   dtruep[2].setCol( 0, ( crossProduct( dcross[2].getCol(0), bisector ) + crossProduct( cross, dbisector[2].getCol(0) ) ) );
     338        4680 :   dtruep[2].setCol( 1, ( crossProduct( dcross[2].getCol(1), bisector ) + crossProduct( cross, dbisector[2].getCol(1) ) ) );
     339        3120 :   dtruep[2].setCol( 2, ( crossProduct( dcross[2].getCol(2), bisector ) + crossProduct( cross, dbisector[2].getCol(2) ) ) );
     340             : 
     341             :   // Now convert these to the derivatives of the true axis
     342        6240 :   for(unsigned i=0; i<3; ++i) {
     343        9360 :     dbi[i] = cos(pi/4.0)*dbisector[i] + sin(pi/4.0)*dtruep[i];
     344        4680 :     dperp[i] = cos(pi/4.0)*dbisector[i] - sin(pi/4.0)*dtruep[i];
     345             :   }
     346             : 
     347             :   // Ensure that all lengths are positive
     348        1560 :   if( len_bi<0 ) {
     349           0 :     bi=-bi;
     350           0 :     len_bi=-len_bi;
     351           0 :     for(unsigned i=0; i<3; ++i) {
     352           0 :       dbi[i]*=-1.0;
     353             :     }
     354             :   }
     355        1560 :   if( len_cross<0 ) {
     356           0 :     cross=-cross;
     357           0 :     len_cross=-len_cross;
     358           0 :     for(unsigned i=0; i<3; ++i) {
     359           0 :       dcross[i]*=-1.0;
     360             :     }
     361             :   }
     362        1560 :   if( len_perp<0 ) {
     363           0 :     perp=-perp;
     364           0 :     len_perp=-len_perp;
     365           0 :     for(unsigned i=0; i<3; ++i) {
     366           0 :       dperp[i]*=-1.0;
     367             :     }
     368             :   }
     369        1560 :   if( len_bi<=0 || len_cross<=0 || len_bi<=0 ) {
     370           0 :     plumed_merror("Invalid box coordinates");
     371             :   }
     372             : 
     373             :   // Now derivatives of lengths
     374             :   Tensor dd3( Tensor::identity() );
     375        1560 :   Vector ddb2=d1;
     376        1560 :   if( lbi==2 ) {
     377        1560 :     ddb2=d2;
     378             :   }
     379             :   dlbi[1].zero();
     380             :   dlbi[2].zero();
     381             :   dlbi[3].zero();
     382        1560 :   dlbi[0] = matmul(ddb2,dbi[0]) - matmul(bi,dd3);
     383        1560 :   dlbi[lbi] = matmul(ddb2,dbi[lbi]) + matmul(bi,dd3);  // Derivative wrt d1
     384             : 
     385        1560 :   dlcross[0] = matmul(d3,dcross[0]) - matmul(cross,dd3);
     386        1560 :   dlcross[1] = matmul(d3,dcross[1]);
     387        1560 :   dlcross[2] = matmul(d3,dcross[2]);
     388        1560 :   dlcross[3] = matmul(cross,dd3);
     389             : 
     390        1560 :   ddb2=d1;
     391        1560 :   if( lpi==2 ) {
     392           0 :     ddb2=d2;
     393             :   }
     394             :   dlperp[1].zero();
     395             :   dlperp[2].zero();
     396             :   dlperp[3].zero();
     397        1560 :   dlperp[0] = matmul(ddb2,dperp[0]) - matmul( perp, dd3 );
     398        1560 :   dlperp[lpi] = matmul(ddb2,dperp[lpi]) + matmul(perp, dd3);
     399             : 
     400             :   // Need to calculate the jacobian
     401             :   Tensor jacob;
     402        1560 :   jacob(0,0)=bi[0];
     403        1560 :   jacob(1,0)=bi[1];
     404        1560 :   jacob(2,0)=bi[2];
     405        1560 :   jacob(0,1)=cross[0];
     406        1560 :   jacob(1,1)=cross[1];
     407        1560 :   jacob(2,1)=cross[2];
     408        1560 :   jacob(0,2)=perp[0];
     409        1560 :   jacob(1,2)=perp[1];
     410        1560 :   jacob(2,2)=perp[2];
     411        1560 :   jacob_det = fabs( jacob.determinant() );
     412        1560 : }
     413             : 
     414        1560 : void VolumeTetrapore::calculateNumberInside( const VolumeInput& input,
     415             :     const VolumeTetrapore& actioninput,
     416             :     VolumeOutput& output ) {
     417             :   // Calculate distance of atom from origin of new coordinate frame
     418        1560 :   Vector datom=input.pbc.distance(
     419        1560 :                  Vector(input.refpos[0][0],input.refpos[0][1],input.refpos[0][2]),
     420        1560 :                  Vector(input.cpos[0],input.cpos[1],input.cpos[2]) );
     421             :   double ucontr, uder, vcontr, vder, wcontr, wder;
     422             : 
     423             :   // Calculate contribution from integral along bi
     424             :   // Setup the histogram bead
     425        1560 :   HistogramBead bead( actioninput.kerneltype,
     426        1560 :                       0, actioninput.len_bi, actioninput.sigma );
     427             :   double upos=dotProduct( datom, actioninput.bi );
     428        1560 :   ucontr=bead.calculate( upos, uder );
     429        1560 :   double udlen=bead.uboundDerivative( upos );
     430        1560 :   double uder2 = bead.lboundDerivative( upos ) - udlen;
     431             : 
     432             :   // Calculate contribution from integral along cross
     433        1560 :   bead.set( 0, actioninput.len_cross, actioninput.sigma );
     434             :   double vpos=dotProduct( datom, actioninput.cross );
     435        1560 :   vcontr=bead.calculate( vpos, vder );
     436        1560 :   double vdlen=bead.uboundDerivative( vpos );
     437        1560 :   double vder2 = bead.lboundDerivative( vpos ) - vdlen;
     438             : 
     439             :   // Calculate contribution from integral along perp
     440        1560 :   bead.set( 0, actioninput.len_perp, actioninput.sigma );
     441             :   double wpos=dotProduct( datom, actioninput.perp );
     442        1560 :   wcontr=bead.calculate( wpos, wder );
     443        1560 :   double wdlen=bead.uboundDerivative( wpos );
     444        1560 :   double wder2 = bead.lboundDerivative( wpos ) - wdlen;
     445             : 
     446             :   Vector dfd;
     447        1560 :   dfd[0]=uder*vcontr*wcontr;
     448        1560 :   dfd[1]=ucontr*vder*wcontr;
     449        1560 :   dfd[2]=ucontr*vcontr*wder;
     450        1560 :   output.derivatives[0] = (dfd[0]*actioninput.bi[0]+dfd[1]*actioninput.cross[0]+dfd[2]*actioninput.perp[0]);
     451        1560 :   output.derivatives[1] = (dfd[0]*actioninput.bi[1]+dfd[1]*actioninput.cross[1]+dfd[2]*actioninput.perp[1]);
     452        1560 :   output.derivatives[2] = (dfd[0]*actioninput.bi[2]+dfd[1]*actioninput.cross[2]+dfd[2]*actioninput.perp[2]);
     453        1560 :   output.values[0] = ucontr*vcontr*wcontr*actioninput.jacob_det;
     454             : 
     455             :   // Add reference atom derivatives
     456        1560 :   dfd[0]=uder2*vcontr*wcontr;
     457        1560 :   dfd[1]=ucontr*vder2*wcontr;
     458        1560 :   dfd[2]=ucontr*vcontr*wder2;
     459             :   Vector dfld;
     460        1560 :   dfld[0]=udlen*vcontr*wcontr;
     461        1560 :   dfld[1]=ucontr*vdlen*wcontr;
     462        1560 :   dfld[2]=ucontr*vcontr*wdlen;
     463        1560 :   output.refders[0] = dfd[0]*matmul(datom,actioninput.dbi[0])
     464        1560 :                       + dfd[1]*matmul(datom,actioninput.dcross[0])
     465        1560 :                       + dfd[2]*matmul(datom,actioninput.dperp[0])
     466             :                       + dfld[0]*actioninput.dlbi[0]
     467             :                       + dfld[1]*actioninput.dlcross[0]
     468             :                       + dfld[2]*actioninput.dlperp[0]
     469        1560 :                       - Vector(output.derivatives[0],output.derivatives[1],output.derivatives[2]);
     470        1560 :   output.refders[1] = dfd[0]*matmul(datom,actioninput.dbi[1])
     471        1560 :                       + dfd[1]*matmul(datom,actioninput.dcross[1])
     472        1560 :                       + dfd[2]*matmul(datom,actioninput.dperp[1])
     473             :                       + dfld[0]*actioninput.dlbi[1]
     474             :                       + dfld[1]*actioninput.dlcross[1]
     475             :                       + dfld[2]*actioninput.dlperp[1];
     476        1560 :   output.refders[2] = dfd[0]*matmul(datom,actioninput.dbi[2])
     477        1560 :                       + dfd[1]*matmul(datom,actioninput.dcross[2])
     478        1560 :                       + dfd[2]*matmul(datom,actioninput.dperp[2])
     479             :                       + dfld[0]*actioninput.dlbi[2]
     480             :                       + dfld[1]*actioninput.dlcross[2]
     481             :                       + dfld[2]*actioninput.dlperp[2];
     482             :   output.refders[3] = dfld[0]*actioninput.dlbi[3]
     483             :                       + dfld[1]*actioninput.dlcross[3]
     484             :                       + dfld[2]*actioninput.dlperp[3];
     485             : 
     486             :   Tensor vir;
     487        1560 :   vir=-Tensor( Vector(input.cpos[0],input.cpos[1],input.cpos[2]),
     488        1560 :                Vector(output.derivatives[0],output.derivatives[1],output.derivatives[2]) );
     489        7800 :   for(unsigned i=0; i<4; ++i) {
     490        6240 :     vir -= Tensor( Vector(input.refpos[i][0],input.refpos[i][1],input.refpos[i][2]),
     491       12480 :                    Vector(output.refders[i][0],output.refders[i][1],output.refders[i][2]) );
     492             :   }
     493        1560 :   output.virial.set( 0, vir );
     494        1560 : }
     495             : 
     496             : }
     497             : }

Generated by: LCOV version 1.16