OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
TimeCorrFunc.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 appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44
45#include "applications/dynamicProps/TimeCorrFunc.hpp"
46
47#include <memory>
48
51#include "utils/Revision.hpp"
52#include "utils/simError.h"
53
54using namespace std;
55namespace OpenMD {
56
57 template<typename T>
58 TimeCorrFunc<T>::TimeCorrFunc(SimInfo* info, const string& filename,
59 const string& sele1, const string& sele2) :
60 info_(info),
61 dumpFilename_(filename), seleMan1_(info_), seleMan2_(info_),
62 selectionScript1_(sele1), selectionScript2_(sele2), evaluator1_(info_),
63 evaluator2_(info_), autoCorrFunc_(false), doSystemProperties_(false),
64 doMolecularProperties_(false), doObjectProperties_(false),
65 doBondProperties_(false) {
66 reader_ = new DumpReader(info_, dumpFilename_);
67
68 uniqueSelections_ = (sele1.compare(sele2) != 0) ? true : false;
69
70 Globals* simParams = info_->getSimParams();
71 if (simParams->haveSampleTime()) {
72 deltaTime_ = simParams->getSampleTime();
73 } else {
74 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
75 "TimeCorrFunc Error: can not figure out deltaTime\n");
76 painCave.isFatal = 1;
77 simError();
78 }
79
80 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "Scanning for frames.");
81 painCave.isFatal = 0;
82 painCave.severity = OPENMD_INFO;
83 simError();
84
85 nFrames_ = reader_->getNFrames();
86 nTimeBins_ = nFrames_;
87
88 T zeroType(0.0);
89 histogram_.resize(nTimeBins_, zeroType);
90 count_.resize(nTimeBins_, 0);
91
92 times_.resize(nFrames_);
93 sele1ToIndex_.resize(nFrames_);
94 if (uniqueSelections_) { sele2ToIndex_.resize(nFrames_); }
95
96 progressBar_ = std::make_unique<ProgressBar>();
97 }
98
99 template<typename T>
100 void TimeCorrFunc<T>::preCorrelate() {
101 evaluator1_.loadScriptString(selectionScript1_);
102 // if selection is static, we only need to evaluate it once
103 if (!evaluator1_.isDynamic()) {
104 seleMan1_.setSelectionSet(evaluator1_.evaluate());
105 validateSelection(seleMan1_);
106 }
107
108 if (uniqueSelections_) {
109 evaluator2_.loadScriptString(selectionScript2_);
110 if (!evaluator2_.isDynamic()) {
111 seleMan2_.setSelectionSet(evaluator2_.evaluate());
112 validateSelection(seleMan2_);
113 }
114 }
115
116 progressBar_->clear();
117
118 for (int istep = 0; istep < nFrames_; istep++) {
119 reader_->readFrame(istep);
120 currentSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
121 times_[istep] = currentSnapshot_->getTime();
122
123 progressBar_->setStatus(istep + 1, nFrames_);
124 progressBar_->update();
125
126 computeFrame(istep);
127 }
128 }
129
130 template<typename T>
131 void TimeCorrFunc<T>::computeFrame(int istep) {
132 Molecule* mol;
133 StuntDouble* sd;
134 Bond* bond;
135 int imol1, imol2, isd1, isd2, ibond1, ibond2;
136 unsigned int index;
137
138 if (evaluator1_.isDynamic()) {
139 seleMan1_.setSelectionSet(evaluator1_.evaluate());
140 validateSelection(seleMan1_);
141 }
142
143 if (uniqueSelections_ && evaluator2_.isDynamic()) {
144 seleMan2_.setSelectionSet(evaluator2_.evaluate());
145 validateSelection(seleMan2_);
146 }
147
148 if (doSystemProperties_) {
149 computeProperty1(istep);
150 if (!autoCorrFunc_) computeProperty2(istep);
151 }
152
153 if (doMolecularProperties_) {
154 for (mol = seleMan1_.beginSelectedMolecule(imol1); mol != NULL;
155 mol = seleMan1_.nextSelectedMolecule(imol1)) {
156 index = computeProperty1(istep, mol);
157
158 if (index == sele1ToIndex_[istep].size()) {
159 sele1ToIndex_[istep].push_back(mol->getGlobalIndex());
160 } else {
161 sele1ToIndex_[istep].resize(index + 1);
162 sele1ToIndex_[istep][index] = mol->getGlobalIndex();
163 }
164
165 if (!uniqueSelections_) { index = computeProperty2(istep, mol); }
166 }
167
168 if (uniqueSelections_) {
169 for (mol = seleMan2_.beginSelectedMolecule(imol2); mol != NULL;
170 mol = seleMan2_.nextSelectedMolecule(imol2)) {
171 if (autoCorrFunc_) {
172 index = computeProperty1(istep, mol);
173 } else {
174 index = computeProperty2(istep, mol);
175 }
176 if (index == sele2ToIndex_[istep].size()) {
177 sele2ToIndex_[istep].push_back(mol->getGlobalIndex());
178 } else {
179 sele2ToIndex_[istep].resize(index + 1);
180 sele2ToIndex_[istep][index] = mol->getGlobalIndex();
181 }
182 }
183 }
184 }
185
186 if (doObjectProperties_) {
187 for (sd = seleMan1_.beginSelected(isd1); sd != NULL;
188 sd = seleMan1_.nextSelected(isd1)) {
189 index = computeProperty1(istep, sd);
190
191 if (index == sele1ToIndex_[istep].size()) {
192 sele1ToIndex_[istep].push_back(sd->getGlobalIndex());
193 } else {
194 sele1ToIndex_[istep].resize(index + 1);
195 sele1ToIndex_[istep][index] = sd->getGlobalIndex();
196 }
197
198 if (!uniqueSelections_) { index = computeProperty2(istep, sd); }
199 }
200
201 if (uniqueSelections_) {
202 for (sd = seleMan2_.beginSelected(isd2); sd != NULL;
203 sd = seleMan2_.nextSelected(isd2)) {
204 if (autoCorrFunc_) {
205 index = computeProperty1(istep, sd);
206 } else {
207 index = computeProperty2(istep, sd);
208 }
209 if (index == sele2ToIndex_[istep].size()) {
210 sele2ToIndex_[istep].push_back(sd->getGlobalIndex());
211 } else {
212 sele2ToIndex_[istep].resize(index + 1);
213 sele2ToIndex_[istep][index] = sd->getGlobalIndex();
214 }
215 }
216 }
217 }
218
219 if (doBondProperties_) {
220 for (bond = seleMan1_.beginSelectedBond(ibond1); bond != NULL;
221 bond = seleMan1_.nextSelectedBond(ibond1)) {
222 index = computeProperty1(istep, bond);
223
224 if (index == sele1ToIndex_[istep].size()) {
225 sele1ToIndex_[istep].push_back(bond->getGlobalIndex());
226 } else {
227 sele1ToIndex_[istep].resize(index + 1);
228 sele1ToIndex_[istep][index] = bond->getGlobalIndex();
229 }
230
231 if (!uniqueSelections_) { index = computeProperty2(istep, bond); }
232 }
233
234 if (uniqueSelections_) {
235 for (bond = seleMan2_.beginSelectedBond(ibond2); bond != NULL;
236 bond = seleMan2_.nextSelectedBond(ibond2)) {
237 if (autoCorrFunc_) {
238 index = computeProperty1(istep, bond);
239 } else {
240 index = computeProperty2(istep, bond);
241 }
242 if (index == sele2ToIndex_[istep].size()) {
243 sele2ToIndex_[istep].push_back(bond->getGlobalIndex());
244 } else {
245 sele2ToIndex_[istep].resize(index + 1);
246 sele2ToIndex_[istep][index] = bond->getGlobalIndex();
247 }
248 }
249 }
250 }
251 }
252
253 template<typename T>
254 void TimeCorrFunc<T>::doCorrelate() {
255 painCave.isFatal = 0;
256 painCave.severity = OPENMD_INFO;
257 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
258 "Starting pre-correlate scan.");
259 simError();
260 preCorrelate();
261
262 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
263 "Calculating correlation function.");
264 simError();
265 correlation();
266
267 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
268 "Doing post-correlation calculations.");
269 simError();
270 postCorrelate();
271
272 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH, "Writing output.");
273 simError();
274 writeCorrelate();
275 }
276
277 template<typename T>
278 void TimeCorrFunc<T>::correlation() {
279 T zeroType(0.0);
280 for (unsigned int i = 0; i < nTimeBins_; ++i) {
281 histogram_[i] = zeroType;
282 count_[i] = 0;
283 }
284
285 progressBar_->clear();
286 RealType samples = 0.5 * (nFrames_ + 1) * nFrames_;
287 int visited = 0;
288
289 for (int i = 0; i < nFrames_; ++i) {
290 RealType time1 = times_[i];
291
292 for (int j = i; j < nFrames_; ++j) {
293 visited++;
294 progressBar_->setStatus(visited, samples);
295 progressBar_->update();
296
297 // Perform a sanity check on the actual configuration times to
298 // make sure the configurations are spaced the same amount the
299 // sample time said they were spaced:
300
301 RealType time2 = times_[j];
302
303 if (fabs((time2 - time1) - (j - i) * deltaTime_) > 1.0e-4) {
304 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
305 "TimeCorrFunc::correlateBlocks Error: sampleTime (%f)\n"
306 "\tin %s does not match actual time-spacing between\n"
307 "\tconfigurations %d (t = %f) and %d (t = %f).\n",
308 deltaTime_, dumpFilename_.c_str(), i, time1, j, time2);
309 painCave.isFatal = 1;
310 simError();
311 }
312
313 int timeBin = int((time2 - time1) / deltaTime_ + 0.5);
314 correlateFrames(i, j, timeBin);
315 }
316 }
317 }
318
319 /*
320template<typename T>
321void TimeCorrFunc<T>::validateSelection(SelectionManager& seleMan) {
322}
323*/
324
325 template<typename T>
326 void TimeCorrFunc<T>::correlateFrames(int frame1, int frame2, int timeBin) {
327 std::vector<int> s1;
328 std::vector<int> s2;
329
330 std::vector<int>::iterator i1;
331 std::vector<int>::iterator i2;
332
333 T corrVal(0.0);
334
335 if (doSystemProperties_) {
336 corrVal = calcCorrVal(frame1, frame2);
337 histogram_[timeBin] += corrVal;
338 count_[timeBin]++;
339
340 } else {
341 s1 = sele1ToIndex_[frame1];
342
343 if (uniqueSelections_)
344 s2 = sele2ToIndex_[frame2];
345 else
346 s2 = sele1ToIndex_[frame2];
347
348 for (i1 = s1.begin(), i2 = s2.begin(); i1 != s1.end() && i2 != s2.end();
349 ++i1, ++i2) {
350 // If the selections are dynamic, they might not have the
351 // same objects in both frames, so we need to roll either of
352 // the selections until we have the same object to
353 // correlate.
354
355 while (i1 != s1.end() && *i1 < *i2) {
356 ++i1;
357 }
358
359 while (i2 != s2.end() && *i2 < *i1) {
360 ++i2;
361 }
362
363 if (i1 == s1.end() || i2 == s2.end()) break;
364
365 corrVal = calcCorrVal(frame1, frame2, i1 - s1.begin(), i2 - s2.begin());
366 histogram_[timeBin] += corrVal;
367 count_[timeBin]++;
368 }
369 }
370 }
371
372 template<typename T>
373 void TimeCorrFunc<T>::postCorrelate() {
374 T zeroType(0.0);
375 for (unsigned int i = 0; i < nTimeBins_; ++i) {
376 if (count_[i] > 0) {
377 histogram_[i] /= count_[i];
378 } else {
379 histogram_[i] = zeroType;
380 }
381 }
382 }
383
384 template<typename T>
385 void TimeCorrFunc<T>::validateSelection(SelectionManager&) {}
386
387 template<typename T>
388 void TimeCorrFunc<T>::writeCorrelate() {
389 ofstream ofs(outputFilename_.c_str());
390
391 if (ofs.is_open()) {
392 Revision r;
393
394 ofs << "# " << getCorrFuncType() << "\n";
395 ofs << "# OpenMD " << r.getFullRevision() << "\n";
396 ofs << "# " << r.getBuildDate() << "\n";
397 ofs << "# selection script1: \"" << selectionScript1_;
398 ofs << "\"\tselection script2: \"" << selectionScript2_ << "\"\n";
399 if (!paramString_.empty())
400 ofs << "# parameters: " << paramString_ << "\n";
401 if (!labelString_.empty())
402 ofs << "#time\t" << labelString_ << "\n";
403 else
404 ofs << "#time\tcorrVal\n";
405
406 for (unsigned int i = 0; i < nTimeBins_; ++i) {
407 ofs << times_[i] - times_[0] << "\t" << histogram_[i] << "\n";
408 }
409
410 } else {
411 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
412 "TimeCorrFunc::writeCorrelate Error: fail to open %s\n",
413 outputFilename_.c_str());
414 painCave.isFatal = 1;
415 simError();
416 }
417
418 ofs.close();
419 }
420
421 // Template specialization of writeCorrelate for Vector3d
422 template<>
423 void TimeCorrFunc<Vector3d>::writeCorrelate() {
424 ofstream ofs(outputFilename_.c_str());
425
426 if (ofs.is_open()) {
427 Revision r;
428
429 ofs << "# " << getCorrFuncType() << "\n";
430 ofs << "# OpenMD " << r.getFullRevision() << "\n";
431 ofs << "# " << r.getBuildDate() << "\n";
432 ofs << "# selection script1: \"" << selectionScript1_;
433 ofs << "\"\tselection script2: \"" << selectionScript2_ << "\"\n";
434 if (!paramString_.empty())
435 ofs << "# parameters: " << paramString_ << "\n";
436 if (!labelString_.empty())
437 ofs << "#time\t" << labelString_ << "\n";
438 else
439 ofs << "#time\tcorrVal\n";
440
441 for (unsigned int i = 0; i < nTimeBins_; ++i) {
442 ofs << times_[i] - times_[0] << "\t";
443 for (int j = 0; j < 3; j++) {
444 ofs << histogram_[i](j) << '\t';
445 }
446 ofs << '\n';
447 }
448
449 } else {
450 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
451 "TimeCorrFunc::writeCorrelate Error: fail to open %s\n",
452 outputFilename_.c_str());
453 painCave.isFatal = 1;
454 simError();
455 }
456
457 ofs.close();
458 }
459
460 // Template specialization of writeCorrelate for Mat3x3d
461 template<>
462 void TimeCorrFunc<Mat3x3d>::writeCorrelate() {
463 ofstream ofs(outputFilename_.c_str());
464
465 if (ofs.is_open()) {
466 Revision r;
467
468 ofs << "# " << getCorrFuncType() << "\n";
469 ofs << "# OpenMD " << r.getFullRevision() << "\n";
470 ofs << "# " << r.getBuildDate() << "\n";
471 ofs << "# selection script1: \"" << selectionScript1_;
472 ofs << "\"\tselection script2: \"" << selectionScript2_ << "\"\n";
473 if (!paramString_.empty())
474 ofs << "# parameters: " << paramString_ << "\n";
475 if (!labelString_.empty())
476 ofs << "#time\t" << labelString_ << "\n";
477 else
478 ofs << "#time\tcorrVal\n";
479
480 for (unsigned int i = 0; i < nTimeBins_; ++i) {
481 ofs << times_[i] - times_[0] << "\t";
482 for (int j = 0; j < 3; j++) {
483 for (int k = 0; k < 3; k++) {
484 ofs << histogram_[i](j, k) << '\t';
485 }
486 }
487 ofs << '\n';
488 }
489
490 } else {
491 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
492 "TimeCorrFunc::writeCorrelate Error: fail to open %s\n",
493 outputFilename_.c_str());
494 painCave.isFatal = 1;
495 simError();
496 }
497
498 ofs.close();
499 }
500
501 // it is necessary to keep the constructor definitions here or the code wont
502 // be generated and linking issues will occur. Blame templating
503 template<typename T>
504 CrossCorrFunc<T>::CrossCorrFunc(SimInfo* info, const std::string& filename,
505 const std::string& sele1,
506 const std::string& sele2) :
507 TimeCorrFunc<T>(info, filename, sele1, sele2) {
508 this->autoCorrFunc_ = false;
509 }
510
511 template<typename T>
512 AutoCorrFunc<T>::AutoCorrFunc(SimInfo* info, const std::string& filename,
513 const std::string& sele1,
514 const std::string& sele2) :
515 TimeCorrFunc<T>(info, filename, sele1, sele2) {
516 this->autoCorrFunc_ = true;
517 }
518
519 template<typename T>
520 SystemACF<T>::SystemACF(SimInfo* info, const std::string& filename,
521 const std::string& sele1, const std::string& sele2) :
522 AutoCorrFunc<T>(info, filename, sele1, sele2) {
523 this->autoCorrFunc_ = true;
524 this->doSystemProperties_ = true;
525 this->doMolecularProperties_ = false;
526 this->doObjectProperties_ = false;
527 this->doBondProperties_ = false;
528 }
529
530 template<typename T>
531 SystemCCF<T>::SystemCCF(SimInfo* info, const std::string& filename,
532 const std::string& sele1, const std::string& sele2) :
533 CrossCorrFunc<T>(info, filename, sele1, sele2) {
534 this->autoCorrFunc_ = false;
535 this->doSystemProperties_ = true;
536 this->doMolecularProperties_ = false;
537 this->doObjectProperties_ = false;
538 this->doBondProperties_ = false;
539 }
540
541 template<typename T>
542 ObjectACF<T>::ObjectACF(SimInfo* info, const std::string& filename,
543 const std::string& sele1, const std::string& sele2) :
544 AutoCorrFunc<T>(info, filename, sele1, sele2) {
545 this->autoCorrFunc_ = true;
546 this->doSystemProperties_ = false;
547 this->doMolecularProperties_ = false;
548 this->doObjectProperties_ = true;
549 this->doBondProperties_ = false;
550 }
551
552 template<typename T>
553 ObjectCCF<T>::ObjectCCF(SimInfo* info, const std::string& filename,
554 const std::string& sele1, const std::string& sele2) :
555 CrossCorrFunc<T>(info, filename, sele1, sele2) {
556 this->autoCorrFunc_ = false;
557 this->doSystemProperties_ = false;
558 this->doMolecularProperties_ = false;
559 this->doObjectProperties_ = true;
560 this->doBondProperties_ = false;
561 }
562
563 template<typename T>
564 MoleculeACF<T>::MoleculeACF(SimInfo* info, const std::string& filename,
565 const std::string& sele1,
566 const std::string& sele2) :
567 AutoCorrFunc<T>(info, filename, sele1, sele2) {
568 this->autoCorrFunc_ = true;
569 this->doSystemProperties_ = false;
570 this->doMolecularProperties_ = true;
571 this->doObjectProperties_ = false;
572 this->doBondProperties_ = false;
573 }
574
575 template<typename T>
576 MoleculeCCF<T>::MoleculeCCF(SimInfo* info, const std::string& filename,
577 const std::string& sele1,
578 const std::string& sele2) :
579 CrossCorrFunc<T>(info, filename, sele1, sele2) {
580 this->autoCorrFunc_ = false;
581 this->doSystemProperties_ = false;
582 this->doMolecularProperties_ = true;
583 this->doObjectProperties_ = false;
584 this->doBondProperties_ = false;
585 }
586
587 template class AutoCorrFunc<RealType>;
588 template class TimeCorrFunc<RealType>;
589 template class CrossCorrFunc<RealType>;
590
591 template class AutoCorrFunc<Vector3d>;
592 template class TimeCorrFunc<Vector3d>;
593 template class CrossCorrFunc<Vector3d>;
594
595 template class AutoCorrFunc<Mat3x3d>;
596 template class TimeCorrFunc<Mat3x3d>;
597 template class CrossCorrFunc<Mat3x3d>;
598
599 template class TimeCorrFunc<DynamicVector<RealType>>;
600
601 template class AutoCorrFunc<Vector<RealType, 4>>;
602 template class TimeCorrFunc<Vector<RealType, 4>>;
603 template class CrossCorrFunc<Vector<RealType, 4>>;
604
605 template class SystemACF<RealType>;
606 template class SystemACF<Vector3d>;
607 template class SystemACF<Mat3x3d>;
608
609 template class SystemCCF<RealType>;
610 template class SystemCCF<Vector3d>;
611 template class SystemCCF<Mat3x3d>;
612
613 template class ObjectACF<RealType>;
614 template class ObjectACF<Vector3d>;
615 template class ObjectACF<Mat3x3d>;
616
617 template class ObjectCCF<RealType>;
618 template class ObjectCCF<Vector3d>;
619 template class ObjectCCF<Mat3x3d>;
620 template class ObjectCCF<int>;
621
622 template class MoleculeACF<RealType>;
623 template class MoleculeACF<Vector3d>;
624 template class MoleculeACF<Mat3x3d>;
625 template class MoleculeACF<Vector<RealType, 4>>;
626
627 template class MoleculeCCF<RealType>;
628 template class MoleculeCCF<Vector3d>;
629 template class MoleculeCCF<Mat3x3d>;
630} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.