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

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2014-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 "tools/HistogramBead.h"
      28             : #include "VolumeShortcut.h"
      29             : 
      30             : //+PLUMEDOC VOLUMES CAVITY
      31             : /*
      32             : This quantity can be used to calculate functions of the distribution of collective variables for the atoms that lie in a box defined by the positions of four atoms.
      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             : In this expression $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. The first of these unit vectors points from the first reference atom to the second.
      64             : The second is then the normal to the plane containing atoms 1,2 and 3 and the the third is the unit vector orthogonal to
      65             : these first two vectors.  $(x_o,y_o,z_o)$, meanwhile, specifies the position of the first reference atom.
      66             : 
      67             : In the first expression above $K$ is one of the kernel functions described in the documentation for the action [BETWEEN](BETWEEN.md)
      68             : and $\sigma$ is a bandwidth parameter.  Furthermore, The vector connecting atom 1 to atom 4 is used to define the extent of the box in
      69             : each of the $u$, $v$ and $w$ directions.  This vector connecting atom 1 to atom 4 is projected onto the three unit vectors
      70             : described above and the resulting projections determine the $u'$, $v'$ and $w'$ parameters in the above expression.
      71             : 
      72             : 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
      73             : in an ion channel in a protein.  The extent of the channel is calculated from the positions of atoms 1, 4, 5 and 11.
      74             : 
      75             : ```plumed
      76             : cav: CAVITY ...
      77             :   ATOMS=20-500 BOX=1,4,5,11
      78             :   SIGMA=0.1 KERNEL=gaussian
      79             : ...
      80             : s: SUM ARG=cav PERIODIC=NO
      81             : PRINT ARG=s FILE=colvar
      82             : ```
      83             : 
      84             : If you want to calculate the number of atoms that are not inthe protein chanel you can use the `OUTSIDE` flag as shown below:
      85             : 
      86             : ```plumed
      87             : cav: CAVITY ...
      88             :   ATOMS=20-500 BOX=1,4,5,11
      89             :   SIGMA=0.1 KERNEL=gaussian
      90             :   OUTSIDE
      91             : ...
      92             : s: SUM ARG=cav PERIODIC=NO
      93             : PRINT ARG=s FILE=colvar
      94             : ```
      95             : 
      96             : By contrast the following command tells plumed to calculate the coordination numbers (with other water molecules) for the water
      97             : molecules in the protein channel described above. The average coordination number and the number of coordination
      98             : numbers more than 4 is then calculated for those molecules that are in the region of interest.
      99             : 
     100             : ```plumed
     101             : # Calculate the atoms that are in the cavity
     102             : cav: CAVITY ATOMS=20-500 BOX=1,4,5,11 SIGMA=0.1
     103             : # Calculate the coordination numbers of all the atoms
     104             : d1: COORDINATIONNUMBER SPECIES=20-500 SWITCH={RATIONAL R_0=0.1}
     105             : # Do atoms have a coordination number that is greater than 4
     106             : fd1: MORE_THAN ARG=d1 SWITCH={RATIONAL R_0=4}
     107             : # Calculate the mean coordination number in the channel
     108             : nnn: CUSTOM ARG=cav,d1 FUNC=x*y PERIODIC=NO
     109             : numer: SUM ARG=nnn PERIODIC=NO
     110             : denom: SUM ARG=cav PERIODIC=NO
     111             : mean: CUSTOM ARG=numer,denom FUNC=x/y PERIODIC=NO
     112             : # Calculate the number of atoms that are in the channel and that
     113             : # have a coordination number that is greater than 4
     114             : sss: CUSTOM ARG=fd1,cav FUNC=x*y PERIODIC=NO
     115             : mt: SUM ARG=sss PERIODIC=NO
     116             : # And print these two quantities to a file
     117             : PRINT ARG=mean,mt FILE=colvar
     118             : ```
     119             : 
     120             : !!! note ""
     121             : 
     122             :     As with [AROUND](AROUND.md) earlier version of PLUMED used a different syntax for doing these types of calculations, which can
     123             :     still be used with this new version of the command.  We strongly recommend using the newer syntax but if you are interested in the
     124             :     old syntax you can find more information in the old syntax section of the documentation for [AROUND](AROUND.md).
     125             : 
     126             : */
     127             : //+ENDPLUMEDOC
     128             : 
     129             : namespace PLMD {
     130             : namespace volumes {
     131             : 
     132             : class VolumeCavity {
     133             : public:
     134             :   double jacob_det;
     135             :   double len_bi, len_cross, len_perp, sigma;
     136             :   Vector bi, cross, perp;
     137             :   HistogramBead::KernelType kerneltype{HistogramBead::KernelType::gaussian};
     138             :   std::vector<Vector> dlbi, dlcross, dlperp;
     139             :   std::vector<Tensor> dbi, dcross, dperp;
     140             :   static void registerKeywords( Keywords& keys );
     141           2 :   VolumeCavity() : jacob_det(0), len_bi(0), len_cross(0), len_perp(0), sigma(0), dlbi(4), dlcross(4), dlperp(4), dbi(3), dcross(3), dperp(3) {}
     142             :   void setupRegions( ActionVolume<VolumeCavity>* action, const Pbc& pbc, const std::vector<Vector>& positions );
     143             :   void parseInput( ActionVolume<VolumeCavity>* action );
     144             :   static void parseAtoms( ActionVolume<VolumeCavity>* action, std::vector<AtomNumber>& atoms );
     145           1 :   VolumeCavity& operator=( const VolumeCavity& m ) {
     146           1 :     jacob_det=m.jacob_det;
     147           1 :     len_bi=m.len_bi;
     148           1 :     len_cross=m.len_cross;
     149           1 :     len_perp=m.len_perp;
     150           1 :     sigma=m.sigma;
     151           1 :     dlbi.resize(4);
     152           1 :     dlcross.resize(4);
     153           1 :     dlperp.resize(4);
     154           1 :     dbi.resize(3);
     155           1 :     dcross.resize(3);
     156           1 :     dperp.resize(3);
     157           1 :     kerneltype=m.kerneltype;
     158           1 :     return *this;
     159             :   }
     160             :   static void calculateNumberInside( const VolumeInput& input, const VolumeCavity& actioninput, VolumeOutput& output );
     161             : };
     162             : 
     163             : typedef ActionVolume<VolumeCavity> VolCav;
     164             : PLUMED_REGISTER_ACTION(VolCav,"CAVITY_CALC")
     165             : char glob_cavity[] = "CAVITY";
     166             : typedef VolumeShortcut<glob_cavity> VolumeCavityShortcut;
     167             : PLUMED_REGISTER_ACTION(VolumeCavityShortcut,"CAVITY")
     168             : 
     169           7 : void VolumeCavity::registerKeywords( Keywords& keys ) {
     170          14 :   keys.setDisplayName("CAVITY");
     171           7 :   keys.add("compulsory","SIGMA","the width of the function to be used for kernel density estimation");
     172           7 :   keys.add("compulsory","KERNEL","gaussian","the type of kernel function to be used");
     173           7 :   keys.add("atoms","BOX","the positions of four atoms that define spatial extent of the cavity");
     174           7 : }
     175             : 
     176           1 : void VolumeCavity::parseInput( ActionVolume<VolumeCavity>* action ) {
     177           2 :   action->parse("SIGMA",sigma);
     178             :   std::string mykerneltype;
     179           1 :   action->parse("KERNEL",mykerneltype);
     180           1 :   kerneltype=HistogramBead::getKernelType(mykerneltype);
     181           1 :   action->log.printf("  using %s kernels with a bandwidth of %d \n", mykerneltype.c_str(), sigma );
     182           1 : }
     183             : 
     184           1 : void VolumeCavity::parseAtoms( ActionVolume<VolumeCavity>* action, std::vector<AtomNumber>& atoms ) {
     185           2 :   action->parseAtomList("BOX",atoms);
     186           1 :   if( atoms.size()!=4 ) {
     187           0 :     action->error("number of atoms in box should be equal to four");
     188             :   }
     189             : 
     190           1 :   action->log.printf("  boundaries for region are calculated based on positions of atoms : ");
     191           5 :   for(unsigned i=0; i<atoms.size(); ++i) {
     192           4 :     action->log.printf("%d ",atoms[i].serial() );
     193             :   }
     194           1 :   action->log.printf("\n");
     195           1 : }
     196             : 
     197        1560 : void VolumeCavity::setupRegions( ActionVolume<VolumeCavity>* action, const Pbc& pbc, const std::vector<Vector>& positions ) {
     198             :   // Make some space for things
     199             :   Vector d1, d2, d3;
     200             : 
     201             :   // Set the position of the origin
     202        1560 :   Vector origin=positions[0];
     203             : 
     204             :   // Get two vectors
     205        1560 :   d1 = pbc.distance(origin,positions[1]);
     206        1560 :   double d1l=d1.modulo();
     207        1560 :   d2 = pbc.distance(origin,positions[2]);
     208             : 
     209             :   // Find the vector connecting the origin to the top corner of
     210             :   // the subregion
     211        1560 :   d3 = pbc.distance(origin,positions[3]);
     212             : 
     213             :   // Create a set of unit vectors
     214        1560 :   bi = d1 / d1l;
     215        1560 :   len_bi=dotProduct( d3, bi );
     216        1560 :   cross = crossProduct( d1, d2 );
     217        1560 :   double crossmod=cross.modulo();
     218        1560 :   cross = cross / crossmod;
     219        1560 :   len_cross=dotProduct( d3, cross );
     220        1560 :   perp = crossProduct( cross, bi );
     221        1560 :   len_perp=dotProduct( d3, perp );
     222             : 
     223             :   // Calculate derivatives of box shape with respect to atoms
     224        1560 :   double d1l3=d1l*d1l*d1l;
     225        1560 :   dbi[0](0,0) = ( -(d1[1]*d1[1]+d1[2]*d1[2])/d1l3 );   // dx/dx
     226        1560 :   dbi[0](0,1) = (  d1[0]*d1[1]/d1l3 );                 // dx/dy
     227        1560 :   dbi[0](0,2) = (  d1[0]*d1[2]/d1l3 );                 // dx/dz
     228        1560 :   dbi[0](1,0) = (  d1[1]*d1[0]/d1l3 );                 // dy/dx
     229        1560 :   dbi[0](1,1) = ( -(d1[0]*d1[0]+d1[2]*d1[2])/d1l3 );   // dy/dy
     230        1560 :   dbi[0](1,2) = (  d1[1]*d1[2]/d1l3 );
     231        1560 :   dbi[0](2,0) = (  d1[2]*d1[0]/d1l3 );
     232        1560 :   dbi[0](2,1) = (  d1[2]*d1[1]/d1l3 );
     233        1560 :   dbi[0](2,2) = ( -(d1[1]*d1[1]+d1[0]*d1[0])/d1l3 );
     234             : 
     235        1560 :   dbi[1](0,0) = ( (d1[1]*d1[1]+d1[2]*d1[2])/d1l3 );
     236        1560 :   dbi[1](0,1) = ( -d1[0]*d1[1]/d1l3 );
     237        1560 :   dbi[1](0,2) = ( -d1[0]*d1[2]/d1l3 );
     238        1560 :   dbi[1](1,0) = ( -d1[1]*d1[0]/d1l3 );
     239        1560 :   dbi[1](1,1) = ( (d1[0]*d1[0]+d1[2]*d1[2])/d1l3 );
     240        1560 :   dbi[1](1,2) = ( -d1[1]*d1[2]/d1l3 );
     241        1560 :   dbi[1](2,0) = ( -d1[2]*d1[0]/d1l3 );
     242        1560 :   dbi[1](2,1) = ( -d1[2]*d1[1]/d1l3 );
     243        1560 :   dbi[1](2,2) = ( (d1[1]*d1[1]+d1[0]*d1[0])/d1l3 );
     244             :   dbi[2].zero();
     245             : 
     246             :   Tensor tcderiv;
     247        1560 :   double cmod3=crossmod*crossmod*crossmod;
     248             :   Vector ucross=crossmod*cross;
     249        3120 :   tcderiv.setCol( 0, crossProduct( d1, Vector(-1.0,0.0,0.0) ) + crossProduct( Vector(-1.0,0.0,0.0), d2 ) );
     250        3120 :   tcderiv.setCol( 1, crossProduct( d1, Vector(0.0,-1.0,0.0) ) + crossProduct( Vector(0.0,-1.0,0.0), d2 ) );
     251        1560 :   tcderiv.setCol( 2, crossProduct( d1, Vector(0.0,0.0,-1.0) ) + crossProduct( Vector(0.0,0.0,-1.0), d2 ) );
     252        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
     253        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
     254        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
     255        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
     256        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
     257        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
     258        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
     259        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
     260        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
     261             : 
     262        3120 :   tcderiv.setCol( 0, crossProduct( Vector(1.0,0.0,0.0), d2 ) );
     263        3120 :   tcderiv.setCol( 1, crossProduct( Vector(0.0,1.0,0.0), d2 ) );
     264        1560 :   tcderiv.setCol( 2, crossProduct( Vector(0.0,0.0,1.0), d2 ) );
     265        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
     266        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
     267        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
     268        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
     269        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
     270        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
     271        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
     272        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
     273        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
     274             : 
     275        3120 :   tcderiv.setCol( 0, crossProduct( d1, Vector(1.0,0.0,0.0) ) );
     276        3120 :   tcderiv.setCol( 1, crossProduct( d1, Vector(0.0,1.0,0.0) ) );
     277        1560 :   tcderiv.setCol( 2, crossProduct( d1, Vector(0.0,0.0,1.0) ) );
     278        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
     279        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
     280        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
     281        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
     282        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
     283        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
     284        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
     285        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
     286        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
     287             : 
     288        4680 :   dperp[0].setCol( 0, ( crossProduct( dcross[0].getCol(0), bi ) + crossProduct( cross, dbi[0].getCol(0) ) ) );
     289        4680 :   dperp[0].setCol( 1, ( crossProduct( dcross[0].getCol(1), bi ) + crossProduct( cross, dbi[0].getCol(1) ) ) );
     290        4680 :   dperp[0].setCol( 2, ( crossProduct( dcross[0].getCol(2), bi ) + crossProduct( cross, dbi[0].getCol(2) ) ) );
     291             : 
     292        4680 :   dperp[1].setCol( 0, ( crossProduct( dcross[1].getCol(0), bi ) + crossProduct( cross, dbi[1].getCol(0) ) ) );
     293        4680 :   dperp[1].setCol( 1, ( crossProduct( dcross[1].getCol(1), bi ) + crossProduct( cross, dbi[1].getCol(1) ) ) );
     294        4680 :   dperp[1].setCol( 2, ( crossProduct( dcross[1].getCol(2), bi ) + crossProduct( cross, dbi[1].getCol(2) ) ) );
     295             : 
     296        3120 :   dperp[2].setCol( 0, ( crossProduct( dcross[2].getCol(0), bi ) ) );
     297        3120 :   dperp[2].setCol( 1, ( crossProduct( dcross[2].getCol(1), bi ) ) );
     298        1560 :   dperp[2].setCol( 2, ( crossProduct( dcross[2].getCol(2), bi ) ) );
     299             : 
     300             :   // Ensure that all lengths are positive
     301        1560 :   if( len_bi<0 ) {
     302           0 :     bi=-bi;
     303           0 :     len_bi=-len_bi;
     304           0 :     for(unsigned i=0; i<3; ++i) {
     305           0 :       dbi[i]*=-1.0;
     306             :     }
     307             :   }
     308        1560 :   if( len_cross<0 ) {
     309           0 :     cross=-cross;
     310           0 :     len_cross=-len_cross;
     311           0 :     for(unsigned i=0; i<3; ++i) {
     312           0 :       dcross[i]*=-1.0;
     313             :     }
     314             :   }
     315        1560 :   if( len_perp<0 ) {
     316           0 :     perp=-perp;
     317           0 :     len_perp=-len_perp;
     318           0 :     for(unsigned i=0; i<3; ++i) {
     319           0 :       dperp[i]*=-1.0;
     320             :     }
     321             :   }
     322        1560 :   if( len_bi<=0 || len_cross<=0 || len_bi<=0 ) {
     323           0 :     plumed_merror("Invalid box coordinates");
     324             :   }
     325             : 
     326             :   // Now derivatives of lengths
     327             :   Tensor dd3( Tensor::identity() );
     328        1560 :   dlbi[0] = matmul(d3,dbi[0]) - matmul(bi,dd3);
     329        1560 :   dlbi[1] = matmul(d3,dbi[1]);
     330        1560 :   dlbi[2] = matmul(d3,dbi[2]);
     331        1560 :   dlbi[3] = matmul(bi,dd3);
     332             : 
     333        1560 :   dlcross[0] = matmul(d3,dcross[0]) - matmul(cross,dd3);
     334        1560 :   dlcross[1] = matmul(d3,dcross[1]);
     335        1560 :   dlcross[2] = matmul(d3,dcross[2]);
     336        1560 :   dlcross[3] = matmul(cross,dd3);
     337             : 
     338        1560 :   dlperp[0] = matmul(d3,dperp[0]) - matmul(perp,dd3);
     339        1560 :   dlperp[1] = matmul(d3,dperp[1]);
     340        1560 :   dlperp[2] = matmul(d3,dperp[2]);
     341        1560 :   dlperp[3] = matmul(perp,dd3);
     342             : 
     343             :   // Need to calculate the jacobian
     344             :   Tensor jacob;
     345        1560 :   jacob(0,0)=bi[0];
     346        1560 :   jacob(1,0)=bi[1];
     347        1560 :   jacob(2,0)=bi[2];
     348        1560 :   jacob(0,1)=cross[0];
     349        1560 :   jacob(1,1)=cross[1];
     350        1560 :   jacob(2,1)=cross[2];
     351        1560 :   jacob(0,2)=perp[0];
     352        1560 :   jacob(1,2)=perp[1];
     353        1560 :   jacob(2,2)=perp[2];
     354        1560 :   jacob_det = fabs( jacob.determinant() );
     355        1560 : }
     356             : 
     357        1560 : void VolumeCavity::calculateNumberInside( const VolumeInput& input, const VolumeCavity& actioninput, VolumeOutput& output ) {
     358             :   // Calculate distance of atom from origin of new coordinate frame
     359        1560 :   Vector datom=input.pbc.distance( Vector(input.refpos[0][0],input.refpos[0][1],input.refpos[0][2]),
     360        1560 :                                    Vector(input.cpos[0],input.cpos[1],input.cpos[2]) );
     361             :   double ucontr, uder, vcontr, vder, wcontr, wder;
     362             : 
     363             :   // Setup the histogram bead
     364        1560 :   HistogramBead bead( actioninput.kerneltype, 0, actioninput.len_bi, actioninput.sigma);
     365             :   // Calculate contribution from integral along bi
     366             :   double upos=dotProduct( datom, actioninput.bi );
     367        1560 :   ucontr=bead.calculate( upos, uder );
     368        1560 :   double udlen=bead.uboundDerivative( upos );
     369        1560 :   double uder2 = bead.lboundDerivative( upos ) - udlen;
     370             : 
     371             :   // Calculate contribution from integral along cross
     372        1560 :   bead.set( 0, actioninput.len_cross, actioninput.sigma );
     373             :   double vpos=dotProduct( datom, actioninput.cross );
     374        1560 :   vcontr=bead.calculate( vpos, vder );
     375        1560 :   double vdlen=bead.uboundDerivative( vpos );
     376        1560 :   double vder2 = bead.lboundDerivative( vpos ) - vdlen;
     377             : 
     378             :   // Calculate contribution from integral along perp
     379        1560 :   bead.set( 0, actioninput.len_perp, actioninput.sigma );
     380             :   double wpos=dotProduct( datom, actioninput.perp );
     381        1560 :   wcontr=bead.calculate( wpos, wder );
     382        1560 :   double wdlen=bead.uboundDerivative( wpos );
     383        1560 :   double wder2 = bead.lboundDerivative( wpos ) - wdlen;
     384             : 
     385             :   Vector dfd;
     386        1560 :   dfd[0]=uder*vcontr*wcontr;
     387        1560 :   dfd[1]=ucontr*vder*wcontr;
     388        1560 :   dfd[2]=ucontr*vcontr*wder;
     389        1560 :   output.derivatives[0] = (dfd[0]*actioninput.bi[0]+dfd[1]*actioninput.cross[0]+dfd[2]*actioninput.perp[0]);
     390        1560 :   output.derivatives[1] = (dfd[0]*actioninput.bi[1]+dfd[1]*actioninput.cross[1]+dfd[2]*actioninput.perp[1]);
     391        1560 :   output.derivatives[2] = (dfd[0]*actioninput.bi[2]+dfd[1]*actioninput.cross[2]+dfd[2]*actioninput.perp[2]);
     392        1560 :   output.values[0] = ucontr*vcontr*wcontr*actioninput.jacob_det;
     393             : 
     394             :   // Add reference atom derivatives
     395        1560 :   dfd[0]=uder2*vcontr*wcontr;
     396        1560 :   dfd[1]=ucontr*vder2*wcontr;
     397        1560 :   dfd[2]=ucontr*vcontr*wder2;
     398             :   Vector dfld;
     399        1560 :   dfld[0]=udlen*vcontr*wcontr;
     400        1560 :   dfld[1]=ucontr*vdlen*wcontr;
     401        1560 :   dfld[2]=ucontr*vcontr*wdlen;
     402        1560 :   output.refders[0] = dfd[0]*matmul(datom,actioninput.dbi[0]) + dfd[1]*matmul(datom,actioninput.dcross[0]) + dfd[2]*matmul(datom,actioninput.dperp[0]) +
     403        1560 :                       dfld[0]*actioninput.dlbi[0] + dfld[1]*actioninput.dlcross[0] + dfld[2]*actioninput.dlperp[0] - Vector(output.derivatives[0],output.derivatives[1],output.derivatives[2]);
     404        1560 :   output.refders[1] = dfd[0]*matmul(datom,actioninput.dbi[1]) + dfd[1]*matmul(datom,actioninput.dcross[1]) + dfd[2]*matmul(datom,actioninput.dperp[1]) +
     405             :                       dfld[0]*actioninput.dlbi[1] + dfld[1]*actioninput.dlcross[1] + dfld[2]*actioninput.dlperp[1];
     406        1560 :   output.refders[2] = dfd[0]*matmul(datom,actioninput.dbi[2]) + dfd[1]*matmul(datom,actioninput.dcross[2]) + dfd[2]*matmul(datom,actioninput.dperp[2]) +
     407             :                       dfld[0]*actioninput.dlbi[2] + dfld[1]*actioninput.dlcross[2] + dfld[2]*actioninput.dlperp[2];
     408             :   output.refders[3] = dfld[0]*actioninput.dlbi[3] + dfld[1]*actioninput.dlcross[3] + dfld[2]*actioninput.dlperp[3];
     409             : 
     410             :   Tensor vir;
     411        1560 :   vir=-Tensor( Vector(input.cpos[0],input.cpos[1],input.cpos[2]), Vector(output.derivatives[0],output.derivatives[1],output.derivatives[2]) );
     412        7800 :   for(unsigned i=0; i<4; ++i) {
     413       12480 :     vir -= Tensor( Vector(input.refpos[i][0],input.refpos[i][1],input.refpos[i][2]), Vector(output.refders[i][0],output.refders[i][1],output.refders[i][2]) );
     414             :   }
     415        1560 :   output.virial.set( 0, vir );
     416        1560 : }
     417             : 
     418             : }
     419             : }

Generated by: LCOV version 1.16