LCOV - code coverage report
Current view: top level - function - Bessel.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 26 32 81.2 %
Date: 2026-11-05 10:19:04 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-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 "FunctionSetup.h"
      23             : #include "FunctionShortcut.h"
      24             : #include "FunctionOfScalar.h"
      25             : #include "FunctionOfVector.h"
      26             : #include "core/ActionRegister.h"
      27             : #include <array>
      28             : #include <cmath>
      29             : 
      30             : namespace PLMD {
      31             : namespace function {
      32             : 
      33             : //+PLUMEDOC FUNCTION BESSEL
      34             : /*
      35             : Calculate the value of a Bessel function.
      36             : 
      37             : This action can be used to evaluate [Bessel functions](https://en.wikipedia.org/wiki/Bessel_function).
      38             : This was action is needed because the modified Bessel function of the first kind of order 0 is needed in
      39             : order to calculate the [von-Misses distribution](https://en.wikipedia.org/wiki/Von_Mises_distribution)
      40             : that is used in the implementation of [KERNEL](KERNEL.md).  You can thus only use this function to evaluate the
      41             : modified Bessel function of the first kind of order 0.  Notice that the function that is computed is the
      42             : exponentially scaled one, $e^{-|x|} I_0(x)$ (for example, it is equal to 0.4658 at $x=1$, where $I_0(1)=1.2661$),
      43             : which is the quantity needed to normalize the von Mises kernels that are used in [KERNEL](KERNEL.md).
      44             : Derivatives are not implemented, so this action cannot be used in a bias.
      45             : You can evaluate the value of this Bessel function at $x=1$ by using the following input:
      46             : 
      47             : ```plumed
      48             : c: CONSTANT VALUE=1
      49             : b: BESSEL ARG=c ORDER=0
      50             : ```
      51             : 
      52             : Notice that you can also use a vector in the input for this action as illustrated below:
      53             : 
      54             : ```plumed
      55             : c: CONSTANT VALUES=1,1.5,2
      56             : b: BESSEL ARG=c ORDER=0
      57             : ```
      58             : 
      59             : The value output by BESSEL in this case is also a vector with three components.  These components give the values
      60             : of the Bessel function at 1.0, 1.5 and 2.0 respectively.
      61             : 
      62             : ## The MASK keyword
      63             : 
      64             : As this action only takes one argument in input the MASK keyword action works in the way that is described in the documentation for
      65             : [LESS_THAN](LESS_THAN.md).  This keyword is only used if the input argument is a vector.  You pass to it a vector that contains ones
      66             : and zeros and that has the same length as the vector that is passed in the input to ARG. If the $i$th element of the vector that was passed
      67             : in the input to MASK is one then the corresponding element of the output vector is evaluated.  If it is equal to zero then that element is not
      68             : evaluated.  Consequently in the example input shown below only the 1st and 4th element of the output vector are evaluated.
      69             : 
      70             : ```plumed
      71             : m: CONSTANT VALUES=1,0,0,1
      72             : c: CONSTANT VALUES=1,1.5,2,5
      73             : b: BESSEL ARG=c MASK=m ORDER=0
      74             : ```
      75             : 
      76             : */
      77             : //+ENDPLUMEDOC
      78             : 
      79             : class Bessel {
      80             :   static constexpr std::array<double,30> A{-4.41534164647933937950E-18,
      81             :            3.33079451882223809783E-17,
      82             :            -2.43127984654795469359E-16,
      83             :            1.71539128555513303061E-15,
      84             :            -1.16853328779934516808E-14,
      85             :            7.67618549860493561688E-14,
      86             :            -4.85644678311192946090E-13,
      87             :            2.95505266312963983461E-12,
      88             :            -1.72682629144155570723E-11,
      89             :            9.67580903537323691224E-11,
      90             :            -5.18979560163526290666E-10,
      91             :            2.65982372468238665035E-9,
      92             :            -1.30002500998624804212E-8,
      93             :            6.04699502254191894932E-8,
      94             :            -2.67079385394061173391E-7,
      95             :            1.11738753912010371815E-6,
      96             :            -4.41673835845875056359E-6,
      97             :            1.64484480707288970893E-5,
      98             :            -5.75419501008210370398E-5,
      99             :            1.88502885095841655729E-4,
     100             :            -5.76375574538582365885E-4,
     101             :            1.63947561694133579842E-3,
     102             :            -4.32430999505057594430E-3,
     103             :            1.05464603945949983183E-2,
     104             :            -2.37374148058994688156E-2,
     105             :            4.93052842396707084878E-2,
     106             :            -9.49010970480476444210E-2,
     107             :            1.71620901522208775349E-1,
     108             :            -3.04682672343198398683E-1,
     109             :            6.76795274409476084995E-1};
     110             :   static constexpr std::array<double,25>B {-7.23318048787475395456E-18,
     111             :            -4.83050448594418207126E-18,
     112             :            4.46562142029675999901E-17,
     113             :            3.46122286769746109310E-17,
     114             :            -2.82762398051658348494E-16,
     115             :            -3.42548561967721913462E-16,
     116             :            1.77256013305652638360E-15,
     117             :            3.81168066935262242075E-15,
     118             :            -9.55484669882830764870E-15,
     119             :            -4.15056934728722208663E-14,
     120             :            1.54008621752140982691E-14,
     121             :            3.85277838274214270114E-13,
     122             :            7.18012445138366623367E-13,
     123             :            -1.79417853150680611778E-12,
     124             :            -1.32158118404477131188E-11,
     125             :            -3.14991652796324136454E-11,
     126             :            1.18891471078464383424E-11,
     127             :            4.94060238822496958910E-10,
     128             :            3.39623202570838634515E-9,
     129             :            2.26666899049817806459E-8,
     130             :            2.04891858946906374183E-7,
     131             :            2.89137052083475648297E-6,
     132             :            6.88975834691682398426E-5,
     133             :            3.36911647825569408990E-3,
     134             :            8.04490411014108831608E-1};
     135             : public:
     136             :   unsigned order{0};
     137             :   static void registerKeywords( Keywords& keys );
     138             :   explicit Bessel();
     139             :   static void read( Bessel& func, ActionWithArguments* action, FunctionOptions& options );
     140             :   template <size_t N>
     141             :   static double chbevl(double x, const std::array<double,N>& array);
     142             :   static void calc( const Bessel& func, bool noderiv, View<const double> args, FunctionOutput& funcout );
     143             : };
     144             : 
     145             : typedef FunctionShortcut<Bessel> BesselShortcut;
     146             : PLUMED_REGISTER_ACTION(BesselShortcut,"BESSEL")
     147             : typedef FunctionOfScalar<Bessel> ScalarBessel;
     148             : PLUMED_REGISTER_ACTION(ScalarBessel,"BESSEL_SCALAR")
     149             : typedef FunctionOfVector<Bessel> VectorBessel;
     150             : PLUMED_REGISTER_ACTION(VectorBessel,"BESSEL_VECTOR")
     151             : 
     152          40 : void Bessel::registerKeywords(Keywords& keys) {
     153          40 :   keys.add("compulsory","ORDER","0","the order of Bessel function to use.  Can only be zero at the moment.");
     154          80 :   keys.setValueDescription("scalar/vector","the value of the exponentially scaled modified bessel function of the first kind, exp(-|x|) I_0(x)");
     155          40 : }
     156             : 
     157          14 : Bessel::Bessel() {
     158          14 : }
     159             : 
     160           7 : void Bessel::read( Bessel& func, ActionWithArguments* action, FunctionOptions& options ) {
     161           7 :   if( action->getNumberOfArguments()!=1 ) {
     162           0 :     ActionWithVector* av = dynamic_cast<ActionWithVector*>( action );
     163           0 :     if( !av || (av && action->getNumberOfArguments()-av->getNumberOfMasks()!=1) ) {
     164           0 :       action->error("should only be one argument to this action");
     165             :     }
     166             :   }
     167           7 :   if( action->getPntrToArgument(0)->isPeriodic() ) {
     168           0 :     action->error("cannot use this function on periodic functions");
     169             :   }
     170           7 :   action->parse("ORDER",func.order);
     171           7 :   action->log.printf("  computing %dth order bessel function \n", func.order );
     172           7 :   if( func.order!=0 ) {
     173           0 :     action->error("only zero order bessel function is implemented");
     174             :   }
     175           7 : }
     176             : 
     177             : template <size_t N>
     178          21 : double Bessel::chbevl(const double x, const std::array<double,N>& array) {
     179             :   double b0, b1, b2;
     180             : 
     181          21 :   b0 = array[0];
     182             :   b1 = 0.0;
     183             :   b2 = b1 ;
     184             : 
     185         585 :   for(unsigned index = 1 ; index < N ; index++) {
     186             :     b2 = b1;
     187             :     b1 = b0;
     188         564 :     b0 = x * b1 - b2 + array[index];
     189             :   }
     190          21 :   return (0.5 * (b0 - b2));
     191             : }
     192             : 
     193             : 
     194          21 : void Bessel::calc( const Bessel& func,
     195             :                    bool noderiv,
     196             :                    const View<const double> args,
     197             :                    FunctionOutput& funcout ) {
     198             : // NDEBUG seems to be ignored by nvcc...
     199             :   plumed_dbg_assert( args.size()==1 );
     200          21 :   if( !noderiv ) {
     201           0 :     plumed_merror("derivatives have not been implemented for this function");
     202             :   }
     203             :   //I trust the parser into avoiding geting here with order!=0
     204          21 :   double x = fabs(args[0]);
     205          21 :   if (x <= 8.0) {
     206          12 :     double y = (x / 2.0) - 2.0;
     207          12 :     funcout.values[0] =  chbevl(y, Bessel::A) ;
     208          12 :     return;
     209             :   }
     210           9 :   funcout.values[0] = chbevl(32.0 / x - 2.0, Bessel::B) / sqrt(x) ;
     211             :   ///TODO: higher order bessel functions
     212             : 
     213             : }
     214             : 
     215             : }
     216             : }
     217             : 
     218             : 

Generated by: LCOV version 1.16