6 #include <boost/filesystem/fstream.hpp>
7 #include <boost/filesystem/operations.hpp>
8 #include <boost/lexical_cast.hpp>
9 #include <boost/regex.hpp>
10 #include <boost/tokenizer.hpp>
17 using namespace OpenMPCD;
20 : structureBlockProcessed(false),
21 primarySimulationVolumeSizeSet(false),
22 primarySimulationVolumeSize(0, 0, 0)
24 std::ios_base::openmode openmode;
26 if(boost::filesystem::is_regular_file(path))
28 writeModeFlag =
false;
29 openmode = std::ios_base::in;
34 openmode = std::ios_base::out;
36 (boost::filesystem::ofstream(path));
39 file.open(path.c_str(), openmode);
43 std::string msg =
"Failed to open snapshot file: ";
51 file.precision(std::numeric_limits<FP>::digits10 + 2);
62 writeStructureBlock();
66 const FP& x,
const FP& y,
const FP& z)
69 assertStructureBlockNotProcessed();
71 primarySimulationVolumeSize.
setX(x);
72 primarySimulationVolumeSize.
setY(y);
73 primarySimulationVolumeSize.
setZ(z);
75 primarySimulationVolumeSizeSet =
true;
78 const std::pair<std::size_t, std::size_t>
82 assertStructureBlockNotProcessed();
85 return std::pair<std::size_t, std::size_t>(0, 0);
89 range.last = range.first + count - 1;
90 range.properties = defaultAtomProperties;
92 atomRanges.push_back(range);
94 return std::make_pair(range.first, range.last);
97 const std::pair<std::size_t, std::size_t>
99 const std::size_t count,
const FP radius,
100 const std::string& name,
const std::string& type)
103 assertStructureBlockNotProcessed();
106 return std::pair<std::size_t, std::size_t>(0, 0);
108 if(radius < 0 && radius != -1)
120 range.last = range.first + count - 1;
121 range.properties = defaultAtomProperties;
124 range.properties.
radius = radius;
127 range.properties.name = name;
130 range.properties.type = type;
132 atomRanges.push_back(range);
134 return std::make_pair(range.first, range.last);
140 typedef std::list<AtomRange>::const_iterator It;
141 for(It it = atomRanges.begin(); it != atomRanges.end(); ++it)
143 if(atomID > it->last)
146 return it->properties;
155 assertStructureBlockNotProcessed();
164 std::swap(atom1, atom2);
166 const std::pair<std::size_t, std::size_t> bond(atom1, atom2);
168 if(bonds.count(bond))
183 std::swap(atom1, atom2);
185 const std::pair<std::size_t, std::size_t> bond(atom1, atom2);
187 return bonds.count(bond);
191 const FP*
const positions,
const FP*
const velocities)
195 if(!structureBlockProcessed)
196 writeStructureBlock();
201 file <<
"timestep\n";
205 file << positions[3*i + 0] <<
" ";
206 file << positions[3*i + 1] <<
" ";
207 file << positions[3*i + 2];
211 file <<
" " << velocities[3*i + 0];
212 file <<
" " << velocities[3*i + 1];
213 file <<
" " << velocities[3*i + 2];
222 FP*
const velocities,
223 bool*
const velocitiesEncountered)
227 const boost::char_separator<char> separator(
" \t");
228 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
229 typedef Tokenizer::iterator TokenIt;
237 line = getLine(
true);
238 }
while(line.empty());
240 Tokenizer tokenizer(line, separator);
242 TokenIt it = tokenizer.begin();
243 const std::string firstToken = *it;
246 if( firstToken ==
"t" || firstToken ==
"timestep" ||
247 firstToken ==
"c" || firstToken ==
"coordinates")
249 if(it != tokenizer.end())
251 const std::string secondToken = *it;
254 if(it != tokenizer.end())
257 if(secondToken ==
"i" || secondToken ==
"indexed")
260 "Only ordered timestep blocks are supported.");
262 if(secondToken !=
"o" && secondToken !=
"ordered")
271 bool lineWithVelocitiesEncountered =
false;
272 bool lineWithoutVelocitiesEncountered =
false;
273 std::size_t atom = 0;
279 const std::string line = getLine(
true);
284 Tokenizer tokenizer(line, separator);
286 unsigned int token = 0;
287 for(TokenIt it = tokenizer.begin(); it != tokenizer.end(); ++it)
289 if(token < 3 && positions)
290 positions[3 * atom + token] = lexicalCast<FP>(*it);
292 if(token >= 3 && token < 6 && velocities)
293 velocities[3 * atom + token - 3] = lexicalCast<FP>(*it);
300 lineWithoutVelocitiesEncountered =
true;
304 lineWithVelocitiesEncountered =
true;
314 if(velocitiesEncountered)
315 *velocitiesEncountered = lineWithVelocitiesEncountered;
320 if(lineWithVelocitiesEncountered && lineWithoutVelocitiesEncountered)
324 "Some lines contained velocity information, while others did not.");
333 void VTFSnapshotFile::assertWriteMode()
const
338 "Tried to change snapshot in read mode.");
341 void VTFSnapshotFile::assertReadMode()
const
346 "Instance is not in read mode.");
349 void VTFSnapshotFile::assertStructureBlockNotProcessed()
const
351 if(structureBlockProcessed)
354 "The structure block has been processed already.");
357 void VTFSnapshotFile::writeStructureBlock()
360 assertStructureBlockNotProcessed();
365 file << primarySimulationVolumeSize.
getX() <<
" ";
366 file << primarySimulationVolumeSize.
getY() <<
" ";
367 file << primarySimulationVolumeSize.
getZ() <<
"\n";
374 structureBlockProcessed =
true;
377 void VTFSnapshotFile::readStructureBlock()
380 assertStructureBlockNotProcessed();
384 const std::string line = getLine(
false);
390 else if(line[0] ==
'#')
394 else if(line[0] ==
'b')
398 else if(line[0] ==
'p' || line[0] ==
'u')
400 readUnitcellLine(line);
403 line[0] ==
't' || line[0] ==
'c' ||
404 line[0] ==
'i' || line[0] ==
'o')
418 structureBlockProcessed =
true;
421 void VTFSnapshotFile::readUnitcellLine(
const std::string& line)
424 assertStructureBlockNotProcessed();
427 std::string regexString =
"(?:(?:p|pbc)|(?:u|unitcell))";
430 for(
unsigned int i=0; i<3; ++i)
431 regexString +=
"\\s+(\\S+)";
434 regexString +=
"(?:";
435 for(
unsigned int i=0; i<3; ++i)
436 regexString +=
"\\s+(\\S+)";
440 regexString+=
"\\s*";
442 const boost::regex re(regexString);
443 boost::cmatch captures;
445 if(!boost::regex_match(line.c_str(), captures, re))
453 primarySimulationVolumeSize.
setX(lexicalCast<FP>(captures.str(1)));
454 primarySimulationVolumeSize.
setY(lexicalCast<FP>(captures.str(2)));
455 primarySimulationVolumeSize.
setZ(lexicalCast<FP>(captures.str(3)));
457 primarySimulationVolumeSizeSet =
true;
460 void VTFSnapshotFile::writeAtomLines()
463 assertStructureBlockNotProcessed();
465 typedef std::list<AtomRange>::const_iterator It;
466 for(It it = atomRanges.begin(); it != atomRanges.end(); ++it)
468 file <<
"atom " << it->first;
469 if(it->first != it->last)
470 file <<
":" << it->last;
472 if(it->properties.name.is_initialized())
473 file <<
" name " << it->properties.name.get();
475 if(it->properties.type.is_initialized())
476 file <<
" type " << it->properties.type.get();
478 if(it->properties.radius.is_initialized())
479 file <<
" radius " << it->properties.radius.get();
485 void VTFSnapshotFile::readAtomLine(
const std::string& line_)
488 assertStructureBlockNotProcessed();
490 const boost::regex commaRegex(
"\\s*,\\s*");
491 const std::string line = boost::regex_replace(line_, commaRegex,
", ");
493 const boost::char_separator<char> separator(
" \t");
494 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
495 Tokenizer tokenizer(line, separator);
497 const boost::regex aidRegex(
"([0-9]+)(?::([0-9]+))?(,)?");
498 const boost::regex aidDefaultRegex(
"default(,)?");
500 AtomProperties properties = defaultAtomProperties;
501 std::vector<std::pair<std::size_t, std::size_t> > newRanges;
502 std::string optionName;
503 bool isNewDefault =
false;
504 bool expectAID =
true;
505 for(Tokenizer::iterator it = tokenizer.begin(); it != tokenizer.end(); ++it)
507 if(it == tokenizer.begin() && (*it ==
"a" || *it ==
"atom"))
510 using boost::regex_match;
512 boost::cmatch capturesDefault;
514 regex_match(it->c_str(), capturesDefault, aidDefaultRegex))
518 if(!capturesDefault[1].matched)
526 boost::cmatch captures;
527 if(expectAID && regex_match(it->c_str(), captures, aidRegex))
532 const std::size_t first = lexicalCast<std::size_t>(captures.str(1));
533 const std::size_t last =
534 captures[2].matched ?
535 lexicalCast<std::size_t>(captures.str(2)) : first;
541 newRanges.push_back(std::make_pair(first, last));
543 if(!captures[3].matched)
553 if(optionName.empty())
556 if(*it ==
"n" || *it ==
"name")
560 else if(*it ==
"t" || *it ==
"type")
564 else if(*it ==
"r" || *it ==
"radius")
566 optionName =
"radius";
577 if(optionName ==
"name")
583 properties.name = *it;
585 else if(optionName ==
"type")
591 properties.type = *it;
593 else if(optionName ==
"radius")
595 properties.radius = lexicalCast<FP>(*it);
597 if(properties.radius.get() < 0)
610 if(!optionName.empty())
614 defaultAtomProperties = properties;
616 for(std::size_t i=0; i<newRanges.size(); ++i)
619 newRange.first = newRanges[i].first;
620 newRange.last = newRanges[i].second;
621 newRange.properties = properties;
622 setAtomRange(newRange);
626 void VTFSnapshotFile::setAtomRange(
const AtomRange& range)
628 assertAtomRangesContiguous();
631 if(atomRanges.empty())
637 newRange.last = range.first - 1;
638 newRange.properties = defaultAtomProperties;
640 atomRanges.push_back(newRange);
643 atomRanges.push_back(range);
646 typedef std::list<AtomRange>::iterator It;
647 for(It it = atomRanges.begin(); it != atomRanges.end(); ++it)
649 if(range.first > it->last)
654 if(range.first > it->first)
656 AtomRange prefix = *it;
657 prefix.last = range.first - 1;
658 atomRanges.insert(it, prefix);
660 it->first = range.first;
665 while(range.last >= it->last)
667 it = atomRanges.erase(it);
669 if(it == atomRanges.end())
671 atomRanges.push_back(range);
680 it->first = range.last + 1;
684 atomRanges.insert(it, range);
689 if(lastAtomID + 1 < range.first)
692 newRange.first = lastAtomID + 1;
693 newRange.last = range.first - 1;
694 newRange.properties = defaultAtomProperties;
696 atomRanges.push_back(newRange);
699 atomRanges.push_back(range);
702 void VTFSnapshotFile::assertAtomRangesContiguous()
const
704 std::size_t nextFirst = 0;
705 typedef std::list<AtomRange>::const_iterator It;
706 for(It it = atomRanges.begin(); it != atomRanges.end(); ++it)
708 if(it->first != nextFirst)
711 if(it->first > it->last)
714 nextFirst = it->last + 1;
718 void VTFSnapshotFile::writeBondLines()
721 assertStructureBlockNotProcessed();
723 typedef std::set<std::pair<std::size_t, std::size_t> > Set;
724 for(Set::const_iterator it = bonds.begin(); it != bonds.end(); ++it)
725 file <<
"bond " << it->first <<
":" << it->second <<
"\n";
728 void VTFSnapshotFile::readBondLine(
const std::string& line_)
731 assertStructureBlockNotProcessed();
733 const boost::regex commaRegex(
"\\s*,\\s*");
734 const std::string line = boost::regex_replace(line_, commaRegex,
", ");
736 const boost::char_separator<char> separator(
" \t");
737 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
738 Tokenizer tokenizer(line, separator);
740 const boost::regex bondSpecifierRegex(
"([0-9]+)(::?)([0-9]+)(,)?");
742 bool expectBondSpecifier =
true;
743 for(Tokenizer::iterator it = tokenizer.begin(); it != tokenizer.end(); ++it)
745 if(it == tokenizer.begin() && (*it ==
"b" || *it ==
"bond"))
748 if(!expectBondSpecifier)
751 using boost::regex_match;
753 boost::cmatch captures;
754 if(!regex_match(it->c_str(), captures, bondSpecifierRegex))
762 const std::size_t first = lexicalCast<std::size_t>(captures.str(1));
763 const std::size_t last =
764 captures[3].matched ?
765 lexicalCast<std::size_t>(captures.str(3)) : first;
777 captures.str(2) ==
":" || captures.str(2) ==
"::");
779 const bool isBondChain = captures.str(2).length() == 2;
784 for(std::size_t current = first; current < last; ++current)
786 const std::pair<std::size_t, std::size_t>
787 bond(current, current + 1);
789 if(bonds.count(bond))
791 std::stringstream ss;
792 ss <<
"Bond declared twice: ";
793 ss << first <<
":" << last <<
"\n";
794 ss <<
"In line: " << line;
803 const std::pair<std::size_t, std::size_t> bond(first, last);
805 if(bonds.count(bond))
807 std::stringstream ss;
808 ss <<
"Bond declared twice: ";
809 ss << first <<
":" << last <<
"\n";
810 ss <<
"In line: " << line;
818 if(!captures[4].matched)
819 expectBondSpecifier =
false;
823 const std::string VTFSnapshotFile::getLine(
const bool extract)
830 if(file.peek() == std::char_traits<std::fstream::char_type>::eof())
835 file.setstate(std::ios_base::eofbit);
843 std::getline(file, line);
844 return stripLeadingWhitespace(line);
847 const std::fstream::pos_type position = file.tellg();
848 if(position == std::fstream::pos_type(-1))
851 std::getline(file, line);
857 file.seekg(position);
862 return stripLeadingWhitespace(line);
865 const std::string VTFSnapshotFile::stripLeadingWhitespace(
866 const std::string& str)
868 const std::string::size_type firstNonWhitespace =
869 str.find_first_not_of(
" \t\n");
871 if(firstNonWhitespace == std::string::npos)
874 return str.substr(firstNonWhitespace);