double TopologyMatrix::compute( const unsigned& tindex, multicolvar::AtomValuePack& myatoms ) const { HistogramBead bead; bead.isNotPeriodic(); bead.setKernelType( kerneltype ); // Initialise to zero density on all bins for(unsigned bin=0; bin<maxbins; ++bin) myatoms.setValue(bin+1,0); // Calculate whether or not atoms 1 and 2 are within cutoff (can use delta here as pbc are done in atom setup) Vector d1 = getSeparation( myatoms.getPosition(0), myatoms.getPosition(1) ); double d1_len = d1.modulo(); d1 = d1 / d1_len; // Convert vector into director AtomNumber a1 = myatoms.getAbsoluteIndex( 0 ); AtomNumber a2 = myatoms.getAbsoluteIndex( 1 ); for(unsigned i=2; i<myatoms.getNumberOfAtoms(); ++i) { AtomNumber a3 = myatoms.getAbsoluteIndex( i ); if( a3!=a1 && a3!=a2 ) calculateForThreeAtoms( i, d1, d1_len, bead, myatoms ); } // std::vector<double> binvals( 1+maxbins ); for(unsigned i=1;i<maxbins;++i) binvals[i]=myatoms.getValue(i); // unsigned ii; double fdf; //std::cout<<"HELLO DENSITY "<<myatoms.getIndex(0)<<" "<<myatoms.getIndex(1)<<" "<<transformStoredValues( binvals, ii, fdf )<<std::endl; // Now find the element for which the density is maximal unsigned vout=2; double max=myatoms.getValue( 2 ); for(unsigned i=3; i<myatoms.getUnderlyingMultiValue().getNumberOfValues()-1; ++i) { if( myatoms.getValue(i)>max ) { max=myatoms.getValue(i); vout=i; } } // Calculate value and derivative of switching function between atoms 1 and 2 double dfuncl, sw = switchingFunction( getBaseColvarNumber( myatoms.getIndex(0) ), getBaseColvarNumber( myatoms.getIndex(1) ) ).calculate( d1_len, dfuncl ); // Transform the density double df, tsw = threshold_switch.calculate( max, df ); if( !doNotCalculateDerivatives() ) { // Factor of d1_len is required here because d1 is normalized d1 *= d1_len; addAtomDerivatives( 2+maxbins, 0, -dfuncl*d1, myatoms ); addAtomDerivatives( 2+maxbins, 1, dfuncl*d1, myatoms ); myatoms.addBoxDerivatives( 2+maxbins, (-dfuncl)*Tensor(d1,d1) ); // Update active atoms so that next bit works updateActiveAtoms( myatoms ); // Now finish caclulation of derivatives MultiValue& myvals=myatoms.getUnderlyingMultiValue(); for(unsigned jd=0; jd<myvals.getNumberActive(); ++jd) { unsigned ider=myvals.getActiveIndex(jd); myvals.addDerivative( 1, ider, sw*df*max*myvals.getDerivative( vout, ider ) + tsw*myvals.getDerivative( 2+maxbins, ider ) ); } } return sw*tsw; }
double OrientationSphere::compute( const unsigned& tindex, multicolvar::AtomValuePack& myatoms ) const { // Make sure derivatives for central atom are only calculated once VectorMultiColvar* vv = dynamic_cast<VectorMultiColvar*>( getBaseMultiColvar(0) ); vv->firstcall=true; double d2, sw, value=0, denom=0, dot, f_dot, dot_df, dfunc; unsigned ncomponents=getBaseMultiColvar(0)->getNumberOfQuantities(); unsigned nder=myatoms.getNumberOfDerivatives(); std::vector<double> catom_orient( ncomponents ), this_orient( ncomponents ), catom_der( ncomponents ); Vector catom_pos = myatoms.getPosition(0); getVectorForTask( myatoms.getIndex(0), true, catom_orient ); multicolvar::CatomPack atom0; MultiValue myder0(0,0), myder1(0,0); if( !doNotCalculateDerivatives() ){ myder0.resize( ncomponents,nder ); myder1.resize(ncomponents,nder); atom0=getCentralAtomPackFromInput( myatoms.getIndex(0) ); getVectorDerivatives( myatoms.getIndex(0), true, myder0 ); } for(unsigned i=1;i<myatoms.getNumberOfAtoms();++i){ Vector& distance=myatoms.getPosition(i); if ( (d2=distance[0]*distance[0])<rcut2 && (d2+=distance[1]*distance[1])<rcut2 && (d2+=distance[2]*distance[2])<rcut2) { sw = switchingFunction.calculateSqr( d2, dfunc ); getVectorForTask( myatoms.getIndex(i), true, this_orient ); // Calculate the dot product wrt to this position dot=0; for(unsigned k=2;k<catom_orient.size();++k) dot+=catom_orient[k]*this_orient[k]; f_dot = transformDotProduct( dot, dot_df ); if( !doNotCalculateDerivatives() ){ // N.B. We are assuming here that the imaginary part of the dot product is zero for(unsigned k=2;k<catom_orient.size();++k){ this_orient[k]*=sw*dot_df; catom_der[k]=sw*dot_df*catom_orient[k]; } getVectorDerivatives( myatoms.getIndex(i), true, myder1 ); mergeVectorDerivatives( 1, 2, this_orient.size(), myatoms.getIndex(0), this_orient, myder0, myatoms ); mergeVectorDerivatives( 1, 2, catom_der.size(), myatoms.getIndex(i), catom_der, myder1, myatoms ); myatoms.addComDerivatives( 1, f_dot*(-dfunc)*distance, atom0 ); addAtomDerivatives( 1, i, f_dot*(dfunc)*distance, myatoms ); myatoms.addBoxDerivatives( 1, (-dfunc)*f_dot*Tensor(distance,distance) ); myder1.clearAll(); myatoms.addComDerivatives( -1, (-dfunc)*distance, atom0 ); addAtomDerivatives( -1, i, (dfunc)*distance, myatoms ); myatoms.addTemporyBoxDerivatives( (-dfunc)*Tensor(distance,distance) ); } value += sw*f_dot; denom += sw; } } double rdenom, df2, pref=calculateCoordinationPrefactor( denom, df2 ); if( fabs(denom)>epsilon ){ rdenom = 1.0 / denom; } else { plumed_assert(fabs(value)<epsilon); rdenom=1.0; } // Now divide everything double rdenom2=rdenom*rdenom; updateActiveAtoms( myatoms ); MultiValue& myvals=myatoms.getUnderlyingMultiValue(); for(unsigned i=0;i<myvals.getNumberActive();++i){ unsigned ider=myvals.getActiveIndex(i); double dgd=myvals.getTemporyDerivative(ider); myvals.setDerivative( 1, ider, rdenom*(pref*myvals.getDerivative(1,ider)+value*df2*dgd) - (value*pref*dgd)*rdenom2 ); } return pref*rdenom*value; }
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 ); }