All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Pbc.cpp
Go to the documentation of this file.
1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2  Copyright (c) 2013 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-code.org for more information.
6 
7  This file is part of plumed, version 2.0.
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 
33  type(unset)
34 {
35  box.zero();
36  invBox.zero();
37 }
38 
39 void Pbc::buildShifts(std::vector<Vector> shifts[2][2][2])const{
40  const double small=1e-28;
41 
42 // clear all shifts
43  for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) shifts[i][j][k].clear();
44 
45 // enumerate all possible shifts
46 // since box is reduced, only 27 shifts have to be attempted
47  for(int l=-1;l<=1;l++) for(int m=-1;m<=1;m++) for(int n=-1;n<=1;n++){
48 
49 // int/double shift vectors
50  int ishift[3]={l,m,n};
51  Vector dshift(l,m,n);
52 
53 // count how many components are != 0
54  unsigned count=0;
55  for(int s=0;s<3;s++) if(ishift[s]!=0) count++;
56 
57 // skips trivial (0,0,0) and cases with three shifts
58 // only 18 shifts survive past this point
59  if(count==0 || count==3) continue;
60 
61 // check if that Wigner-Seitz face is perpendicular to the axis.
62 // this allows to eliminate shifts in symmetric cells.
63 // e.g., if one lactice vector is orthogonal to the plane spanned
64 // by the other two vectors, that shift should never be tried
65  Vector cosdir=matmul(reduced,transpose(reduced),dshift);
66  double dp=dotProduct(dshift,cosdir);
67  double ref=modulo2(dshift)*modulo2(cosdir);
68  if(std::fabs(ref-dp*dp)<small) continue;
69 
70 // here we start pruning depending on the sign of the scaled coordinate
71  for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++){
72 
73  int block[3]={2*i-1,2*j-1,2*k-1};
74 
75 // skip cases where shift would bring too far from origin
76  bool skip=false;
77  for(int s=0;s<3;s++) if(ishift[s]*block[s]>0) skip=true;
78  if(skip) continue;
79  skip=true;
80  for(int s=0;s<3;s++){
81 // check that the components of cosdir along the non-shifted directions
82 // have the proper sign
83  if(((1-ishift[s]*ishift[s])*block[s])*cosdir[s]<-small) skip=false;
84  }
85  if(skip)continue;
86 
87 // if we arrive to this point, shift is eligible and is added to the list
88  shifts[i][j][k].push_back(matmul(transpose(reduced),dshift));
89  }
90  }
91 }
92 
93 void Pbc::fullSearch(Vector&d)const{
94  if(type==unset) return;
96  for(int i=0;i<3;i++) s[i]=Tools::pbc(s[i]);
97  d=matmul(reduced.transpose(),s);
98  const int smax=4;
99  Vector a0(reduced.getRow(0));
100  Vector a1(reduced.getRow(1));
101  Vector a2(reduced.getRow(2));
102  Vector best(d);
103  double lbest=d.modulo2();
104  for(int i=-smax;i<=smax;i++) for(int j=-smax;j<=smax;j++) for(int k=-smax;k<=smax;k++){
105  Vector trial=d+i*a0+j*a1+k*a2;
106  double ltrial=trial.modulo2();
107  if(ltrial<lbest){
108  best=trial;
109  lbest=ltrial;
110  }
111  }
112  d=best;
113 }
114 
115 void Pbc::setBox(const Tensor&b){
116  box=b;
117 // detect type:
118  const double epsilon=1e-28;
119 
120  type=unset;
121  double det=box.determinant();
122  if(det*det<epsilon) return;
123 
124  bool cxy=false;
125  bool cxz=false;
126  bool cyz=false;
127  if(box(0,1)*box(0,1)<epsilon && box(1,0)*box(1,0)<epsilon) cxy=true;
128  if(box(0,2)*box(0,2)<epsilon && box(2,0)*box(2,0)<epsilon) cxz=true;
129  if(box(1,2)*box(1,2)<epsilon && box(2,1)*box(2,1)<epsilon) cyz=true;
130 
131  invBox=box.inverse();
132 
133  if(cxy && cxz && cyz) type=orthorombic;
134  else type=generic;
135 
136  if(type==orthorombic){
137  reduced=box;
139  for(unsigned i=0;i<3;i++){
140  diag[i]=box[i][i];
141  hdiag[i]=0.5*box[i][i];
142  mdiag[i]=-0.5*box[i][i];
143  }
144  } else {
145  reduced=box;
149 }
150 
151 }
152 
153 double Pbc::distance( const bool pbc, const Vector& v1, const Vector& v2 ) const {
154  if(pbc){ return ( distance(v1,v2) ).modulo(); }
155  else{ return ( delta(v1,v2) ).modulo(); }
156 }
157 
158 Vector Pbc::distance(const Vector&v1,const Vector&v2,int*nshifts)const{
159  Vector d=delta(v1,v2);
160  if(type==unset){
161  } else if(type==orthorombic) {
162  for(int i=0;i<3;i++) d[i]=Tools::pbc(d[i]*invBox(i,i))*box(i,i);
163 // this is another possibility:
164 // for(unsigned i=0;i<3;i++){
165 // while(d[i]>hdiag[i]) d[i]-=diag[i];
166 // while(d[i]<=mdiag[i]) d[i]+=diag[i];
167 // }
168  } else if(type==generic) {
169  Vector s=matmul(d,invReduced);
170 // check if images have to be computed:
171 // if((std::fabs(s[0])+std::fabs(s[1])+std::fabs(s[2])>0.5)){
172 // NOTICE: the check in the previous line, albeit correct, is breaking many regtest
173 // since it does not apply Tools::pbc in many cases. Moreover, it does not
174 // introduce a significant gain. I thus leave it out for the moment.
175  if(true){
176 // bring to -0.5,+0.5 region in scaled coordinates:
177  for(int i=0;i<3;i++) s[i]=Tools::pbc(s[i]);
178  d=matmul(s,reduced);
179 // check if shifts have to be attempted:
180  if((std::fabs(s[0])+std::fabs(s[1])+std::fabs(s[2])>0.5)){
181 // list of shifts is specific for that "octant" (depends on signs of s[i]):
182  const std::vector<Vector> & myshifts(shifts[(s[0]>0?1:0)][(s[1]>0?1:0)][(s[2]>0?1:0)]);
183  Vector best(d);
184  double lbest(modulo2(best));
185 // loop over possible shifts:
186  if(nshifts) *nshifts+=myshifts.size();
187  for(int i=0;i<myshifts.size();i++){
188  Vector trial=d+myshifts[i];
189  double ltrial=modulo2(trial);
190  if(ltrial<lbest){
191  lbest=ltrial;
192  best=trial;
193  }
194  }
195  d=best;
196  }
197  }
198  } else plumed_merror("unknown pbc type");
199  return d;
200 }
201 
203  return matmul(invBox.transpose(),d);
204 }
205 
207  return matmul(box.transpose(),d);
208 }
209 
210 bool Pbc::isOrthorombic()const{
211  return type==orthorombic;
212 }
213 
214 const Tensor& Pbc::getBox()const{
215  return box;
216 }
217 
218 const Tensor& Pbc::getInvBox()const{
219  return invBox;
220 }
221 
222 void Pbc::test(){
223  Random r;
224  r.setSeed(-20);
225  for(int i=0;i<1000;i++){
226 // random matrix with some zero element
227  Tensor box;
228  for(int j=0;j<3;j++) for(int k=0;k<3;k++) if(r.U01()>0.2){
229  box[j][k]=2.0*r.U01()-1.0;
230  }
231  int boxtype=i%10;
232  switch(boxtype){
233  case 0:
234 // cubic
235  for(int j=0;j<3;j++) for(int k=0;k<3;k++) if(j!=k) box[j][k]=0.0;
236  for(int j=1;j<3;j++) box[j][j]=box[0][0];
237  break;
238  case 1:
239 // orthorombic
240  for(int j=0;j<3;j++) for(int k=0;k<3;k++) if(j!=k) box[j][k]=0.0;
241  break;
242  case 2:
243 // hexagonal
244  {
245  int perm=r.U01()*100;
246  Vector a;
247  a(0)=r.U01()*2-2; a(1)=0.0;a(2)=0.0;
248  double d=r.U01()*2-2;
249  Vector b(0.0,d,0.0);
250  Vector c(0.0,0.5*d,sqrt(3.0)*d*0.5);
251  box.setRow((perm+0)%3,a);
252  box.setRow((perm+1)%3,b);
253  box.setRow((perm+2)%3,c);
254  }
255  break;
256  case 3:
257 // bcc
258  {
259  int perm=r.U01()*100;
260  double d=r.U01()*2-2;
261  Vector a(d,d,d);
262  Vector b(d,-d,d);
263  Vector c(d,d,-d);
264  box.setRow((perm+0)%3,a);
265  box.setRow((perm+1)%3,b);
266  box.setRow((perm+2)%3,c);
267  }
268  break;
269  case 4:
270 // fcc
271  {
272  int perm=r.U01()*100;
273  double d=r.U01()*2-2;
274  Vector a(d,d,0);
275  Vector b(d,0,d);
276  Vector c(0,d,d);
277  box.setRow((perm+0)%3,a);
278  box.setRow((perm+1)%3,b);
279  box.setRow((perm+2)%3,c);
280  }
281  break;
282  default:
283 // triclinic
284  break;
285  }
286 
287  Pbc pbc;
288  pbc.setBox(box);
289  std::cerr<<"( "<<boxtype<<" )\n";
290  std::cerr<<"Box:";
291  for(int j=0;j<3;j++) for(int k=0;k<3;k++) std::cerr<<" "<<box[j][k];
292  std::cerr<<"\n";
293  std::cerr<<"Determinant: "<<determinant(box)<<"\n";
294  std::cerr<<"Shifts:";
295  for(int j=0;j<2;j++) for(int k=0;k<2;k++) for(int l=0;l<2;l++) std::cerr<<" "<<pbc.shifts[j][k][l].size();
296  std::cerr<<"\n";
297  int nshifts=0;
298  int ntot=10000;
299  for(int j=0;j<ntot;j++){
300  Vector v(r.U01()-0.5,r.U01()-0.5,r.U01()-0.5);
301  v*=5;
302  for(int j=0;j<3;j++) if(r.U01()>0.2) v(j)=0.0;
303  Vector full(v);
304  Vector fast=pbc.distance(Vector(0,0,0),v,&nshifts);
305  full=fast;
306 
307  pbc.fullSearch(full);
308 
309  if(modulo2(fast-full)>1e-10) {
310  std::cerr<<"orig "<<v[0]<<" "<<v[1]<<" "<<v[2]<<"\n";
311  std::cerr<<"fast "<<fast[0]<<" "<<fast[1]<<" "<<fast[2]<<"\n";
312  std::cerr<<"full "<<full[0]<<" "<<full[1]<<" "<<full[2]<<"\n";
313  std::cerr<<"diff "<<modulo2(fast)-modulo2(full)<<std::endl;
314  if(std::fabs(modulo2(fast)-modulo2(full))>1e-15) plumed_error();
315  }
316  }
317  std::cerr<<"Average number of shifts: "<<double(nshifts)/double(ntot)<<"\n";
318  }
319 }
320 
321 
322 
323 }
Tensor invBox
Inverse box.
Definition: Pbc.h:44
void zero()
set it to zero
Definition: Tensor.h:198
double determinant(const TensorGeneric< 3, 3 > &t)
Definition: Tensor.h:381
const double epsilon
TensorGeneric< 3, 3 > inverse(const TensorGeneric< 3, 3 > &t)
Definition: Tensor.h:386
double modulo() const
Compute the modulo.
Definition: Vector.h:325
TensorGeneric< m, n > transpose() const
return the transpose matrix
Definition: Tensor.h:325
Class implementing fixed size matrices of doubles.
Definition: Tensor.h:70
enum PLMD::Pbc::@6 type
Type of box.
TensorGeneric< n, l > matmul(const TensorGeneric< n, m > &a, const TensorGeneric< m, l > &b)
Definition: Tensor.h:343
Class implementing fixed size vectors of doubles.
Definition: Vector.h:74
Tensor box
Box.
Definition: Pbc.h:42
Vector scaledToReal(const Vector &) const
Transform a vector in scaled coordinates to a vector in real space.
Definition: Pbc.cpp:206
Tensor reduced
Reduced box.
Definition: Pbc.h:48
Vector mdiag
Definition: Pbc.h:58
static double pbc(double)
Apply pbc for a unitary cell.
Definition: Tools.h:170
Vector diag
Alternative representation for orthorombic cells.
Definition: Pbc.h:58
double U01()
Definition: Random.cpp:74
Tensor invReduced
Inverse of the reduced box.
Definition: Pbc.h:50
void const char const char int * n
Definition: Matrix.h:42
Definition: Pbc.h:38
void transpose(const Matrix< T > &A, Matrix< T > &AT)
Definition: Matrix.h:185
Vector distance(const Vector &, const Vector &, int *nshifts) const
internal version of distance, also returns the number of attempted shifts (used in Pbc::test())...
Definition: Pbc.cpp:158
double determinant() const
returns the determinant
void setSeed(int idum)
Definition: Random.cpp:51
std::vector< Vector > shifts[2][2][2]
List of shifts that should be attempted.
Definition: Pbc.h:54
VectorGeneric< n > delta(const VectorGeneric< n > &v1, const VectorGeneric< n > &v2)
Definition: Vector.h:262
T dotProduct(const std::vector< T > &A, const std::vector< T > &B)
Calculate the dot product between two vectors.
Definition: Matrix.h:54
const Tensor & getInvBox() const
Returns the inverse matrix of box.
Definition: Pbc.cpp:218
double modulo2() const
compute the squared modulo
Definition: Vector.h:267
double modulo2(const VectorGeneric< n > &v)
Definition: Vector.h:330
static void test()
Perform some check. Useful for debugging.
Definition: Pbc.cpp:222
void buildShifts(std::vector< Vector > shifts[2][2][2]) const
Build list of shifts.
Definition: Pbc.cpp:39
void const char const char int double int double double int int double int * m
Definition: Matrix.h:42
void fullSearch(Vector &) const
Full search (for testing)
Definition: Pbc.cpp:93
static void reduce(Vector &a, Vector &b)
Gaussian reduction.
bool isOrthorombic() const
Returns true if the box vectors are orthogonal.
Definition: Pbc.cpp:210
TensorGeneric & setRow(unsigned i, const VectorGeneric< m > &r)
set i-th row
Definition: Tensor.h:254
Pbc()
Constructor.
Definition: Pbc.cpp:32
void const char const char int double * a
Definition: Matrix.h:42
Vector hdiag
Definition: Pbc.h:58
Vector3d Vector
Alias for three dimensional vectors.
Definition: Vector.h:351
Vector realToScaled(const Vector &) const
Transform a vector in real space to a vector in scaled coordinates.
Definition: Pbc.cpp:202
VectorGeneric< m > getRow(unsigned i) const
get i-th row
Definition: Tensor.h:267
TensorGeneric inverse() const
return the matrix inverse
const Tensor & getBox() const
Returns the box.
Definition: Pbc.cpp:214
void setBox(const Tensor &b)
Set the lattice vectors.
Definition: Pbc.cpp:115