LCOV - code coverage report
Current view: top level - symfunc - SphericalHarmonic.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 123 134 91.8 %
Date: 2025-11-25 13:55:50 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2017 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 "function/FunctionTemplateBase.h"
      23             : #include "function/FunctionShortcut.h"
      24             : #include "function/FunctionOfMatrix.h"
      25             : #include "core/ActionRegister.h"
      26             : 
      27             : #include <complex>
      28             : 
      29             : namespace PLMD {
      30             : namespace symfunc {
      31             : 
      32             : //+PLUMEDOC MCOLVAR SPHERICAL_HARMONIC
      33             : /*
      34             : Calculate the values of all the spherical harmonic funtions for a particular value of l.
      35             : 
      36             : \par Examples
      37             : 
      38             : 
      39             : */
      40             : //+ENDPLUMEDOC
      41             : 
      42             : //+PLUMEDOC MCOLVAR SPHERICAL_HARMONIC_MATRIX
      43             : /*
      44             : Calculate the values of all the spherical harmonic funtions for a particular value of l for all the elements of a set of three input matrices
      45             : 
      46             : \par Examples
      47             : 
      48             : 
      49             : */
      50             : //+ENDPLUMEDOC
      51             : 
      52         299 : class SphericalHarmonic : public function::FunctionTemplateBase {
      53             : private:
      54             :   int tmom;
      55             :   std::vector<double> coeff_poly;
      56             :   std::vector<double> normaliz;
      57             :   unsigned factorial( const unsigned& n ) const ;
      58             :   double deriv_poly( const unsigned& m, const double& val, double& df ) const ;
      59             :   void addVectorDerivatives( const unsigned& ival, const Vector& der, Matrix<double>& derivatives ) const ;
      60             : public:
      61             :   void registerKeywords( Keywords& keys ) override;
      62             :   void read( ActionWithArguments* action ) override;
      63             :   std::vector<std::string> getComponentsPerLabel() const override;
      64             :   void setPeriodicityForOutputs( ActionWithValue* action ) override;
      65             :   void calc( const ActionWithArguments* action, const std::vector<double>& args, std::vector<double>& vals, Matrix<double>& derivatives ) const override;
      66             : };
      67             : 
      68             : typedef function::FunctionShortcut<SphericalHarmonic> SpHarmShortcut;
      69             : PLUMED_REGISTER_ACTION(SpHarmShortcut,"SPHERICAL_HARMONIC")
      70             : typedef function::FunctionOfMatrix<SphericalHarmonic> MatrixSpHarm;
      71             : PLUMED_REGISTER_ACTION(MatrixSpHarm,"SPHERICAL_HARMONIC_MATRIX")
      72             : 
      73         215 : void SphericalHarmonic::registerKeywords( Keywords& keys ) {
      74         430 :   keys.add("compulsory","L","the value of the angular momentum");
      75         430 :   keys.addOutputComponent("rm","default","the real parts of the spherical harmonic values with the m value given");
      76         430 :   keys.addOutputComponent("im","default","the real parts of the spherical harmonic values with the m value given");
      77         215 : }
      78             : 
      79        1858 : unsigned SphericalHarmonic::factorial( const unsigned& n ) const {
      80        1858 :   return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
      81             : }
      82             : 
      83          42 : void SphericalHarmonic::read( ActionWithArguments* action ) {
      84          42 :   parse(action,"L",tmom);
      85          42 :   action->log.printf("  calculating %dth order spherical harmonic with %s, %s and %s as input \n", tmom, action->getPntrToArgument(0)->getName().c_str(), action->getPntrToArgument(1)->getName().c_str(), action->getPntrToArgument(2)->getName().c_str() );
      86          42 :   if( action->getNumberOfArguments()==4 ) {
      87          42 :     action->log.printf("  multiplying cylindrical harmonic by weight from %s \n", action->getPntrToArgument(3)->getName().c_str() );
      88             :   }
      89             : 
      90          42 :   normaliz.resize( tmom+1 );
      91         232 :   for(unsigned i=0; i<=tmom; ++i) {
      92         190 :     normaliz[i] = sqrt( (2*tmom+1)*factorial(tmom-i)/(4*pi*factorial(tmom+i)) );
      93         190 :     if( i%2==1 ) {
      94          84 :       normaliz[i]*=-1;
      95             :     }
      96             :   }
      97             : 
      98          42 :   coeff_poly.resize( tmom+1 );
      99          42 :   if( tmom==1 ) {
     100             :     // Legendre polynomial coefficients of order one
     101          19 :     coeff_poly[0]=0;
     102          19 :     coeff_poly[1]=1.0;
     103             :   } else if( tmom==2 ) {
     104             :     // Legendre polynomial coefficients of order two
     105           0 :     coeff_poly[0]=-0.5;
     106           0 :     coeff_poly[1]=0.0;
     107           0 :     coeff_poly[2]=1.5;
     108             :   } else if( tmom==3 ) {
     109             :     // Legendre polynomial coefficients of order three
     110           1 :     coeff_poly[0]=0.0;
     111           1 :     coeff_poly[1]=-1.5;
     112           1 :     coeff_poly[2]=0.0;
     113           1 :     coeff_poly[3]=2.5;
     114             :   } else if( tmom==4 ) {
     115             :     // Legendre polynomial coefficients of order four
     116           3 :     coeff_poly[0]=0.375;
     117           3 :     coeff_poly[1]=0.0;
     118           3 :     coeff_poly[2]=-3.75;
     119           3 :     coeff_poly[3]=0.0;
     120           3 :     coeff_poly[4]=4.375;
     121             :   } else if( tmom==5 ) {
     122             :     // Legendre polynomial coefficients of order five
     123           0 :     coeff_poly[0]=0.0;
     124           0 :     coeff_poly[1]=1.875;
     125           0 :     coeff_poly[2]=0.0;
     126           0 :     coeff_poly[3]=-8.75;
     127           0 :     coeff_poly[4]=0.0;
     128           0 :     coeff_poly[5]=7.875;
     129             :   } else if( tmom==6 ) {
     130             :     // Legendre polynomial coefficients of order six
     131          19 :     coeff_poly[0]=-0.3125;
     132          19 :     coeff_poly[1]=0.0;
     133          19 :     coeff_poly[2]=6.5625;
     134          19 :     coeff_poly[3]=0.0;
     135          19 :     coeff_poly[4]=-19.6875;
     136          19 :     coeff_poly[5]=0.0;
     137          19 :     coeff_poly[6]=14.4375;
     138             :   } else {
     139           0 :     action->error("Insert Legendre polynomial coefficients into SphericalHarmonics code");
     140             :   }
     141          42 : }
     142             : 
     143          84 : std::vector<std::string> SphericalHarmonic::getComponentsPerLabel() const {
     144             :   std::vector<std::string> comp;
     145             :   std::string num;
     146         760 :   for(int i=-tmom; i<=tmom; ++i) {
     147         676 :     Tools::convert(fabs(i),num);
     148         676 :     if( i<0 ) {
     149         592 :       comp.push_back( "-n" + num );
     150         380 :     } else if( i>0 ) {
     151         592 :       comp.push_back( "-p" + num );
     152             :     } else {
     153         168 :       comp.push_back( "-0");
     154             :     }
     155             :   }
     156          84 :   return comp;
     157           0 : }
     158             : 
     159          42 : void SphericalHarmonic::setPeriodicityForOutputs( ActionWithValue* action ) {
     160          42 :   std::vector<std::string> comp( getComponentsPerLabel() );
     161         380 :   for(unsigned i=0; i<comp.size(); ++i) {
     162         676 :     action->componentIsNotPeriodic("rm" + comp[i]);
     163         676 :     action->componentIsNotPeriodic("im" + comp[i]);
     164             :   }
     165          42 : }
     166             : 
     167     1742755 : void SphericalHarmonic::calc( const ActionWithArguments* action, const std::vector<double>& args, std::vector<double>& vals, Matrix<double>& derivatives ) const {
     168             :   double weight=1;
     169     1742755 :   if( args.size()==4 ) {
     170     1742755 :     weight = args[3];
     171             :   }
     172     1742755 :   if( weight<epsilon ) {
     173     1468550 :     return;
     174             :   }
     175             : 
     176      274205 :   double dlen2 = args[0]*args[0]+args[1]*args[1]+args[2]*args[2];
     177      274205 :   double dlen = sqrt( dlen2 );
     178      274205 :   double dlen3 = dlen2*dlen;
     179      274205 :   double dpoly_ass, poly_ass=deriv_poly( 0, args[2]/dlen, dpoly_ass );
     180             :   // Derivatives of z/r wrt x, y, z
     181      274205 :   Vector dz;
     182      274205 :   dz[0] = -( args[2] / dlen3 )*args[0];
     183      274205 :   dz[1] = -( args[2] / dlen3 )*args[1];
     184      274205 :   dz[2] = -( args[2] / dlen3 )*args[2] + (1.0 / dlen);
     185             :   // Accumulate for m=0
     186      274205 :   vals[tmom] = weight*poly_ass;
     187      274205 :   addVectorDerivatives( tmom, weight*dpoly_ass*dz, derivatives );
     188      274205 :   if( args.size()==4 ) {
     189      274205 :     derivatives(tmom, 3) = poly_ass;
     190             :   }
     191             : 
     192             :   // The complex number of which we have to take powers
     193      274205 :   std::complex<double> com1( args[0]/dlen,args[1]/dlen ), dp_x, dp_y, dp_z;
     194             :   std::complex<double> powered = std::complex<double>(1.0,0.0);
     195             :   std::complex<double> ii( 0.0, 1.0 );
     196      274205 :   Vector myrealvec, myimagvec, real_dz, imag_dz;
     197             :   // Do stuff for all other m values
     198     1794050 :   for(unsigned m=1; m<=tmom; ++m) {
     199             :     // Calculate Legendre Polynomial
     200     1519845 :     poly_ass=deriv_poly( m, args[2]/dlen, dpoly_ass );
     201             :     // Real and imaginary parts of z
     202             :     double real_z = real(com1*powered), imag_z = imag(com1*powered);
     203             : 
     204             :     // Calculate steinhardt parameter
     205     1519845 :     double tq6=poly_ass*real_z;   // Real part of steinhardt parameter
     206     1519845 :     double itq6=poly_ass*imag_z;  // Imaginary part of steinhardt parameter
     207             : 
     208             :     // Derivatives wrt ( x/r + iy )^m
     209     1519845 :     double md=static_cast<double>(m);
     210     1519845 :     dp_x = md*powered*( (1.0/dlen)-(args[0]*args[0])/dlen3-ii*(args[0]*args[1])/dlen3 );
     211     1519845 :     dp_y = md*powered*( ii*(1.0/dlen)-(args[0]*args[1])/dlen3-ii*(args[1]*args[1])/dlen3 );
     212     1519845 :     dp_z = md*powered*( -(args[0]*args[2])/dlen3-ii*(args[1]*args[2])/dlen3 );
     213             : 
     214             :     // Derivatives of real and imaginary parts of above
     215     1519845 :     real_dz[0] = real( dp_x );
     216     1519845 :     real_dz[1] = real( dp_y );
     217     1519845 :     real_dz[2] = real( dp_z );
     218     1519845 :     imag_dz[0] = imag( dp_x );
     219     1519845 :     imag_dz[1] = imag( dp_y );
     220     1519845 :     imag_dz[2] = imag( dp_z );
     221             : 
     222             :     // Complete derivative of steinhardt parameter
     223     1519845 :     myrealvec = weight*dpoly_ass*real_z*dz + weight*poly_ass*real_dz;
     224     1519845 :     myimagvec = weight*dpoly_ass*imag_z*dz + weight*poly_ass*imag_dz;
     225             : 
     226             :     // Real part
     227     1519845 :     vals[tmom+m] = weight*tq6;
     228     1519845 :     addVectorDerivatives( tmom+m, myrealvec, derivatives );
     229             :     // Imaginary part
     230     1519845 :     vals[3*tmom+1+m] = weight*itq6;
     231     1519845 :     addVectorDerivatives( 3*tmom+1+m, myimagvec, derivatives );
     232             :     // Store -m part of vector
     233     1519845 :     double pref=pow(-1.0,m);
     234             :     // -m part of vector is just +m part multiplied by (-1.0)**m and multiplied by complex
     235             :     // conjugate of Legendre polynomial
     236             :     // Real part
     237     1519845 :     vals[tmom-m] = pref*weight*tq6;
     238     1519845 :     addVectorDerivatives( tmom-m, pref*myrealvec, derivatives );
     239             :     // Imaginary part
     240     1519845 :     vals[3*tmom+1-m] = -pref*weight*itq6;
     241     1519845 :     addVectorDerivatives( 3*tmom+1-m, -pref*myimagvec, derivatives );
     242     1519845 :     if( args.size()==4 ) {
     243     1519845 :       derivatives(tmom+m,3)=tq6;
     244     1519845 :       derivatives(3*tmom+1+m, 3)=itq6;
     245     1519845 :       derivatives(tmom-m,3)=pref*tq6;
     246     1519845 :       derivatives(3*tmom+1-m, 3)=-pref*itq6;
     247             :     }
     248             :     // Calculate next power of complex number
     249             :     powered *= com1;
     250             :   }
     251             : }
     252             : 
     253     1794050 : double SphericalHarmonic::deriv_poly( const unsigned& m, const double& val, double& df ) const {
     254             :   double fact=1.0;
     255     7004030 :   for(unsigned j=1; j<=m; ++j) {
     256     5209980 :     fact=fact*j;
     257             :   }
     258     1794050 :   double res=coeff_poly[m]*fact;
     259             : 
     260     1794050 :   double pow=1.0, xi=val, dxi=1.0;
     261     1794050 :   df=0.0;
     262     7004030 :   for(int i=m+1; i<=tmom; ++i) {
     263             :     fact=1.0;
     264    13759570 :     for(unsigned j=i-m+1; j<=i; ++j) {
     265     8549590 :       fact=fact*j;
     266             :     }
     267     5209980 :     res=res+coeff_poly[i]*fact*xi;
     268     5209980 :     df = df + pow*coeff_poly[i]*fact*dxi;
     269     5209980 :     xi=xi*val;
     270     5209980 :     dxi=dxi*val;
     271     5209980 :     pow+=1.0;
     272             :   }
     273     1794050 :   df = df*normaliz[m];
     274     1794050 :   return normaliz[m]*res;
     275             : }
     276             : 
     277     6353585 : void SphericalHarmonic::addVectorDerivatives( const unsigned& ival, const Vector& der, Matrix<double>& derivatives ) const {
     278    25414340 :   for(unsigned j=0; j<3; ++j) {
     279    19060755 :     derivatives(ival,j) = der[j];
     280             :   }
     281     6353585 : }
     282             : 
     283             : }
     284             : }
     285             : 

Generated by: LCOV version 1.16