OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
nanorodBuilder.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 <config.h>
46
47#include <algorithm>
48#include <cmath>
49#include <cstdio>
50#include <cstdlib>
51#include <cstring>
52#include <fstream>
53#include <iostream>
54#include <map>
55#include <random>
56#include <string>
57
58#include "brains/Register.hpp"
59#include "brains/SimCreator.hpp"
60#include "brains/SimInfo.hpp"
61#include "io/DumpWriter.hpp"
62#include "lattice/Lattice.hpp"
65#include "math/Vector3.hpp"
66#include "nanorodBuilderCmd.hpp"
67#include "shapedLatticeEllipsoid.hpp"
68#include "shapedLatticeRod.hpp"
69#include "utils/MoLocator.hpp"
70#include "utils/StringUtils.hpp"
71
72using namespace std;
73using namespace OpenMD;
74void createMdFile(const std::string& oldMdFileName,
75 const std::string& newMdFileName, std::vector<int> numMol);
76
77int main(int argc, char* argv[]) {
79
80 gengetopt_args_info args_info;
81 std::string latticeType;
82 std::string inputFileName;
83 std::string outputFileName;
84 MoLocator* locator;
85 int nComponents;
86 double latticeConstant;
87 RealType rodRadius;
88 RealType rodLength;
89 Mat3x3d hmat;
90 DumpWriter* writer;
91
92 // Parse Command Line Arguments
93 if (cmdline_parser(argc, argv, &args_info) != 0) exit(1);
94
95 /* get lattice type */
96 latticeType = "FCC";
97
98 /* get input file name */
99 if (args_info.inputs_num)
100 inputFileName = args_info.inputs[0];
101 else {
102 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
103 "No input .omd file name was specified "
104 "on the command line");
105 painCave.isFatal = 1;
106 cmdline_parser_print_help();
107 simError();
108 }
109
110 /* parse md file and set up the system */
111 SimCreator oldCreator;
112 SimInfo* oldInfo = oldCreator.createSim(inputFileName, false);
113
114 latticeConstant = args_info.latticeConstant_arg;
115 rodRadius = args_info.radius_arg;
116 rodLength = args_info.length_arg;
117 Globals* simParams = oldInfo->getSimParams();
118
119 vector<Vector3d> sites;
120 vector<Vector3d> orientations;
121
122 if (args_info.ellipsoid_flag) {
123 shapedLatticeEllipsoid nanoEllipsoid(latticeConstant, latticeType,
124 rodLength, rodRadius);
125 sites = nanoEllipsoid.getSites();
126 orientations = nanoEllipsoid.getOrientations();
127 } else {
128 /* Create nanorod */
129 shapedLatticeRod nanoRod(latticeConstant, latticeType, rodRadius,
130 rodLength);
131 /* Build a lattice and get lattice points for this lattice constant */
132 sites = nanoRod.getSites();
133 orientations = nanoRod.getOrientations();
134 }
135
136 /* Set up the random number generator engine */
137 std::random_device rd; // Non-deterministic, uniformly-distributed integer
138 // random number generator
139 std::mt19937 gen(rd()); // 32-bit Mersenne Twister random number engine
140
141 std::vector<std::size_t> vacancyTargets;
142 vector<bool> isVacancy;
143
144 Vector3d myLoc;
145 RealType myR;
146
147 for (std::size_t i = 0; i < sites.size(); i++)
148 isVacancy.push_back(false);
149
150 // cerr << "checking vacancyPercent" << "\n";
151 if (args_info.vacancyPercent_given) {
152 // cerr << "vacancyPercent given" << "\n";
153 if (args_info.vacancyPercent_arg < 0.0 ||
154 args_info.vacancyPercent_arg > 100.0) {
155 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
156 "vacancyPercent was set to a non-sensical value.");
157 painCave.isFatal = 1;
158 simError();
159 } else {
160 RealType vF = args_info.vacancyPercent_arg / 100.0;
161 // cerr << "vacancyPercent = " << vF << "\n";
162 RealType vIR;
163 RealType vOR;
164 if (args_info.vacancyInnerRadius_given) {
165 vIR = args_info.vacancyInnerRadius_arg;
166 } else {
167 vIR = 0.0;
168 }
169 if (args_info.vacancyOuterRadius_given) {
170 vOR = args_info.vacancyOuterRadius_arg;
171 } else {
172 vOR = rodRadius;
173 }
174 if (vIR >= 0.0 && vOR <= rodRadius && vOR >= vIR) {
175 for (std::size_t i = 0; i < sites.size(); i++) {
176 myLoc = sites[i];
177 myR = myLoc.length();
178 if (myR >= vIR && myR <= vOR) { vacancyTargets.push_back(i); }
179 }
180 std::shuffle(vacancyTargets.begin(), vacancyTargets.end(), gen);
181
182 std::size_t nTargets = vacancyTargets.size();
183 vacancyTargets.resize((int)(vF * nTargets));
184
185 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
186 "Removing %d atoms from randomly-selected\n"
187 "\tsites between %lf and %lf.",
188 (int)vacancyTargets.size(), vIR, vOR);
189 painCave.isFatal = 0;
190 painCave.severity = OPENMD_INFO;
191 simError();
192
193 isVacancy.clear();
194 for (std::size_t i = 0; i < sites.size(); i++) {
195 bool vac = false;
196 for (std::size_t j = 0; j < vacancyTargets.size(); j++) {
197 if (i == vacancyTargets[j]) vac = true;
198 }
199 isVacancy.push_back(vac);
200 }
201
202 } else {
203 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
204 "Something is strange about the vacancy\n"
205 "\tinner or outer radii. Check their values.");
206 painCave.isFatal = 1;
207 simError();
208 }
209 }
210 }
211
212 /* Get number of lattice sites */
213 std::size_t nSites = sites.size() - vacancyTargets.size();
214
215 // cerr << "sites.size() = " << sites.size() << "\n";
216 // cerr << "nSites = " << nSites << "\n";
217 // cerr << "vacancyTargets = " << vacancyTargets.size() << "\n";
218
219 std::vector<Component*> components = simParams->getComponents();
220 std::vector<RealType> molFractions;
221 std::vector<RealType> shellRadii;
222 std::vector<int> nMol;
223 std::map<int, int> componentFromSite;
224 nComponents = components.size();
225 // cerr << "nComponents = " << nComponents << "\n";
226
227 if (args_info.molFraction_given && args_info.shellRadius_given) {
228 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
229 "Specify either molFraction or shellRadius "
230 "arguments, but not both!");
231 painCave.isFatal = 1;
232 simError();
233 }
234
235 if (nComponents == 1) {
236 molFractions.push_back(1.0);
237 shellRadii.push_back(rodRadius);
238 } else if (args_info.molFraction_given) {
239 if ((int)args_info.molFraction_given == nComponents) {
240 for (int i = 0; i < nComponents; i++) {
241 molFractions.push_back(args_info.molFraction_arg[i]);
242 }
243 } else if ((int)args_info.molFraction_given == nComponents - 1) {
244 RealType remainingFraction = 1.0;
245 for (int i = 0; i < nComponents - 1; i++) {
246 molFractions.push_back(args_info.molFraction_arg[i]);
247 remainingFraction -= molFractions[i];
248 }
249 molFractions.push_back(remainingFraction);
250 } else {
251 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
252 "nanorodBuilder can't figure out molFractions "
253 "for all of the components in the <MetaData> block.");
254 painCave.isFatal = 1;
255 simError();
256 }
257 } else if ((int)args_info.shellRadius_given) {
258 if ((int)args_info.shellRadius_given == nComponents) {
259 for (int i = 0; i < nComponents; i++) {
260 shellRadii.push_back(args_info.shellRadius_arg[i]);
261 }
262 } else if ((int)args_info.shellRadius_given == nComponents - 1) {
263 for (int i = 0; i < nComponents - 1; i++) {
264 shellRadii.push_back(args_info.shellRadius_arg[i]);
265 }
266 shellRadii.push_back(rodRadius);
267 } else {
268 snprintf(
269 painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
270 "nanorodBuilder can't figure out the\n"
271 "\tshell radii for all of the components in the <MetaData> block.");
272 painCave.isFatal = 1;
273 simError();
274 }
275 } else {
276 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
277 "You have a multi-component <MetaData> block,\n"
278 "\tbut have not specified either molFraction or shellRadius "
279 "arguments.");
280 painCave.isFatal = 1;
281 simError();
282 }
283
284 if (args_info.molFraction_given) {
285 RealType totalFraction = 0.0;
286
287 /* Do some simple sanity checking*/
288
289 for (int i = 0; i < nComponents; i++) {
290 if (molFractions.at(i) < 0.0) {
291 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
292 "One of the requested molFractions was"
293 " less than zero!");
294 painCave.isFatal = 1;
295 simError();
296 }
297 if (molFractions.at(i) > 1.0) {
298 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
299 "One of the requested molFractions was"
300 " greater than one!");
301 painCave.isFatal = 1;
302 simError();
303 }
304 totalFraction += molFractions.at(i);
305 }
306 if (abs(totalFraction - 1.0) > 1e-6) {
307 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
308 "The sum of molFractions was not close enough to 1.0");
309 painCave.isFatal = 1;
310 simError();
311 }
312
313 int remaining = nSites;
314 for (int i = 0; i < nComponents - 1; i++) {
315 nMol.push_back(int((RealType)nSites * molFractions.at(i)));
316 remaining -= nMol.at(i);
317 }
318 nMol.push_back(remaining);
319
320 // recompute actual mol fractions and perform final sanity check:
321
322 std::size_t totalMolecules = 0;
323 for (int i = 0; i < nComponents; i++) {
324 molFractions[i] = (RealType)(nMol.at(i)) / (RealType)nSites;
325 totalMolecules += nMol.at(i);
326 }
327 if (totalMolecules != nSites) {
328 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
329 "Computed total number of molecules is not equal "
330 "to the number of lattice sites!");
331 painCave.isFatal = 1;
332 simError();
333 }
334 } else {
335 for (unsigned int i = 0; i < shellRadii.size(); i++) {
336 if (shellRadii.at(i) > rodRadius + 1e-6) {
337 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
338 "One of the shellRadius values exceeds the rod Radius.");
339 painCave.isFatal = 1;
340 simError();
341 }
342 if (shellRadii.at(i) <= 0.0) {
343 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
344 "One of the shellRadius values is smaller than zero!");
345 painCave.isFatal = 1;
346 simError();
347 }
348 }
349 }
350
351 vector<int> ids;
352 if ((int)args_info.molFraction_given) {
353 // cerr << "molFraction given 2" << "\n";
354 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
355 "Creating a randomized spherically-capped nanorod.");
356 painCave.isFatal = 0;
357 painCave.severity = OPENMD_INFO;
358 simError();
359 /* Random rod is the default case*/
360
361 for (std::size_t i = 0; i < sites.size(); i++)
362 if (!isVacancy[i]) ids.push_back(i);
363
364 std::shuffle(ids.begin(), ids.end(), gen);
365
366 } else {
367 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
368 "Creating an fcc nanorod.");
369 painCave.isFatal = 0;
370 painCave.severity = OPENMD_INFO;
371 simError();
372
373 // RealType smallestSoFar;
374 int myComponent = -1;
375 nMol.clear();
376 nMol.resize(nComponents);
377
378 // cerr << "shellRadii[0] " << shellRadii[0] << "\n";
379 // cerr << "rodRadius " << rodRadius << "\n";
380
381 for (unsigned int i = 0; i < sites.size(); i++) {
382 myLoc = sites[i];
383 myR = myLoc.length();
384 // smallestSoFar = rodRadius;
385 // cerr << "vac = " << isVacancy[i]<< "\n";
386
387 if (!isVacancy[i]) {
388 // for (int j = 0; j < nComponents; j++) {
389 // if (myR <= shellRadii[j]) {
390 // if (shellRadii[j] <= smallestSoFar) {
391 // smallestSoFar = shellRadii[j];
392 // myComponent = j;
393 // }
394 // }
395 // }
396 myComponent = 0;
397 componentFromSite[i] = myComponent;
398 nMol[myComponent]++;
399 // cerr << "nMol for myComp(" << myComponent<<") = " <<
400 // nMol[myComponent] <<
401 //"\n";
402 }
403 }
404 }
405 // cerr << "nMol = " << nMol.at(0) << "\n";
406
407 outputFileName = args_info.output_arg;
408
409 // creat new .omd file on fly which corrects the number of molecule
410
411 createMdFile(inputFileName, outputFileName, nMol);
412
413 delete oldInfo;
414
415 SimCreator newCreator;
416 SimInfo* NewInfo = newCreator.createSim(outputFileName, false);
417
418 // Place molecules
419 Molecule* mol;
420 SimInfo::MoleculeIterator mi;
421 mol = NewInfo->beginMolecule(mi);
422
423 int l = 0;
424
425 for (int i = 0; i < nComponents; i++) {
426 locator =
427 new MoLocator(NewInfo->getMoleculeStamp(i), NewInfo->getForceField());
428
429 // cerr << "nMol = " << nMol.at(i) << "\n";
430 if (!args_info.molFraction_given) {
431 for (unsigned int n = 0; n < sites.size(); n++) {
432 if (!isVacancy[n]) {
433 if (componentFromSite[n] == i) {
434 mol = NewInfo->getMoleculeByGlobalIndex(l);
435 locator->placeMol(sites[n], orientations[n], mol);
436 l++;
437 }
438 }
439 }
440 } else {
441 for (int n = 0; n < nMol.at(i); n++) {
442 mol = NewInfo->getMoleculeByGlobalIndex(l);
443 locator->placeMol(sites[ids[l]], orientations[ids[l]], mol);
444 l++;
445 }
446 }
447 }
448
449 // fill Hmat
450 hmat(0, 0) = 10.0 * rodRadius;
451 hmat(0, 1) = 0.0;
452 hmat(0, 2) = 0.0;
453
454 hmat(1, 0) = 0.0;
455 hmat(1, 1) = 10.0 * rodRadius;
456 hmat(1, 2) = 0.0;
457
458 hmat(2, 0) = 0.0;
459 hmat(2, 1) = 0.0;
460 hmat(2, 2) = 5.0 * rodLength + 2.0 * rodRadius;
461
462 // set Hmat
463 NewInfo->getSnapshotManager()->getCurrentSnapshot()->setHmat(hmat);
464
465 // create dumpwriter and write out the coordinates
466 writer = new DumpWriter(NewInfo, outputFileName);
467
468 if (writer == NULL) {
469 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
470 "Error in creating dumpwriter object ");
471 painCave.isFatal = 1;
472 simError();
473 }
474
475 writer->writeDump();
476
477 // deleting the writer will put the closing at the end of the dump file
478
479 delete writer;
480
481 // cleanup a by calling sim error.....
482 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
483 "A new OpenMD file called \"%s\" has been "
484 "generated.\n",
485 outputFileName.c_str());
486 painCave.isFatal = 0;
487 painCave.severity = OPENMD_INFO;
488 simError();
489 return 0;
490}
491
492void createMdFile(const std::string& oldMdFileName,
493 const std::string& newMdFileName, std::vector<int> nMol) {
494 ifstream oldMdFile;
495 ofstream newMdFile;
496 const int MAXLEN = 65535;
497 char buffer[MAXLEN];
498
499 // create new .omd file based on old .omd file
500 oldMdFile.open(oldMdFileName.c_str());
501 newMdFile.open(newMdFileName.c_str());
502 oldMdFile.getline(buffer, MAXLEN);
503
504 unsigned int i = 0;
505 while (!oldMdFile.eof()) {
506 // correct molecule number
507 if (strstr(buffer, "nMol") != NULL) {
508 if (i < nMol.size()) {
509 snprintf(buffer, MAXLEN, "\tnMol = %i;", nMol.at(i));
510 newMdFile << buffer << std::endl;
511 i++;
512 }
513 } else
514 newMdFile << buffer << std::endl;
515
516 oldMdFile.getline(buffer, MAXLEN);
517 }
518
519 oldMdFile.close();
520 newMdFile.close();
521
522 if (i != nMol.size()) {
523 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
524 "Couldn't replace the correct number of nMol\n"
525 "\tstatements in component blocks. Make sure that all\n"
526 "\tcomponents in the template file have nMol=1");
527 painCave.isFatal = 1;
528 simError();
529 }
530}
The only responsibility of SimCreator is to parse the meta-data file and create a SimInfo instance ba...
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:93
Implements an ellipsoid-shaped lattice.
Implements a spherically-capped rod-shaped lattice.
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
void registerLattice()
Register all lattice.
Definition Register.cpp:131
The header file for the command line option parser generated by GNU Gengetopt version 2....
Where the command line options are stored.