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 : }
|