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 421473 : Pbc::Pbc():
33 6322095 : type(unset)
34 : {
35 421473 : box.zero();
36 421473 : invBox.zero();
37 421473 : }
38 :
39 12552 : void Pbc::buildShifts(std::vector<Vector> shifts[2][2][2])const {
40 : const double small=1e-28;
41 :
42 : // clear all shifts
43 188280 : 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 502080 : 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 338904 : int ishift[3]= {l,m,n};
51 338904 : Vector dshift(l,m,n);
52 :
53 : // count how many components are != 0
54 : unsigned count=0;
55 1355616 : 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 413636 : 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 225936 : Vector cosdir=matmul(reduced,transpose(reduced),dshift);
66 225936 : double dp=dotProduct(dshift,cosdir);
67 225936 : double ref=modulo2(dshift)*modulo2(cosdir);
68 225936 : if(std::fabs(ref-dp*dp)<small) continue;
69 :
70 : // here we start pruning depending on the sign of the scaled coordinate
71 2268060 : for(int i=0; i<2; i++) for(int j=0; j<2; j++) for(int k=0; k<2; k++) {
72 :
73 1209632 : 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 4838528 : for(int s=0; s<3; s++) if(ishift[s]*block[s]>0) skip=true;
78 1371470 : if(skip) continue;
79 : skip=true;
80 1644880 : 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 1233660 : if(((1-ishift[s]*ishift[s])*block[s])*cosdir[s]<-small) skip=false;
84 : }
85 411220 : if(skip)continue;
86 :
87 : // if we arrive to this point, shift is eligible and is added to the list
88 498764 : shifts[i][j][k].push_back(matmul(transpose(reduced),dshift));
89 : }
90 : }
91 12552 : }
92 :
93 600000 : void Pbc::fullSearch(Vector&d)const {
94 600000 : if(type==unset) return;
95 528100 : Vector s=matmul(invReduced.transpose(),d);
96 2112400 : for(int i=0; i<3; i++) s[i]=Tools::pbc(s[i]);
97 528100 : d=matmul(reduced.transpose(),s);
98 : const int smax=4;
99 528100 : Vector a0(reduced.getRow(0));
100 528100 : Vector a1(reduced.getRow(1));
101 528100 : Vector a2(reduced.getRow(2));
102 528100 : Vector best(d);
103 528100 : double lbest=d.modulo2();
104 433042000 : for(int i=-smax; i<=smax; i++) for(int j=-smax; j<=smax; j++) for(int k=-smax; k<=smax; k++) {
105 384984900 : Vector trial=d+i*a0+j*a1+k*a2;
106 384984900 : double ltrial=trial.modulo2();
107 384984900 : if(ltrial<lbest) {
108 29897 : best=trial;
109 : lbest=ltrial;
110 : }
111 : }
112 528100 : d=best;
113 : }
114 :
115 63109 : void Pbc::setBox(const Tensor&b) {
116 63109 : box=b;
117 : // detect type:
118 : const double epsilon=1e-28;
119 :
120 63109 : type=unset;
121 63109 : double det=box.determinant();
122 63109 : if(det*det<epsilon) return;
123 :
124 : bool cxy=false;
125 : bool cxz=false;
126 : bool cyz=false;
127 57024 : if(box(0,1)*box(0,1)<epsilon && box(1,0)*box(1,0)<epsilon) cxy=true;
128 57024 : if(box(0,2)*box(0,2)<epsilon && box(2,0)*box(2,0)<epsilon) cxz=true;
129 57024 : if(box(1,2)*box(1,2)<epsilon && box(2,1)*box(2,1)<epsilon) cyz=true;
130 :
131 57024 : invBox=box.inverse();
132 :
133 57024 : if(cxy && cxz && cyz) type=orthorombic;
134 12552 : else type=generic;
135 :
136 57024 : if(type==orthorombic) {
137 44472 : reduced=box;
138 44472 : invReduced=inverse(reduced);
139 177888 : for(unsigned i=0; i<3; i++) {
140 133416 : diag[i]=box[i][i];
141 133416 : hdiag[i]=0.5*box[i][i];
142 133416 : mdiag[i]=-0.5*box[i][i];
143 : }
144 : } else {
145 12552 : reduced=box;
146 12552 : LatticeReduction::reduce(reduced);
147 12552 : invReduced=inverse(reduced);
148 12552 : buildShifts(shifts);
149 : }
150 :
151 : }
152 :
153 0 : double Pbc::distance( const bool pbc, const Vector& v1, const Vector& v2 ) const {
154 0 : if(pbc) { return ( distance(v1,v2) ).modulo(); }
155 0 : else { return ( delta(v1,v2) ).modulo(); }
156 : }
157 :
158 220567 : void Pbc::apply(std::vector<Vector>& dlist, unsigned max_index) const {
159 220567 : if (max_index==0) max_index=dlist.size();
160 220567 : if(type==unset) {
161 : // do nothing
162 214143 : } 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 1603494693 : 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 1810 : } else if(type==generic) {
176 67768 : for(unsigned k=0; k<max_index; ++k) dlist[k]=distance(Vector(0.0,0.0,0.0),dlist[k]);
177 0 : } else plumed_merror("unknown pbc type");
178 220567 : }
179 :
180 208158419 : Vector Pbc::distance(const Vector&v1,const Vector&v2,int*nshifts)const {
181 208158419 : Vector d=delta(v1,v2);
182 208158419 : if(type==unset) {
183 : // do nothing
184 182935082 : } 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 538436180 : for(int i=0; i<3; i++) d[i]=Tools::pbc(d[i]*invBox(i,i))*box(i,i);
192 : #endif
193 48326037 : } else if(type==generic) {
194 48326037 : 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 193304148 : for(int i=0; i<3; i++) s[i]=Tools::pbc(s[i]);
203 48326037 : d=matmul(s,reduced);
204 : // check if shifts have to be attempted:
205 48326037 : 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 78765012 : const std::vector<Vector> & myshifts(shifts[(s[0]>0?1:0)][(s[1]>0?1:0)][(s[2]>0?1:0)]);
208 39867229 : Vector best(d);
209 39867229 : double lbest(modulo2(best));
210 : // loop over possible shifts:
211 39867229 : if(nshifts) *nshifts+=myshifts.size();
212 179420109 : for(unsigned i=0; i<myshifts.size(); i++) {
213 139552880 : Vector trial=d+myshifts[i];
214 139552880 : double ltrial=modulo2(trial);
215 139552880 : if(ltrial<lbest) {
216 : lbest=ltrial;
217 3759325 : best=trial;
218 : }
219 : }
220 39867229 : d=best;
221 : }
222 : }
223 0 : } else plumed_merror("unknown pbc type");
224 208158419 : return d;
225 : }
226 :
227 919918 : Vector Pbc::realToScaled(const Vector&d)const {
228 919918 : return matmul(invBox.transpose(),d);
229 : }
230 :
231 223368 : Vector Pbc::scaledToReal(const Vector&d)const {
232 223368 : return matmul(box.transpose(),d);
233 : }
234 :
235 440 : bool Pbc::isOrthorombic()const {
236 440 : return type==orthorombic;
237 : }
238 :
239 34215 : const Tensor& Pbc::getBox()const {
240 34215 : return box;
241 : }
242 :
243 2173 : const Tensor& Pbc::getInvBox()const {
244 2173 : return invBox;
245 : }
246 :
247 : }
|