1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Copyright (c) 2011-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 "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
Pbc()32 Pbc::Pbc():
33 type(unset)
34 {
35 box.zero();
36 invBox.zero();
37 }
38
buildShifts(std::vector<Vector> shifts[2][2][2]) const39 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
fullSearch(Vector & d) const93 void Pbc::fullSearch(Vector&d)const {
94 if(type==unset) return;
95 Vector s=matmul(invReduced.transpose(),d);
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
setBox(const Tensor & b)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;
138 invReduced=inverse(reduced);
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;
146 LatticeReduction::reduce(reduced);
147 invReduced=inverse(reduced);
148 buildShifts(shifts);
149 }
150
151 }
152
distance(const bool pbc,const Vector & v1,const Vector & v2) const153 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
apply(std::vector<Vector> & dlist,unsigned max_index) const158 void Pbc::apply(std::vector<Vector>& dlist, unsigned max_index) const {
159 if (max_index==0) max_index=dlist.size();
160 if(type==unset) {
161 // do nothing
162 } else if(type==orthorombic) {
163 #ifdef __PLUMED_PBC_WHILE
164 for(unsigned k=0; k<max_index; ++k) {
165 while(dlist[k][0]>hdiag[0]) dlist[k][0]-=diag[0];
166 while(dlist[k][0]<=mdiag[0]) dlist[k][0]+=diag[0];
167 while(dlist[k][1]>hdiag[1]) dlist[k][1]-=diag[1];
168 while(dlist[k][1]<=mdiag[1]) dlist[k][1]+=diag[1];
169 while(dlist[k][2]>hdiag[2]) dlist[k][2]-=diag[2];
170 while(dlist[k][2]<=mdiag[2]) dlist[k][2]+=diag[2];
171 }
172 #else
173 for(unsigned k=0; k<max_index; ++k) for(int i=0; i<3; i++) dlist[k][i]=Tools::pbc(dlist[k][i]*invBox(i,i))*box(i,i);
174 #endif
175 } else if(type==generic) {
176 for(unsigned k=0; k<max_index; ++k) dlist[k]=distance(Vector(0.0,0.0,0.0),dlist[k]);
177 } else plumed_merror("unknown pbc type");
178 }
179
distance(const Vector & v1,const Vector & v2,int * nshifts) const180 Vector Pbc::distance(const Vector&v1,const Vector&v2,int*nshifts)const {
181 Vector d=delta(v1,v2);
182 if(type==unset) {
183 // do nothing
184 } else if(type==orthorombic) {
185 #ifdef __PLUMED_PBC_WHILE
186 for(unsigned i=0; i<3; i++) {
187 while(d[i]>hdiag[i]) d[i]-=diag[i];
188 while(d[i]<=mdiag[i]) d[i]+=diag[i];
189 }
190 #else
191 for(int i=0; i<3; i++) d[i]=Tools::pbc(d[i]*invBox(i,i))*box(i,i);
192 #endif
193 } else if(type==generic) {
194 Vector s=matmul(d,invReduced);
195 // check if images have to be computed:
196 // if((std::fabs(s[0])+std::fabs(s[1])+std::fabs(s[2])>0.5)){
197 // NOTICE: the check in the previous line, albeit correct, is breaking many regtest
198 // since it does not apply Tools::pbc in many cases. Moreover, it does not
199 // introduce a significant gain. I thus leave it out for the moment.
200 if(true) {
201 // bring to -0.5,+0.5 region in scaled coordinates:
202 for(int i=0; i<3; i++) s[i]=Tools::pbc(s[i]);
203 d=matmul(s,reduced);
204 // check if shifts have to be attempted:
205 if((std::fabs(s[0])+std::fabs(s[1])+std::fabs(s[2])>0.5)) {
206 // list of shifts is specific for that "octant" (depends on signs of s[i]):
207 const std::vector<Vector> & myshifts(shifts[(s[0]>0?1:0)][(s[1]>0?1:0)][(s[2]>0?1:0)]);
208 Vector best(d);
209 double lbest(modulo2(best));
210 // loop over possible shifts:
211 if(nshifts) *nshifts+=myshifts.size();
212 for(unsigned i=0; i<myshifts.size(); i++) {
213 Vector trial=d+myshifts[i];
214 double ltrial=modulo2(trial);
215 if(ltrial<lbest) {
216 lbest=ltrial;
217 best=trial;
218 }
219 }
220 d=best;
221 }
222 }
223 } else plumed_merror("unknown pbc type");
224 return d;
225 }
226
realToScaled(const Vector & d) const227 Vector Pbc::realToScaled(const Vector&d)const {
228 return matmul(invBox.transpose(),d);
229 }
230
scaledToReal(const Vector & d) const231 Vector Pbc::scaledToReal(const Vector&d)const {
232 return matmul(box.transpose(),d);
233 }
234
isOrthorombic() const235 bool Pbc::isOrthorombic()const {
236 return type==orthorombic;
237 }
238
getBox() const239 const Tensor& Pbc::getBox()const {
240 return box;
241 }
242
getInvBox() const243 const Tensor& Pbc::getInvBox()const {
244 return invBox;
245 }
246
247 }
248