n2p2 - A neural network potential package
pair_hdnnp_external.cpp
Go to the documentation of this file.
1// n2p2 - A neural network potential package
2// Copyright (C) 2018 Andreas Singraber (University of Vienna)
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#include <mpi.h>
18#include <cstdlib>
19#include <fstream>
20#include <string.h>
21#include <vector>
22#include "pair_hdnnp_external.h"
23#include "atom.h"
24#include "comm.h"
25#include "domain.h"
26#include "memory.h"
27#include "error.h"
28#include "update.h"
29#include "utils.h"
30#include "Atom.h" // nnp::Atom
31#include "utility.h" // nnp::
32
33using namespace LAMMPS_NS;
34using namespace std;
35
36/* ---------------------------------------------------------------------- */
37
38PairHDNNPExternal::PairHDNNPExternal(LAMMPS *lmp) : Pair(lmp)
39{
40}
41
42/* ----------------------------------------------------------------------
43 check if allocated, since class can be destructed when incomplete
44------------------------------------------------------------------------- */
45
47{
48}
49
50/* ---------------------------------------------------------------------- */
51
52void PairHDNNPExternal::compute(int eflag, int vflag)
53{
54 if(eflag || vflag) ev_setup(eflag,vflag);
55 else evflag = vflag_fdotr = eflag_global = eflag_atom = 0;
56
57 // Clear structure completely.
59
60 // Set simulation box.
61 if (domain->nonperiodic < 2) structure.isPeriodic = true;
62 else structure.isPeriodic = false;
63 if (structure.isPeriodic) structure.isTriclinic = (bool)domain->triclinic;
64 structure.box[0][0] = domain->h[0] * cflength;
65 structure.box[0][1] = 0.0;
66 structure.box[0][2] = 0.0;
67 structure.box[1][0] = domain->h[5] * cflength;
68 structure.box[1][1] = domain->h[1] * cflength;
69 structure.box[1][2] = 0.0;
70 structure.box[2][0] = domain->h[4] * cflength;
71 structure.box[2][1] = domain->h[3] * cflength;
72 structure.box[2][2] = domain->h[2] * cflength;
73
74 // Fill structure with atoms.
75 for (int i = 0; i < atom->nlocal; ++i)
76 {
77 structure.atoms.push_back(nnp::Atom());
78 nnp::Atom& a = structure.atoms.back();
79 a.r[0] = atom->x[i][0] * cflength;
80 a.r[1] = atom->x[i][1] * cflength;
81 a.r[2] = atom->x[i][2] * cflength;
82 a.element = atom->type[i] - 1;
84 }
85
86 // Write "input.data" file to disk.
87 structure.writeToFile(string(directory) + "input.data");
88
89 // Run external command and throw away stdout output.
90 string com = "cd " + string(directory) + "; " + string(command) + " > external.out";
91 system(com.c_str());
92
93 // Read back in total potential energy.
94 ifstream f;
95 f.open(string(directory) + "energy.out");
96 if (!f.is_open()) error->all(FLERR,"Could not open energy output file");
97 string line;
98 double energy = 0.0;
99 getline(f, line); // Ignore first line, RuNNer and n2p2 have header here.
100 while (getline(f, line))
101 {
102 if ((line.size() > 0) && (line.at(0) != '#')) // Ignore n2p2 header.
103 {
104 // 4th columns contains NNP energy.
105 sscanf(line.c_str(), "%*s %*s %*s %lf", &energy);
106 }
107 }
108 f.close();
109 energy /= cfenergy;
110
111 // Add energy contribution to total energy.
112 if (eflag_global)
113 ev_tally(0,0,atom->nlocal,1,energy,0.0,0.0,0.0,0.0,0.0);
114
115 // Read back in forces.
116 f.open(string(directory) + "nnforces.out");
117 if (!f.is_open()) error->all(FLERR,"Could not open force output file");
118 int c = 0;
119 double const cfforce = cfenergy / cflength;
120 getline(f, line); // Ignore first line, RuNNer and n2p2 have header here.
121 while (getline(f, line))
122 {
123 if ((line.size() > 0) && (line.at(0) != '#')) // Ignore n2p2 header.
124 {
125 if (c > atom->nlocal - 1) error->all(FLERR,"Too many atoms in force file.");
126 double fx;
127 double fy;
128 double fz;
129 sscanf(line.c_str(), "%*s %*s %*s %*s %*s %lf %lf %lf", &fx, &fy, &fz);
130 atom->f[c][0] = fx / cfforce;
131 atom->f[c][1] = fy / cfforce;
132 atom->f[c][2] = fz / cfforce;
133 c++;
134 }
135 }
136
137 f.close();
138
139 // If virial needed calculate via F dot r.
140 // TODO: Pressure calculation is probably wrong anyway, tell user only to use
141 // in NVE, NVT ensemble.
142 if (vflag_fdotr) virial_fdotr_compute();
143}
144
145/* ----------------------------------------------------------------------
146 global settings
147------------------------------------------------------------------------- */
148
149void PairHDNNPExternal::settings(int narg, char **arg)
150{
151 int iarg = 0;
152
153 // elements list is mandatory
154 if (narg < 1) error->all(FLERR,"Illegal pair_style command");
155
156 // element list, mandatory
157 int len = strlen(arg[iarg]) + 1;
158 elements = new char[len];
159 sprintf(elements, "%s", arg[iarg]);
160 iarg++;
162 vector<FILE*> fp;
163 if (screen) fp.push_back(screen);
164 if (logfile) fp.push_back(logfile);
166
167 // default settings
168 len = strlen("hdnnp/") + 1;
169 directory = new char[len];
170 strcpy(directory,"hdnnp/");
171 len = strlen("nnp-predict 0") + 1;
172 command = new char[len];
173 strcpy(command,"nnp-predict 0");
174 cflength = 1.0;
175 cfenergy = 1.0;
176
177 while(iarg < narg) {
178 // set NNP directory
179 if (strcmp(arg[iarg],"dir") == 0) {
180 if (iarg+2 > narg)
181 error->all(FLERR,"Illegal pair_style command");
182 delete[] directory;
183 len = strlen(arg[iarg+1]) + 2;
184 directory = new char[len];
185 sprintf(directory, "%s/", arg[iarg+1]);
186 iarg += 2;
187 // set external prediction command
188 } else if (strcmp(arg[iarg],"command") == 0) {
189 if (iarg+2 > narg)
190 error->all(FLERR,"Illegal pair_style command");
191 delete[] command;
192 len = strlen(arg[iarg+1]) + 1;
193 command = new char[len];
194 sprintf(command, "%s", arg[iarg+1]);
195 iarg += 2;
196 // length unit conversion factor
197 } else if (strcmp(arg[iarg],"cflength") == 0) {
198 if (iarg+2 > narg)
199 error->all(FLERR,"Illegal pair_style command");
200 cflength = utils::numeric(FLERR,arg[iarg+1],false,lmp);
201 iarg += 2;
202 // energy unit conversion factor
203 } else if (strcmp(arg[iarg],"cfenergy") == 0) {
204 if (iarg+2 > narg)
205 error->all(FLERR,"Illegal pair_style command");
206 cfenergy = utils::numeric(FLERR,arg[iarg+1],false,lmp);
207 iarg += 2;
208 } else error->all(FLERR,"Illegal pair_style command");
209 }
210
211 for (auto f : fp)
212 {
213 fprintf(f, "*****************************************"
214 "**************************************\n");
215 fprintf(f, "pair_style hdnnp/external settings:\n");
216 fprintf(f, "-----------------------------------\n");
217 fprintf(f, "elements = %s\n", elements);
218 fprintf(f, "dir = %s\n", directory);
219 fprintf(f, "command = %s\n", command);
220 fprintf(f, "cflength = %16.8E\n", cflength);
221 fprintf(f, "cfenergy = %16.8E\n", cfenergy);
222 fprintf(f, "*****************************************"
223 "**************************************\n");
224 fprintf(f, "CAUTION: Explicit element mapping is not available for hdnnp/external,\n");
225 fprintf(f, " please carefully check whether this map between LAMMPS\n");
226 fprintf(f, " atom types and element strings is correct:\n");
227 fprintf(f, "---------------------------\n");
228 fprintf(f, "LAMMPS type | NNP element\n");
229 fprintf(f, "---------------------------\n");
230 int lammpsNtypes = em.size();
231 for (int i = 0; i < lammpsNtypes; ++i)
232 {
233 fprintf(f, "%11d <-> %2s (%3zu)\n",
234 i, em[i].c_str(), em.atomicNumber(i));
235 }
236 fprintf(f, "*****************************************"
237 "**************************************\n");
238 }
239}
240
241/* ----------------------------------------------------------------------
242 set coeffs for one or more type pairs
243------------------------------------------------------------------------- */
244
245void PairHDNNPExternal::coeff(int narg, char **arg)
246{
247 if (!allocated) allocate();
248
249 if (narg != 3) error->all(FLERR,"Incorrect args for pair coefficients");
250
251 int ilo,ihi,jlo,jhi;
252 utils::bounds(FLERR,arg[0],1,atom->ntypes,ilo,ihi,error);
253 utils::bounds(FLERR,arg[1],1,atom->ntypes,jlo,jhi,error);
254
255 maxCutoffRadius = utils::numeric(FLERR,arg[2],false,lmp);
256
257 // TODO: Check how this flag is set.
258 int count = 0;
259 for(int i=ilo; i<=ihi; i++) {
260 for(int j=MAX(jlo,i); j<=jhi; j++) {
261 setflag[i][j] = 1;
262 count++;
263 }
264 }
265
266 if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients");
267}
268
269/* ----------------------------------------------------------------------
270 init specific to this pair style
271------------------------------------------------------------------------- */
272
274{
275 if (comm->nprocs > 1)
276 {
277 error->all(FLERR,"MPI is not supported for this pair_style");
278 }
279}
280
281/* ----------------------------------------------------------------------
282 init for one type pair i,j and corresponding j,i
283------------------------------------------------------------------------- */
284
285double PairHDNNPExternal::init_one(int i, int j)
286{
287 // TODO: Check how this actually works for different cutoffs.
288 return maxCutoffRadius;
289}
290
291/* ----------------------------------------------------------------------
292 allocate all arrays
293------------------------------------------------------------------------- */
294
296{
297 allocated = 1;
298 int n = atom->ntypes;
299
300 memory->create(setflag,n+1,n+1,"pair:setflag");
301 for (int i = 1; i <= n; i++)
302 for (int j = i; j <= n; j++)
303 setflag[i][j] = 0;
304
305 memory->create(cutsq,n+1,n+1,"pair:cutsq");
306}
void coeff(int, char **) override
void settings(int, char **) override
double init_one(int, int) override
void compute(int, int) override
std::size_t registerElements(std::string const &elementLine)
Extract all elements and store in element map.
Definition: ElementMap.cpp:36
std::size_t size() const
Get element map size.
Definition: ElementMap.h:140
std::size_t atomicNumber(std::size_t index) const
Get atomic number from element index.
Definition: ElementMap.h:145
Storage for a single atom.
Definition: Atom.h:33
Vec3D r
Cartesian coordinates.
Definition: Atom.h:125
std::size_t element
Element index of this atom.
Definition: Atom.h:107
Vec3D box[3]
Simulation box vectors.
Definition: Structure.h:112
bool isTriclinic
If the simulation box is triclinic.
Definition: Structure.h:63
void writeToFile(std::string const fileName="output.data", bool const ref=true, bool const append=false) const
Write configuration to file.
Definition: Structure.cpp:1449
bool isPeriodic
If periodic boundary conditions apply.
Definition: Structure.h:61
void setElementMap(ElementMap const &elementMap)
Set element map of structure.
Definition: Structure.cpp:71
void reset()
Reset everything but elementMap.
Definition: Structure.cpp:1319
std::size_t numAtoms
Total number of atoms present in this structure.
Definition: Structure.h:75
std::vector< Atom > atoms
Vector of all atoms in this structure.
Definition: Structure.h:122