OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
SimCreator.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48/**
49 * @file SimCreator.cpp
50 * @author tlin
51 * @date 11/03/2004
52 * @version 1.0
53 */
54
55#include "brains/SimCreator.hpp"
56
57#include <exception>
58#include <iostream>
59#include <sstream>
60#include <string>
61
62#ifdef IS_MPI
63#include <mpi.h>
64#endif
65
66#include "antlr4-runtime.h"
67#include "brains/ForceField.hpp"
70#include "io/DumpReader.hpp"
71#include "omdParser/OMDLexer.h"
72#include "omdParser/OMDParser.h"
73#include "omdParser/OMDTreeVisitor.hpp"
74#include "omdParser/SimplePreprocessor.hpp"
75#include "types/DirectionalAdapter.hpp"
76#include "types/EAMAdapter.hpp"
77#include "types/FixedChargeAdapter.hpp"
78#include "types/FluctuatingChargeAdapter.hpp"
79#include "types/MultipoleAdapter.hpp"
80#include "types/PolarizableAdapter.hpp"
81#include "types/SuttonChenAdapter.hpp"
82#include "utils/RandNumGen.hpp"
83#include "utils/Revision.hpp"
84#include "utils/Trim.hpp"
85#include "utils/simError.h"
86
87namespace OpenMD {
88
89 // Custom error listener for better error reporting
90 class OMDErrorListener : public antlr4::BaseErrorListener {
91 public:
92 void syntaxError(
93 antlr4::Recognizer *recognizer,
94 antlr4::Token *offendingSymbol,
95 size_t line,
96 size_t charPositionInLine,
97 const std::string &msg,
98 std::exception_ptr e
99 ) override {
100 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
101 "Syntax error at line %zu:%zu %s\n", line, charPositionInLine,
102 msg.c_str());
103 painCave.isFatal = 1;
104 simError();
105
106 // Throw an exception here to halt parsing
107 std::runtime_error("Parse error");
108 }
109 };
110
111
112 Globals* SimCreator::parseFile(std::istream& rawMetaDataStream,
113 const std::string& filename, int mdFileVersion,
114 int startOfMetaDataBlock) {
115 Globals* simParams = NULL;
116 try {
117 // Create a preprocessor that preprocesses md file into an ostringstream
118 std::stringstream ppStream;
119#ifdef IS_MPI
120 int streamSize;
121 const int primaryNode = 0;
122
123 if (worldRank == primaryNode) {
124 MPI_Bcast(&mdFileVersion, 1, MPI_INT, primaryNode, MPI_COMM_WORLD);
125#endif
126 SimplePreprocessor preprocessor;
127 preprocessor.preprocess(rawMetaDataStream, filename,
128 startOfMetaDataBlock, ppStream);
129
130#ifdef IS_MPI
131 // broadcasting the stream size
132 streamSize = ppStream.str().size() + 1;
133 MPI_Bcast(&streamSize, 1, MPI_INT, primaryNode, MPI_COMM_WORLD);
134 MPI_Bcast(static_cast<void*>(const_cast<char*>(ppStream.str().c_str())),
135 streamSize, MPI_CHAR, primaryNode, MPI_COMM_WORLD);
136 } else {
137 MPI_Bcast(&mdFileVersion, 1, MPI_INT, primaryNode, MPI_COMM_WORLD);
138
139 // get stream size
140 MPI_Bcast(&streamSize, 1, MPI_INT, primaryNode, MPI_COMM_WORLD);
141 char* buf = new char[streamSize];
142 assert(buf);
143
144 // receive file content
145 MPI_Bcast(buf, streamSize, MPI_CHAR, primaryNode, MPI_COMM_WORLD);
146
147 ppStream.str(buf);
148 delete[] buf;
149 }
150#endif
151
152 // Create ANTLR input stream from file
153 antlr4::ANTLRInputStream input(ppStream);
154
155 // Create lexer
156 OMDLexer lexer(&input);
157 // Set up filename observer for preprocessor directives
158 FilenameObserver* observer = new FilenameObserver();
159 lexer.setObserver(observer);
160
161 // Create token stream
162 antlr4::CommonTokenStream tokens(&lexer);
163
164 // Create parser
165 OMDParser parser(&tokens);
166
167 // Optional: Add custom error listener
168 OMDErrorListener errorListener;
169 parser.removeErrorListeners(); // Remove default console error listener
170 parser.addErrorListener(&errorListener);
171
172 // Parse the file (top-level rule is 'omdfile')
173 OMDParser::OmdfileContext* tree = parser.omdfile();
174
175 // Check for parse errors
176 if (parser.getNumberOfSyntaxErrors() > 0) {
177 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
178 "Parsing failed with: %lu errors\n",
179 parser.getNumberOfSyntaxErrors());
180 painCave.isFatal = 1;
181 simError();
182 delete observer;
183 return nullptr;
184 }
185
186 // Create visitor and walk the parse tree
187 OMDTreeVisitor visitor;
188 Globals* simParams = visitor.walkTree(tree);
189 simParams->setMDfileVersion(mdFileVersion);
190 delete observer;
191 return simParams;
192 } catch (const std::exception& e) {
193 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
194 "Exception during parsing: %s\n", e.what());
195 painCave.isFatal = 1;
196 simError();
197 return nullptr;
198 }
199 return nullptr;
200 }
201
202 SimInfo* SimCreator::createSim(const std::string& mdFileName,
203 bool loadInitCoords) {
204 const int bufferSize = 65535;
205 char buffer[bufferSize];
206 int lineNo = 0;
207 std::string mdRawData;
208 int metaDataBlockStart = -1;
209 int metaDataBlockEnd = -1;
210 streamoff mdOffset {};
211 int mdFileVersion(2);
212
213 // Create a string for embedding the version information in the MetaData
214 std::string version;
215 version.assign("## Last run using OpenMD version: ");
216 version.append(OPENMD_VERSION_MAJOR);
217 version.append(".");
218 version.append(OPENMD_VERSION_MINOR);
219 version.append(",");
220
221 std::string rev(revision, strnlen(revision, 40));
222 rev.append(40 - rev.length(), ' ');
223
224 version.append(" revision: ");
225 // If there's no GIT revision, just call this the RELEASE revision.
226 if (!rev.empty()) {
227 version.append(rev);
228 } else {
229 version.append("RELEASE");
230 }
231
232#ifdef IS_MPI
233 const int primaryNode = 0;
234 if (worldRank == primaryNode) {
235#endif
236
237 std::ifstream mdFile_;
238 mdFile_.open(mdFileName.c_str(), ifstream::in | ifstream::binary);
239
240 if (mdFile_.fail()) {
241 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
242 "SimCreator: Cannot open file: %s\n", mdFileName.c_str());
243 painCave.isFatal = 1;
244 simError();
245 }
246
247 mdFile_.getline(buffer, bufferSize);
248 ++lineNo;
249 std::string line = Utils::trimLeftCopy(buffer);
250 std::size_t i = CaseInsensitiveFind(line, "<OpenMD");
251 if (i == string::npos) {
252 // try the older file strings to see if that works:
253 i = CaseInsensitiveFind(line, "<OOPSE");
254 }
255
256 if (i == string::npos) {
257 // still no luck!
258 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
259 "SimCreator: File: %s is not a valid OpenMD file!\n",
260 mdFileName.c_str());
261 painCave.isFatal = 1;
262 simError();
263 }
264
265 // found the correct opening string, now try to get the file
266 // format version number.
267
268 StringTokenizer tokenizer(line, "=<> \t\n\r");
269 std::string fileType = tokenizer.nextToken();
270 toUpper(fileType);
271
272 mdFileVersion = 0;
273
274 if (fileType == "OPENMD") {
275 while (tokenizer.hasMoreTokens()) {
276 std::string token(tokenizer.nextToken());
277 toUpper(token);
278 if (token == "VERSION") {
279 mdFileVersion = tokenizer.nextTokenAsInt();
280 break;
281 }
282 }
283 }
284
285 bool startFound(false);
286 bool endFound(false);
287 // scan through the input stream and find MetaData tag
288 while (mdFile_.getline(buffer, bufferSize)) {
289 ++lineNo;
290
291 std::string line = Utils::trimLeftCopy(buffer);
292 if (metaDataBlockStart == -1) {
293 std::size_t i = CaseInsensitiveFind(line, "<MetaData>");
294 if (i != string::npos) {
295 metaDataBlockStart = lineNo;
296 startFound = true;
297 mdOffset = mdFile_.tellg();
298 }
299 } else {
300 std::size_t i = CaseInsensitiveFind(line, "</MetaData>");
301 if (i != string::npos) {
302 metaDataBlockEnd = lineNo;
303 endFound = true;
304 }
305 }
306 if (startFound && endFound) break;
307 }
308
309 if (metaDataBlockStart == -1) {
310 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
311 "SimCreator: File: %s did not contain a <MetaData> tag!\n",
312 mdFileName.c_str());
313 painCave.isFatal = 1;
314 simError();
315 }
316 if (metaDataBlockEnd == -1) {
317 snprintf(
318 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
319 "SimCreator: File: %s did not contain a closed MetaData block!\n",
320 mdFileName.c_str());
321 painCave.isFatal = 1;
322 simError();
323 }
324
325 mdFile_.clear();
326 mdFile_.seekg(0);
327 mdFile_.seekg(mdOffset);
328
329 mdRawData.clear();
330
331 bool foundVersion = false;
332
333 for (int i = 0; i < metaDataBlockEnd - metaDataBlockStart - 1; ++i) {
334 mdFile_.getline(buffer, bufferSize);
335 std::string line = Utils::trimLeftCopy(buffer);
336 std::size_t j =
337 CaseInsensitiveFind(line, "## Last run using OpenMD Version");
338 if (j != string::npos) {
339 foundVersion = true;
340 mdRawData += version;
341 } else {
342 mdRawData += buffer;
343 }
344 mdRawData += "\n";
345 }
346
347 if (!foundVersion) mdRawData += version + "\n";
348
349 mdFile_.close();
350
351#ifdef IS_MPI
352 }
353#endif
354
355 std::stringstream rawMetaDataStream(mdRawData);
356
357 // parse meta-data file
358 Globals* simParams = parseFile(rawMetaDataStream, mdFileName, mdFileVersion,
359 metaDataBlockStart + 1);
360
361 // create the force field
362 ForceField* ff = new ForceField(simParams->getForceField());
363
364 if (ff == NULL) {
365 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
366 "ForceField Factory can not create %s force field\n",
367 simParams->getForceField().c_str());
368 painCave.isFatal = 1;
369 simError();
370 }
371
372 if (simParams->haveForceFieldFileName()) {
373 ff->setForceFieldFileName(simParams->getForceFieldFileName());
374 }
375
376 std::string forcefieldFileName;
377 forcefieldFileName = ff->getForceFieldFileName();
378
379 if (simParams->haveForceFieldVariant()) {
380 // If the force field has variant, the variant force field name will be
381 // Base.variant.frc. For exampel EAM.u6.frc
382
383 std::string variant = simParams->getForceFieldVariant();
384
385 std::string::size_type pos = forcefieldFileName.rfind(".frc");
386 variant = "." + variant;
387 if (pos != std::string::npos) {
388 forcefieldFileName.insert(pos, variant);
389 } else {
390 // If the default force field file name does not containt .frc suffix,
391 // just append the .variant
392 forcefieldFileName.append(variant);
393 }
394 }
395
396 ff->parse(forcefieldFileName);
397 // create SimInfo
398 SimInfo* info = new SimInfo(ff, simParams);
399
400 info->setRawMetaData(mdRawData);
401
402 // gather parameters (SimCreator only retrieves part of the
403 // parameters)
404 gatherParameters(info, mdFileName);
405
406 // divide the molecules and determine the global index of molecules
407#ifdef IS_MPI
408 divideMolecules(info);
409#endif
410
411 // create the molecules
412 createMolecules(info);
413
414 // find the storage layout
415
416 computeStorageLayouts(info);
417
418 int asl = info->getAtomStorageLayout();
419 int rbsl = info->getRigidBodyStorageLayout();
420 int cgsl = info->getCutoffGroupStorageLayout();
421
422 // allocate memory for DataStorage(circular reference, need to
423 // break it)
424 info->setSnapshotManager(new SimSnapshotManager(info, asl, rbsl, cgsl));
425
426 // set the global index of atoms, rigidbodies and cutoffgroups
427 //(only need to be set once, the global index will never change
428 // again). Local indices of atoms and rigidbodies are already set
429 // by MoleculeCreator class which actually delegates the
430 // responsibility to LocalIndexManager.
431 setGlobalIndex(info);
432
433 // Although addInteractionPairs is called inside SimInfo's addMolecule
434 // method, at that point atoms don't have the global index yet
435 //(their global index are all initialized to -1). Therefore we
436 // have to call addInteractionPairs explicitly here. A way to work
437 // around is that we can determine the beginning global indices of
438 // atoms before they get created.
439 SimInfo::MoleculeIterator mi;
440 Molecule* mol;
441 for (mol = info->beginMolecule(mi); mol != NULL;
442 mol = info->nextMolecule(mi)) {
443 info->addInteractionPairs(mol);
444 }
445
446 if (loadInitCoords) loadCoordinates(info, mdFileName);
447
448 info->update();
449 return info;
450 }
451
452 void SimCreator::gatherParameters(SimInfo* info, const std::string& mdfile) {
453 // figure out the output file names
454 std::string prefix;
455
456#ifdef IS_MPI
457
458 if (worldRank == 0) {
459#endif // is_mpi
460 Globals* simParams = info->getSimParams();
461 if (simParams->haveFinalConfig()) {
462 prefix = getPrefix(simParams->getFinalConfig());
463 } else {
464 prefix = getPrefix(mdfile);
465 }
466
467 info->setFinalConfigFileName(prefix + ".eor");
468 info->setDumpFileName(prefix + ".dump");
469 info->setStatFileName(prefix + ".stat");
470 info->setReportFileName(prefix + ".report");
471 info->setRestFileName(prefix + ".zang");
472
473#ifdef IS_MPI
474 }
475#endif
476 }
477
478#ifdef IS_MPI
479 void SimCreator::divideMolecules(SimInfo* info) {
480 RealType a;
481 int nProcessors;
482 std::vector<int> atomsPerProc;
483 int nGlobalMols = info->getNGlobalMolecules();
484 std::vector<int> molToProcMap(nGlobalMols, -1); // default to an error
485 // condition:
486
487 MPI_Comm_size(MPI_COMM_WORLD, &nProcessors);
488
489 if (nProcessors > nGlobalMols) {
490 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
491 "nProcessors (%d) > nMol (%d)\n"
492 "\tThe number of processors is larger than\n"
493 "\tthe number of molecules. This will not result in a \n"
494 "\tusable division of atoms for force decomposition.\n"
495 "\tEither try a smaller number of processors, or run the\n"
496 "\tsingle-processor version of OpenMD.\n",
497 nProcessors, nGlobalMols);
498
499 painCave.isFatal = 1;
500 simError();
501 }
502
503 a = 3.0 * nGlobalMols / info->getNGlobalAtoms();
504
505 // initialize atomsPerProc
506 atomsPerProc.insert(atomsPerProc.end(), nProcessors, 0);
507
508 if (worldRank == 0) {
509 Utils::RandNumGenPtr myRandom = info->getRandomNumberGenerator();
510
511 std::uniform_int_distribution<> processorDistribution {0,
512 nProcessors - 1};
513 std::uniform_real_distribution<RealType> yDistribution {0, 1};
514
515 RealType numerator = info->getNGlobalAtoms();
516 RealType denominator = nProcessors;
517 RealType precast = numerator / denominator;
518 int nTarget = (int)(precast + 0.5);
519 int which_proc {0};
520
521 for (int i = 0; i < nGlobalMols; i++) {
522 // get the molecule stamp first
523 int stampId = info->getMoleculeStampId(i);
524 MoleculeStamp* moleculeStamp = info->getMoleculeStamp(stampId);
525 int add_atoms = moleculeStamp->getNAtoms();
526
527 if (nProcessors > 1) {
528 int done = 0;
529 int loops = 0;
530
531 while (!done) {
532 loops++;
533
534 // Pick a processor at random
535 which_proc = processorDistribution(*myRandom);
536
537 // How many atoms does this processor have so far?
538 int old_atoms = atomsPerProc[which_proc];
539 int new_atoms = old_atoms + add_atoms;
540
541 // If we've been through this loop too many times, we need
542 // to just give up and assign the molecule to this processor
543 // and be done with it.
544
545 if (loops > 100) {
546 snprintf(
547 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
548 "There have been 100 attempts to assign molecule %d to an\n"
549 "\tunderworked processor, but there's no good place to\n"
550 "\tleave it. OpenMD is assigning it at random to processor "
551 "%d.\n",
552 i, which_proc);
553
554 painCave.isFatal = 0;
555 painCave.severity = OPENMD_INFO;
556 simError();
557
558 molToProcMap[i] = which_proc;
559 atomsPerProc[which_proc] += add_atoms;
560
561 done = 1;
562 continue;
563 }
564
565 // If we can add this molecule to this processor without sending
566 // it above nTarget, then go ahead and do it:
567
568 if (new_atoms <= nTarget) {
569 molToProcMap[i] = which_proc;
570 atomsPerProc[which_proc] += add_atoms;
571
572 done = 1;
573 continue;
574 }
575
576 // The only situation left is when new_atoms > nTarget. We
577 // want to accept this with some probability that dies off the
578 // farther we are from nTarget
579
580 // roughly: x = new_atoms - nTarget
581 // Pacc(x) = exp(- a * x)
582 // where a = penalty / (average atoms per molecule)
583
584 RealType x = (RealType)(new_atoms - nTarget);
585 RealType y = yDistribution(*myRandom);
586
587 if (y < exp(-a * x)) {
588 molToProcMap[i] = which_proc;
589 atomsPerProc[which_proc] += add_atoms;
590
591 done = 1;
592 continue;
593 } else {
594 continue;
595 }
596 }
597 } else {
598 which_proc = 0;
599 molToProcMap[i] = which_proc;
600 atomsPerProc[which_proc] += add_atoms;
601 }
602 }
603
604 // Spray out this nonsense to all other processors:
605 MPI_Bcast(&molToProcMap[0], nGlobalMols, MPI_INT, 0, MPI_COMM_WORLD);
606
607 } else {
608 // Listen to your marching orders from processor 0:
609 MPI_Bcast(&molToProcMap[0], nGlobalMols, MPI_INT, 0, MPI_COMM_WORLD);
610 }
611
612 info->setMolToProcMap(molToProcMap);
613 snprintf(checkPointMsg, MAX_SIM_ERROR_MSG_LENGTH,
614 "Successfully divided the molecules among the processors.\n");
615 errorCheckPoint();
616 }
617
618#endif
619
620 void SimCreator::createMolecules(SimInfo* info) {
621 MoleculeCreator molCreator;
622 int stampId;
623
624 for (int i = 0; i < info->getNGlobalMolecules(); i++) {
625 stampId = info->getMoleculeStampId(i);
626 molCreator.createOverrideAtomTypes(info->getForceField(),
627 info->getMoleculeStamp(stampId));
628
629#ifdef IS_MPI
630 if (info->getMolToProc(i) == worldRank) {
631#endif
632
633 Molecule* mol = molCreator.createMolecule(
634 info->getForceField(), info->getMoleculeStamp(stampId), i,
635 info->getLocalIndexManager());
636
637 info->addMolecule(mol);
638
639#ifdef IS_MPI
640 }
641#endif
642 }
643 }
644
645 void SimCreator::computeStorageLayouts(SimInfo* info) {
646 Globals* simParams = info->getSimParams();
647 int nRigidBodies = info->getNGlobalRigidBodies();
648 AtomTypeSet atomTypes = info->getSimulatedAtomTypes();
649 AtomTypeSet::iterator i;
650 bool hasDirectionalAtoms = false;
651 bool hasFixedCharge = false;
652 bool hasDipoles = false;
653 bool hasQuadrupoles = false;
654 bool hasPolarizable = false;
655 bool hasFluctuatingCharge = false;
656 bool hasMetallic = false;
657 int atomStorageLayout = 0;
658 int rigidBodyStorageLayout = 0;
659 int cutoffGroupStorageLayout = 0;
660
661 atomStorageLayout |= DataStorage::dslPosition;
662 atomStorageLayout |= DataStorage::dslVelocity;
663 atomStorageLayout |= DataStorage::dslForce;
664 cutoffGroupStorageLayout |= DataStorage::dslPosition;
665
666 for (i = atomTypes.begin(); i != atomTypes.end(); ++i) {
667 DirectionalAdapter da = DirectionalAdapter((*i));
668 MultipoleAdapter ma = MultipoleAdapter((*i));
669 EAMAdapter ea = EAMAdapter((*i));
670 SuttonChenAdapter sca = SuttonChenAdapter((*i));
671 PolarizableAdapter pa = PolarizableAdapter((*i));
672 FixedChargeAdapter fca = FixedChargeAdapter((*i));
673 FluctuatingChargeAdapter fqa = FluctuatingChargeAdapter((*i));
674
675 if (da.isDirectional()) { hasDirectionalAtoms = true; }
676 if (ma.isDipole()) { hasDipoles = true; }
677 if (ma.isQuadrupole()) { hasQuadrupoles = true; }
678 if (ea.isEAM() || sca.isSuttonChen()) { hasMetallic = true; }
679 if (fca.isFixedCharge()) { hasFixedCharge = true; }
680 if (fqa.isFluctuatingCharge()) { hasFluctuatingCharge = true; }
681 if (pa.isPolarizable()) { hasPolarizable = true; }
682 }
683
684 if (nRigidBodies > 0) {
685 rigidBodyStorageLayout |= DataStorage::dslPosition;
686 rigidBodyStorageLayout |= DataStorage::dslVelocity;
687 rigidBodyStorageLayout |= DataStorage::dslForce;
688 rigidBodyStorageLayout |= DataStorage::dslAmat;
689 rigidBodyStorageLayout |= DataStorage::dslAngularMomentum;
690 rigidBodyStorageLayout |= DataStorage::dslTorque;
691 }
692 if (hasDirectionalAtoms) {
693 atomStorageLayout |= DataStorage::dslAmat;
694 if (atomStorageLayout & DataStorage::dslVelocity) {
695 atomStorageLayout |= DataStorage::dslAngularMomentum;
696 }
697 if (atomStorageLayout & DataStorage::dslForce) {
698 atomStorageLayout |= DataStorage::dslTorque;
699 }
700 }
701 if (hasDipoles) { atomStorageLayout |= DataStorage::dslDipole; }
702 if (hasQuadrupoles) { atomStorageLayout |= DataStorage::dslQuadrupole; }
703 if (hasFixedCharge || hasFluctuatingCharge) {
704 atomStorageLayout |= DataStorage::dslSkippedCharge;
705 }
706 if (hasMetallic) {
707 atomStorageLayout |= DataStorage::dslDensity;
708 atomStorageLayout |= DataStorage::dslFunctional;
709 atomStorageLayout |= DataStorage::dslFunctionalDerivative;
710 }
711 if (hasPolarizable) { atomStorageLayout |= DataStorage::dslElectricField; }
712 if (hasFluctuatingCharge) {
713 atomStorageLayout |= DataStorage::dslFlucQPosition;
714 if (atomStorageLayout & DataStorage::dslVelocity) {
715 atomStorageLayout |= DataStorage::dslFlucQVelocity;
716 }
717 if (atomStorageLayout & DataStorage::dslForce) {
718 atomStorageLayout |= DataStorage::dslFlucQForce;
719 }
720 }
721
722 // if the user has asked for them, make sure we've got the memory for the
723 // objects defined.
724
725 if (simParams->getOutputParticlePotential()) {
726 atomStorageLayout |= DataStorage::dslParticlePot;
727 }
728
729 if (simParams->havePrintHeatFlux()) {
730 if (simParams->getPrintHeatFlux()) {
731 atomStorageLayout |= DataStorage::dslParticlePot;
732 }
733 }
734
735 if (simParams->getOutputElectricField() | simParams->haveElectricField() |
736 simParams->haveUniformField() |
737 simParams->haveUniformGradientStrength() |
738 simParams->haveUniformGradientDirection1() |
739 simParams->haveUniformGradientDirection2() |
740 simParams->getLightParameters()->getUseLight()) {
741 atomStorageLayout |= DataStorage::dslElectricField;
742 rigidBodyStorageLayout |= DataStorage::dslElectricField;
743 }
744
745 if (simParams->getRNEMDParameters()->haveUseRNEMD()) {
746 if (simParams->getRNEMDParameters()->getUseRNEMD()) {
747 if (simParams->getRNEMDParameters()->requiresElectricField()) {
748 atomStorageLayout |= DataStorage::dslElectricField;
749 rigidBodyStorageLayout |= DataStorage::dslElectricField;
750 }
751 }
752 }
753
754 if (simParams->getOutputSitePotential()) {
755 atomStorageLayout |= DataStorage::dslSitePotential;
756 rigidBodyStorageLayout |= DataStorage::dslSitePotential;
757 }
758
759 if (simParams->getOutputFluctuatingCharges()) {
760 atomStorageLayout |= DataStorage::dslFlucQPosition;
761 atomStorageLayout |= DataStorage::dslFlucQVelocity;
762 atomStorageLayout |= DataStorage::dslFlucQForce;
763 }
764
765 info->setAtomStorageLayout(atomStorageLayout);
766 info->setRigidBodyStorageLayout(rigidBodyStorageLayout);
767 info->setCutoffGroupStorageLayout(cutoffGroupStorageLayout);
768
769 return;
770 }
771
772 void SimCreator::setGlobalIndex(SimInfo* info) {
773 SimInfo::MoleculeIterator mi;
774 Molecule::AtomIterator ai;
775 Molecule::RigidBodyIterator ri;
776 Molecule::CutoffGroupIterator ci;
777 Molecule::BondIterator boi;
778 Molecule::BendIterator bei;
779 Molecule::TorsionIterator ti;
780 Molecule::InversionIterator ii;
781 Molecule::IntegrableObjectIterator ioi;
782 Molecule* mol;
783 Atom* atom;
784 RigidBody* rb;
785 CutoffGroup* cg;
786 Bond* bond;
787 Bend* bend;
788 Torsion* torsion;
789 Inversion* inversion;
790 int beginAtomIndex;
791 int beginRigidBodyIndex;
792 int beginCutoffGroupIndex;
793 int beginBondIndex;
794 int beginBendIndex;
795 int beginTorsionIndex;
796 int beginInversionIndex;
797#ifdef IS_MPI
798 int nGlobalAtoms = info->getNGlobalAtoms();
799 int nGlobalRigidBodies = info->getNGlobalRigidBodies();
800#endif
801
802 beginAtomIndex = 0;
803 // The rigid body indices begin immediately after the atom indices:
804 beginRigidBodyIndex = info->getNGlobalAtoms();
805 beginCutoffGroupIndex = 0;
806 beginBondIndex = 0;
807 beginBendIndex = 0;
808 beginTorsionIndex = 0;
809 beginInversionIndex = 0;
810
811 for (int i = 0; i < info->getNGlobalMolecules(); i++) {
812#ifdef IS_MPI
813 if (info->getMolToProc(i) == worldRank) {
814#endif
815 // stuff to do if I own this molecule
816 mol = info->getMoleculeByGlobalIndex(i);
817
818 // The local index(index in DataStorge) of the atom is important:
819 for (atom = mol->beginAtom(ai); atom != NULL;
820 atom = mol->nextAtom(ai)) {
821 atom->setGlobalIndex(beginAtomIndex++);
822 }
823
824 for (rb = mol->beginRigidBody(ri); rb != NULL;
825 rb = mol->nextRigidBody(ri)) {
826 rb->setGlobalIndex(beginRigidBodyIndex++);
827 }
828
829 // The local index of other objects only depends on the order
830 // of traversal:
831 for (cg = mol->beginCutoffGroup(ci); cg != NULL;
832 cg = mol->nextCutoffGroup(ci)) {
833 cg->setGlobalIndex(beginCutoffGroupIndex++);
834 }
835 for (bond = mol->beginBond(boi); bond != NULL;
836 bond = mol->nextBond(boi)) {
837 bond->setGlobalIndex(beginBondIndex++);
838 }
839 for (bend = mol->beginBend(bei); bend != NULL;
840 bend = mol->nextBend(bei)) {
841 bend->setGlobalIndex(beginBendIndex++);
842 }
843 for (torsion = mol->beginTorsion(ti); torsion != NULL;
844 torsion = mol->nextTorsion(ti)) {
845 torsion->setGlobalIndex(beginTorsionIndex++);
846 }
847 for (inversion = mol->beginInversion(ii); inversion != NULL;
848 inversion = mol->nextInversion(ii)) {
849 inversion->setGlobalIndex(beginInversionIndex++);
850 }
851
852#ifdef IS_MPI
853 } else {
854 // stuff to do if I don't own this molecule
855
856 int stampId = info->getMoleculeStampId(i);
857 MoleculeStamp* stamp = info->getMoleculeStamp(stampId);
858
859 beginAtomIndex += stamp->getNAtoms();
860 beginRigidBodyIndex += stamp->getNRigidBodies();
861 beginCutoffGroupIndex +=
862 stamp->getNCutoffGroups() + stamp->getNFreeAtoms();
863 beginBondIndex += stamp->getNBonds();
864 beginBendIndex += stamp->getNBends();
865 beginTorsionIndex += stamp->getNTorsions();
866 beginInversionIndex += stamp->getNInversions();
867 }
868#endif
869
870 } // end for(int i=0)
871
872 // fill globalGroupMembership
873 std::vector<int> globalGroupMembership(info->getNGlobalAtoms(), 0);
874 for (mol = info->beginMolecule(mi); mol != NULL;
875 mol = info->nextMolecule(mi)) {
876 for (cg = mol->beginCutoffGroup(ci); cg != NULL;
877 cg = mol->nextCutoffGroup(ci)) {
878 for (atom = cg->beginAtom(ai); atom != NULL; atom = cg->nextAtom(ai)) {
879 globalGroupMembership[atom->getGlobalIndex()] = cg->getGlobalIndex();
880 }
881 }
882 }
883
884#ifdef IS_MPI
885 // Since the globalGroupMembership has been zero filled and we've only
886 // poked values into the atoms we know, we can do an Allreduce
887 // to get the full globalGroupMembership array (We think).
888 // This would be prettier if we could use MPI_IN_PLACE like the MPI-2
889 // docs said we could.
890 std::vector<int> tmpGroupMembership(info->getNGlobalAtoms(), 0);
891 MPI_Allreduce(&globalGroupMembership[0], &tmpGroupMembership[0],
892 nGlobalAtoms, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
893
894 info->setGlobalGroupMembership(tmpGroupMembership);
895#else
896 info->setGlobalGroupMembership(globalGroupMembership);
897#endif
898
899 // fill molMembership
900 std::vector<int> globalMolMembership(
901 info->getNGlobalAtoms() + info->getNGlobalRigidBodies(), 0);
902
903 for (mol = info->beginMolecule(mi); mol != NULL;
904 mol = info->nextMolecule(mi)) {
905 for (atom = mol->beginAtom(ai); atom != NULL; atom = mol->nextAtom(ai)) {
906 globalMolMembership[atom->getGlobalIndex()] = mol->getGlobalIndex();
907 }
908 for (rb = mol->beginRigidBody(ri); rb != NULL;
909 rb = mol->nextRigidBody(ri)) {
910 globalMolMembership[rb->getGlobalIndex()] = mol->getGlobalIndex();
911 }
912 }
913
914#ifdef IS_MPI
915 std::vector<int> tmpMolMembership(
916 info->getNGlobalAtoms() + info->getNGlobalRigidBodies(), 0);
917 MPI_Allreduce(&globalMolMembership[0], &tmpMolMembership[0],
918 nGlobalAtoms + nGlobalRigidBodies, MPI_INT, MPI_SUM,
919 MPI_COMM_WORLD);
920
921 info->setGlobalMolMembership(tmpMolMembership);
922#else
923 info->setGlobalMolMembership(globalMolMembership);
924#endif
925
926 // nIOPerMol holds the number of integrable objects per molecule
927 // here the molecules are listed by their global indices.
928
929 std::vector<int> nIOPerMol(info->getNGlobalMolecules(), 0);
930 for (mol = info->beginMolecule(mi); mol != NULL;
931 mol = info->nextMolecule(mi)) {
932 nIOPerMol[mol->getGlobalIndex()] = mol->getNIntegrableObjects();
933 }
934
935#ifdef IS_MPI
936 std::vector<int> numIntegrableObjectsPerMol(info->getNGlobalMolecules(), 0);
937 MPI_Allreduce(&nIOPerMol[0], &numIntegrableObjectsPerMol[0],
938 info->getNGlobalMolecules(), MPI_INT, MPI_SUM,
939 MPI_COMM_WORLD);
940#else
941 std::vector<int> numIntegrableObjectsPerMol = nIOPerMol;
942#endif
943
944 std::vector<int> startingIOIndexForMol(info->getNGlobalMolecules());
945
946 int startingIndex = 0;
947 for (int i = 0; i < info->getNGlobalMolecules(); i++) {
948 startingIOIndexForMol[i] = startingIndex;
949 startingIndex += numIntegrableObjectsPerMol[i];
950 }
951
952 std::vector<StuntDouble*> IOIndexToIntegrableObject(
953 info->getNGlobalIntegrableObjects(), (StuntDouble*)NULL);
954 for (mol = info->beginMolecule(mi); mol != NULL;
955 mol = info->nextMolecule(mi)) {
956 int myGlobalIndex = mol->getGlobalIndex();
957 int globalIO = startingIOIndexForMol[myGlobalIndex];
958 for (StuntDouble* sd = mol->beginIntegrableObject(ioi); sd != NULL;
959 sd = mol->nextIntegrableObject(ioi)) {
960 sd->setGlobalIntegrableObjectIndex(globalIO);
961 IOIndexToIntegrableObject[globalIO] = sd;
962 globalIO++;
963 }
964 }
965
966 info->setIOIndexToIntegrableObject(IOIndexToIntegrableObject);
967 }
968
969 void SimCreator::loadCoordinates(SimInfo* info,
970 const std::string& mdFileName) {
971 DumpReader reader(info, mdFileName);
972 int nframes = reader.getNFrames();
973
974 if (nframes > 0) {
975 reader.readFrame(nframes - 1);
976 } else {
977 // invalid initial coordinate file
978 snprintf(
979 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
980 "Initial configuration file %s should at least contain one frame\n",
981 mdFileName.c_str());
982 painCave.isFatal = 1;
983 simError();
984 }
985 // copy the current snapshot to previous snapshot
986 info->getSnapshotManager()->advance();
987 }
988
989} // namespace OpenMD
size_t getNIntegrableObjects()
Returns the total number of integrable objects in this molecule.
Definition Molecule.hpp:183
int getGlobalIndex()
Returns the global index of this molecule.
Definition Molecule.hpp:109
void setGlobalIndex(int index)
Sets the global index of this ShortRangeInteraction.
SimInfo * createSim(const std::string &mdFileName, bool loadInitCoords=true)
Setup Simulation.
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
Molecule * getMoleculeByGlobalIndex(int index)
Finds a molecule with a specified global index.
Definition SimInfo.hpp:303
int getNGlobalIntegrableObjects()
Returns the total number of integrable objects (total number of rigid bodies plus the total number of...
Definition SimInfo.hpp:142
void setGlobalMolMembership(const std::vector< int > &gmm)
Sets GlobalMolMembership.
Definition SimInfo.hpp:373
void setAtomStorageLayout(int asl)
Sets the storage layouts (computed by SimCreator).
Definition SimInfo.hpp:259
int getMolToProc(int globalIndex)
Finds the processor where a molecule resides.
Definition SimInfo.hpp:670
ForceField * getForceField()
Returns the force field.
Definition SimInfo.hpp:269
Molecule * beginMolecule(MoleculeIterator &i)
Returns the first molecule in this SimInfo and intialize the iterator.
Definition SimInfo.cpp:243
MoleculeStamp * getMoleculeStamp(int id)
Returns the molecule stamp.
Definition SimInfo.hpp:293
int getNGlobalRigidBodies()
Returns the total number of integrable objects (total number of rigid bodies plus the total number of...
Definition SimInfo.hpp:149
int getAtomStorageLayout()
Returns the storage layouts (computed by SimCreator).
Definition SimInfo.hpp:254
int getNGlobalAtoms()
Returns the total number of atoms in the system.
Definition SimInfo.hpp:132
int getNGlobalMolecules()
Returns the total number of molecules in the system.
Definition SimInfo.hpp:129
void setSnapshotManager(SnapshotManager *sman)
Sets the snapshot manager.
Definition SimInfo.cpp:975
void setGlobalGroupMembership(const std::vector< int > &ggm)
Sets GlobalGroupMembership.
Definition SimInfo.hpp:365
bool addMolecule(Molecule *mol)
Adds a molecule.
Definition SimInfo.cpp:190
void addInteractionPairs(Molecule *mol)
add all special interaction pairs (including excluded interactions) in a molecule into the appropriat...
Definition SimInfo.cpp:379
void update()
update
Definition SimInfo.cpp:701
Molecule * nextMolecule(MoleculeIterator &i)
Returns the next avaliable Molecule based on the iterator.
Definition SimInfo.cpp:248
SnapshotManager * getSnapshotManager()
Returns the snapshot manager.
Definition SimInfo.hpp:251
void setMolToProcMap(const std::vector< int > &molToProcMap)
Set MolToProcMap array.
Definition SimInfo.hpp:678
AtomTypeSet getSimulatedAtomTypes()
Returns the set of atom types present in this simulation.
Definition SimInfo.cpp:716
LocalIndexManager * getLocalIndexManager()
Returns the local index manager.
Definition SimInfo.hpp:285
"brains/SimSnapshotManager.hpp"
The string tokenizer class allows an application to break a string into tokens The set of delimiters ...
std::string nextToken()
Returns the next token from this string tokenizer.
int nextTokenAsInt()
Returns the next token from this string tokenizer as an integer.
bool hasMoreTokens()
Tests if there are more tokens available from this tokenizer's string.
void setGlobalIndex(int index)
Sets the global index of this stuntDouble.
int getGlobalIndex()
Returns the global index of this stuntDouble.
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
std::string getPrefix(const std::string &str)