# | Line 1 | Line 1 | |
---|---|---|
1 | + | #include <cmath> |
2 | #include "Atom.hpp" | |
3 | #include "SRI.hpp" | |
4 | #include "AbstractClasses.hpp" | |
# | Line 8 | Line 9 | |
9 | #include "Integrator.hpp" | |
10 | #include "simError.h" | |
11 | ||
12 | + | #ifdef IS_MPI |
13 | + | #include "mpiSimulation.hpp" |
14 | + | #endif |
15 | ||
16 | // Basic isotropic thermostating and barostating via the Melchionna | |
17 | // modification of the Hoover algorithm: | |
# | Line 19 | Line 23 | |
23 | // | |
24 | // Hoover, W. G., 1986, Phys. Rev. A, 34, 2499. | |
25 | ||
26 | < | NPTi::NPTi ( SimInfo *theInfo, ForceFields* the_ff): |
27 | < | Integrator( theInfo, the_ff ) |
26 | > | template<typename T> NPTi<T>::NPTi ( SimInfo *theInfo, ForceFields* the_ff): |
27 | > | T( theInfo, the_ff ) |
28 | { | |
29 | chi = 0.0; | |
30 | eta = 0.0; | |
31 | + | integralOfChidt = 0.0; |
32 | have_tau_thermostat = 0; | |
33 | have_tau_barostat = 0; | |
34 | have_target_temp = 0; | |
35 | have_target_pressure = 0; | |
36 | + | have_chi_tolerance = 0; |
37 | + | have_eta_tolerance = 0; |
38 | + | have_pos_iter_tolerance = 0; |
39 | + | |
40 | + | oldPos = new double[3*nAtoms]; |
41 | + | oldVel = new double[3*nAtoms]; |
42 | + | oldJi = new double[3*nAtoms]; |
43 | + | #ifdef IS_MPI |
44 | + | Nparticles = mpiSim->getTotAtoms(); |
45 | + | #else |
46 | + | Nparticles = theInfo->n_atoms; |
47 | + | #endif |
48 | + | |
49 | } | |
50 | ||
51 | < | void NPTi::moveA() { |
52 | < | |
53 | < | int i,j,k; |
54 | < | int atomIndex, aMatIndex; |
51 | > | template<typename T> NPTi<T>::~NPTi() { |
52 | > | delete[] oldPos; |
53 | > | delete[] oldVel; |
54 | > | delete[] oldJi; |
55 | > | } |
56 | > | |
57 | > | template<typename T> void NPTi<T>::moveA() { |
58 | > | |
59 | > | //new version of NPTi |
60 | > | int i, j, k; |
61 | DirectionalAtom* dAtom; | |
62 | < | double Tb[3]; |
63 | < | double ji[3]; |
62 | > | double Tb[3], ji[3]; |
63 | > | double A[3][3], I[3][3]; |
64 | > | double angle, mass; |
65 | > | double vel[3], pos[3], frc[3]; |
66 | > | |
67 | double rj[3]; | |
68 | double instaTemp, instaPress, instaVol; | |
69 | < | double tt2, tb2; |
70 | < | double angle; |
69 | > | double tt2, tb2, scaleFactor; |
70 | > | double COM[3]; |
71 | ||
72 | tt2 = tauThermostat * tauThermostat; | |
73 | tb2 = tauBarostat * tauBarostat; | |
# | Line 48 | Line 75 | void NPTi::moveA() { | |
75 | instaTemp = tStats->getTemperature(); | |
76 | instaPress = tStats->getPressure(); | |
77 | instaVol = tStats->getVolume(); | |
51 | – | |
52 | – | // first evolve chi a half step |
78 | ||
79 | < | chi += dt2 * ( instaTemp / targetTemp - 1.0) / tt2; |
80 | < | eta += dt2 * ( instaVol * (instaPress - targetPressure) / (NkBT*tb2)); |
81 | < | |
79 | > | tStats->getCOM(COM); |
80 | > | |
81 | > | //evolve velocity half step |
82 | for( i=0; i<nAtoms; i++ ){ | |
58 | – | atomIndex = i * 3; |
59 | – | aMatIndex = i * 9; |
60 | – | |
61 | – | // velocity half step |
62 | – | for( j=atomIndex; j<(atomIndex+3); j++ ) |
63 | – | vel[j] += dt2 * ((frc[j]/atoms[i]->getMass())*eConvert |
64 | – | - vel[j]*(chi+eta)); |
83 | ||
84 | < | // position whole step |
84 | > | atoms[i]->getVel( vel ); |
85 | > | atoms[i]->getFrc( frc ); |
86 | ||
87 | < | rj[0] = pos[atomIndex]; |
69 | < | rj[1] = pos[atomIndex+1]; |
70 | < | rj[2] = pos[atomIndex+2]; |
71 | < | |
72 | < | info->wrapVector(rj); |
87 | > | mass = atoms[i]->getMass(); |
88 | ||
89 | < | pos[atomIndex] += dt * (vel[atomIndex] + eta*rj[0]); |
90 | < | pos[atomIndex+1] += dt * (vel[atomIndex+1] + eta*rj[1]); |
91 | < | pos[atomIndex+2] += dt * (vel[atomIndex+2] + eta*rj[2]); |
89 | > | for (j=0; j < 3; j++) { |
90 | > | // velocity half step |
91 | > | vel[j] += dt2 * ((frc[j] / mass ) * eConvert - vel[j]*(chi + eta)); |
92 | > | } |
93 | > | |
94 | > | atoms[i]->setVel( vel ); |
95 | ||
96 | if( atoms[i]->isDirectional() ){ | |
97 | ||
98 | dAtom = (DirectionalAtom *)atoms[i]; | |
99 | < | |
99 | > | |
100 | // get and convert the torque to body frame | |
101 | ||
102 | < | Tb[0] = dAtom->getTx(); |
85 | < | Tb[1] = dAtom->getTy(); |
86 | < | Tb[2] = dAtom->getTz(); |
87 | < | |
102 | > | dAtom->getTrq( Tb ); |
103 | dAtom->lab2Body( Tb ); | |
104 | ||
105 | // get the angular momentum, and propagate a half step | |
106 | ||
107 | < | ji[0] = dAtom->getJx(); |
108 | < | ji[1] = dAtom->getJy(); |
109 | < | ji[2] = dAtom->getJz(); |
107 | > | dAtom->getJ( ji ); |
108 | > | |
109 | > | for (j=0; j < 3; j++) |
110 | > | ji[j] += dt2 * (Tb[j] * eConvert - ji[j]*chi); |
111 | ||
96 | – | ji[0] += dt2 * (Tb[0] * eConvert - ji[0]*chi); |
97 | – | ji[1] += dt2 * (Tb[1] * eConvert - ji[1]*chi); |
98 | – | ji[2] += dt2 * (Tb[2] * eConvert - ji[2]*chi); |
99 | – | |
112 | // use the angular velocities to propagate the rotation matrix a | |
113 | // full time step | |
114 | < | |
114 | > | |
115 | > | dAtom->getA(A); |
116 | > | dAtom->getI(I); |
117 | > | |
118 | // rotate about the x-axis | |
119 | < | angle = dt2 * ji[0] / dAtom->getIxx(); |
120 | < | this->rotate( 1, 2, angle, ji, &Amat[aMatIndex] ); |
121 | < | |
119 | > | angle = dt2 * ji[0] / I[0][0]; |
120 | > | this->rotate( 1, 2, angle, ji, A ); |
121 | > | |
122 | // rotate about the y-axis | |
123 | < | angle = dt2 * ji[1] / dAtom->getIyy(); |
124 | < | this->rotate( 2, 0, angle, ji, &Amat[aMatIndex] ); |
123 | > | angle = dt2 * ji[1] / I[1][1]; |
124 | > | this->rotate( 2, 0, angle, ji, A ); |
125 | ||
126 | // rotate about the z-axis | |
127 | < | angle = dt * ji[2] / dAtom->getIzz(); |
128 | < | this->rotate( 0, 1, angle, ji, &Amat[aMatIndex] ); |
127 | > | angle = dt * ji[2] / I[2][2]; |
128 | > | this->rotate( 0, 1, angle, ji, A); |
129 | ||
130 | // rotate about the y-axis | |
131 | < | angle = dt2 * ji[1] / dAtom->getIyy(); |
132 | < | this->rotate( 2, 0, angle, ji, &Amat[aMatIndex] ); |
131 | > | angle = dt2 * ji[1] / I[1][1]; |
132 | > | this->rotate( 2, 0, angle, ji, A ); |
133 | ||
134 | // rotate about the x-axis | |
135 | < | angle = dt2 * ji[0] / dAtom->getIxx(); |
136 | < | this->rotate( 1, 2, angle, ji, &Amat[aMatIndex] ); |
135 | > | angle = dt2 * ji[0] / I[0][0]; |
136 | > | this->rotate( 1, 2, angle, ji, A ); |
137 | ||
138 | < | dAtom->setJx( ji[0] ); |
139 | < | dAtom->setJy( ji[1] ); |
140 | < | dAtom->setJz( ji[2] ); |
138 | > | dAtom->setJ( ji ); |
139 | > | dAtom->setA( A ); |
140 | > | } |
141 | > | } |
142 | > | |
143 | > | // advance chi half step |
144 | > | |
145 | > | chi += dt2 * ( instaTemp / targetTemp - 1.0) / tt2; |
146 | > | |
147 | > | // calculate the integral of chidt |
148 | > | |
149 | > | integralOfChidt += dt2*chi; |
150 | > | |
151 | > | // advance eta half step |
152 | > | |
153 | > | eta += dt2 * ( instaVol * (instaPress - targetPressure) / (p_convert*NkBT*tb2)); |
154 | > | |
155 | > | //save the old positions |
156 | > | for(i = 0; i < nAtoms; i++){ |
157 | > | atoms[i]->getPos(pos); |
158 | > | for(j = 0; j < 3; j++) |
159 | > | oldPos[i*3 + j] = pos[j]; |
160 | > | } |
161 | > | |
162 | > | //the first estimation of r(t+dt) is equal to r(t) |
163 | > | |
164 | > | for(k = 0; k < 4; k ++){ |
165 | > | |
166 | > | for(i =0 ; i < nAtoms; i++){ |
167 | > | |
168 | > | atoms[i]->getVel(vel); |
169 | > | atoms[i]->getPos(pos); |
170 | > | |
171 | > | for(j = 0; j < 3; j++) |
172 | > | rj[j] = (oldPos[i*3 + j] + pos[j])/2 - COM[j]; |
173 | > | |
174 | > | for(j = 0; j < 3; j++) |
175 | > | pos[j] = oldPos[i*3 + j] + dt*(vel[j] + eta*rj[j]); |
176 | > | |
177 | > | atoms[i]->setPos( pos ); |
178 | } | |
179 | ||
180 | + | if (nConstrained){ |
181 | + | constrainA(); |
182 | + | } |
183 | } | |
184 | + | |
185 | + | |
186 | // Scale the box after all the positions have been moved: | |
187 | + | |
188 | + | scaleFactor = exp(dt*eta); |
189 | ||
190 | < | info->scaleBox(exp(dt*eta)); |
190 | > | if ((scaleFactor > 1.1) || (scaleFactor < 0.9)) { |
191 | > | sprintf( painCave.errMsg, |
192 | > | "NPTi error: Attempting a Box scaling of more than 10 percent" |
193 | > | " check your tauBarostat, as it is probably too small!\n" |
194 | > | " eta = %lf, scaleFactor = %lf\n", eta, scaleFactor |
195 | > | ); |
196 | > | painCave.isFatal = 1; |
197 | > | simError(); |
198 | > | } else { |
199 | > | info->scaleBox(scaleFactor); |
200 | > | } |
201 | ||
202 | } | |
203 | ||
204 | < | void NPTi::moveB( void ){ |
205 | < | int i,j,k; |
206 | < | int atomIndex; |
204 | > | template<typename T> void NPTi<T>::moveB( void ){ |
205 | > | |
206 | > | //new version of NPTi |
207 | > | int i, j, k; |
208 | DirectionalAtom* dAtom; | |
209 | < | double Tb[3]; |
210 | < | double ji[3]; |
209 | > | double Tb[3], ji[3]; |
210 | > | double vel[3], frc[3]; |
211 | > | double mass; |
212 | > | |
213 | double instaTemp, instaPress, instaVol; | |
214 | double tt2, tb2; | |
215 | + | double oldChi, prevChi; |
216 | + | double oldEta, prevEta; |
217 | ||
218 | tt2 = tauThermostat * tauThermostat; | |
219 | tb2 = tauBarostat * tauBarostat; | |
220 | ||
221 | < | instaTemp = tStats->getTemperature(); |
148 | < | instaPress = tStats->getPressure(); |
149 | < | instaVol = tStats->getVolume(); |
221 | > | // Set things up for the iteration: |
222 | ||
223 | < | chi += dt2 * ( instaTemp / targetTemp - 1.0) / tt2; |
224 | < | eta += dt2 * ( instaVol * (instaPress - targetPressure) / (NkBT*tb2)); |
225 | < | |
223 | > | oldChi = chi; |
224 | > | oldEta = eta; |
225 | > | |
226 | for( i=0; i<nAtoms; i++ ){ | |
227 | < | atomIndex = i * 3; |
228 | < | |
229 | < | // velocity half step |
230 | < | for( j=atomIndex; j<(atomIndex+3); j++ ) |
231 | < | for( j=atomIndex; j<(atomIndex+3); j++ ) |
232 | < | vel[j] += dt2 * ((frc[j]/atoms[i]->getMass())*eConvert |
161 | < | - vel[j]*(chi+eta)); |
162 | < | |
227 | > | |
228 | > | atoms[i]->getVel( vel ); |
229 | > | |
230 | > | for (j=0; j < 3; j++) |
231 | > | oldVel[3*i + j] = vel[j]; |
232 | > | |
233 | if( atoms[i]->isDirectional() ){ | |
234 | < | |
234 | > | |
235 | dAtom = (DirectionalAtom *)atoms[i]; | |
236 | + | |
237 | + | dAtom->getJ( ji ); |
238 | + | |
239 | + | for (j=0; j < 3; j++) |
240 | + | oldJi[3*i + j] = ji[j]; |
241 | + | |
242 | + | } |
243 | + | } |
244 | + | |
245 | + | // do the iteration: |
246 | + | |
247 | + | instaVol = tStats->getVolume(); |
248 | + | |
249 | + | for (k=0; k < 4; k++) { |
250 | + | |
251 | + | instaTemp = tStats->getTemperature(); |
252 | + | instaPress = tStats->getPressure(); |
253 | + | |
254 | + | // evolve chi another half step using the temperature at t + dt/2 |
255 | + | |
256 | + | prevChi = chi; |
257 | + | chi = oldChi + dt2 * ( instaTemp / targetTemp - 1.0) / tt2; |
258 | + | |
259 | + | prevEta = eta; |
260 | + | |
261 | + | // advance eta half step and calculate scale factor for velocity |
262 | + | |
263 | + | eta = oldEta + dt2 * ( instaVol * (instaPress - targetPressure) / |
264 | + | (p_convert*NkBT*tb2)); |
265 | + | |
266 | + | |
267 | + | for( i=0; i<nAtoms; i++ ){ |
268 | + | |
269 | + | atoms[i]->getFrc( frc ); |
270 | + | atoms[i]->getVel(vel); |
271 | ||
272 | < | // get and convert the torque to body frame |
272 | > | mass = atoms[i]->getMass(); |
273 | ||
274 | < | Tb[0] = dAtom->getTx(); |
275 | < | Tb[1] = dAtom->getTy(); |
276 | < | Tb[2] = dAtom->getTz(); |
274 | > | // velocity half step |
275 | > | for (j=0; j < 3; j++) |
276 | > | vel[j] = oldVel[3*i+j] + dt2 * ((frc[j] / mass ) * eConvert - oldVel[3*i + j]*(chi + eta)); |
277 | ||
278 | < | dAtom->lab2Body( Tb ); |
278 | > | atoms[i]->setVel( vel ); |
279 | ||
280 | < | // get the angular momentum, and complete the angular momentum |
281 | < | // half step |
280 | > | if( atoms[i]->isDirectional() ){ |
281 | > | |
282 | > | dAtom = (DirectionalAtom *)atoms[i]; |
283 | > | |
284 | > | // get and convert the torque to body frame |
285 | > | |
286 | > | dAtom->getTrq( Tb ); |
287 | > | dAtom->lab2Body( Tb ); |
288 | > | |
289 | > | for (j=0; j < 3; j++) |
290 | > | ji[j] = oldJi[3*i + j] + dt2 * (Tb[j] * eConvert - oldJi[3*i+j]*chi); |
291 | ||
292 | < | ji[0] = dAtom->getJx(); |
293 | < | ji[1] = dAtom->getJy(); |
180 | < | ji[2] = dAtom->getJz(); |
181 | < | |
182 | < | ji[0] += dt2 * (Tb[0] * eConvert - ji[0]*chi); |
183 | < | ji[1] += dt2 * (Tb[1] * eConvert - ji[1]*chi); |
184 | < | ji[2] += dt2 * (Tb[2] * eConvert - ji[2]*chi); |
185 | < | |
186 | < | dAtom->setJx( ji[0] ); |
187 | < | dAtom->setJy( ji[1] ); |
188 | < | dAtom->setJz( ji[2] ); |
292 | > | dAtom->setJ( ji ); |
293 | > | } |
294 | } | |
295 | + | |
296 | + | if (nConstrained){ |
297 | + | constrainB(); |
298 | + | } |
299 | + | |
300 | + | if (fabs(prevChi - chi) <= |
301 | + | chiTolerance && fabs(prevEta -eta) <= etaTolerance) |
302 | + | break; |
303 | } | |
304 | + | |
305 | + | //calculate integral of chidt |
306 | + | integralOfChidt += dt2*chi; |
307 | + | |
308 | } | |
309 | ||
310 | < | int NPTi::readyCheck() { |
310 | > | template<typename T> void NPTi<T>::resetIntegrator() { |
311 | > | chi = 0.0; |
312 | > | eta = 0.0; |
313 | > | } |
314 | > | |
315 | > | template<typename T> int NPTi<T>::readyCheck() { |
316 | > | |
317 | > | //check parent's readyCheck() first |
318 | > | if (T::readyCheck() == -1) |
319 | > | return -1; |
320 | ||
321 | // First check to see if we have a target temperature. | |
322 | // Not having one is fatal. | |
# | Line 237 | Line 363 | int NPTi::readyCheck() { | |
363 | return -1; | |
364 | } | |
365 | ||
366 | < | // We need NkBT a lot, so just set it here: |
366 | > | if (!have_chi_tolerance) { |
367 | > | sprintf( painCave.errMsg, |
368 | > | "NPTi warning: setting chi tolerance to 1e-6\n"); |
369 | > | chiTolerance = 1e-6; |
370 | > | have_chi_tolerance = 1; |
371 | > | painCave.isFatal = 0; |
372 | > | simError(); |
373 | > | } |
374 | ||
375 | < | NkBT = (double)info->ndf * kB * targetTemp; |
375 | > | if (!have_eta_tolerance) { |
376 | > | sprintf( painCave.errMsg, |
377 | > | "NPTi warning: setting eta tolerance to 1e-6\n"); |
378 | > | etaTolerance = 1e-6; |
379 | > | have_eta_tolerance = 1; |
380 | > | painCave.isFatal = 0; |
381 | > | simError(); |
382 | > | } |
383 | > | |
384 | > | |
385 | > | // We need NkBT a lot, so just set it here: This is the RAW number |
386 | > | // of particles, so no subtraction or addition of constraints or |
387 | > | // orientational degrees of freedom: |
388 | > | |
389 | > | NkBT = (double)Nparticles * kB * targetTemp; |
390 | > | |
391 | > | // fkBT is used because the thermostat operates on more degrees of freedom |
392 | > | // than the barostat (when there are particles with orientational degrees |
393 | > | // of freedom). ndf = 3 * (n_atoms + n_oriented -1) - n_constraint - nZcons |
394 | > | |
395 | > | fkBT = (double)info->ndf * kB * targetTemp; |
396 | ||
397 | return 1; | |
398 | } | |
399 | + | |
400 | + | template<typename T> double NPTi<T>::getConservedQuantity(void){ |
401 | + | |
402 | + | double conservedQuantity; |
403 | + | double Three_NkBT; |
404 | + | double Energy; |
405 | + | double thermostat_kinetic; |
406 | + | double thermostat_potential; |
407 | + | double barostat_kinetic; |
408 | + | double barostat_potential; |
409 | + | double tb2; |
410 | + | double eta2; |
411 | + | |
412 | + | Energy = tStats->getTotalE(); |
413 | + | |
414 | + | thermostat_kinetic = fkBT* tauThermostat * tauThermostat * chi * chi / |
415 | + | (2.0 * eConvert); |
416 | + | |
417 | + | thermostat_potential = fkBT* integralOfChidt / eConvert; |
418 | + | |
419 | + | |
420 | + | barostat_kinetic = 3.0 * NkBT * tauBarostat * tauBarostat * eta * eta / |
421 | + | (2.0 * eConvert); |
422 | + | |
423 | + | barostat_potential = (targetPressure * tStats->getVolume() / p_convert) / |
424 | + | eConvert; |
425 | + | |
426 | + | conservedQuantity = Energy + thermostat_kinetic + thermostat_potential + |
427 | + | barostat_kinetic + barostat_potential; |
428 | + | |
429 | + | cout.width(8); |
430 | + | cout.precision(8); |
431 | + | |
432 | + | cerr << info->getTime() << "\t" << Energy << "\t" << thermostat_kinetic << |
433 | + | "\t" << thermostat_potential << "\t" << barostat_kinetic << |
434 | + | "\t" << barostat_potential << "\t" << conservedQuantity << endl; |
435 | + | |
436 | + | return conservedQuantity; |
437 | + | } |
– | Removed lines |
+ | Added lines |
< | Changed lines |
> | Changed lines |