1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2014-2021 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 "LinkCells.h"
23 #include "Communicator.h"
24 #include "Tools.h"
25 
26 namespace PLMD {
27 
LinkCells(Communicator & cc)28 LinkCells::LinkCells( Communicator& cc ) :
29   comm(cc),
30   cutoffwasset(false),
31   link_cutoff(0.0),
32   ncells(3),
33   nstride(3)
34 {
35 }
36 
setCutoff(const double & lcut)37 void LinkCells::setCutoff( const double& lcut ) {
38   cutoffwasset=true; link_cutoff=lcut;
39 }
40 
getCutoff() const41 double LinkCells::getCutoff() const {
42   plumed_assert( cutoffwasset ); return link_cutoff;
43 }
44 
buildCellLists(const std::vector<Vector> & pos,const std::vector<unsigned> & indices,const Pbc & pbc)45 void LinkCells::buildCellLists( const std::vector<Vector>& pos, const std::vector<unsigned>& indices, const Pbc& pbc ) {
46   plumed_assert( cutoffwasset && pos.size()==indices.size() );
47 
48   // Must be able to check that pbcs are not nonsensical in some way?? -- GAT
49 
50   // Setup the pbc object by copying it from action
51   mypbc.setBox( pbc.getBox() );
52 
53   // Setup the lists
54   if( pos.size()!=allcells.size() ) {
55     allcells.resize( pos.size() ); lcell_lists.resize( pos.size() );
56   }
57 
58   {
59 // This is the reciprocal lattice
60 // notice that reciprocal.getRow(0) is a vector that is orthogonal to b and c
61 // This allows to use linked cells in non orthorhomic boxes
62     Tensor reciprocal(transpose(mypbc.getInvBox()));
63     ncells[0] = std::floor( 1.0/ reciprocal.getRow(0).modulo() / link_cutoff );
64     if( ncells[0]==0 ) ncells[0]=1;
65     ncells[1] = std::floor( 1.0/ reciprocal.getRow(1).modulo() / link_cutoff );
66     if( ncells[1]==0 ) ncells[1]=1;
67     ncells[2] = std::floor( 1.0/ reciprocal.getRow(2).modulo() / link_cutoff );
68     if( ncells[2]==0 ) ncells[2]=1;
69   }
70   // Setup the strides
71   nstride[0]=1; nstride[1]=ncells[0]; nstride[2]=ncells[0]*ncells[1];
72 
73   // Setup the storage for link cells
74   unsigned ncellstot=ncells[0]*ncells[1]*ncells[2];
75   if( lcell_tots.size()!=ncellstot ) {
76     lcell_tots.resize( ncellstot ); lcell_starts.resize( ncellstot );
77   }
78   // Clear nlcells
79   for(unsigned i=0; i<ncellstot; ++i) lcell_tots[i]=0;
80   // Clear allcells
81   allcells.assign( allcells.size(), 0 );
82 
83   // Find out what cell everyone is in
84   unsigned rank=comm.Get_rank(), size=comm.Get_size();
85   for(unsigned i=rank; i<pos.size(); i+=size) {
86     allcells[i]=findCell( pos[i] );
87     lcell_tots[allcells[i]]++;
88   }
89   // And gather all this information on every node
90   comm.Sum( allcells ); comm.Sum( lcell_tots );
91 
92   // Now prepare the link cell lists
93   unsigned tot=0;
94   for(unsigned i=0; i<lcell_tots.size(); ++i) { lcell_starts[i]=tot; tot+=lcell_tots[i]; lcell_tots[i]=0; }
95   plumed_assert( tot==pos.size() );
96 
97   // And setup the link cells properly
98   for(unsigned j=0; j<pos.size(); ++j) {
99     unsigned myind = lcell_starts[ allcells[j] ] + lcell_tots[ allcells[j] ];
100     lcell_lists[ myind ] = indices[j];
101     lcell_tots[allcells[j]]++;
102   }
103 }
104 
105 #define LINKC_MIN(n) ((n<2)? 0 : -1)
106 #define LINKC_MAX(n) ((n<3)? 1 : 2)
107 #define LINKC_PBC(n,num) ((n<0)? num-1 : n%num )
108 
addRequiredCells(const std::array<unsigned,3> & celn,unsigned & ncells_required,std::vector<unsigned> & cells_required) const109 void LinkCells::addRequiredCells( const std::array<unsigned,3>& celn, unsigned& ncells_required,
110                                   std::vector<unsigned>& cells_required ) const {
111   unsigned nnew_cells=0;
112   for(int nx=LINKC_MIN(ncells[0]); nx<LINKC_MAX(ncells[0]); ++nx) {
113     int xval = celn[0] + nx;
114     xval=LINKC_PBC(xval,ncells[0])*nstride[0];
115     for(int ny=LINKC_MIN(ncells[1]); ny<LINKC_MAX(ncells[1]); ++ny) {
116       int yval = celn[1] + ny;
117       yval=LINKC_PBC(yval,ncells[1])*nstride[1];
118       for(int nz=LINKC_MIN(ncells[2]); nz<LINKC_MAX(ncells[2]); ++nz) {
119         int zval = celn[2] + nz;
120         zval=LINKC_PBC(zval,ncells[2])*nstride[2];
121 
122         unsigned mybox=xval+yval+zval; bool added=false;
123         for(unsigned k=0; k<ncells_required; ++k) {
124           if( mybox==cells_required[k] ) { added=true; break; }
125         }
126         if( !added ) { cells_required[ncells_required+nnew_cells]=mybox; nnew_cells++; }
127       }
128     }
129   }
130   ncells_required += nnew_cells;
131 }
132 
retrieveNeighboringAtoms(const Vector & pos,std::vector<unsigned> & cell_list,unsigned & natomsper,std::vector<unsigned> & atoms) const133 void LinkCells::retrieveNeighboringAtoms( const Vector& pos, std::vector<unsigned>& cell_list,
134     unsigned& natomsper, std::vector<unsigned>& atoms ) const {
135   if( cell_list.size()!=getNumberOfCells() ) cell_list.resize( getNumberOfCells() );
136   unsigned ncellt=0; addRequiredCells( findMyCell( pos ), ncellt, cell_list );
137   retrieveAtomsInCells( ncellt, cell_list, natomsper, atoms );
138 }
139 
retrieveAtomsInCells(const unsigned & ncells_required,const std::vector<unsigned> & cells_required,unsigned & natomsper,std::vector<unsigned> & atoms) const140 void LinkCells::retrieveAtomsInCells( const unsigned& ncells_required,
141                                       const std::vector<unsigned>& cells_required,
142                                       unsigned& natomsper, std::vector<unsigned>& atoms ) const {
143   plumed_assert( natomsper==1 || natomsper==2 );  // This is really a bug. If you are trying to reuse this ask GAT for help
144   for(unsigned i=0; i<ncells_required; ++i) {
145     unsigned mybox=cells_required[i];
146     for(unsigned k=0; k<lcell_tots[mybox]; ++k) {
147       unsigned myatom = lcell_lists[lcell_starts[mybox]+k];
148       if( myatom!=atoms[0] ) { // Ideally would provide an option to not do this
149         atoms[natomsper]=myatom;
150         natomsper++;
151       }
152     }
153   }
154 }
155 
findMyCell(const Vector & pos) const156 std::array<unsigned,3> LinkCells::findMyCell( const Vector& pos ) const {
157   Vector fpos=mypbc.realToScaled( pos );
158   std::array<unsigned,3> celn;
159   for(unsigned j=0; j<3; ++j) {
160     celn[j] = std::floor( ( Tools::pbc(fpos[j]) + 0.5 ) * ncells[j] );
161     plumed_assert( celn[j]>=0 && celn[j]<ncells[j] ); // Check that atom is in box
162   }
163   return celn;
164 }
165 
convertIndicesToIndex(const unsigned & nx,const unsigned & ny,const unsigned & nz) const166 unsigned LinkCells::convertIndicesToIndex( const unsigned& nx, const unsigned& ny, const unsigned& nz ) const {
167   return nx*nstride[0] + ny*nstride[1] + nz*nstride[2];
168 }
169 
findCell(const Vector & pos) const170 unsigned LinkCells::findCell( const Vector& pos ) const {
171   std::array<unsigned,3> celn( findMyCell(pos ) );
172   return convertIndicesToIndex( celn[0], celn[1], celn[2] );
173 }
174 
175 
176 }
177