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