MDStressLab++
Loading...
Searching...
No Matches
BoxConfiguration.cpp
Go to the documentation of this file.
1/*
2 * BoxConfiguration.cpp
3 *
4 * Created on: Nov 4, 2019
5 * Author: Nikhil
6 */
7
8#include <fstream>
9#include <algorithm>
10#include <sstream>
11#include <unordered_map>
12#include "BoxConfiguration.h"
13#include "Configuration.h"
14#include "typedef.h"
15#include "neighbor_list.h"
16#include "helper.hpp"
17
18
19BoxConfiguration::BoxConfiguration(int numberOfParticles, int referenceAndFinal):
20 Configuration(numberOfParticles,referenceAndFinal)
21{
22 MY_HEADING("Initializing a box configuration");
23 if (referenceAndFinal)
24 std::cout << "Creating a configuration of " << numberOfParticles << " particles" <<
25 " along with reference coordinates in a box of size zero" << std::endl;
26 else
27 std::cout << "Creating a configuration of " << numberOfParticles << " particles" <<
28 " in a box of size zero" << std::endl;
29 // Default to zero box sizes and no pbc
30 box.setZero();
31 reference_box.setZero();
32 box_origin.setZero();
33 reference_box_origin.setZero();
34 pbc.setZero();
35}
36
37
38void BoxConfiguration::read(std::string configFileName, int referenceAndFinal)
39{
40 // Read in the atomistic system
41 std::cout << "Reading the box configuration from file " << configFileName << std::endl;
42 std::ifstream file(configFileName);
43 if(!file)
44 {
45 // Print an error and exit
46 std::cerr << "ERROR: " << configFileName << " could not be opened for reading!" << std::endl;
47 exit(1);
48 }
49
50 int numberOfParticlesInFile;
51 file >> numberOfParticlesInFile;
52 if (numberOfParticles != numberOfParticlesInFile)
53 MY_ERROR("Error: Number of particles in file does not equal to that of BoxConfiguration");
54
55 file.ignore(32767, '\n');
56 auto nextDataLine = [&]() {
57 std::string dataLine;
58 do
59 {
60 if (!std::getline(file,dataLine))
61 MY_ERROR("ERROR: Unexpected end of file while reading box configuration.");
62 }
63 while (dataLine.empty() ||
64 dataLine.find_first_not_of(" \t\r\n") == std::string::npos ||
65 dataLine[dataLine.find_first_not_of(" \t\r\n")] == '#');
66 return dataLine;
67 };
68
69 std::string firstBoxLine= nextDataLine();
70 std::istringstream keywordStream(firstBoxLine);
71 std::string keyword;
72 keywordStream >> keyword;
73 std::transform(keyword.begin(),keyword.end(),keyword.begin(),::tolower);
74 if (keyword=="origin")
75 {
76 Vector3d origin;
77 if (!(keywordStream >> origin(0) >> origin(1) >> origin(2)))
78 MY_ERROR("ERROR: Expected three coordinates after origin.");
80 box_origin= origin;
81 firstBoxLine= nextDataLine();
82 }
83
84 std::istringstream firstBoxLineStream(firstBoxLine);
85 auto readBoxScalar = [&](double& value, const std::string& errorMessage) {
86 if (firstBoxLineStream >> value)
87 return;
88 if (!(file >> value))
89 MY_ERROR(errorMessage);
90 };
91
92 for(int i=0;i<DIM*DIM;++i)
93 readBoxScalar(reference_box(i),"ERROR: Reference box size.");
94 for(int i=0;i<DIM*DIM;++i)
95 readBoxScalar(box(i),"ERROR: Box size.");
96 for(int i=0;i<DIM;++i)
97 {
98 if (firstBoxLineStream >> pbc(i))
99 continue;
100 if (!(file >> pbc(i))) MY_ERROR("ERROR: PBC.");
101 }
102
103 file.ignore(32767, '\n');
104 std::string speciesMassLine;
105 do
106 {
107 if (!std::getline(file, speciesMassLine))
108 MY_ERROR("ERROR: Expected species-mass line after PBC.");
109 }
110 while (speciesMassLine.empty() || speciesMassLine.find_first_not_of(" \t\r\n") == std::string::npos);
111
112 std::map<std::string,double> speciesMasses;
113 std::istringstream speciesMassStream(speciesMassLine);
114 std::string speciesName;
115 double speciesMass;
116 while (speciesMassStream >> speciesName)
117 {
118 if (!(speciesMassStream >> speciesMass))
119 MY_ERROR("ERROR: Expected mass after species " + speciesName + " in species-mass line.");
120 speciesMasses[speciesName]= speciesMass;
121 }
122 if (speciesMasses.empty())
123 MY_ERROR("ERROR: No species masses found after PBC.");
124
125 std::string speciesTemp;
126 for(int i=0;i<numberOfParticles;++i)
127 {
128 if(!(file >> speciesTemp)) MY_ERROR("ERROR: Species code of particle " + std::to_string(i));
129 if (speciesMasses.find(speciesTemp) == speciesMasses.end())
130 MY_ERROR("ERROR: Species " + speciesTemp + " has no mass in the species-mass line.");
131 species.push_back(speciesTemp);
132 masses(i)= speciesMasses.at(speciesTemp);
133 for(int j=0;j<DIM;++j)
134 if(!(file >> coordinates[Current](i,j))) MY_ERROR("ERROR: Coordinate of particle " + std::to_string(i));
135 for(int j=0;j<DIM;++j)
136 if(!(file >> velocities(i,j))) MY_ERROR("ERROR: Velocity of particle " + std::to_string(i));
137 if (referenceAndFinal == true)
138 {
139 for(int j=0;j<DIM;++j)
140 if(!(file >> coordinates[Reference](i,j)))
141 MY_ERROR("ERROR: Reference coordinate of particle " + std::to_string(i) + "\n");
142 }
143 else
144 {
145 file.ignore(32767, '\n');
146 }
147 }
148 std::cout << std::endl;
149 std::cout << "Box size = " << std::endl;
150 std::cout << box << std::endl;
151 std::cout << std::endl;
152 std::cout << "Reference box size = " << std::endl;
153 std::cout << reference_box << std::endl;
154 std::cout << std::endl;
155 std::cout << "Periodic boundary conditions = " << pbc << std::endl;
156}
157
158void BoxConfiguration::readLMP(const std::string& configFileName,
159 const ConfigType& configType){
160 // Read in the atomistic system
161 if (configType==Current)
162 std::cout << "Reading the current box configuration from lammps data file " << configFileName << std::endl;
163 else if (configType==Reference)
164 std::cout << "Reading the reference box configuration from lammps data file " << configFileName << std::endl;
165 std::ifstream file(configFileName);
166 if(!file)
167 {
168 std::cerr << "ERROR: " << configFileName << " could not be opened for reading!" << std::endl;
169 exit(1);
170 }
171
172 // Lambda version of hasEnding
173 auto hasEnding = [](const std::string& fullString, const std::string& ending) -> bool {
174 return fullString.size() >= ending.size() &&
175 fullString.compare(fullString.size() - ending.size(), ending.size(), ending) == 0;
176 };
177
178 if (hasEnding(configFileName, ".lmp")) {
179 lmpParser(file,configType);
180 } else {
181 std::cerr << "ERROR: Expecting file with extension .lmp!" << std::endl;
182 exit(1);
183 }
184}
185
186void BoxConfiguration::readLMP(const std::string& currentConfigFileName,
187 const std::string& referenceConfigFileName){
188 readLMP(currentConfigFileName,Current);
189 if(coordinates[Reference].rows()>0)
190 readLMP(referenceConfigFileName,Reference);
191 else
192 MY_ERROR("Error: Memory not assigned to store reference configuration.");
193}
194
195void BoxConfiguration::lmpParser(std::ifstream& file, const ConfigType& configType)
196{
197 std::string line;
198 int numAtoms = 0;
199 int numberOfAtomTypes = 0;
200 std::unordered_map<int, std::string> typeToSpecies;
201 std::unordered_map<int, double> typeToMass;
202 double xlo=0.0, xhi=0.0, ylo=0.0, yhi=0.0, zlo=0.0, zhi=0.0;
203 double xy=0.0, xz=0.0, yz=0.0;
204 auto updateBox = [&]() {
205 double xloTrue= xlo - std::min({0.0,xy,xz,xy+xz});
206 double xhiTrue= xhi - std::max({0.0,xy,xz,xy+xz});
207 double yloTrue= ylo - std::min(0.0,yz);
208 double yhiTrue= yhi - std::max(0.0,yz);
209 Matrix3d lammpsBox= Matrix3d::Zero();
210 lammpsBox.col(0)= Vector3d(xhiTrue-xloTrue,0.0,0.0);
211 lammpsBox.col(1)= Vector3d(xy,yhiTrue-yloTrue,0.0);
212 lammpsBox.col(2)= Vector3d(xz,yz,zhi-zlo);
213 if (configType==Current)
214 {
215 box= lammpsBox;
216 box_origin= Vector3d(xloTrue,yloTrue,zlo);
217 }
218 else
219 {
220 reference_box= lammpsBox;
221 reference_box_origin= Vector3d(xloTrue,yloTrue,zlo);
222 }
223 };
224
225 while (std::getline(file, line)) {
226 // Normalize to lowercase for keyword checks (optional but helpful)
227 std::string loweredLine = line;
228 std::transform(loweredLine.begin(), loweredLine.end(), loweredLine.begin(), ::tolower);
229
230 // Parse total number of atoms
231 if (loweredLine.find("atoms") != std::string::npos && (std::stringstream(line) >> numAtoms) ) {
232 //std::istringstream ss(line);
233 //ss >> numAtoms;
234 if (numberOfParticles != numAtoms)
235 MY_ERROR("Error: Number of particles in file does not equal to that of BoxConfiguration");
236 }
237 // Parse number of atom types
238 else if (loweredLine.find("atom types") != std::string::npos) {
239 std::istringstream ss(line);
240 ss >> numberOfAtomTypes;
241 }
242 // Parse box dimensions
243 else if (loweredLine.find("xlo xhi") != std::string::npos) {
244 std::istringstream ss(line);
245 ss >> xlo >> xhi;
246 updateBox();
247 } else if (loweredLine.find("ylo yhi") != std::string::npos) {
248 std::istringstream ss(line);
249 ss >> ylo >> yhi;
250 updateBox();
251 } else if (loweredLine.find("zlo zhi") != std::string::npos) {
252 std::istringstream ss(line);
253 ss >> zlo >> zhi;
254 updateBox();
255 } else if (loweredLine.find("xy xz yz") != std::string::npos) {
256 std::istringstream ss(line);
257 ss >> xy >> xz >> yz;
258 updateBox();
259 }
260
261 // Process Masses section
262 else if (loweredLine.find("masses") != std::string::npos) {
263 int massLinesRead = 0;
264
265 // Read lines until we've read all the atom types
266 while (std::getline(file, line)) {
267 // Skip blank or whitespace-only lines
268 if (line.empty() || line.find_first_not_of(" \t\r\n") == std::string::npos)
269 continue;
270
271 // Skip comment lines
272 if (line[0] == '#')
273 continue;
274
275 // Read: <type> <mass> # optional comment with species name
276 std::istringstream ss(line);
277 int type;
278 double mass;
279 std::string comment;
280
281 ss >> type >> mass;
282 std::getline(ss, comment); // grab remainder of line (comment)
283
284 std::string speciesName = "Unknown";
285
286 // Extract species name from comment if present
287 size_t hashPos = comment.find('#');
288 if (hashPos != std::string::npos) {
289 speciesName = comment.substr(hashPos + 1);
290 // Trim whitespace
291 speciesName.erase(0, speciesName.find_first_not_of(" \t"));
292 speciesName.erase(speciesName.find_last_not_of(" \t\r\n") + 1);
293 }
294
295 typeToSpecies[type] = speciesName;
296 typeToMass[type] = mass;
297
298 if (++massLinesRead >= numberOfAtomTypes)
299 break; // We've read all the expected mass lines
300 }
301 }
302
303 // Process Atoms section
304 else if (loweredLine.find("atoms") != std::string::npos) {
305 // Skip lines until we reach actual atom data
306 while (std::getline(file, line)) {
307 if (line.empty() || line.find_first_not_of(" \t\r\n") == std::string::npos)
308 continue;
309 if (line[0] == '#')
310 continue;
311 break; // first data line found
312 }
313
314 if(configType==Current) species.resize(numberOfParticles);
315 // Read atom lines
316 for (int i = 0; i < numberOfParticles; ++i) {
317 std::istringstream ss(line); // first valid line
318 int id, type;
319 double x, y, z;
320 if (!(ss >> id >> type >> x >> y >> z))
321 MY_ERROR("ERROR: Coordinate of particle " + std::to_string(i));
322 if (typeToSpecies.find(type) == typeToSpecies.end() ||
323 typeToMass.find(type) == typeToMass.end())
324 MY_ERROR("ERROR: Atom type " + std::to_string(type) + " is missing from the Masses section.");
325
326 int idxFlagx= 0; int idxFlagy= 0; int idxFlagz= 0;
327 int tmpx, tmpy, tmpz;
328 if (ss >> tmpx >> tmpy >> tmpz) {
329 idxFlagx = tmpx;
330 idxFlagy = tmpy;
331 idxFlagz = tmpz;
332 }
333
334 //if(configType==Current) species.push_back(typeToSpecies[type]);
335 if(configType==Current) {
336 species[id-1]= typeToSpecies.at(type);
337 masses(id-1)= typeToMass.at(type);
338 }
339 if(configType==Reference) {
340 assert(species[id-1] == typeToSpecies.at(type) &&
341 "Species in the reference configuration do not match with those in the "
342 "current configuration");
343 assert(masses(id-1) == typeToMass.at(type) &&
344 "Masses in the reference configuration do not match with those in the "
345 "current configuration");
346 }
347 const Matrix3d& boxMatrix= (configType==Reference) ? reference_box : box;
348 Vector3d imageShift= idxFlagx*boxMatrix.col(0).transpose() +
349 idxFlagy*boxMatrix.col(1).transpose() +
350 idxFlagz*boxMatrix.col(2).transpose();
351 coordinates[configType].row(id - 1)= Vector3d(x,y,z) + imageShift;
352
353 if (i < numberOfParticles - 1) {
354 std::getline(file, line); // read next line
355 }
356 }
357
358 }
359
360 // Process Velocities section
361 else if (loweredLine.find("velocities") != std::string::npos) {
362 // Skip lines until we reach actual velocity data
363 while (std::getline(file, line)) {
364 if (line.empty() || line.find_first_not_of(" \t\r\n") == std::string::npos)
365 continue;
366 if (line[0] == '#')
367 continue;
368 break; // first data line found
369 }
370
371 for (int i = 0; i < numberOfParticles; ++i) {
372 std::istringstream ss(line);
373 int id;
374 double vx, vy, vz;
375 if (!(ss >> id >> vx >> vy >> vz))
376 MY_ERROR("ERROR: Velocity of particle " + std::to_string(i));
377
378 if(configType==Current) {
379 velocities(id - 1, 0) = vx;
380 velocities(id - 1, 1) = vy;
381 velocities(id - 1, 2) = vz;
382 }
383
384 if (i < numberOfParticles - 1) {
385 std::getline(file, line);
386 }
387 }
388 }
389 }
390
391 // Assume no periodicity for now
392 pbc = Eigen::Vector3i::Zero();
393}
394
395
397{
398 // Build padding atoms
399 int numberOfPaddings{0};
400 std::vector<double> reference_coordinatesOfPaddings,coordinatesOfPaddings;
401 std::vector<std::string> speciesOfPaddings;
402 std::vector<int> masterOfPaddings;
403 int referenceAndFinal= (coordinates.at(Reference).rows()>0);
404
406 padding,
408 box_origin.data(),
409 reference_box.data(),
410 box.data(),
411 pbc.data(),
412 coordinates.at(Reference).data(),
413 coordinates.at(Current).data(),
414 species,
415 numberOfPaddings,
416 reference_coordinatesOfPaddings,
417 coordinatesOfPaddings,
418 speciesOfPaddings,
419 masterOfPaddings,
420 referenceAndFinal);
421
422 int total= numberOfParticles + numberOfPaddings;
423
424 Configuration* config_ptr(new Configuration{total,referenceAndFinal});
425
426 // copy the coordinates, particleContributing and species
427 // of contributing atoms from BoxConfiguration to Configuration
428 if (referenceAndFinal) (config_ptr->coordinates.at(Reference)).topRows(numberOfParticles)= coordinates.at(Reference);
429 (config_ptr->coordinates.at(Current)).topRows(numberOfParticles)= coordinates.at(Current);
430 config_ptr->velocities.topRows(numberOfParticles)= velocities;
431 config_ptr->masses.head(numberOfParticles)= masses;
432 for (auto it= species.begin();it!= species.end();it++)
433 config_ptr->species.push_back(*it);
434
435 if (numberOfPaddings)
436 {
437 using RowMajorMatrixXd = Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor>;
438 if (referenceAndFinal)
439 {
440 config_ptr->coordinates.at(Reference).bottomRows(numberOfPaddings)=
441 Eigen::Map<const RowMajorMatrixXd> (reference_coordinatesOfPaddings.data(),numberOfPaddings,DIM);
442 }
443 config_ptr->coordinates.at(Current).bottomRows(numberOfPaddings)=
444 Eigen::Map<const RowMajorMatrixXd> (coordinatesOfPaddings.data(),numberOfPaddings,DIM);
445 for (int i_padding=0; i_padding<numberOfPaddings; ++i_padding)
446 {
447 int master= masterOfPaddings[i_padding];
448 config_ptr->velocities.row(numberOfParticles+i_padding)= velocities.row(master);
449 config_ptr->masses(numberOfParticles+i_padding)= masses(master);
450 }
451 for (auto it= speciesOfPaddings.begin();it!= speciesOfPaddings.end();it++)
452 config_ptr->species.push_back(*it);
453 }
454
455 return config_ptr;
456}
458 // TODO Auto-generated destructor stub
459}
void readLMP(const std::string &, const ConfigType &configType)
Reads a configuration from a LAMMPS data file.
BoxConfiguration(int numberOfParticles, int referenceAndFinal)
Constructs a BoxConfiguration with a given number of particles.
Vector3i pbc
Periodic boundary conditions. pbc=(1,0,1) implies periodicity along the and -directions.
void read(std::string configFileName, int referenceAndFinal)
A function to read the properties of atoms from a file in a MDStressLab format.
Configuration * getConfiguration(double padding) const
This function returns a padded configuration by adding padding atoms originating due to pbcs.
void lmpParser(std::ifstream &, const ConfigType &)
Parses a LAMMPS data file to populate configuration data.
Matrix3d box
The current and reference box vectors stored as columns of respective matrices, and their origins.
Vector3d reference_box_origin
Represents atomic configuration data including coordinates, velocities, species, and masses.
VectorXd masses
Mass of each particle.
std::vector< std::string > species
Species names for each particle (size equals numberOfParticles).
MatrixXd velocities
Velocities of particles.
int numberOfParticles
Total number of particles in the configuration.
std::map< ConfigType, MatrixXd > coordinates
Map from configuration type (Reference or Current) to coordinate matrices.
#define DIM
int nbl_create_paddings(int const numberOfParticles, double const cutoff, double const *reference_origin, double const *origin, double const *reference_cell, double const *cell, int const *PBC, double const *reference_coordinates, double const *coordinates, const std::vector< std::string > &speciesCode, int &numberOfPaddings, std::vector< double > &reference_coordinatesOfPaddings, std::vector< double > &coordinatesOfPaddings, std::vector< std::string > &speciesCodeOfPaddings, std::vector< int > &masterOfPaddings, int referenceAndFinal)
#define MY_ERROR(message)
Definition typedef.h:17
Eigen::Matrix< double, DIM, DIM, Eigen::RowMajor > Matrix3d
Definition typedef.h:56
Eigen::Matrix< double, 1, DIM, Eigen::RowMajor > Vector3d
Definition typedef.h:60
ConfigType
Definition typedef.h:68
@ Current
Definition typedef.h:70
@ Reference
Definition typedef.h:69
#define MY_HEADING(heading)
Definition typedef.h:36