void BondOrientation::calculateVector( multicolvar::AtomValuePack& myatoms ) const {
  Vector distance=getSeparation( myatoms.getPosition(0), myatoms.getPosition(1) );

  addAtomDerivatives( 2, 0, Vector(-1.0,0,0), myatoms );
  addAtomDerivatives( 2, 1, Vector(+1.0,0,0), myatoms );
  myatoms.addBoxDerivatives( 2, Tensor(distance,Vector(-1.0,0,0)) );
  myatoms.addValue( 2, distance[0] );

  addAtomDerivatives( 3, 0, Vector(0,-1.0,0), myatoms );
  addAtomDerivatives( 3, 1, Vector(0,+1.0,0), myatoms );
  myatoms.addBoxDerivatives( 3, Tensor(distance,Vector(0,-1.0,0)) );
  myatoms.addValue( 3, distance[1] );

  addAtomDerivatives( 4, 0, Vector(0,0,-1.0), myatoms );
  addAtomDerivatives( 4, 1, Vector(0,0,+1.0), myatoms );
  myatoms.addBoxDerivatives( 4, Tensor(distance,Vector(0,0,-1.0)) );
  myatoms.addValue( 4, distance[2] );
}
Exemple #2
0
void Steinhardt::calculateVector( multicolvar::AtomValuePack& myatoms ) const {
  double dfunc, dpoly_ass, md, tq6, itq6, real_z, imag_z; 
  Vector dz, myrealvec, myimagvec, real_dz, imag_dz;
  // The square root of -1
  std::complex<double> ii( 0.0, 1.0 ), dp_x, dp_y, dp_z;

  unsigned ncomp=2*tmom+1;
  double sw, poly_ass, d2, dlen, nbond=0.0; std::complex<double> powered;
  for(unsigned i=1;i<myatoms.getNumberOfAtoms();++i){
      Vector& distance=myatoms.getPosition(i);  // getSeparation( myatoms.getPosition(0), myatoms.getPosition(i) );
      if ( (d2=distance[0]*distance[0])<rcut2 &&
           (d2+=distance[1]*distance[1])<rcut2 &&
           (d2+=distance[2]*distance[2])<rcut2) {
         dlen = sqrt(d2);
         sw = switchingFunction.calculate( dlen, dfunc ); 
   
         nbond += sw;  // Accumulate total number of bonds
         double dlen3 = d2*dlen;

         // Store derivatives of weight
         addAtomDerivatives( -1, 0, (-dfunc)*distance, myatoms );
         addAtomDerivatives( -1, i, (+dfunc)*distance, myatoms );
         myatoms.addTemporyBoxDerivatives( (-dfunc)*Tensor( distance,distance ) ); 

         // Do stuff for m=0
         poly_ass=deriv_poly( 0, distance[2]/dlen, dpoly_ass );
         // Derivatives of z/r wrt x, y, z
         dz = -( distance[2] / dlen3 )*distance; dz[2] += (1.0 / dlen);
         // Derivative wrt to the vector connecting the two atoms
         myrealvec = (+sw)*dpoly_ass*dz + poly_ass*(+dfunc)*distance;
         // Accumulate the derivatives
         addAtomDerivatives( 2 + tmom, 0, -myrealvec, myatoms );      
         addAtomDerivatives( 2 + tmom, i, myrealvec, myatoms); 
         myatoms.addBoxDerivatives( 2 + tmom, Tensor( -myrealvec,distance ) );
         // And store the vector function
         myatoms.addValue( 2 + tmom, sw*poly_ass );

         // The complex number of which we have to take powers
         std::complex<double> com1( distance[0]/dlen ,distance[1]/dlen );

         // Do stuff for all other m values
         for(unsigned m=1;m<=tmom;++m){
             // Calculate Legendre Polynomial
             poly_ass=deriv_poly( m, distance[2]/dlen, dpoly_ass );
             // Calculate powe of complex number
             powered=pow(com1,m-1); md=static_cast<double>(m);
             // Real and imaginary parts of z
             real_z = real(com1*powered); imag_z = imag(com1*powered );
 
             // Calculate steinhardt parameter
             tq6=poly_ass*real_z;   // Real part of steinhardt parameter
             itq6=poly_ass*imag_z;  // Imaginary part of steinhardt parameter

             // Derivatives wrt ( x/r + iy )^m
             dp_x = md*powered*( (1.0/dlen)-(distance[0]*distance[0])/dlen3-ii*(distance[0]*distance[1])/dlen3 );
             dp_y = md*powered*( ii*(1.0/dlen)-(distance[0]*distance[1])/dlen3-ii*(distance[1]*distance[1])/dlen3 );
             dp_z = md*powered*( -(distance[0]*distance[2])/dlen3-ii*(distance[1]*distance[2])/dlen3 );

             // Derivatives of real and imaginary parts of above
             real_dz[0] = real( dp_x ); real_dz[1] = real( dp_y ); real_dz[2] = real( dp_z );
             imag_dz[0] = imag( dp_x ); imag_dz[1] = imag( dp_y ); imag_dz[2] = imag( dp_z );  

             // Complete derivative of steinhardt parameter
             myrealvec = (+sw)*dpoly_ass*real_z*dz + (+dfunc)*distance*tq6 + (+sw)*poly_ass*real_dz; 
             myimagvec = (+sw)*dpoly_ass*imag_z*dz + (+dfunc)*distance*itq6 + (+sw)*poly_ass*imag_dz;

             // Real part
             myatoms.addValue( 2+tmom+m, sw*tq6 );
             addAtomDerivatives( 2+tmom+m, 0, -myrealvec, myatoms );
             addAtomDerivatives( 2+tmom+m, i, myrealvec, myatoms );
             myatoms.addBoxDerivatives( 2+tmom+m, Tensor( -myrealvec,distance ) );
             // Imaginary part 
             myatoms.addValue( 2+ncomp+tmom+m, sw*itq6 );
             addAtomDerivatives( 2+ncomp+tmom+m, 0, -myimagvec, myatoms );
             addAtomDerivatives( 2+ncomp+tmom+m, i, myimagvec, myatoms );
             myatoms.addBoxDerivatives( 2+ncomp+tmom+m, Tensor( -myimagvec,distance ) );
             // Store -m part of vector
             double pref=pow(-1.0,m); 
             // -m part of vector is just +m part multiplied by (-1.0)**m and multiplied by complex
             // conjugate of Legendre polynomial
             // Real part
             myatoms.addValue( 2+tmom-m, pref*sw*tq6 );
             addAtomDerivatives( 2+tmom-m, 0, -pref*myrealvec, myatoms );
             addAtomDerivatives( 2+tmom-m, i, pref*myrealvec, myatoms );
             myatoms.addBoxDerivatives( 2+tmom-m, pref*Tensor( -myrealvec,distance ) );
             // Imaginary part
             myatoms.addValue( 2+ncomp+tmom-m, -pref*sw*itq6 );
             addAtomDerivatives( 2+ncomp+tmom-m, 0, pref*myimagvec, myatoms );
             addAtomDerivatives( 2+ncomp+tmom-m, i, -pref*myimagvec, myatoms );
             myatoms.addBoxDerivatives( 2+ncomp+tmom-m, pref*Tensor( myimagvec,distance ) );
         }
     }
  } 

  // Normalize 
  updateActiveAtoms( myatoms );
  for(unsigned i=0;i<getNumberOfComponentsInVector();++i) myatoms.getUnderlyingMultiValue().quotientRule( 2+i, nbond, 2+i ); 
}
void TopologyMatrix::calculateForThreeAtoms( const unsigned& iat, const Vector& d1, const double& d1_len,
    HistogramBead& bead, multicolvar::AtomValuePack& myatoms ) const {
  // Calculate if there are atoms in the cylinder (can use delta here as pbc are done in atom setup)
  Vector d2 = getSeparation( myatoms.getPosition(0), myatoms.getPosition(iat) );
  // Now calculate projection of d2 on d1
  double proj=dotProduct(d2,d1);
  // This tells us if we are outside the end of the cylinder
  double excess = proj - d1_len;
  // Return if we are outside of the cylinder as calculated based on excess
  if( excess>low_sf( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).get_dmax() ) return;
  // Find the length of the cylinder
  double binw = binw_mat( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) );
  double lcylinder = (std::floor( d1_len / binw ) + 1)*binw;
  // Return if the projection is outside the length of interest
  if( proj<-bead.getCutoff() || proj>(lcylinder+bead.getCutoff()) ) return;

  // Calculate the excess swiching function
  double edf, eval = low_sf( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).calculate( excess, edf );
  // Calculate the projection on the perpendicular distance from the center of the tube
  double cm = d2.modulo2() - proj*proj;

  // Now calculate the density in the cylinder
  if( cm<cylinder_sw( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).get_dmax2() ) {
    double dfuncr, val = cylinder_sw( getBaseColvarNumber( myatoms.getIndex(0) ),
                                      getBaseColvarNumber( myatoms.getIndex(1) ) ).calculateSqr( cm, dfuncr );
    double cellv = cell_volume( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) );
    Vector dc1, dc2, dc3, dd1, dd2, dd3, de1, de2, de3;
    if( !doNotCalculateDerivatives() ) {
      Tensor d1_a1;
      // Derivative of director connecting atom1 - atom2 wrt the position of atom 1
      d1_a1(0,0) = ( -(d1[1]*d1[1]+d1[2]*d1[2])/d1_len );   // dx/dx
      d1_a1(0,1) = (  d1[0]*d1[1]/d1_len );                 // dx/dy
      d1_a1(0,2) = (  d1[0]*d1[2]/d1_len );                 // dx/dz
      d1_a1(1,0) = (  d1[1]*d1[0]/d1_len );                 // dy/dx
      d1_a1(1,1) = ( -(d1[0]*d1[0]+d1[2]*d1[2])/d1_len );   // dy/dy
      d1_a1(1,2) = (  d1[1]*d1[2]/d1_len );
      d1_a1(2,0) = (  d1[2]*d1[0]/d1_len );
      d1_a1(2,1) = (  d1[2]*d1[1]/d1_len );
      d1_a1(2,2) = ( -(d1[1]*d1[1]+d1[0]*d1[0])/d1_len );

      // Calculate derivatives of dot product
      dd1 = matmul(d2, d1_a1) - d1;
      dd2 = matmul(d2, -d1_a1);
      dd3 = d1;

      // Calculate derivatives of cross product
      dc1 = dfuncr*( -d2 - proj*dd1 );
      dc2 = dfuncr*( -proj*dd2 );
      dc3 = dfuncr*( d2 - proj*dd3 );

      // Calculate derivatives of excess
      de1 = edf*excess*( dd1 + d1 );
      de2 = edf*excess*( dd2 - d1 );
      de3 = edf*excess*dd3;
    }

    Vector pos1 = myatoms.getPosition(0) + d1_len*d1;
    Vector pos2 = myatoms.getPosition(0) + d2;
    Vector g1derivf,g2derivf,lderivf; Tensor vir;
    for(unsigned bin=0; bin<maxbins; ++bin) {
      bead.set( bin*binw, (bin+1)*binw, sigma );
      if( proj<(bin*binw-bead.getCutoff()) || proj>binw*(bin+1)+bead.getCutoff() ) continue;
      double der, contr=bead.calculateWithCutoff( proj, der ) / cellv; der /= cellv;
      myatoms.addValue( 2+bin, contr*val*eval );

      if( !doNotCalculateDerivatives() ) {
        g1derivf=contr*eval*dc1 + val*eval*der*dd1 + contr*val*de1;
        addAtomDerivatives( 2+bin, 0, g1derivf, myatoms );
        g2derivf=contr*eval*dc2 + val*eval*der*dd2 + contr*val*de2;
        addAtomDerivatives( 2+bin, 1, g2derivf, myatoms );
        lderivf=contr*eval*dc3 + val*eval*der*dd3 + contr*val*de3;
        addAtomDerivatives( 2+bin, iat, lderivf, myatoms );
        // Virial
        vir = -Tensor( myatoms.getPosition(0), g1derivf ) - Tensor( pos1, g2derivf ) - Tensor( pos2, lderivf );
        myatoms.addBoxDerivatives( 2+bin, vir );
      }
    }
  }
}