ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/trunk/src/rnemd/RNEMD.cpp
Revision: 2071
Committed: Sat Mar 7 21:41:51 2015 UTC (10 years, 1 month ago) by gezelter
File size: 77435 byte(s)
Log Message:
Reducing the number of warnings when using g++ to compile.

File Contents

# Content
1 /*
2 * Copyright (c) 2005 The University of Notre Dame. All Rights Reserved.
3 *
4 * The University of Notre Dame grants you ("Licensee") a
5 * non-exclusive, royalty free, license to use, modify and
6 * redistribute this software in source and binary code form, provided
7 * that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * This software is provided "AS IS," without a warranty of any
18 * kind. All express or implied conditions, representations and
19 * warranties, including any implied warranty of merchantability,
20 * fitness for a particular purpose or non-infringement, are hereby
21 * excluded. The University of Notre Dame and its licensors shall not
22 * be liable for any damages suffered by licensee as a result of
23 * using, modifying or distributing the software or its
24 * derivatives. In no event will the University of Notre Dame or its
25 * licensors be liable for any lost revenue, profit or data, or for
26 * direct, indirect, special, consequential, incidental or punitive
27 * damages, however caused and regardless of the theory of liability,
28 * arising out of the use of or inability to use software, even if the
29 * University of Notre Dame has been advised of the possibility of
30 * such damages.
31 *
32 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
33 * research, please cite the appropriate papers when you publish your
34 * work. Good starting points are:
35 *
36 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
37 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
38 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
39 * [4] Vardeman & Gezelter, in progress (2009).
40 */
41 #ifdef IS_MPI
42 #include <mpi.h>
43 #endif
44
45 #include <cmath>
46 #include <sstream>
47 #include <string>
48
49 #include "rnemd/RNEMD.hpp"
50 #include "math/Vector3.hpp"
51 #include "math/Vector.hpp"
52 #include "math/SquareMatrix3.hpp"
53 #include "math/Polynomial.hpp"
54 #include "primitives/Molecule.hpp"
55 #include "primitives/StuntDouble.hpp"
56 #include "utils/PhysicalConstants.hpp"
57 #include "utils/Tuple.hpp"
58 #include "brains/Thermo.hpp"
59 #include "math/ConvexHull.hpp"
60
61 #ifdef _MSC_VER
62 #define isnan(x) _isnan((x))
63 #define isinf(x) (!_finite(x) && !_isnan(x))
64 #endif
65
66 #define HONKING_LARGE_VALUE 1.0e10
67
68 using namespace std;
69 namespace OpenMD {
70
71 RNEMD::RNEMD(SimInfo* info) : info_(info),
72 evaluator_(info_), seleMan_(info_),
73 evaluatorA_(info_), seleManA_(info_),
74 evaluatorB_(info_), seleManB_(info_),
75 commonA_(info_), commonB_(info_),
76 usePeriodicBoundaryConditions_(info_->getSimParams()->getUsePeriodicBoundaryConditions()),
77 hasDividingArea_(false),
78 hasData_(false) {
79
80 trialCount_ = 0;
81 failTrialCount_ = 0;
82 failRootCount_ = 0;
83
84 Globals* simParams = info->getSimParams();
85 RNEMDParameters* rnemdParams = simParams->getRNEMDParameters();
86
87 doRNEMD_ = rnemdParams->getUseRNEMD();
88 if (!doRNEMD_) return;
89
90 stringToMethod_["Swap"] = rnemdSwap;
91 stringToMethod_["NIVS"] = rnemdNIVS;
92 stringToMethod_["VSS"] = rnemdVSS;
93
94 stringToFluxType_["KE"] = rnemdKE;
95 stringToFluxType_["Px"] = rnemdPx;
96 stringToFluxType_["Py"] = rnemdPy;
97 stringToFluxType_["Pz"] = rnemdPz;
98 stringToFluxType_["Pvector"] = rnemdPvector;
99 stringToFluxType_["Lx"] = rnemdLx;
100 stringToFluxType_["Ly"] = rnemdLy;
101 stringToFluxType_["Lz"] = rnemdLz;
102 stringToFluxType_["Lvector"] = rnemdLvector;
103 stringToFluxType_["KE+Px"] = rnemdKePx;
104 stringToFluxType_["KE+Py"] = rnemdKePy;
105 stringToFluxType_["KE+Pvector"] = rnemdKePvector;
106 stringToFluxType_["KE+Lx"] = rnemdKeLx;
107 stringToFluxType_["KE+Ly"] = rnemdKeLy;
108 stringToFluxType_["KE+Lz"] = rnemdKeLz;
109 stringToFluxType_["KE+Lvector"] = rnemdKeLvector;
110
111 runTime_ = simParams->getRunTime();
112 statusTime_ = simParams->getStatusTime();
113
114 const string methStr = rnemdParams->getMethod();
115 bool hasFluxType = rnemdParams->haveFluxType();
116
117 rnemdObjectSelection_ = rnemdParams->getObjectSelection();
118
119 string fluxStr;
120 if (hasFluxType) {
121 fluxStr = rnemdParams->getFluxType();
122 } else {
123 sprintf(painCave.errMsg,
124 "RNEMD: No fluxType was set in the md file. This parameter,\n"
125 "\twhich must be one of the following values:\n"
126 "\tKE, Px, Py, Pz, Pvector, Lx, Ly, Lz, Lvector,\n"
127 "\tKE+Px, KE+Py, KE+Pvector, KE+Lx, KE+Ly, KE+Lz, KE+Lvector\n"
128 "\tmust be set to use RNEMD\n");
129 painCave.isFatal = 1;
130 painCave.severity = OPENMD_ERROR;
131 simError();
132 }
133
134 bool hasKineticFlux = rnemdParams->haveKineticFlux();
135 bool hasMomentumFlux = rnemdParams->haveMomentumFlux();
136 bool hasMomentumFluxVector = rnemdParams->haveMomentumFluxVector();
137 bool hasAngularMomentumFlux = rnemdParams->haveAngularMomentumFlux();
138 bool hasAngularMomentumFluxVector = rnemdParams->haveAngularMomentumFluxVector();
139 hasSelectionA_ = rnemdParams->haveSelectionA();
140 hasSelectionB_ = rnemdParams->haveSelectionB();
141 bool hasSlabWidth = rnemdParams->haveSlabWidth();
142 bool hasSlabACenter = rnemdParams->haveSlabACenter();
143 bool hasSlabBCenter = rnemdParams->haveSlabBCenter();
144 bool hasSphereARadius = rnemdParams->haveSphereARadius();
145 hasSphereBRadius_ = rnemdParams->haveSphereBRadius();
146 bool hasCoordinateOrigin = rnemdParams->haveCoordinateOrigin();
147 bool hasOutputFileName = rnemdParams->haveOutputFileName();
148 bool hasOutputFields = rnemdParams->haveOutputFields();
149
150 map<string, RNEMDMethod>::iterator i;
151 i = stringToMethod_.find(methStr);
152 if (i != stringToMethod_.end())
153 rnemdMethod_ = i->second;
154 else {
155 sprintf(painCave.errMsg,
156 "RNEMD: The current method,\n"
157 "\t\t%s is not one of the recognized\n"
158 "\texchange methods: Swap, NIVS, or VSS\n",
159 methStr.c_str());
160 painCave.isFatal = 1;
161 painCave.severity = OPENMD_ERROR;
162 simError();
163 }
164
165 map<string, RNEMDFluxType>::iterator j;
166 j = stringToFluxType_.find(fluxStr);
167 if (j != stringToFluxType_.end())
168 rnemdFluxType_ = j->second;
169 else {
170 sprintf(painCave.errMsg,
171 "RNEMD: The current fluxType,\n"
172 "\t\t%s\n"
173 "\tis not one of the recognized flux types.\n",
174 fluxStr.c_str());
175 painCave.isFatal = 1;
176 painCave.severity = OPENMD_ERROR;
177 simError();
178 }
179
180 bool methodFluxMismatch = false;
181 bool hasCorrectFlux = false;
182 switch(rnemdMethod_) {
183 case rnemdSwap:
184 switch (rnemdFluxType_) {
185 case rnemdKE:
186 hasCorrectFlux = hasKineticFlux;
187 break;
188 case rnemdPx:
189 case rnemdPy:
190 case rnemdPz:
191 hasCorrectFlux = hasMomentumFlux;
192 break;
193 default :
194 methodFluxMismatch = true;
195 break;
196 }
197 break;
198 case rnemdNIVS:
199 switch (rnemdFluxType_) {
200 case rnemdKE:
201 case rnemdRotKE:
202 case rnemdFullKE:
203 hasCorrectFlux = hasKineticFlux;
204 break;
205 case rnemdPx:
206 case rnemdPy:
207 case rnemdPz:
208 hasCorrectFlux = hasMomentumFlux;
209 break;
210 case rnemdKePx:
211 case rnemdKePy:
212 hasCorrectFlux = hasMomentumFlux && hasKineticFlux;
213 break;
214 default:
215 methodFluxMismatch = true;
216 break;
217 }
218 break;
219 case rnemdVSS:
220 switch (rnemdFluxType_) {
221 case rnemdKE:
222 case rnemdRotKE:
223 case rnemdFullKE:
224 hasCorrectFlux = hasKineticFlux;
225 break;
226 case rnemdPx:
227 case rnemdPy:
228 case rnemdPz:
229 hasCorrectFlux = hasMomentumFlux;
230 break;
231 case rnemdLx:
232 case rnemdLy:
233 case rnemdLz:
234 hasCorrectFlux = hasAngularMomentumFlux;
235 break;
236 case rnemdPvector:
237 hasCorrectFlux = hasMomentumFluxVector;
238 break;
239 case rnemdLvector:
240 hasCorrectFlux = hasAngularMomentumFluxVector;
241 break;
242 case rnemdKePx:
243 case rnemdKePy:
244 hasCorrectFlux = hasMomentumFlux && hasKineticFlux;
245 break;
246 case rnemdKeLx:
247 case rnemdKeLy:
248 case rnemdKeLz:
249 hasCorrectFlux = hasAngularMomentumFlux && hasKineticFlux;
250 break;
251 case rnemdKePvector:
252 hasCorrectFlux = hasMomentumFluxVector && hasKineticFlux;
253 break;
254 case rnemdKeLvector:
255 hasCorrectFlux = hasAngularMomentumFluxVector && hasKineticFlux;
256 break;
257 default:
258 methodFluxMismatch = true;
259 break;
260 }
261 default:
262 break;
263 }
264
265 if (methodFluxMismatch) {
266 sprintf(painCave.errMsg,
267 "RNEMD: The current method,\n"
268 "\t\t%s\n"
269 "\tcannot be used with the current flux type, %s\n",
270 methStr.c_str(), fluxStr.c_str());
271 painCave.isFatal = 1;
272 painCave.severity = OPENMD_ERROR;
273 simError();
274 }
275 if (!hasCorrectFlux) {
276 sprintf(painCave.errMsg,
277 "RNEMD: The current method, %s, and flux type, %s,\n"
278 "\tdid not have the correct flux value specified. Options\n"
279 "\tinclude: kineticFlux, momentumFlux, angularMomentumFlux,\n"
280 "\tmomentumFluxVector, and angularMomentumFluxVector.\n",
281 methStr.c_str(), fluxStr.c_str());
282 painCave.isFatal = 1;
283 painCave.severity = OPENMD_ERROR;
284 simError();
285 }
286
287 if (hasKineticFlux) {
288 // convert the kcal / mol / Angstroms^2 / fs values in the md file
289 // into amu / fs^3:
290 kineticFlux_ = rnemdParams->getKineticFlux()
291 * PhysicalConstants::energyConvert;
292 } else {
293 kineticFlux_ = 0.0;
294 }
295 if (hasMomentumFluxVector) {
296 std::vector<RealType> mf = rnemdParams->getMomentumFluxVector();
297 if (mf.size() != 3) {
298 sprintf(painCave.errMsg,
299 "RNEMD: Incorrect number of parameters specified for momentumFluxVector.\n"
300 "\tthere should be 3 parameters, but %lu were specified.\n",
301 mf.size());
302 painCave.isFatal = 1;
303 simError();
304 }
305 momentumFluxVector_.x() = mf[0];
306 momentumFluxVector_.y() = mf[1];
307 momentumFluxVector_.z() = mf[2];
308 } else {
309 momentumFluxVector_ = V3Zero;
310 if (hasMomentumFlux) {
311 RealType momentumFlux = rnemdParams->getMomentumFlux();
312 switch (rnemdFluxType_) {
313 case rnemdPx:
314 momentumFluxVector_.x() = momentumFlux;
315 break;
316 case rnemdPy:
317 momentumFluxVector_.y() = momentumFlux;
318 break;
319 case rnemdPz:
320 momentumFluxVector_.z() = momentumFlux;
321 break;
322 case rnemdKePx:
323 momentumFluxVector_.x() = momentumFlux;
324 break;
325 case rnemdKePy:
326 momentumFluxVector_.y() = momentumFlux;
327 break;
328 default:
329 break;
330 }
331 }
332 }
333 if (hasAngularMomentumFluxVector) {
334 std::vector<RealType> amf = rnemdParams->getAngularMomentumFluxVector();
335 if (amf.size() != 3) {
336 sprintf(painCave.errMsg,
337 "RNEMD: Incorrect number of parameters specified for angularMomentumFluxVector.\n"
338 "\tthere should be 3 parameters, but %lu were specified.\n",
339 amf.size());
340 painCave.isFatal = 1;
341 simError();
342 }
343 angularMomentumFluxVector_.x() = amf[0];
344 angularMomentumFluxVector_.y() = amf[1];
345 angularMomentumFluxVector_.z() = amf[2];
346 } else {
347 angularMomentumFluxVector_ = V3Zero;
348 if (hasAngularMomentumFlux) {
349 RealType angularMomentumFlux = rnemdParams->getAngularMomentumFlux();
350 switch (rnemdFluxType_) {
351 case rnemdLx:
352 angularMomentumFluxVector_.x() = angularMomentumFlux;
353 break;
354 case rnemdLy:
355 angularMomentumFluxVector_.y() = angularMomentumFlux;
356 break;
357 case rnemdLz:
358 angularMomentumFluxVector_.z() = angularMomentumFlux;
359 break;
360 case rnemdKeLx:
361 angularMomentumFluxVector_.x() = angularMomentumFlux;
362 break;
363 case rnemdKeLy:
364 angularMomentumFluxVector_.y() = angularMomentumFlux;
365 break;
366 case rnemdKeLz:
367 angularMomentumFluxVector_.z() = angularMomentumFlux;
368 break;
369 default:
370 break;
371 }
372 }
373 }
374
375 if (hasCoordinateOrigin) {
376 std::vector<RealType> co = rnemdParams->getCoordinateOrigin();
377 if (co.size() != 3) {
378 sprintf(painCave.errMsg,
379 "RNEMD: Incorrect number of parameters specified for coordinateOrigin.\n"
380 "\tthere should be 3 parameters, but %lu were specified.\n",
381 co.size());
382 painCave.isFatal = 1;
383 simError();
384 }
385 coordinateOrigin_.x() = co[0];
386 coordinateOrigin_.y() = co[1];
387 coordinateOrigin_.z() = co[2];
388 } else {
389 coordinateOrigin_ = V3Zero;
390 }
391
392 // do some sanity checking
393
394 int selectionCount = seleMan_.getSelectionCount();
395 int nIntegrable = info->getNGlobalIntegrableObjects();
396 if (selectionCount > nIntegrable) {
397 sprintf(painCave.errMsg,
398 "RNEMD: The current objectSelection,\n"
399 "\t\t%s\n"
400 "\thas resulted in %d selected objects. However,\n"
401 "\tthe total number of integrable objects in the system\n"
402 "\tis only %d. This is almost certainly not what you want\n"
403 "\tto do. A likely cause of this is forgetting the _RB_0\n"
404 "\tselector in the selection script!\n",
405 rnemdObjectSelection_.c_str(),
406 selectionCount, nIntegrable);
407 painCave.isFatal = 0;
408 painCave.severity = OPENMD_WARNING;
409 simError();
410 }
411
412 areaAccumulator_ = new Accumulator();
413
414 nBins_ = rnemdParams->getOutputBins();
415 binWidth_ = rnemdParams->getOutputBinWidth();
416
417 data_.resize(RNEMD::ENDINDEX);
418 OutputData z;
419 z.units = "Angstroms";
420 z.title = "Z";
421 z.dataType = "RealType";
422 z.accumulator.reserve(nBins_);
423 for (int i = 0; i < nBins_; i++)
424 z.accumulator.push_back( new Accumulator() );
425 data_[Z] = z;
426 outputMap_["Z"] = Z;
427
428 OutputData r;
429 r.units = "Angstroms";
430 r.title = "R";
431 r.dataType = "RealType";
432 r.accumulator.reserve(nBins_);
433 for (int i = 0; i < nBins_; i++)
434 r.accumulator.push_back( new Accumulator() );
435 data_[R] = r;
436 outputMap_["R"] = R;
437
438 OutputData temperature;
439 temperature.units = "K";
440 temperature.title = "Temperature";
441 temperature.dataType = "RealType";
442 temperature.accumulator.reserve(nBins_);
443 for (int i = 0; i < nBins_; i++)
444 temperature.accumulator.push_back( new Accumulator() );
445 data_[TEMPERATURE] = temperature;
446 outputMap_["TEMPERATURE"] = TEMPERATURE;
447
448 OutputData velocity;
449 velocity.units = "angstroms/fs";
450 velocity.title = "Velocity";
451 velocity.dataType = "Vector3d";
452 velocity.accumulator.reserve(nBins_);
453 for (int i = 0; i < nBins_; i++)
454 velocity.accumulator.push_back( new VectorAccumulator() );
455 data_[VELOCITY] = velocity;
456 outputMap_["VELOCITY"] = VELOCITY;
457
458 OutputData angularVelocity;
459 angularVelocity.units = "angstroms^2/fs";
460 angularVelocity.title = "AngularVelocity";
461 angularVelocity.dataType = "Vector3d";
462 angularVelocity.accumulator.reserve(nBins_);
463 for (int i = 0; i < nBins_; i++)
464 angularVelocity.accumulator.push_back( new VectorAccumulator() );
465 data_[ANGULARVELOCITY] = angularVelocity;
466 outputMap_["ANGULARVELOCITY"] = ANGULARVELOCITY;
467
468 OutputData density;
469 density.units = "g cm^-3";
470 density.title = "Density";
471 density.dataType = "RealType";
472 density.accumulator.reserve(nBins_);
473 for (int i = 0; i < nBins_; i++)
474 density.accumulator.push_back( new Accumulator() );
475 data_[DENSITY] = density;
476 outputMap_["DENSITY"] = DENSITY;
477
478 if (hasOutputFields) {
479 parseOutputFileFormat(rnemdParams->getOutputFields());
480 } else {
481 if (usePeriodicBoundaryConditions_)
482 outputMask_.set(Z);
483 else
484 outputMask_.set(R);
485 switch (rnemdFluxType_) {
486 case rnemdKE:
487 case rnemdRotKE:
488 case rnemdFullKE:
489 outputMask_.set(TEMPERATURE);
490 break;
491 case rnemdPx:
492 case rnemdPy:
493 outputMask_.set(VELOCITY);
494 break;
495 case rnemdPz:
496 case rnemdPvector:
497 outputMask_.set(VELOCITY);
498 outputMask_.set(DENSITY);
499 break;
500 case rnemdLx:
501 case rnemdLy:
502 case rnemdLz:
503 case rnemdLvector:
504 outputMask_.set(ANGULARVELOCITY);
505 break;
506 case rnemdKeLx:
507 case rnemdKeLy:
508 case rnemdKeLz:
509 case rnemdKeLvector:
510 outputMask_.set(TEMPERATURE);
511 outputMask_.set(ANGULARVELOCITY);
512 break;
513 case rnemdKePx:
514 case rnemdKePy:
515 outputMask_.set(TEMPERATURE);
516 outputMask_.set(VELOCITY);
517 break;
518 case rnemdKePvector:
519 outputMask_.set(TEMPERATURE);
520 outputMask_.set(VELOCITY);
521 outputMask_.set(DENSITY);
522 break;
523 default:
524 break;
525 }
526 }
527
528 if (hasOutputFileName) {
529 rnemdFileName_ = rnemdParams->getOutputFileName();
530 } else {
531 rnemdFileName_ = getPrefix(info->getFinalConfigFileName()) + ".rnemd";
532 }
533
534 exchangeTime_ = rnemdParams->getExchangeTime();
535
536 Snapshot* currentSnap_ = info->getSnapshotManager()->getCurrentSnapshot();
537 // total exchange sums are zeroed out at the beginning:
538
539 kineticExchange_ = 0.0;
540 momentumExchange_ = V3Zero;
541 angularMomentumExchange_ = V3Zero;
542
543 std::ostringstream selectionAstream;
544 std::ostringstream selectionBstream;
545
546 if (hasSelectionA_) {
547 selectionA_ = rnemdParams->getSelectionA();
548 } else {
549 if (usePeriodicBoundaryConditions_) {
550 Mat3x3d hmat = currentSnap_->getHmat();
551
552 if (hasSlabWidth)
553 slabWidth_ = rnemdParams->getSlabWidth();
554 else
555 slabWidth_ = hmat(2,2) / 10.0;
556
557 if (hasSlabACenter)
558 slabACenter_ = rnemdParams->getSlabACenter();
559 else
560 slabACenter_ = 0.0;
561
562 selectionAstream << "select wrappedz > "
563 << slabACenter_ - 0.5*slabWidth_
564 << " && wrappedz < "
565 << slabACenter_ + 0.5*slabWidth_;
566 selectionA_ = selectionAstream.str();
567 } else {
568 if (hasSphereARadius)
569 sphereARadius_ = rnemdParams->getSphereARadius();
570 else {
571 // use an initial guess to the size of the inner slab to be 1/10 the
572 // radius of an approximately spherical hull:
573 Thermo thermo(info);
574 RealType hVol = thermo.getHullVolume();
575 sphereARadius_ = 0.1 * pow((3.0 * hVol / (4.0 * M_PI)), 1.0/3.0);
576 }
577 selectionAstream << "select r < " << sphereARadius_;
578 selectionA_ = selectionAstream.str();
579 }
580 }
581
582 if (hasSelectionB_) {
583 selectionB_ = rnemdParams->getSelectionB();
584
585 } else {
586 if (usePeriodicBoundaryConditions_) {
587 Mat3x3d hmat = currentSnap_->getHmat();
588
589 if (hasSlabWidth)
590 slabWidth_ = rnemdParams->getSlabWidth();
591 else
592 slabWidth_ = hmat(2,2) / 10.0;
593
594 if (hasSlabBCenter)
595 slabBCenter_ = rnemdParams->getSlabBCenter();
596 else
597 slabBCenter_ = hmat(2,2) / 2.0;
598
599 selectionBstream << "select wrappedz > "
600 << slabBCenter_ - 0.5*slabWidth_
601 << " && wrappedz < "
602 << slabBCenter_ + 0.5*slabWidth_;
603 selectionB_ = selectionBstream.str();
604 } else {
605 if (hasSphereBRadius_) {
606 sphereBRadius_ = rnemdParams->getSphereBRadius();
607 selectionBstream << "select r > " << sphereBRadius_;
608 selectionB_ = selectionBstream.str();
609 } else {
610 selectionB_ = "select hull";
611 BisHull_ = true;
612 hasSelectionB_ = true;
613 }
614 }
615 }
616
617
618 // object evaluator:
619 evaluator_.loadScriptString(rnemdObjectSelection_);
620 seleMan_.setSelectionSet(evaluator_.evaluate());
621 evaluatorA_.loadScriptString(selectionA_);
622 evaluatorB_.loadScriptString(selectionB_);
623 seleManA_.setSelectionSet(evaluatorA_.evaluate());
624 seleManB_.setSelectionSet(evaluatorB_.evaluate());
625 commonA_ = seleManA_ & seleMan_;
626 commonB_ = seleManB_ & seleMan_;
627 }
628
629
630 RNEMD::~RNEMD() {
631 if (!doRNEMD_) return;
632 #ifdef IS_MPI
633 if (worldRank == 0) {
634 #endif
635
636 writeOutputFile();
637
638 rnemdFile_.close();
639
640 #ifdef IS_MPI
641 }
642 #endif
643
644 // delete all of the objects we created:
645 delete areaAccumulator_;
646 data_.clear();
647 }
648
649 void RNEMD::doSwap(SelectionManager& smanA, SelectionManager& smanB) {
650 if (!doRNEMD_) return;
651 int selei;
652 int selej;
653
654 Snapshot* currentSnap_ = info_->getSnapshotManager()->getCurrentSnapshot();
655 Mat3x3d hmat = currentSnap_->getHmat();
656
657 StuntDouble* sd;
658
659 RealType min_val(0.0);
660 int min_found = 0;
661 StuntDouble* min_sd = NULL;
662
663 RealType max_val(0.0);
664 int max_found = 0;
665 StuntDouble* max_sd = NULL;
666
667 for (sd = seleManA_.beginSelected(selei); sd != NULL;
668 sd = seleManA_.nextSelected(selei)) {
669
670 Vector3d pos = sd->getPos();
671
672 // wrap the stuntdouble's position back into the box:
673
674 if (usePeriodicBoundaryConditions_)
675 currentSnap_->wrapVector(pos);
676
677 RealType mass = sd->getMass();
678 Vector3d vel = sd->getVel();
679 RealType value(0.0);
680
681 switch(rnemdFluxType_) {
682 case rnemdKE :
683
684 value = mass * vel.lengthSquare();
685
686 if (sd->isDirectional()) {
687 Vector3d angMom = sd->getJ();
688 Mat3x3d I = sd->getI();
689
690 if (sd->isLinear()) {
691 int i = sd->linearAxis();
692 int j = (i + 1) % 3;
693 int k = (i + 2) % 3;
694 value += angMom[j] * angMom[j] / I(j, j) +
695 angMom[k] * angMom[k] / I(k, k);
696 } else {
697 value += angMom[0]*angMom[0]/I(0, 0)
698 + angMom[1]*angMom[1]/I(1, 1)
699 + angMom[2]*angMom[2]/I(2, 2);
700 }
701 } //angular momenta exchange enabled
702 value *= 0.5;
703 break;
704 case rnemdPx :
705 value = mass * vel[0];
706 break;
707 case rnemdPy :
708 value = mass * vel[1];
709 break;
710 case rnemdPz :
711 value = mass * vel[2];
712 break;
713 default :
714 break;
715 }
716 if (!max_found) {
717 max_val = value;
718 max_sd = sd;
719 max_found = 1;
720 } else {
721 if (max_val < value) {
722 max_val = value;
723 max_sd = sd;
724 }
725 }
726 }
727
728 for (sd = seleManB_.beginSelected(selej); sd != NULL;
729 sd = seleManB_.nextSelected(selej)) {
730
731 Vector3d pos = sd->getPos();
732
733 // wrap the stuntdouble's position back into the box:
734
735 if (usePeriodicBoundaryConditions_)
736 currentSnap_->wrapVector(pos);
737
738 RealType mass = sd->getMass();
739 Vector3d vel = sd->getVel();
740 RealType value(0.0);
741
742 switch(rnemdFluxType_) {
743 case rnemdKE :
744
745 value = mass * vel.lengthSquare();
746
747 if (sd->isDirectional()) {
748 Vector3d angMom = sd->getJ();
749 Mat3x3d I = sd->getI();
750
751 if (sd->isLinear()) {
752 int i = sd->linearAxis();
753 int j = (i + 1) % 3;
754 int k = (i + 2) % 3;
755 value += angMom[j] * angMom[j] / I(j, j) +
756 angMom[k] * angMom[k] / I(k, k);
757 } else {
758 value += angMom[0]*angMom[0]/I(0, 0)
759 + angMom[1]*angMom[1]/I(1, 1)
760 + angMom[2]*angMom[2]/I(2, 2);
761 }
762 } //angular momenta exchange enabled
763 value *= 0.5;
764 break;
765 case rnemdPx :
766 value = mass * vel[0];
767 break;
768 case rnemdPy :
769 value = mass * vel[1];
770 break;
771 case rnemdPz :
772 value = mass * vel[2];
773 break;
774 default :
775 break;
776 }
777
778 if (!min_found) {
779 min_val = value;
780 min_sd = sd;
781 min_found = 1;
782 } else {
783 if (min_val > value) {
784 min_val = value;
785 min_sd = sd;
786 }
787 }
788 }
789
790 #ifdef IS_MPI
791 int worldRank;
792 MPI_Comm_rank( MPI_COMM_WORLD, &worldRank);
793
794 int my_min_found = min_found;
795 int my_max_found = max_found;
796
797 // Even if we didn't find a minimum, did someone else?
798 MPI_Allreduce(&my_min_found, &min_found, 1, MPI_INT, MPI_LOR,
799 MPI_COMM_WORLD);
800 // Even if we didn't find a maximum, did someone else?
801 MPI_Allreduce(&my_max_found, &max_found, 1, MPI_INT, MPI_LOR,
802 MPI_COMM_WORLD);
803 #endif
804
805 if (max_found && min_found) {
806
807 #ifdef IS_MPI
808 struct {
809 RealType val;
810 int rank;
811 } max_vals, min_vals;
812
813 if (my_min_found) {
814 min_vals.val = min_val;
815 } else {
816 min_vals.val = HONKING_LARGE_VALUE;
817 }
818 min_vals.rank = worldRank;
819
820 // Who had the minimum?
821 MPI_Allreduce(&min_vals, &min_vals,
822 1, MPI_REALTYPE_INT, MPI_MINLOC, MPI_COMM_WORLD);
823 min_val = min_vals.val;
824
825 if (my_max_found) {
826 max_vals.val = max_val;
827 } else {
828 max_vals.val = -HONKING_LARGE_VALUE;
829 }
830 max_vals.rank = worldRank;
831
832 // Who had the maximum?
833 MPI_Allreduce(&max_vals, &max_vals,
834 1, MPI_REALTYPE_INT, MPI_MAXLOC, MPI_COMM_WORLD);
835 max_val = max_vals.val;
836 #endif
837
838 if (min_val < max_val) {
839
840 #ifdef IS_MPI
841 if (max_vals.rank == worldRank && min_vals.rank == worldRank) {
842 // I have both maximum and minimum, so proceed like a single
843 // processor version:
844 #endif
845
846 Vector3d min_vel = min_sd->getVel();
847 Vector3d max_vel = max_sd->getVel();
848 RealType temp_vel;
849
850 switch(rnemdFluxType_) {
851 case rnemdKE :
852 min_sd->setVel(max_vel);
853 max_sd->setVel(min_vel);
854 if (min_sd->isDirectional() && max_sd->isDirectional()) {
855 Vector3d min_angMom = min_sd->getJ();
856 Vector3d max_angMom = max_sd->getJ();
857 min_sd->setJ(max_angMom);
858 max_sd->setJ(min_angMom);
859 }//angular momenta exchange enabled
860 //assumes same rigid body identity
861 break;
862 case rnemdPx :
863 temp_vel = min_vel.x();
864 min_vel.x() = max_vel.x();
865 max_vel.x() = temp_vel;
866 min_sd->setVel(min_vel);
867 max_sd->setVel(max_vel);
868 break;
869 case rnemdPy :
870 temp_vel = min_vel.y();
871 min_vel.y() = max_vel.y();
872 max_vel.y() = temp_vel;
873 min_sd->setVel(min_vel);
874 max_sd->setVel(max_vel);
875 break;
876 case rnemdPz :
877 temp_vel = min_vel.z();
878 min_vel.z() = max_vel.z();
879 max_vel.z() = temp_vel;
880 min_sd->setVel(min_vel);
881 max_sd->setVel(max_vel);
882 break;
883 default :
884 break;
885 }
886
887 #ifdef IS_MPI
888 // the rest of the cases only apply in parallel simulations:
889 } else if (max_vals.rank == worldRank) {
890 // I had the max, but not the minimum
891
892 Vector3d min_vel;
893 Vector3d max_vel = max_sd->getVel();
894 MPI_Status status;
895
896 // point-to-point swap of the velocity vector
897 MPI_Sendrecv(max_vel.getArrayPointer(), 3, MPI_REALTYPE,
898 min_vals.rank, 0,
899 min_vel.getArrayPointer(), 3, MPI_REALTYPE,
900 min_vals.rank, 0, MPI_COMM_WORLD, &status);
901
902 switch(rnemdFluxType_) {
903 case rnemdKE :
904 max_sd->setVel(min_vel);
905 //angular momenta exchange enabled
906 if (max_sd->isDirectional()) {
907 Vector3d min_angMom;
908 Vector3d max_angMom = max_sd->getJ();
909
910 // point-to-point swap of the angular momentum vector
911 MPI_Sendrecv(max_angMom.getArrayPointer(), 3,
912 MPI_REALTYPE, min_vals.rank, 1,
913 min_angMom.getArrayPointer(), 3,
914 MPI_REALTYPE, min_vals.rank, 1,
915 MPI_COMM_WORLD, &status);
916
917 max_sd->setJ(min_angMom);
918 }
919 break;
920 case rnemdPx :
921 max_vel.x() = min_vel.x();
922 max_sd->setVel(max_vel);
923 break;
924 case rnemdPy :
925 max_vel.y() = min_vel.y();
926 max_sd->setVel(max_vel);
927 break;
928 case rnemdPz :
929 max_vel.z() = min_vel.z();
930 max_sd->setVel(max_vel);
931 break;
932 default :
933 break;
934 }
935 } else if (min_vals.rank == worldRank) {
936 // I had the minimum but not the maximum:
937
938 Vector3d max_vel;
939 Vector3d min_vel = min_sd->getVel();
940 MPI_Status status;
941
942 // point-to-point swap of the velocity vector
943 MPI_Sendrecv(min_vel.getArrayPointer(), 3, MPI_REALTYPE,
944 max_vals.rank, 0,
945 max_vel.getArrayPointer(), 3, MPI_REALTYPE,
946 max_vals.rank, 0, MPI_COMM_WORLD, &status);
947
948 switch(rnemdFluxType_) {
949 case rnemdKE :
950 min_sd->setVel(max_vel);
951 //angular momenta exchange enabled
952 if (min_sd->isDirectional()) {
953 Vector3d min_angMom = min_sd->getJ();
954 Vector3d max_angMom;
955
956 // point-to-point swap of the angular momentum vector
957 MPI_Sendrecv(min_angMom.getArrayPointer(), 3,
958 MPI_REALTYPE, max_vals.rank, 1,
959 max_angMom.getArrayPointer(), 3,
960 MPI_REALTYPE, max_vals.rank, 1,
961 MPI_COMM_WORLD, &status);
962
963 min_sd->setJ(max_angMom);
964 }
965 break;
966 case rnemdPx :
967 min_vel.x() = max_vel.x();
968 min_sd->setVel(min_vel);
969 break;
970 case rnemdPy :
971 min_vel.y() = max_vel.y();
972 min_sd->setVel(min_vel);
973 break;
974 case rnemdPz :
975 min_vel.z() = max_vel.z();
976 min_sd->setVel(min_vel);
977 break;
978 default :
979 break;
980 }
981 }
982 #endif
983
984 switch(rnemdFluxType_) {
985 case rnemdKE:
986 kineticExchange_ += max_val - min_val;
987 break;
988 case rnemdPx:
989 momentumExchange_.x() += max_val - min_val;
990 break;
991 case rnemdPy:
992 momentumExchange_.y() += max_val - min_val;
993 break;
994 case rnemdPz:
995 momentumExchange_.z() += max_val - min_val;
996 break;
997 default:
998 break;
999 }
1000 } else {
1001 sprintf(painCave.errMsg,
1002 "RNEMD::doSwap exchange NOT performed because min_val > max_val\n");
1003 painCave.isFatal = 0;
1004 painCave.severity = OPENMD_INFO;
1005 simError();
1006 failTrialCount_++;
1007 }
1008 } else {
1009 sprintf(painCave.errMsg,
1010 "RNEMD::doSwap exchange NOT performed because selected object\n"
1011 "\twas not present in at least one of the two slabs.\n");
1012 painCave.isFatal = 0;
1013 painCave.severity = OPENMD_INFO;
1014 simError();
1015 failTrialCount_++;
1016 }
1017 }
1018
1019 void RNEMD::doNIVS(SelectionManager& smanA, SelectionManager& smanB) {
1020 if (!doRNEMD_) return;
1021 int selei;
1022 int selej;
1023
1024 Snapshot* currentSnap_ = info_->getSnapshotManager()->getCurrentSnapshot();
1025 Mat3x3d hmat = currentSnap_->getHmat();
1026
1027 StuntDouble* sd;
1028
1029 vector<StuntDouble*> hotBin, coldBin;
1030
1031 RealType Phx = 0.0;
1032 RealType Phy = 0.0;
1033 RealType Phz = 0.0;
1034 RealType Khx = 0.0;
1035 RealType Khy = 0.0;
1036 RealType Khz = 0.0;
1037 RealType Khw = 0.0;
1038 RealType Pcx = 0.0;
1039 RealType Pcy = 0.0;
1040 RealType Pcz = 0.0;
1041 RealType Kcx = 0.0;
1042 RealType Kcy = 0.0;
1043 RealType Kcz = 0.0;
1044 RealType Kcw = 0.0;
1045
1046 for (sd = smanA.beginSelected(selei); sd != NULL;
1047 sd = smanA.nextSelected(selei)) {
1048
1049 Vector3d pos = sd->getPos();
1050
1051 // wrap the stuntdouble's position back into the box:
1052
1053 if (usePeriodicBoundaryConditions_)
1054 currentSnap_->wrapVector(pos);
1055
1056
1057 RealType mass = sd->getMass();
1058 Vector3d vel = sd->getVel();
1059
1060 hotBin.push_back(sd);
1061 Phx += mass * vel.x();
1062 Phy += mass * vel.y();
1063 Phz += mass * vel.z();
1064 Khx += mass * vel.x() * vel.x();
1065 Khy += mass * vel.y() * vel.y();
1066 Khz += mass * vel.z() * vel.z();
1067 if (sd->isDirectional()) {
1068 Vector3d angMom = sd->getJ();
1069 Mat3x3d I = sd->getI();
1070 if (sd->isLinear()) {
1071 int i = sd->linearAxis();
1072 int j = (i + 1) % 3;
1073 int k = (i + 2) % 3;
1074 Khw += angMom[j] * angMom[j] / I(j, j) +
1075 angMom[k] * angMom[k] / I(k, k);
1076 } else {
1077 Khw += angMom[0]*angMom[0]/I(0, 0)
1078 + angMom[1]*angMom[1]/I(1, 1)
1079 + angMom[2]*angMom[2]/I(2, 2);
1080 }
1081 }
1082 }
1083 for (sd = smanB.beginSelected(selej); sd != NULL;
1084 sd = smanB.nextSelected(selej)) {
1085 Vector3d pos = sd->getPos();
1086
1087 // wrap the stuntdouble's position back into the box:
1088
1089 if (usePeriodicBoundaryConditions_)
1090 currentSnap_->wrapVector(pos);
1091
1092 RealType mass = sd->getMass();
1093 Vector3d vel = sd->getVel();
1094
1095 coldBin.push_back(sd);
1096 Pcx += mass * vel.x();
1097 Pcy += mass * vel.y();
1098 Pcz += mass * vel.z();
1099 Kcx += mass * vel.x() * vel.x();
1100 Kcy += mass * vel.y() * vel.y();
1101 Kcz += mass * vel.z() * vel.z();
1102 if (sd->isDirectional()) {
1103 Vector3d angMom = sd->getJ();
1104 Mat3x3d I = sd->getI();
1105 if (sd->isLinear()) {
1106 int i = sd->linearAxis();
1107 int j = (i + 1) % 3;
1108 int k = (i + 2) % 3;
1109 Kcw += angMom[j] * angMom[j] / I(j, j) +
1110 angMom[k] * angMom[k] / I(k, k);
1111 } else {
1112 Kcw += angMom[0]*angMom[0]/I(0, 0)
1113 + angMom[1]*angMom[1]/I(1, 1)
1114 + angMom[2]*angMom[2]/I(2, 2);
1115 }
1116 }
1117 }
1118
1119 Khx *= 0.5;
1120 Khy *= 0.5;
1121 Khz *= 0.5;
1122 Khw *= 0.5;
1123 Kcx *= 0.5;
1124 Kcy *= 0.5;
1125 Kcz *= 0.5;
1126 Kcw *= 0.5;
1127
1128 #ifdef IS_MPI
1129 MPI_Allreduce(MPI_IN_PLACE, &Phx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1130 MPI_Allreduce(MPI_IN_PLACE, &Phy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1131 MPI_Allreduce(MPI_IN_PLACE, &Phz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1132 MPI_Allreduce(MPI_IN_PLACE, &Pcx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1133 MPI_Allreduce(MPI_IN_PLACE, &Pcy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1134 MPI_Allreduce(MPI_IN_PLACE, &Pcz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1135
1136 MPI_Allreduce(MPI_IN_PLACE, &Khx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1137 MPI_Allreduce(MPI_IN_PLACE, &Khy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1138 MPI_Allreduce(MPI_IN_PLACE, &Khz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1139 MPI_Allreduce(MPI_IN_PLACE, &Khw, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1140
1141 MPI_Allreduce(MPI_IN_PLACE, &Kcx, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1142 MPI_Allreduce(MPI_IN_PLACE, &Kcy, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1143 MPI_Allreduce(MPI_IN_PLACE, &Kcz, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1144 MPI_Allreduce(MPI_IN_PLACE, &Kcw, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1145 #endif
1146
1147 //solve coldBin coeff's first
1148 RealType px = Pcx / Phx;
1149 RealType py = Pcy / Phy;
1150 RealType pz = Pcz / Phz;
1151 RealType c(0.0), x(0.0), y(0.0), z(0.0);
1152 bool successfulScale = false;
1153 if ((rnemdFluxType_ == rnemdFullKE) ||
1154 (rnemdFluxType_ == rnemdRotKE)) {
1155 //may need sanity check Khw & Kcw > 0
1156
1157 if (rnemdFluxType_ == rnemdFullKE) {
1158 c = 1.0 - kineticTarget_ / (Kcx + Kcy + Kcz + Kcw);
1159 } else {
1160 c = 1.0 - kineticTarget_ / Kcw;
1161 }
1162
1163 if ((c > 0.81) && (c < 1.21)) {//restrict scaling coefficients
1164 c = sqrt(c);
1165
1166 RealType w = 0.0;
1167 if (rnemdFluxType_ == rnemdFullKE) {
1168 x = 1.0 + px * (1.0 - c);
1169 y = 1.0 + py * (1.0 - c);
1170 z = 1.0 + pz * (1.0 - c);
1171 /* more complicated way
1172 w = 1.0 + (Kcw - Kcw * c * c - (c * c * (Kcx + Kcy + Kcz
1173 + Khx * px * px + Khy * py * py + Khz * pz * pz)
1174 - 2.0 * c * (Khx * px * (1.0 + px) + Khy * py * (1.0 + py)
1175 + Khz * pz * (1.0 + pz)) + Khx * px * (2.0 + px)
1176 + Khy * py * (2.0 + py) + Khz * pz * (2.0 + pz)
1177 - Kcx - Kcy - Kcz)) / Khw; the following is simpler
1178 */
1179 if ((fabs(x - 1.0) < 0.1) && (fabs(y - 1.0) < 0.1) &&
1180 (fabs(z - 1.0) < 0.1)) {
1181 w = 1.0 + (kineticTarget_
1182 + Khx * (1.0 - x * x) + Khy * (1.0 - y * y)
1183 + Khz * (1.0 - z * z)) / Khw;
1184 }//no need to calculate w if x, y or z is out of range
1185 } else {
1186 w = 1.0 + kineticTarget_ / Khw;
1187 }
1188 if ((w > 0.81) && (w < 1.21)) {//restrict scaling coefficients
1189 //if w is in the right range, so should be x, y, z.
1190 vector<StuntDouble*>::iterator sdi;
1191 Vector3d vel;
1192 for (sdi = coldBin.begin(); sdi != coldBin.end(); ++sdi) {
1193 if (rnemdFluxType_ == rnemdFullKE) {
1194 vel = (*sdi)->getVel() * c;
1195 (*sdi)->setVel(vel);
1196 }
1197 if ((*sdi)->isDirectional()) {
1198 Vector3d angMom = (*sdi)->getJ() * c;
1199 (*sdi)->setJ(angMom);
1200 }
1201 }
1202 w = sqrt(w);
1203 for (sdi = hotBin.begin(); sdi != hotBin.end(); ++sdi) {
1204 if (rnemdFluxType_ == rnemdFullKE) {
1205 vel = (*sdi)->getVel();
1206 vel.x() *= x;
1207 vel.y() *= y;
1208 vel.z() *= z;
1209 (*sdi)->setVel(vel);
1210 }
1211 if ((*sdi)->isDirectional()) {
1212 Vector3d angMom = (*sdi)->getJ() * w;
1213 (*sdi)->setJ(angMom);
1214 }
1215 }
1216 successfulScale = true;
1217 kineticExchange_ += kineticTarget_;
1218 }
1219 }
1220 } else {
1221 RealType a000(0.0), a110(0.0), c0(0.0);
1222 RealType a001(0.0), a111(0.0), b01(0.0), b11(0.0), c1(0.0);
1223 switch(rnemdFluxType_) {
1224 case rnemdKE :
1225 /* used hotBin coeff's & only scale x & y dimensions
1226 RealType px = Phx / Pcx;
1227 RealType py = Phy / Pcy;
1228 a110 = Khy;
1229 c0 = - Khx - Khy - kineticTarget_;
1230 a000 = Khx;
1231 a111 = Kcy * py * py;
1232 b11 = -2.0 * Kcy * py * (1.0 + py);
1233 c1 = Kcy * py * (2.0 + py) + Kcx * px * ( 2.0 + px) + kineticTarget_;
1234 b01 = -2.0 * Kcx * px * (1.0 + px);
1235 a001 = Kcx * px * px;
1236 */
1237 //scale all three dimensions, let c_x = c_y
1238 a000 = Kcx + Kcy;
1239 a110 = Kcz;
1240 c0 = kineticTarget_ - Kcx - Kcy - Kcz;
1241 a001 = Khx * px * px + Khy * py * py;
1242 a111 = Khz * pz * pz;
1243 b01 = -2.0 * (Khx * px * (1.0 + px) + Khy * py * (1.0 + py));
1244 b11 = -2.0 * Khz * pz * (1.0 + pz);
1245 c1 = Khx * px * (2.0 + px) + Khy * py * (2.0 + py)
1246 + Khz * pz * (2.0 + pz) - kineticTarget_;
1247 break;
1248 case rnemdPx :
1249 c = 1 - momentumTarget_.x() / Pcx;
1250 a000 = Kcy;
1251 a110 = Kcz;
1252 c0 = Kcx * c * c - Kcx - Kcy - Kcz;
1253 a001 = py * py * Khy;
1254 a111 = pz * pz * Khz;
1255 b01 = -2.0 * Khy * py * (1.0 + py);
1256 b11 = -2.0 * Khz * pz * (1.0 + pz);
1257 c1 = Khy * py * (2.0 + py) + Khz * pz * (2.0 + pz)
1258 + Khx * (fastpow(c * px - px - 1.0, 2) - 1.0);
1259 break;
1260 case rnemdPy :
1261 c = 1 - momentumTarget_.y() / Pcy;
1262 a000 = Kcx;
1263 a110 = Kcz;
1264 c0 = Kcy * c * c - Kcx - Kcy - Kcz;
1265 a001 = px * px * Khx;
1266 a111 = pz * pz * Khz;
1267 b01 = -2.0 * Khx * px * (1.0 + px);
1268 b11 = -2.0 * Khz * pz * (1.0 + pz);
1269 c1 = Khx * px * (2.0 + px) + Khz * pz * (2.0 + pz)
1270 + Khy * (fastpow(c * py - py - 1.0, 2) - 1.0);
1271 break;
1272 case rnemdPz ://we don't really do this, do we?
1273 c = 1 - momentumTarget_.z() / Pcz;
1274 a000 = Kcx;
1275 a110 = Kcy;
1276 c0 = Kcz * c * c - Kcx - Kcy - Kcz;
1277 a001 = px * px * Khx;
1278 a111 = py * py * Khy;
1279 b01 = -2.0 * Khx * px * (1.0 + px);
1280 b11 = -2.0 * Khy * py * (1.0 + py);
1281 c1 = Khx * px * (2.0 + px) + Khy * py * (2.0 + py)
1282 + Khz * (fastpow(c * pz - pz - 1.0, 2) - 1.0);
1283 break;
1284 default :
1285 break;
1286 }
1287
1288 RealType v1 = a000 * a111 - a001 * a110;
1289 RealType v2 = a000 * b01;
1290 RealType v3 = a000 * b11;
1291 RealType v4 = a000 * c1 - a001 * c0;
1292 RealType v8 = a110 * b01;
1293 RealType v10 = - b01 * c0;
1294
1295 RealType u0 = v2 * v10 - v4 * v4;
1296 RealType u1 = -2.0 * v3 * v4;
1297 RealType u2 = -v2 * v8 - v3 * v3 - 2.0 * v1 * v4;
1298 RealType u3 = -2.0 * v1 * v3;
1299 RealType u4 = - v1 * v1;
1300 //rescale coefficients
1301 RealType maxAbs = fabs(u0);
1302 if (maxAbs < fabs(u1)) maxAbs = fabs(u1);
1303 if (maxAbs < fabs(u2)) maxAbs = fabs(u2);
1304 if (maxAbs < fabs(u3)) maxAbs = fabs(u3);
1305 if (maxAbs < fabs(u4)) maxAbs = fabs(u4);
1306 u0 /= maxAbs;
1307 u1 /= maxAbs;
1308 u2 /= maxAbs;
1309 u3 /= maxAbs;
1310 u4 /= maxAbs;
1311 //max_element(start, end) is also available.
1312 Polynomial<RealType> poly; //same as DoublePolynomial poly;
1313 poly.setCoefficient(4, u4);
1314 poly.setCoefficient(3, u3);
1315 poly.setCoefficient(2, u2);
1316 poly.setCoefficient(1, u1);
1317 poly.setCoefficient(0, u0);
1318 vector<RealType> realRoots = poly.FindRealRoots();
1319
1320 vector<RealType>::iterator ri;
1321 RealType r1, r2, alpha0;
1322 vector<pair<RealType,RealType> > rps;
1323 for (ri = realRoots.begin(); ri !=realRoots.end(); ++ri) {
1324 r2 = *ri;
1325 // Check to see if FindRealRoots() gave the right answer:
1326 if ( fabs(u0 + r2 * (u1 + r2 * (u2 + r2 * (u3 + r2 * u4)))) > 1e-6 ) {
1327 sprintf(painCave.errMsg,
1328 "RNEMD Warning: polynomial solve seems to have an error!");
1329 painCave.isFatal = 0;
1330 simError();
1331 failRootCount_++;
1332 }
1333 // Might not be useful w/o rescaling coefficients
1334 alpha0 = -c0 - a110 * r2 * r2;
1335 if (alpha0 >= 0.0) {
1336 r1 = sqrt(alpha0 / a000);
1337 if (fabs(c1 + r1 * (b01 + r1 * a001) + r2 * (b11 + r2 * a111))
1338 < 1e-6)
1339 { rps.push_back(make_pair(r1, r2)); }
1340 if (r1 > 1e-6) { //r1 non-negative
1341 r1 = -r1;
1342 if (fabs(c1 + r1 * (b01 + r1 * a001) + r2 * (b11 + r2 * a111))
1343 < 1e-6)
1344 { rps.push_back(make_pair(r1, r2)); }
1345 }
1346 }
1347 }
1348 // Consider combining together the part for solving for the pair
1349 // w/ the searching for the best solution part so that we don't
1350 // need the pairs vector:
1351 if (!rps.empty()) {
1352 RealType smallestDiff = HONKING_LARGE_VALUE;
1353 RealType diff(0.0);
1354 std::pair<RealType,RealType> bestPair = std::make_pair(1.0, 1.0);
1355 std::vector<std::pair<RealType,RealType> >::iterator rpi;
1356 for (rpi = rps.begin(); rpi != rps.end(); ++rpi) {
1357 r1 = (*rpi).first;
1358 r2 = (*rpi).second;
1359 switch(rnemdFluxType_) {
1360 case rnemdKE :
1361 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2)
1362 + fastpow(r1 * r1 / r2 / r2 - Kcz/Kcx, 2)
1363 + fastpow(r1 * r1 / r2 / r2 - Kcz/Kcy, 2);
1364 break;
1365 case rnemdPx :
1366 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2)
1367 + fastpow(r1 * r1 / r2 / r2 - Kcz/Kcy, 2);
1368 break;
1369 case rnemdPy :
1370 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2)
1371 + fastpow(r1 * r1 / r2 / r2 - Kcz/Kcx, 2);
1372 break;
1373 case rnemdPz :
1374 diff = fastpow(1.0 - r1, 2) + fastpow(1.0 - r2, 2)
1375 + fastpow(r1 * r1 / r2 / r2 - Kcy/Kcx, 2);
1376 default :
1377 break;
1378 }
1379 if (diff < smallestDiff) {
1380 smallestDiff = diff;
1381 bestPair = *rpi;
1382 }
1383 }
1384 #ifdef IS_MPI
1385 if (worldRank == 0) {
1386 #endif
1387 // sprintf(painCave.errMsg,
1388 // "RNEMD: roots r1= %lf\tr2 = %lf\n",
1389 // bestPair.first, bestPair.second);
1390 // painCave.isFatal = 0;
1391 // painCave.severity = OPENMD_INFO;
1392 // simError();
1393 #ifdef IS_MPI
1394 }
1395 #endif
1396
1397 switch(rnemdFluxType_) {
1398 case rnemdKE :
1399 x = bestPair.first;
1400 y = bestPair.first;
1401 z = bestPair.second;
1402 break;
1403 case rnemdPx :
1404 x = c;
1405 y = bestPair.first;
1406 z = bestPair.second;
1407 break;
1408 case rnemdPy :
1409 x = bestPair.first;
1410 y = c;
1411 z = bestPair.second;
1412 break;
1413 case rnemdPz :
1414 x = bestPair.first;
1415 y = bestPair.second;
1416 z = c;
1417 break;
1418 default :
1419 break;
1420 }
1421 vector<StuntDouble*>::iterator sdi;
1422 Vector3d vel;
1423 for (sdi = coldBin.begin(); sdi != coldBin.end(); ++sdi) {
1424 vel = (*sdi)->getVel();
1425 vel.x() *= x;
1426 vel.y() *= y;
1427 vel.z() *= z;
1428 (*sdi)->setVel(vel);
1429 }
1430 //convert to hotBin coefficient
1431 x = 1.0 + px * (1.0 - x);
1432 y = 1.0 + py * (1.0 - y);
1433 z = 1.0 + pz * (1.0 - z);
1434 for (sdi = hotBin.begin(); sdi != hotBin.end(); ++sdi) {
1435 vel = (*sdi)->getVel();
1436 vel.x() *= x;
1437 vel.y() *= y;
1438 vel.z() *= z;
1439 (*sdi)->setVel(vel);
1440 }
1441 successfulScale = true;
1442 switch(rnemdFluxType_) {
1443 case rnemdKE :
1444 kineticExchange_ += kineticTarget_;
1445 break;
1446 case rnemdPx :
1447 case rnemdPy :
1448 case rnemdPz :
1449 momentumExchange_ += momentumTarget_;
1450 break;
1451 default :
1452 break;
1453 }
1454 }
1455 }
1456 if (successfulScale != true) {
1457 sprintf(painCave.errMsg,
1458 "RNEMD::doNIVS exchange NOT performed - roots that solve\n"
1459 "\tthe constraint equations may not exist or there may be\n"
1460 "\tno selected objects in one or both slabs.\n");
1461 painCave.isFatal = 0;
1462 painCave.severity = OPENMD_INFO;
1463 simError();
1464 failTrialCount_++;
1465 }
1466 }
1467
1468 void RNEMD::doVSS(SelectionManager& smanA, SelectionManager& smanB) {
1469 if (!doRNEMD_) return;
1470 int selei;
1471 int selej;
1472
1473 Snapshot* currentSnap_ = info_->getSnapshotManager()->getCurrentSnapshot();
1474 Mat3x3d hmat = currentSnap_->getHmat();
1475
1476 StuntDouble* sd;
1477
1478 vector<StuntDouble*> hotBin, coldBin;
1479
1480 Vector3d Ph(V3Zero);
1481 Vector3d Lh(V3Zero);
1482 RealType Mh = 0.0;
1483 Mat3x3d Ih(0.0);
1484 RealType Kh = 0.0;
1485 Vector3d Pc(V3Zero);
1486 Vector3d Lc(V3Zero);
1487 RealType Mc = 0.0;
1488 Mat3x3d Ic(0.0);
1489 RealType Kc = 0.0;
1490
1491 // Constraints can be on only the linear or angular momentum, but
1492 // not both. Usually, the user will specify which they want, but
1493 // in case they don't, the use of periodic boundaries should make
1494 // the choice for us.
1495 bool doLinearPart = false;
1496 bool doAngularPart = false;
1497
1498 switch (rnemdFluxType_) {
1499 case rnemdPx:
1500 case rnemdPy:
1501 case rnemdPz:
1502 case rnemdPvector:
1503 case rnemdKePx:
1504 case rnemdKePy:
1505 case rnemdKePvector:
1506 doLinearPart = true;
1507 break;
1508 case rnemdLx:
1509 case rnemdLy:
1510 case rnemdLz:
1511 case rnemdLvector:
1512 case rnemdKeLx:
1513 case rnemdKeLy:
1514 case rnemdKeLz:
1515 case rnemdKeLvector:
1516 doAngularPart = true;
1517 break;
1518 case rnemdKE:
1519 case rnemdRotKE:
1520 case rnemdFullKE:
1521 default:
1522 if (usePeriodicBoundaryConditions_)
1523 doLinearPart = true;
1524 else
1525 doAngularPart = true;
1526 break;
1527 }
1528
1529 for (sd = smanA.beginSelected(selei); sd != NULL;
1530 sd = smanA.nextSelected(selei)) {
1531
1532 Vector3d pos = sd->getPos();
1533
1534 // wrap the stuntdouble's position back into the box:
1535
1536 if (usePeriodicBoundaryConditions_)
1537 currentSnap_->wrapVector(pos);
1538
1539 RealType mass = sd->getMass();
1540 Vector3d vel = sd->getVel();
1541 Vector3d rPos = sd->getPos() - coordinateOrigin_;
1542 RealType r2;
1543
1544 hotBin.push_back(sd);
1545 Ph += mass * vel;
1546 Mh += mass;
1547 Kh += mass * vel.lengthSquare();
1548 Lh += mass * cross(rPos, vel);
1549 Ih -= outProduct(rPos, rPos) * mass;
1550 r2 = rPos.lengthSquare();
1551 Ih(0, 0) += mass * r2;
1552 Ih(1, 1) += mass * r2;
1553 Ih(2, 2) += mass * r2;
1554
1555 if (rnemdFluxType_ == rnemdFullKE) {
1556 if (sd->isDirectional()) {
1557 Vector3d angMom = sd->getJ();
1558 Mat3x3d I = sd->getI();
1559 if (sd->isLinear()) {
1560 int i = sd->linearAxis();
1561 int j = (i + 1) % 3;
1562 int k = (i + 2) % 3;
1563 Kh += angMom[j] * angMom[j] / I(j, j) +
1564 angMom[k] * angMom[k] / I(k, k);
1565 } else {
1566 Kh += angMom[0] * angMom[0] / I(0, 0) +
1567 angMom[1] * angMom[1] / I(1, 1) +
1568 angMom[2] * angMom[2] / I(2, 2);
1569 }
1570 }
1571 }
1572 }
1573 for (sd = smanB.beginSelected(selej); sd != NULL;
1574 sd = smanB.nextSelected(selej)) {
1575
1576 Vector3d pos = sd->getPos();
1577
1578 // wrap the stuntdouble's position back into the box:
1579
1580 if (usePeriodicBoundaryConditions_)
1581 currentSnap_->wrapVector(pos);
1582
1583 RealType mass = sd->getMass();
1584 Vector3d vel = sd->getVel();
1585 Vector3d rPos = sd->getPos() - coordinateOrigin_;
1586 RealType r2;
1587
1588 coldBin.push_back(sd);
1589 Pc += mass * vel;
1590 Mc += mass;
1591 Kc += mass * vel.lengthSquare();
1592 Lc += mass * cross(rPos, vel);
1593 Ic -= outProduct(rPos, rPos) * mass;
1594 r2 = rPos.lengthSquare();
1595 Ic(0, 0) += mass * r2;
1596 Ic(1, 1) += mass * r2;
1597 Ic(2, 2) += mass * r2;
1598
1599 if (rnemdFluxType_ == rnemdFullKE) {
1600 if (sd->isDirectional()) {
1601 Vector3d angMom = sd->getJ();
1602 Mat3x3d I = sd->getI();
1603 if (sd->isLinear()) {
1604 int i = sd->linearAxis();
1605 int j = (i + 1) % 3;
1606 int k = (i + 2) % 3;
1607 Kc += angMom[j] * angMom[j] / I(j, j) +
1608 angMom[k] * angMom[k] / I(k, k);
1609 } else {
1610 Kc += angMom[0] * angMom[0] / I(0, 0) +
1611 angMom[1] * angMom[1] / I(1, 1) +
1612 angMom[2] * angMom[2] / I(2, 2);
1613 }
1614 }
1615 }
1616 }
1617
1618 Kh *= 0.5;
1619 Kc *= 0.5;
1620
1621 #ifdef IS_MPI
1622 MPI_Allreduce(MPI_IN_PLACE, &Ph[0], 3, MPI_REALTYPE, MPI_SUM,
1623 MPI_COMM_WORLD);
1624 MPI_Allreduce(MPI_IN_PLACE, &Pc[0], 3, MPI_REALTYPE, MPI_SUM,
1625 MPI_COMM_WORLD);
1626 MPI_Allreduce(MPI_IN_PLACE, &Lh[0], 3, MPI_REALTYPE, MPI_SUM,
1627 MPI_COMM_WORLD);
1628 MPI_Allreduce(MPI_IN_PLACE, &Lc[0], 3, MPI_REALTYPE, MPI_SUM,
1629 MPI_COMM_WORLD);
1630 MPI_Allreduce(MPI_IN_PLACE, &Mh, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1631 MPI_Allreduce(MPI_IN_PLACE, &Kh, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1632 MPI_Allreduce(MPI_IN_PLACE, &Mc, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1633 MPI_Allreduce(MPI_IN_PLACE, &Kc, 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1634 MPI_Allreduce(MPI_IN_PLACE, Ih.getArrayPointer(), 9,
1635 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1636 MPI_Allreduce(MPI_IN_PLACE, Ic.getArrayPointer(), 9,
1637 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
1638 #endif
1639
1640
1641 Vector3d ac, acrec, bc, bcrec;
1642 Vector3d ah, ahrec, bh, bhrec;
1643
1644 bool successfulExchange = false;
1645 if ((Mh > 0.0) && (Mc > 0.0)) {//both slabs are not empty
1646 Vector3d vc = Pc / Mc;
1647 ac = -momentumTarget_ / Mc + vc;
1648 acrec = -momentumTarget_ / Mc;
1649
1650 // We now need the inverse of the inertia tensor to calculate the
1651 // angular velocity of the cold slab;
1652 Mat3x3d Ici = Ic.inverse();
1653 Vector3d omegac = Ici * Lc;
1654 bc = -(Ici * angularMomentumTarget_) + omegac;
1655 bcrec = bc - omegac;
1656
1657 RealType cNumerator = Kc - kineticTarget_;
1658 if (doLinearPart)
1659 cNumerator -= 0.5 * Mc * ac.lengthSquare();
1660
1661 if (doAngularPart)
1662 cNumerator -= 0.5 * ( dot(bc, Ic * bc));
1663
1664 if (cNumerator > 0.0) {
1665
1666 RealType cDenominator = Kc;
1667
1668 if (doLinearPart)
1669 cDenominator -= 0.5 * Mc * vc.lengthSquare();
1670
1671 if (doAngularPart)
1672 cDenominator -= 0.5*(dot(omegac, Ic * omegac));
1673
1674 if (cDenominator > 0.0) {
1675 RealType c = sqrt(cNumerator / cDenominator);
1676 if ((c > 0.9) && (c < 1.1)) {//restrict scaling coefficients
1677
1678 Vector3d vh = Ph / Mh;
1679 ah = momentumTarget_ / Mh + vh;
1680 ahrec = momentumTarget_ / Mh;
1681
1682 // We now need the inverse of the inertia tensor to
1683 // calculate the angular velocity of the hot slab;
1684 Mat3x3d Ihi = Ih.inverse();
1685 Vector3d omegah = Ihi * Lh;
1686 bh = (Ihi * angularMomentumTarget_) + omegah;
1687 bhrec = bh - omegah;
1688
1689 RealType hNumerator = Kh + kineticTarget_;
1690 if (doLinearPart)
1691 hNumerator -= 0.5 * Mh * ah.lengthSquare();
1692
1693 if (doAngularPart)
1694 hNumerator -= 0.5 * ( dot(bh, Ih * bh));
1695
1696 if (hNumerator > 0.0) {
1697
1698 RealType hDenominator = Kh;
1699 if (doLinearPart)
1700 hDenominator -= 0.5 * Mh * vh.lengthSquare();
1701 if (doAngularPart)
1702 hDenominator -= 0.5*(dot(omegah, Ih * omegah));
1703
1704 if (hDenominator > 0.0) {
1705 RealType h = sqrt(hNumerator / hDenominator);
1706 if ((h > 0.9) && (h < 1.1)) {
1707
1708 vector<StuntDouble*>::iterator sdi;
1709 Vector3d vel;
1710 Vector3d rPos;
1711
1712 for (sdi = coldBin.begin(); sdi != coldBin.end(); ++sdi) {
1713 //vel = (*sdi)->getVel();
1714 rPos = (*sdi)->getPos() - coordinateOrigin_;
1715 if (doLinearPart)
1716 vel = ((*sdi)->getVel() - vc) * c + ac;
1717 if (doAngularPart)
1718 vel = ((*sdi)->getVel() - cross(omegac, rPos)) * c + cross(bc, rPos);
1719
1720 (*sdi)->setVel(vel);
1721 if (rnemdFluxType_ == rnemdFullKE) {
1722 if ((*sdi)->isDirectional()) {
1723 Vector3d angMom = (*sdi)->getJ() * c;
1724 (*sdi)->setJ(angMom);
1725 }
1726 }
1727 }
1728 for (sdi = hotBin.begin(); sdi != hotBin.end(); ++sdi) {
1729 //vel = (*sdi)->getVel();
1730 rPos = (*sdi)->getPos() - coordinateOrigin_;
1731 if (doLinearPart)
1732 vel = ((*sdi)->getVel() - vh) * h + ah;
1733 if (doAngularPart)
1734 vel = ((*sdi)->getVel() - cross(omegah, rPos)) * h + cross(bh, rPos);
1735
1736 (*sdi)->setVel(vel);
1737 if (rnemdFluxType_ == rnemdFullKE) {
1738 if ((*sdi)->isDirectional()) {
1739 Vector3d angMom = (*sdi)->getJ() * h;
1740 (*sdi)->setJ(angMom);
1741 }
1742 }
1743 }
1744 successfulExchange = true;
1745 kineticExchange_ += kineticTarget_;
1746 momentumExchange_ += momentumTarget_;
1747 angularMomentumExchange_ += angularMomentumTarget_;
1748 }
1749 }
1750 }
1751 }
1752 }
1753 }
1754 }
1755 if (successfulExchange != true) {
1756 sprintf(painCave.errMsg,
1757 "RNEMD::doVSS exchange NOT performed - roots that solve\n"
1758 "\tthe constraint equations may not exist or there may be\n"
1759 "\tno selected objects in one or both slabs.\n");
1760 painCave.isFatal = 0;
1761 painCave.severity = OPENMD_INFO;
1762 simError();
1763 failTrialCount_++;
1764 }
1765 }
1766
1767 RealType RNEMD::getDividingArea() {
1768
1769 if (hasDividingArea_) return dividingArea_;
1770
1771 RealType areaA, areaB;
1772 Snapshot* snap = info_->getSnapshotManager()->getCurrentSnapshot();
1773
1774 if (hasSelectionA_) {
1775
1776 if (evaluatorA_.hasSurfaceArea())
1777 areaA = evaluatorA_.getSurfaceArea();
1778 else {
1779
1780 int isd;
1781 StuntDouble* sd;
1782 vector<StuntDouble*> aSites;
1783 seleManA_.setSelectionSet(evaluatorA_.evaluate());
1784 for (sd = seleManA_.beginSelected(isd); sd != NULL;
1785 sd = seleManA_.nextSelected(isd)) {
1786 aSites.push_back(sd);
1787 }
1788 #if defined(HAVE_QHULL)
1789 ConvexHull* surfaceMeshA = new ConvexHull();
1790 surfaceMeshA->computeHull(aSites);
1791 areaA = surfaceMeshA->getArea();
1792 delete surfaceMeshA;
1793 #else
1794 sprintf( painCave.errMsg,
1795 "RNEMD::getDividingArea : Hull calculation is not possible\n"
1796 "\twithout libqhull. Please rebuild OpenMD with qhull enabled.");
1797 painCave.severity = OPENMD_ERROR;
1798 painCave.isFatal = 1;
1799 simError();
1800 #endif
1801 }
1802
1803 } else {
1804 if (usePeriodicBoundaryConditions_) {
1805 // in periodic boundaries, the surface area is twice the x-y
1806 // area of the current box:
1807 areaA = 2.0 * snap->getXYarea();
1808 } else {
1809 // in non-periodic simulations, without explicitly setting
1810 // selections, the sphere radius sets the surface area of the
1811 // dividing surface:
1812 areaA = 4.0 * M_PI * pow(sphereARadius_, 2);
1813 }
1814 }
1815
1816 if (hasSelectionB_) {
1817 if (evaluatorB_.hasSurfaceArea()) {
1818 areaB = evaluatorB_.getSurfaceArea();
1819 } else {
1820
1821 int isd;
1822 StuntDouble* sd;
1823 vector<StuntDouble*> bSites;
1824 seleManB_.setSelectionSet(evaluatorB_.evaluate());
1825 for (sd = seleManB_.beginSelected(isd); sd != NULL;
1826 sd = seleManB_.nextSelected(isd)) {
1827 bSites.push_back(sd);
1828 }
1829
1830 #if defined(HAVE_QHULL)
1831 ConvexHull* surfaceMeshB = new ConvexHull();
1832 surfaceMeshB->computeHull(bSites);
1833 areaB = surfaceMeshB->getArea();
1834 delete surfaceMeshB;
1835 #else
1836 sprintf( painCave.errMsg,
1837 "RNEMD::getDividingArea : Hull calculation is not possible\n"
1838 "\twithout libqhull. Please rebuild OpenMD with qhull enabled.");
1839 painCave.severity = OPENMD_ERROR;
1840 painCave.isFatal = 1;
1841 simError();
1842 #endif
1843 }
1844
1845 } else {
1846 if (usePeriodicBoundaryConditions_) {
1847 // in periodic boundaries, the surface area is twice the x-y
1848 // area of the current box:
1849 areaB = 2.0 * snap->getXYarea();
1850 } else {
1851 // in non-periodic simulations, without explicitly setting
1852 // selections, but if a sphereBradius has been set, just use that:
1853 areaB = 4.0 * M_PI * pow(sphereBRadius_, 2);
1854 }
1855 }
1856
1857 dividingArea_ = min(areaA, areaB);
1858 hasDividingArea_ = true;
1859 return dividingArea_;
1860 }
1861
1862 void RNEMD::doRNEMD() {
1863 if (!doRNEMD_) return;
1864 trialCount_++;
1865
1866 // object evaluator:
1867 evaluator_.loadScriptString(rnemdObjectSelection_);
1868 seleMan_.setSelectionSet(evaluator_.evaluate());
1869
1870 evaluatorA_.loadScriptString(selectionA_);
1871 evaluatorB_.loadScriptString(selectionB_);
1872
1873 seleManA_.setSelectionSet(evaluatorA_.evaluate());
1874 seleManB_.setSelectionSet(evaluatorB_.evaluate());
1875
1876 commonA_ = seleManA_ & seleMan_;
1877 commonB_ = seleManB_ & seleMan_;
1878
1879 // Target exchange quantities (in each exchange) = dividingArea * dt * flux
1880 // dt = exchange time interval
1881 // flux = target flux
1882 // dividingArea = smallest dividing surface between the two regions
1883
1884 hasDividingArea_ = false;
1885 RealType area = getDividingArea();
1886
1887 kineticTarget_ = kineticFlux_ * exchangeTime_ * area;
1888 momentumTarget_ = momentumFluxVector_ * exchangeTime_ * area;
1889 angularMomentumTarget_ = angularMomentumFluxVector_ * exchangeTime_ * area;
1890
1891 switch(rnemdMethod_) {
1892 case rnemdSwap:
1893 doSwap(commonA_, commonB_);
1894 break;
1895 case rnemdNIVS:
1896 doNIVS(commonA_, commonB_);
1897 break;
1898 case rnemdVSS:
1899 doVSS(commonA_, commonB_);
1900 break;
1901 case rnemdUnkownMethod:
1902 default :
1903 break;
1904 }
1905 }
1906
1907 void RNEMD::collectData() {
1908 if (!doRNEMD_) return;
1909 Snapshot* currentSnap_ = info_->getSnapshotManager()->getCurrentSnapshot();
1910
1911 // collectData can be called more frequently than the doRNEMD, so use the
1912 // computed area from the last exchange time:
1913 RealType area = getDividingArea();
1914 areaAccumulator_->add(area);
1915 Mat3x3d hmat = currentSnap_->getHmat();
1916 Vector3d u = angularMomentumFluxVector_;
1917 u.normalize();
1918
1919 seleMan_.setSelectionSet(evaluator_.evaluate());
1920
1921 int selei(0);
1922 StuntDouble* sd;
1923 int binNo;
1924 RealType mass;
1925 Vector3d vel;
1926 Vector3d rPos;
1927 RealType KE;
1928 Vector3d L;
1929 Mat3x3d I;
1930 RealType r2;
1931
1932 vector<RealType> binMass(nBins_, 0.0);
1933 vector<Vector3d> binP(nBins_, V3Zero);
1934 vector<RealType> binOmega(nBins_, 0.0);
1935 vector<Vector3d> binL(nBins_, V3Zero);
1936 vector<Mat3x3d> binI(nBins_);
1937 vector<RealType> binKE(nBins_, 0.0);
1938 vector<int> binDOF(nBins_, 0);
1939 vector<int> binCount(nBins_, 0);
1940
1941 // alternative approach, track all molecules instead of only those
1942 // selected for scaling/swapping:
1943 /*
1944 SimInfo::MoleculeIterator miter;
1945 vector<StuntDouble*>::iterator iiter;
1946 Molecule* mol;
1947 StuntDouble* sd;
1948 for (mol = info_->beginMolecule(miter); mol != NULL;
1949 mol = info_->nextMolecule(miter))
1950 sd is essentially sd
1951 for (sd = mol->beginIntegrableObject(iiter);
1952 sd != NULL;
1953 sd = mol->nextIntegrableObject(iiter))
1954 */
1955
1956 for (sd = seleMan_.beginSelected(selei); sd != NULL;
1957 sd = seleMan_.nextSelected(selei)) {
1958
1959 Vector3d pos = sd->getPos();
1960
1961 // wrap the stuntdouble's position back into the box:
1962
1963 if (usePeriodicBoundaryConditions_) {
1964 currentSnap_->wrapVector(pos);
1965 // which bin is this stuntdouble in?
1966 // wrapped positions are in the range [-0.5*hmat(2,2), +0.5*hmat(2,2)]
1967 // Shift molecules by half a box to have bins start at 0
1968 // The modulo operator is used to wrap the case when we are
1969 // beyond the end of the bins back to the beginning.
1970 binNo = int(nBins_ * (pos.z() / hmat(2,2) + 0.5)) % nBins_;
1971 } else {
1972 Vector3d rPos = pos - coordinateOrigin_;
1973 binNo = int(rPos.length() / binWidth_);
1974 }
1975
1976 mass = sd->getMass();
1977 vel = sd->getVel();
1978 rPos = sd->getPos() - coordinateOrigin_;
1979 KE = 0.5 * mass * vel.lengthSquare();
1980 L = mass * cross(rPos, vel);
1981 I = outProduct(rPos, rPos) * mass;
1982 r2 = rPos.lengthSquare();
1983 I(0, 0) += mass * r2;
1984 I(1, 1) += mass * r2;
1985 I(2, 2) += mass * r2;
1986
1987 // Project the relative position onto a plane perpendicular to
1988 // the angularMomentumFluxVector:
1989 // Vector3d rProj = rPos - dot(rPos, u) * u;
1990 // Project the velocity onto a plane perpendicular to the
1991 // angularMomentumFluxVector:
1992 // Vector3d vProj = vel - dot(vel, u) * u;
1993 // Compute angular velocity vector (should be nearly parallel to
1994 // angularMomentumFluxVector
1995 // Vector3d aVel = cross(rProj, vProj);
1996
1997 if (binNo >= 0 && binNo < nBins_) {
1998 binCount[binNo]++;
1999 binMass[binNo] += mass;
2000 binP[binNo] += mass*vel;
2001 binKE[binNo] += KE;
2002 binI[binNo] += I;
2003 binL[binNo] += L;
2004 binDOF[binNo] += 3;
2005
2006 if (sd->isDirectional()) {
2007 Vector3d angMom = sd->getJ();
2008 Mat3x3d Ia = sd->getI();
2009 if (sd->isLinear()) {
2010 int i = sd->linearAxis();
2011 int j = (i + 1) % 3;
2012 int k = (i + 2) % 3;
2013 binKE[binNo] += 0.5 * (angMom[j] * angMom[j] / Ia(j, j) +
2014 angMom[k] * angMom[k] / Ia(k, k));
2015 binDOF[binNo] += 2;
2016 } else {
2017 binKE[binNo] += 0.5 * (angMom[0] * angMom[0] / Ia(0, 0) +
2018 angMom[1] * angMom[1] / Ia(1, 1) +
2019 angMom[2] * angMom[2] / Ia(2, 2));
2020 binDOF[binNo] += 3;
2021 }
2022 }
2023 }
2024 }
2025
2026 #ifdef IS_MPI
2027
2028 for (int i = 0; i < nBins_; i++) {
2029
2030 MPI_Allreduce(MPI_IN_PLACE, &binCount[i],
2031 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
2032 MPI_Allreduce(MPI_IN_PLACE, &binMass[i],
2033 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
2034 MPI_Allreduce(MPI_IN_PLACE, binP[i].getArrayPointer(),
2035 3, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
2036 MPI_Allreduce(MPI_IN_PLACE, binL[i].getArrayPointer(),
2037 3, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
2038 MPI_Allreduce(MPI_IN_PLACE, binI[i].getArrayPointer(),
2039 9, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
2040 MPI_Allreduce(MPI_IN_PLACE, &binKE[i],
2041 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
2042 MPI_Allreduce(MPI_IN_PLACE, &binDOF[i],
2043 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
2044 //MPI_Allreduce(MPI_IN_PLACE, &binOmega[i],
2045 // 1, MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
2046 }
2047
2048 #endif
2049
2050 Vector3d omega;
2051 RealType den;
2052 RealType temp;
2053 RealType z;
2054 RealType r;
2055 for (int i = 0; i < nBins_; i++) {
2056 if (usePeriodicBoundaryConditions_) {
2057 z = (((RealType)i + 0.5) / (RealType)nBins_) * hmat(2,2);
2058 den = binMass[i] * nBins_ * PhysicalConstants::densityConvert
2059 / currentSnap_->getVolume() ;
2060 } else {
2061 r = (((RealType)i + 0.5) * binWidth_);
2062 RealType rinner = (RealType)i * binWidth_;
2063 RealType router = (RealType)(i+1) * binWidth_;
2064 den = binMass[i] * 3.0 * PhysicalConstants::densityConvert
2065 / (4.0 * M_PI * (pow(router,3) - pow(rinner,3)));
2066 }
2067 vel = binP[i] / binMass[i];
2068
2069 omega = binI[i].inverse() * binL[i];
2070
2071 // omega = binOmega[i] / binCount[i];
2072
2073 if (binCount[i] > 0) {
2074 // only add values if there are things to add
2075 temp = 2.0 * binKE[i] / (binDOF[i] * PhysicalConstants::kb *
2076 PhysicalConstants::energyConvert);
2077
2078 for (unsigned int j = 0; j < outputMask_.size(); ++j) {
2079 if(outputMask_[j]) {
2080 switch(j) {
2081 case Z:
2082 dynamic_cast<Accumulator *>(data_[j].accumulator[i])->add(z);
2083 break;
2084 case R:
2085 dynamic_cast<Accumulator *>(data_[j].accumulator[i])->add(r);
2086 break;
2087 case TEMPERATURE:
2088 dynamic_cast<Accumulator *>(data_[j].accumulator[i])->add(temp);
2089 break;
2090 case VELOCITY:
2091 dynamic_cast<VectorAccumulator *>(data_[j].accumulator[i])->add(vel);
2092 break;
2093 case ANGULARVELOCITY:
2094 dynamic_cast<VectorAccumulator *>(data_[j].accumulator[i])->add(omega);
2095 break;
2096 case DENSITY:
2097 dynamic_cast<Accumulator *>(data_[j].accumulator[i])->add(den);
2098 break;
2099 }
2100 }
2101 }
2102 }
2103 }
2104 hasData_ = true;
2105 }
2106
2107 void RNEMD::getStarted() {
2108 if (!doRNEMD_) return;
2109 hasDividingArea_ = false;
2110 collectData();
2111 writeOutputFile();
2112 }
2113
2114 void RNEMD::parseOutputFileFormat(const std::string& format) {
2115 if (!doRNEMD_) return;
2116 StringTokenizer tokenizer(format, " ,;|\t\n\r");
2117
2118 while(tokenizer.hasMoreTokens()) {
2119 std::string token(tokenizer.nextToken());
2120 toUpper(token);
2121 OutputMapType::iterator i = outputMap_.find(token);
2122 if (i != outputMap_.end()) {
2123 outputMask_.set(i->second);
2124 } else {
2125 sprintf( painCave.errMsg,
2126 "RNEMD::parseOutputFileFormat: %s is not a recognized\n"
2127 "\toutputFileFormat keyword.\n", token.c_str() );
2128 painCave.isFatal = 0;
2129 painCave.severity = OPENMD_ERROR;
2130 simError();
2131 }
2132 }
2133 }
2134
2135 void RNEMD::writeOutputFile() {
2136 if (!doRNEMD_) return;
2137 if (!hasData_) return;
2138
2139 #ifdef IS_MPI
2140 // If we're the root node, should we print out the results
2141 int worldRank;
2142 MPI_Comm_rank( MPI_COMM_WORLD, &worldRank);
2143
2144 if (worldRank == 0) {
2145 #endif
2146 rnemdFile_.open(rnemdFileName_.c_str(), std::ios::out | std::ios::trunc );
2147
2148 if( !rnemdFile_ ){
2149 sprintf( painCave.errMsg,
2150 "Could not open \"%s\" for RNEMD output.\n",
2151 rnemdFileName_.c_str());
2152 painCave.isFatal = 1;
2153 simError();
2154 }
2155
2156 Snapshot* currentSnap_ = info_->getSnapshotManager()->getCurrentSnapshot();
2157
2158 RealType time = currentSnap_->getTime();
2159 RealType avgArea;
2160 areaAccumulator_->getAverage(avgArea);
2161
2162 RealType Jz(0.0);
2163 Vector3d JzP(V3Zero);
2164 Vector3d JzL(V3Zero);
2165 if (time >= info_->getSimParams()->getDt()) {
2166 Jz = kineticExchange_ / (time * avgArea)
2167 / PhysicalConstants::energyConvert;
2168 JzP = momentumExchange_ / (time * avgArea);
2169 JzL = angularMomentumExchange_ / (time * avgArea);
2170 }
2171
2172 rnemdFile_ << "#######################################################\n";
2173 rnemdFile_ << "# RNEMD {\n";
2174
2175 map<string, RNEMDMethod>::iterator mi;
2176 for(mi = stringToMethod_.begin(); mi != stringToMethod_.end(); ++mi) {
2177 if ( (*mi).second == rnemdMethod_)
2178 rnemdFile_ << "# exchangeMethod = \"" << (*mi).first << "\";\n";
2179 }
2180 map<string, RNEMDFluxType>::iterator fi;
2181 for(fi = stringToFluxType_.begin(); fi != stringToFluxType_.end(); ++fi) {
2182 if ( (*fi).second == rnemdFluxType_)
2183 rnemdFile_ << "# fluxType = \"" << (*fi).first << "\";\n";
2184 }
2185
2186 rnemdFile_ << "# exchangeTime = " << exchangeTime_ << ";\n";
2187
2188 rnemdFile_ << "# objectSelection = \""
2189 << rnemdObjectSelection_ << "\";\n";
2190 rnemdFile_ << "# selectionA = \"" << selectionA_ << "\";\n";
2191 rnemdFile_ << "# selectionB = \"" << selectionB_ << "\";\n";
2192 rnemdFile_ << "# }\n";
2193 rnemdFile_ << "#######################################################\n";
2194 rnemdFile_ << "# RNEMD report:\n";
2195 rnemdFile_ << "# running time = " << time << " fs\n";
2196 rnemdFile_ << "# Target flux:\n";
2197 rnemdFile_ << "# kinetic = "
2198 << kineticFlux_ / PhysicalConstants::energyConvert
2199 << " (kcal/mol/A^2/fs)\n";
2200 rnemdFile_ << "# momentum = " << momentumFluxVector_
2201 << " (amu/A/fs^2)\n";
2202 rnemdFile_ << "# angular momentum = " << angularMomentumFluxVector_
2203 << " (amu/A^2/fs^2)\n";
2204 rnemdFile_ << "# Target one-time exchanges:\n";
2205 rnemdFile_ << "# kinetic = "
2206 << kineticTarget_ / PhysicalConstants::energyConvert
2207 << " (kcal/mol)\n";
2208 rnemdFile_ << "# momentum = " << momentumTarget_
2209 << " (amu*A/fs)\n";
2210 rnemdFile_ << "# angular momentum = " << angularMomentumTarget_
2211 << " (amu*A^2/fs)\n";
2212 rnemdFile_ << "# Actual exchange totals:\n";
2213 rnemdFile_ << "# kinetic = "
2214 << kineticExchange_ / PhysicalConstants::energyConvert
2215 << " (kcal/mol)\n";
2216 rnemdFile_ << "# momentum = " << momentumExchange_
2217 << " (amu*A/fs)\n";
2218 rnemdFile_ << "# angular momentum = " << angularMomentumExchange_
2219 << " (amu*A^2/fs)\n";
2220 rnemdFile_ << "# Actual flux:\n";
2221 rnemdFile_ << "# kinetic = " << Jz
2222 << " (kcal/mol/A^2/fs)\n";
2223 rnemdFile_ << "# momentum = " << JzP
2224 << " (amu/A/fs^2)\n";
2225 rnemdFile_ << "# angular momentum = " << JzL
2226 << " (amu/A^2/fs^2)\n";
2227 rnemdFile_ << "# Exchange statistics:\n";
2228 rnemdFile_ << "# attempted = " << trialCount_ << "\n";
2229 rnemdFile_ << "# failed = " << failTrialCount_ << "\n";
2230 if (rnemdMethod_ == rnemdNIVS) {
2231 rnemdFile_ << "# NIVS root-check errors = "
2232 << failRootCount_ << "\n";
2233 }
2234 rnemdFile_ << "#######################################################\n";
2235
2236
2237
2238 //write title
2239 rnemdFile_ << "#";
2240 for (unsigned int i = 0; i < outputMask_.size(); ++i) {
2241 if (outputMask_[i]) {
2242 rnemdFile_ << "\t" << data_[i].title <<
2243 "(" << data_[i].units << ")";
2244 // add some extra tabs for column alignment
2245 if (data_[i].dataType == "Vector3d") rnemdFile_ << "\t\t";
2246 }
2247 }
2248 rnemdFile_ << std::endl;
2249
2250 rnemdFile_.precision(8);
2251
2252 for (int j = 0; j < nBins_; j++) {
2253
2254 for (unsigned int i = 0; i < outputMask_.size(); ++i) {
2255 if (outputMask_[i]) {
2256 if (data_[i].dataType == "RealType")
2257 writeReal(i,j);
2258 else if (data_[i].dataType == "Vector3d")
2259 writeVector(i,j);
2260 else {
2261 sprintf( painCave.errMsg,
2262 "RNEMD found an unknown data type for: %s ",
2263 data_[i].title.c_str());
2264 painCave.isFatal = 1;
2265 simError();
2266 }
2267 }
2268 }
2269 rnemdFile_ << std::endl;
2270
2271 }
2272
2273 rnemdFile_ << "#######################################################\n";
2274 rnemdFile_ << "# 95% confidence intervals in those quantities follow:\n";
2275 rnemdFile_ << "#######################################################\n";
2276
2277
2278 for (int j = 0; j < nBins_; j++) {
2279 rnemdFile_ << "#";
2280 for (unsigned int i = 0; i < outputMask_.size(); ++i) {
2281 if (outputMask_[i]) {
2282 if (data_[i].dataType == "RealType")
2283 writeRealErrorBars(i,j);
2284 else if (data_[i].dataType == "Vector3d")
2285 writeVectorErrorBars(i,j);
2286 else {
2287 sprintf( painCave.errMsg,
2288 "RNEMD found an unknown data type for: %s ",
2289 data_[i].title.c_str());
2290 painCave.isFatal = 1;
2291 simError();
2292 }
2293 }
2294 }
2295 rnemdFile_ << std::endl;
2296
2297 }
2298
2299 rnemdFile_.flush();
2300 rnemdFile_.close();
2301
2302 #ifdef IS_MPI
2303 }
2304 #endif
2305
2306 }
2307
2308 void RNEMD::writeReal(int index, unsigned int bin) {
2309 if (!doRNEMD_) return;
2310 assert(index >=0 && index < ENDINDEX);
2311 assert(int(bin) < nBins_);
2312 RealType s;
2313 int count;
2314
2315 count = data_[index].accumulator[bin]->count();
2316 if (count == 0) return;
2317
2318 dynamic_cast<Accumulator *>(data_[index].accumulator[bin])->getAverage(s);
2319
2320 if (! isinf(s) && ! isnan(s)) {
2321 rnemdFile_ << "\t" << s;
2322 } else{
2323 sprintf( painCave.errMsg,
2324 "RNEMD detected a numerical error writing: %s for bin %u",
2325 data_[index].title.c_str(), bin);
2326 painCave.isFatal = 1;
2327 simError();
2328 }
2329 }
2330
2331 void RNEMD::writeVector(int index, unsigned int bin) {
2332 if (!doRNEMD_) return;
2333 assert(index >=0 && index < ENDINDEX);
2334 assert(int(bin) < nBins_);
2335 Vector3d s;
2336 int count;
2337
2338 count = data_[index].accumulator[bin]->count();
2339
2340 if (count == 0) return;
2341
2342 dynamic_cast<VectorAccumulator*>(data_[index].accumulator[bin])->getAverage(s);
2343 if (isinf(s[0]) || isnan(s[0]) ||
2344 isinf(s[1]) || isnan(s[1]) ||
2345 isinf(s[2]) || isnan(s[2]) ) {
2346 sprintf( painCave.errMsg,
2347 "RNEMD detected a numerical error writing: %s for bin %u",
2348 data_[index].title.c_str(), bin);
2349 painCave.isFatal = 1;
2350 simError();
2351 } else {
2352 rnemdFile_ << "\t" << s[0] << "\t" << s[1] << "\t" << s[2];
2353 }
2354 }
2355
2356 void RNEMD::writeRealErrorBars(int index, unsigned int bin) {
2357 if (!doRNEMD_) return;
2358 assert(index >=0 && index < ENDINDEX);
2359 assert(int(bin) < nBins_);
2360 RealType s;
2361 int count;
2362
2363 count = data_[index].accumulator[bin]->count();
2364 if (count == 0) return;
2365
2366 dynamic_cast<Accumulator *>(data_[index].accumulator[bin])->get95percentConfidenceInterval(s);
2367
2368 if (! isinf(s) && ! isnan(s)) {
2369 rnemdFile_ << "\t" << s;
2370 } else{
2371 sprintf( painCave.errMsg,
2372 "RNEMD detected a numerical error writing: %s std. dev. for bin %u",
2373 data_[index].title.c_str(), bin);
2374 painCave.isFatal = 1;
2375 simError();
2376 }
2377 }
2378
2379 void RNEMD::writeVectorErrorBars(int index, unsigned int bin) {
2380 if (!doRNEMD_) return;
2381 assert(index >=0 && index < ENDINDEX);
2382 assert(int(bin) < nBins_);
2383 Vector3d s;
2384 int count;
2385
2386 count = data_[index].accumulator[bin]->count();
2387 if (count == 0) return;
2388
2389 dynamic_cast<VectorAccumulator*>(data_[index].accumulator[bin])->get95percentConfidenceInterval(s);
2390 if (isinf(s[0]) || isnan(s[0]) ||
2391 isinf(s[1]) || isnan(s[1]) ||
2392 isinf(s[2]) || isnan(s[2]) ) {
2393 sprintf( painCave.errMsg,
2394 "RNEMD detected a numerical error writing: %s std. dev. for bin %u",
2395 data_[index].title.c_str(), bin);
2396 painCave.isFatal = 1;
2397 simError();
2398 } else {
2399 rnemdFile_ << "\t" << s[0] << "\t" << s[1] << "\t" << s[2];
2400 }
2401 }
2402 }
2403

Properties

Name Value
svn:keywords Author Id Revision Date