LCOV - code coverage report
Current view: top level - tools - Pbc.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 127 134 94.8 %
Date: 2026-03-30 13:16:06 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-2023 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 "Pbc.h"
      23             : #include "Tools.h"
      24             : #include "Exception.h"
      25             : #include "LatticeReduction.h"
      26             : #include <iostream>
      27             : #include "Random.h"
      28             : #include <cmath>
      29             : 
      30             : namespace PLMD {
      31             : 
      32      823074 : Pbc::Pbc():
      33    12346110 :   type(unset) {
      34      823074 :   box.zero();
      35      823074 :   invBox.zero();
      36      823074 : }
      37             : 
      38       20892 : void Pbc::buildShifts(std::vector<Vector> shifts[2][2][2])const {
      39             :   const double small=1e-28;
      40             : 
      41             : // clear all shifts
      42       62676 :   for(int i=0; i<2; i++)
      43      125352 :     for(int j=0; j<2; j++)
      44      250704 :       for(int k=0; k<2; k++) {
      45      167136 :         shifts[i][j][k].clear();
      46             :       }
      47             : 
      48             : // enumerate all possible shifts
      49             : // since box is reduced, only 27 shifts have to be attempted
      50       83568 :   for(int l=-1; l<=1; l++)
      51      250704 :     for(int m=-1; m<=1; m++)
      52      752112 :       for(int n=-1; n<=1; n++) {
      53             : 
      54             : // int/double shift vectors
      55      564084 :         const int ishift[3]= {l,m,n};
      56      564084 :         Vector dshift(l,m,n);
      57             : 
      58             : // count how many components are != 0
      59             :         unsigned count=0;
      60     2256336 :         for(int s=0; s<3; s++)
      61     1692252 :           if(ishift[s]!=0) {
      62     1128168 :             count++;
      63             :           }
      64             : 
      65             : // skips trivial (0,0,0) and cases with three shifts
      66             : // only 18 shifts survive past this point
      67      564084 :         if(count==0 || count==3) {
      68      279132 :           continue;
      69             :         }
      70             : 
      71             : // check if that Wigner-Seitz face is perpendicular to the axis.
      72             : // this allows to eliminate shifts in symmetric cells.
      73             : // e.g., if one lactice vector is orthogonal to the plane spanned
      74             : // by the other two vectors, that shift should never be tried
      75      376056 :         Vector cosdir=matmul(reduced,transpose(reduced),dshift);
      76      376056 :         double dp=dotProduct(dshift,cosdir);
      77      376056 :         double ref=modulo2(dshift)*modulo2(cosdir);
      78      376056 :         if(std::fabs(ref-dp*dp)<small) {
      79       91104 :           continue;
      80             :         }
      81             : 
      82             : // here we start pruning depending on the sign of the scaled coordinate
      83      854856 :         for(int i=0; i<2; i++)
      84     1709712 :           for(int j=0; j<2; j++)
      85     3419424 :             for(int k=0; k<2; k++) {
      86             : 
      87     2279616 :               const int block[3]= {2*i-1,2*j-1,2*k-1};
      88             : 
      89             : // skip cases where shift would bring too far from origin
      90             :               bool skip=false;
      91     9118464 :               for(int s=0; s<3; s++)
      92     6838848 :                 if(ishift[s]*block[s]>0) {
      93             :                   skip=true;
      94             :                 }
      95     2279616 :               if(skip) {
      96     1796478 :                 continue;
      97             :               }
      98             :               skip=true;
      99     3114896 :               for(int s=0; s<3; s++) {
     100             : // check that the components of cosdir along the non-shifted directions
     101             : // have the proper sign
     102     2336172 :                 if(((1-ishift[s]*ishift[s])*block[s])*cosdir[s]<-small) {
     103             :                   skip=false;
     104             :                 }
     105             :               }
     106      778724 :               if(skip) {
     107      295586 :                 continue;
     108             :               }
     109             : 
     110             : // if we arrive to this point, shift is eligible and is added to the list
     111      966276 :               shifts[i][j][k].push_back(matmul(transpose(reduced),dshift));
     112             :             }
     113             :       }
     114       20892 : }
     115             : 
     116      600000 : void Pbc::fullSearch(Vector&d)const {
     117      600000 :   if(type==unset) {
     118       71900 :     return;
     119             :   }
     120      528100 :   Vector s=matmul(invReduced.transpose(),d);
     121     2112400 :   for(int i=0; i<3; i++) {
     122     1584300 :     s[i]=Tools::pbc(s[i]);
     123             :   }
     124      528100 :   d=matmul(reduced.transpose(),s);
     125             :   const int smax=4;
     126      528100 :   Vector a0(reduced.getRow(0));
     127      528100 :   Vector a1(reduced.getRow(1));
     128      528100 :   Vector a2(reduced.getRow(2));
     129      528100 :   Vector best(d);
     130      528100 :   double lbest=d.modulo2();
     131     5281000 :   for(int i=-smax; i<=smax; i++)
     132    47529000 :     for(int j=-smax; j<=smax; j++)
     133   427761000 :       for(int k=-smax; k<=smax; k++) {
     134   384984900 :         Vector trial=d+i*a0+j*a1+k*a2;
     135   384984900 :         double ltrial=trial.modulo2();
     136   384984900 :         if(ltrial<lbest) {
     137       29897 :           best=trial;
     138             :           lbest=ltrial;
     139             :         }
     140             :       }
     141      528100 :   d=best;
     142             : }
     143             : 
     144       73620 : void Pbc::setBox(const Tensor&b) {
     145       73620 :   box=b;
     146             : // detect type:
     147             :   const double epsilon=1e-28;
     148             : 
     149       73620 :   type=unset;
     150       73620 :   double det=box.determinant();
     151       73620 :   if(det*det<epsilon) {
     152             :     return;
     153             :   }
     154             : 
     155             :   bool cxy=false;
     156             :   bool cxz=false;
     157             :   bool cyz=false;
     158       67538 :   if(box(0,1)*box(0,1)<epsilon && box(1,0)*box(1,0)<epsilon) {
     159             :     cxy=true;
     160             :   }
     161       67538 :   if(box(0,2)*box(0,2)<epsilon && box(2,0)*box(2,0)<epsilon) {
     162             :     cxz=true;
     163             :   }
     164       67538 :   if(box(1,2)*box(1,2)<epsilon && box(2,1)*box(2,1)<epsilon) {
     165             :     cyz=true;
     166             :   }
     167             : 
     168       67538 :   invBox=box.inverse();
     169             : 
     170       67538 :   if(cxy && cxz && cyz) {
     171       46646 :     type=orthorombic;
     172             :   } else {
     173       20892 :     type=generic;
     174             :   }
     175             : 
     176       67538 :   if(type==orthorombic) {
     177       46646 :     reduced=box;
     178       46646 :     invReduced=inverse(reduced);
     179      186584 :     for(unsigned i=0; i<3; i++) {
     180      139938 :       diag[i]=box[i][i];
     181      139938 :       hdiag[i]=0.5*box[i][i];
     182      139938 :       mdiag[i]=-0.5*box[i][i];
     183             :     }
     184             :   } else {
     185       20892 :     reduced=box;
     186       20892 :     LatticeReduction::reduce(reduced);
     187       20892 :     invReduced=inverse(reduced);
     188       20892 :     buildShifts(shifts);
     189             :   }
     190             : 
     191             : }
     192             : 
     193           0 : double Pbc::distance( const bool pbc, const Vector& v1, const Vector& v2 ) const {
     194           0 :   if(pbc) {
     195           0 :     return ( distance(v1,v2) ).modulo();
     196             :   } else {
     197           0 :     return ( delta(v1,v2) ).modulo();
     198             :   }
     199             : }
     200             : 
     201      222049 : void Pbc::apply(std::vector<Vector>& dlist, unsigned max_index) const {
     202      222049 :   if (max_index==0) {
     203           0 :     max_index=dlist.size();
     204             :   }
     205      222049 :   if(type==unset) {
     206             :     // do nothing
     207      222045 :   } else if(type==orthorombic) {
     208             : #ifdef __PLUMED_PBC_WHILE
     209             :     for(unsigned k=0; k<max_index; ++k) {
     210             :       while(dlist[k][0]>hdiag[0]) {
     211             :         dlist[k][0]-=diag[0];
     212             :       }
     213             :       while(dlist[k][0]<=mdiag[0]) {
     214             :         dlist[k][0]+=diag[0];
     215             :       }
     216             :       while(dlist[k][1]>hdiag[1]) {
     217             :         dlist[k][1]-=diag[1];
     218             :       }
     219             :       while(dlist[k][1]<=mdiag[1]) {
     220             :         dlist[k][1]+=diag[1];
     221             :       }
     222             :       while(dlist[k][2]>hdiag[2]) {
     223             :         dlist[k][2]-=diag[2];
     224             :       }
     225             :       while(dlist[k][2]<=mdiag[2]) {
     226             :         dlist[k][2]+=diag[2];
     227             :       }
     228             :     }
     229             : #else
     230   401914451 :     for(unsigned k=0; k<max_index; ++k)
     231  1606776864 :       for(int i=0; i<3; i++) {
     232  1205082648 :         dlist[k][i]=Tools::pbc(dlist[k][i]*invBox(i,i))*box(i,i);
     233             :       }
     234             : #endif
     235        1810 :   } else if(type==generic) {
     236       67768 :     for(unsigned k=0; k<max_index; ++k) {
     237      131916 :       dlist[k]=distance(Vector(0.0,0.0,0.0),dlist[k]);
     238             :     }
     239             :   } else {
     240           0 :     plumed_merror("unknown pbc type");
     241             :   }
     242      222049 : }
     243             : 
     244   220419594 : Vector Pbc::distance(const Vector&v1,const Vector&v2,int*nshifts)const {
     245   220419594 :   Vector d=delta(v1,v2);
     246   220419594 :   if(type==unset) {
     247             :     // do nothing
     248   194184801 :   } else if(type==orthorombic) {
     249             : #ifdef __PLUMED_PBC_WHILE
     250             :     for(unsigned i=0; i<3; i++) {
     251             :       while(d[i]>hdiag[i]) {
     252             :         d[i]-=diag[i];
     253             :       }
     254             :       while(d[i]<=mdiag[i]) {
     255             :         d[i]+=diag[i];
     256             :       }
     257             :     }
     258             : #else
     259   583383016 :     for(int i=0; i<3; i++) {
     260   437537262 :       d[i]=Tools::pbc(d[i]*invBox(i,i))*box(i,i);
     261             :     }
     262             : #endif
     263    48339047 :   } else if(type==generic) {
     264    48339047 :     Vector s=matmul(d,invReduced);
     265             : // check if images have to be computed:
     266             : //    if((std::fabs(s[0])+std::fabs(s[1])+std::fabs(s[2])>0.5)){
     267             : // NOTICE: the check in the previous line, albeit correct, is breaking many regtest
     268             : //         since it does not apply Tools::pbc in many cases. Moreover, it does not
     269             : //         introduce a significant gain. I thus leave it out for the moment.
     270             :     if(true) {
     271             : // bring to -0.5,+0.5 region in scaled coordinates:
     272   193356188 :       for(int i=0; i<3; i++) {
     273   145017141 :         s[i]=Tools::pbc(s[i]);
     274             :       }
     275    48339047 :       d=matmul(s,reduced);
     276             : // check if shifts have to be attempted:
     277    48339047 :       if((std::fabs(s[0])+std::fabs(s[1])+std::fabs(s[2])>0.5)) {
     278             : // list of shifts is specific for that "octant" (depends on signs of s[i]):
     279    78765012 :         const std::vector<Vector> & myshifts(shifts[(s[0]>0?1:0)][(s[1]>0?1:0)][(s[2]>0?1:0)]);
     280    39867229 :         Vector best(d);
     281    39867229 :         double lbest(modulo2(best));
     282             : // loop over possible shifts:
     283    39867229 :         if(nshifts) {
     284      109939 :           *nshifts+=myshifts.size();
     285             :         }
     286   179420109 :         for(unsigned i=0; i<myshifts.size(); i++) {
     287   139552880 :           Vector trial=d+myshifts[i];
     288   139552880 :           double ltrial=modulo2(trial);
     289   139552880 :           if(ltrial<lbest) {
     290             :             lbest=ltrial;
     291     3759325 :             best=trial;
     292             :           }
     293             :         }
     294    39867229 :         d=best;
     295             :       }
     296             :     }
     297             :   } else {
     298           0 :     plumed_merror("unknown pbc type");
     299             :   }
     300   220419594 :   return d;
     301             : }
     302             : 
     303     1047903 : Vector Pbc::realToScaled(const Vector&d)const {
     304     1047903 :   return matmul(invBox.transpose(),d);
     305             : }
     306             : 
     307      228786 : Vector Pbc::scaledToReal(const Vector&d)const {
     308      228786 :   return matmul(box.transpose(),d);
     309             : }
     310             : 
     311        8528 : bool Pbc::isOrthorombic()const {
     312        8528 :   return type==orthorombic;
     313             : }
     314             : 
     315       42338 : const Tensor& Pbc::getBox()const {
     316       42338 :   return box;
     317             : }
     318             : 
     319        2174 : const Tensor& Pbc::getInvBox()const {
     320        2174 :   return invBox;
     321             : }
     322             : 
     323             : }

Generated by: LCOV version 1.16