OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
RestReader.cpp
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#include "io/RestReader.hpp"
49
50#include <cmath>
51#include <cstdio>
52#include <cstdlib>
53#include <cstring>
54#include <iostream>
55#include <memory>
56#include <sstream>
57#include <string>
58
59#include <sys/stat.h>
60#include <sys/types.h>
61
62#ifdef IS_MPI
63#include <mpi.h>
64#endif
65
67#include "restraints/MolecularRestraint.hpp"
68#include "restraints/ObjectRestraint.hpp"
70#include "utils/simError.h"
71
72namespace OpenMD {
73
74 void RestReader::scanFile() {
75 std::streampos prevPos;
76 std::streampos currPos;
77
78#ifdef IS_MPI
79
80 if (worldRank == 0) {
81#endif // is_mpi
82
83 inFile_->clear();
84 currPos = inFile_->tellg();
85 prevPos = currPos;
86
87 bool foundOpenSnapshotTag = false;
88 int lineNo = 0;
89 while (!foundOpenSnapshotTag && inFile_->getline(buffer, bufferSize)) {
90 ++lineNo;
91
92 std::string line = buffer;
93 currPos = inFile_->tellg();
94 if (line.find("<Snapshot>") != std::string::npos) {
95 foundOpenSnapshotTag = true;
96 framePos_ = (long long)prevPos;
97 }
98 prevPos = currPos;
99 }
100
101#ifdef IS_MPI
102 }
103 MPI_Bcast(&framePos_, 1, MPI_LONG_LONG, 0, MPI_COMM_WORLD);
104#endif // is_mpi
105 }
106
107 void RestReader::readSet() {
108 std::string line;
109
110#ifndef IS_MPI
111
112 inFile_->clear();
113 inFile_->seekg(framePos_);
114
115 std::istream& inputStream = *inFile_;
116#else
117
118 int primaryNode = 0;
119 std::stringstream sstream;
120 if (worldRank == primaryNode) {
121 std::string sendBuffer;
122
123 inFile_->clear();
124 inFile_->seekg(framePos_);
125
126 while (inFile_->getline(buffer, bufferSize)) {
127 line = buffer;
128 sendBuffer += line;
129 sendBuffer += '\n';
130 if (line.find("</Snapshot>") != std::string::npos) { break; }
131 }
132
133 int sendBufferSize = sendBuffer.size();
134 MPI_Bcast(&sendBufferSize, 1, MPI_INT, primaryNode, MPI_COMM_WORLD);
135 MPI_Bcast((void*)sendBuffer.c_str(), sendBufferSize, MPI_CHAR,
136 primaryNode, MPI_COMM_WORLD);
137
138 sstream.str(sendBuffer);
139 } else {
140 int sendBufferSize;
141 MPI_Bcast(&sendBufferSize, 1, MPI_INT, primaryNode, MPI_COMM_WORLD);
142 char* recvBuffer = new char[sendBufferSize + 1];
143 assert(recvBuffer);
144 recvBuffer[sendBufferSize] = '\0';
145 MPI_Bcast(recvBuffer, sendBufferSize, MPI_CHAR, primaryNode,
146 MPI_COMM_WORLD);
147 sstream.str(recvBuffer);
148 delete[] recvBuffer;
149 }
150
151 std::istream& inputStream = sstream;
152#endif
153
154 inputStream.getline(buffer, bufferSize);
155
156 line = buffer;
157 if (line.find("<Snapshot>") == std::string::npos) {
158 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
159 "RestReader Error: can not find <Snapshot>\n");
160 painCave.isFatal = 1;
161 simError();
162 }
163
164 // read frameData
165 readFrameProperties(inputStream);
166
167 // read StuntDoubles
168 readStuntDoubles(inputStream);
169
170 inputStream.getline(buffer, bufferSize);
171 line = buffer;
172 if (line.find("</Snapshot>") == std::string::npos) {
173 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
174 "RestReader Error: can not find </Snapshot>\n");
175 painCave.isFatal = 1;
176 simError();
177 }
178 }
179
180 void RestReader::readReferenceStructure() {
181 // We need temporary storage to keep track of all StuntDouble positions
182 // in case some of the restraints are molecular (i.e. if they use
183 // multiple SD positions to determine restrained orientations or positions:
184
185 all_pos_.clear();
186 all_pos_.resize(info_->getNGlobalIntegrableObjects());
187
188 // Restraint files are just standard dump files, but with the reference
189 // structure stored in the first frame (frame 0).
190 // RestReader overloads readSet and explicitly handles all of the
191 // ObjectRestraints in that method:
192
193 scanFile();
194
195 readSet();
196
197 // all ObjectRestraints have been handled, now we have to worry about
198 // molecular restraints:
199
200 SimInfo::MoleculeIterator i;
201 Molecule::IntegrableObjectIterator j;
202 Molecule* mol;
203 StuntDouble* sd;
204
205 // no need to worry about parallel molecules, as molecules are not
206 // split across processor boundaries. Just loop over all molecules
207 // we know about:
208
209 for (mol = info_->beginMolecule(i); mol != NULL;
210 mol = info_->nextMolecule(i)) {
211 // is this molecule restrained?
212 std::shared_ptr<GenericData> data = mol->getPropertyByName("Restraint");
213
214 if (data != nullptr) {
215 // make sure we can reinterpret the generic data as restraint data:
216
217 std::shared_ptr<RestraintData> restData =
218 std::dynamic_pointer_cast<RestraintData>(data);
219
220 if (restData != nullptr) {
221 // make sure we can reinterpet the restraint data as a
222 // pointer to a MolecularRestraint:
223
224 MolecularRestraint* mRest =
225 dynamic_cast<MolecularRestraint*>(restData->getData());
226
227 if (mRest == NULL) {
228 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
229 "Can not cast RestraintData to MolecularRestraint\n");
230 painCave.severity = OPENMD_ERROR;
231 painCave.isFatal = 1;
232 simError();
233 } else {
234 // now we need to pack the stunt doubles for the reference
235 // structure:
236
237 std::vector<Vector3d> ref;
238 int count = 0;
239 RealType mass, mTot;
240 Vector3d COM(0.0);
241
242 mTot = 0.0;
243 // loop over the stunt doubles in this molecule in the order we
244 // will be looping them in the restraint code:
245
246 for (sd = mol->beginIntegrableObject(j); sd != NULL;
247 sd = mol->nextIntegrableObject(j)) {
248 // push back the reference positions of the stunt
249 // doubles from the *globally* sorted array of
250 // positions:
251
252 ref.push_back(all_pos_[sd->getGlobalIntegrableObjectIndex()]);
253 mass = sd->getMass();
254 COM = COM + mass * ref[count];
255 mTot = mTot + mass;
256 count = count + 1;
257 }
258 COM /= mTot;
259 mRest->setReferenceStructure(ref, COM);
260 }
261 }
262 }
263 }
264 }
265
266 void RestReader::parseDumpLine(const std::string& line) {
267 StringTokenizer tokenizer(line);
268 int nTokens;
269
270 nTokens = tokenizer.countTokens();
271
272 if (nTokens < 2) {
273 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
274 "RestReader Error: Not enough Tokens.\n%s\n", line.c_str());
275 painCave.isFatal = 1;
276 simError();
277 }
278
279 int index = tokenizer.nextTokenAsInt();
280
281 StuntDouble* sd = info_->getIOIndexToIntegrableObject(index);
282
283 if (sd == NULL) { return; }
284
285 std::string type = tokenizer.nextToken();
286 int size = type.size();
287
288 Vector3d pos;
289 Quat4d q;
290
291 for (int i = 0; i < size; ++i) {
292 switch (type[i]) {
293 case 'p': {
294 pos[0] = tokenizer.nextTokenAsDouble();
295 pos[1] = tokenizer.nextTokenAsDouble();
296 pos[2] = tokenizer.nextTokenAsDouble();
297 break;
298 }
299 case 'v': {
300 Vector3d vel;
301 vel[0] = tokenizer.nextTokenAsDouble();
302 vel[1] = tokenizer.nextTokenAsDouble();
303 vel[2] = tokenizer.nextTokenAsDouble();
304 break;
305 }
306
307 case 'q': {
308 if (sd->isDirectional()) {
309 q[0] = tokenizer.nextTokenAsDouble();
310 q[1] = tokenizer.nextTokenAsDouble();
311 q[2] = tokenizer.nextTokenAsDouble();
312 q[3] = tokenizer.nextTokenAsDouble();
313
314 RealType qlen = q.length();
315 if (qlen < OpenMD::epsilon) { // check quaternion is not equal to 0
316
317 snprintf(
318 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
319 "RestReader Error: initial quaternion error (q0^2 + q1^2 + "
320 "q2^2 + "
321 "q3^2) ~ 0\n");
322 painCave.isFatal = 1;
323 simError();
324 }
325
326 q.normalize();
327 }
328 break;
329 }
330 case 'j': {
331 Vector3d ji;
332 if (sd->isDirectional()) {
333 ji[0] = tokenizer.nextTokenAsDouble();
334 ji[1] = tokenizer.nextTokenAsDouble();
335 ji[2] = tokenizer.nextTokenAsDouble();
336 }
337 break;
338 }
339 case 'f': {
340 Vector3d force;
341 force[0] = tokenizer.nextTokenAsDouble();
342 force[1] = tokenizer.nextTokenAsDouble();
343 force[2] = tokenizer.nextTokenAsDouble();
344 break;
345 }
346 case 't': {
347 Vector3d torque;
348 torque[0] = tokenizer.nextTokenAsDouble();
349 torque[1] = tokenizer.nextTokenAsDouble();
350 torque[2] = tokenizer.nextTokenAsDouble();
351 break;
352 }
353 default: {
354 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
355 "RestReader Error: %s is an unrecognized type\n",
356 type.c_str());
357 painCave.isFatal = 1;
358 simError();
359 break;
360 }
361 }
362 // keep the position in case we need it for a molecular restraint:
363
364 all_pos_[index] = pos;
365
366 // is this io restrained?
367 std::shared_ptr<GenericData> data = sd->getPropertyByName("Restraint");
368
369 if (data != nullptr) {
370 // make sure we can reinterpret the generic data as restraint data:
371 std::shared_ptr<RestraintData> restData =
372 std::dynamic_pointer_cast<RestraintData>(data);
373 if (restData != nullptr) {
374 // make sure we can reinterpet the restraint data as a pointer to
375 // an ObjectRestraint:
376 ObjectRestraint* oRest =
377 dynamic_cast<ObjectRestraint*>(restData->getData());
378 if (oRest == NULL) {
379 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
380 "Can not cast RestraintData to ObjectRestraint\n");
381 painCave.severity = OPENMD_ERROR;
382 painCave.isFatal = 1;
383 simError();
384 } else {
385 if (sd->isDirectional()) {
386 oRest->setReferenceStructure(pos, q.toRotationMatrix3());
387 } else {
388 oRest->setReferenceStructure(pos);
389 }
390 }
391 }
392 }
393 }
394 }
395
396 void RestReader::readStuntDoubles(std::istream& inputStream) {
397 inputStream.getline(buffer, bufferSize);
398 std::string line(buffer);
399
400 if (line.find("<StuntDoubles>") == std::string::npos) {
401 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
402 "RestReader Error: Missing <StuntDoubles>\n");
403 painCave.isFatal = 1;
404 simError();
405 }
406
407 while (inputStream.getline(buffer, bufferSize)) {
408 line = buffer;
409
410 if (line.find("</StuntDoubles>") != std::string::npos) { break; }
411
412 parseDumpLine(line);
413 }
414 }
415
416 void RestReader::readFrameProperties(std::istream& inputStream) {
417 inputStream.getline(buffer, bufferSize);
418 std::string line(buffer);
419
420 if (line.find("<FrameData>") == std::string::npos) {
421 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
422 "RestReader Error: Missing <FrameData>\n");
423 painCave.isFatal = 1;
424 simError();
425 }
426
427 // restraints don't care about frame data (unless we need to wrap
428 // coordinates, but we'll worry about that later), so
429 // we'll just scan ahead until the end of the frame data:
430
431 while (inputStream.getline(buffer, bufferSize)) {
432 line = buffer;
433
434 if (line.find("</FrameData>") != std::string::npos) { break; }
435 }
436 }
437
438} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.