n2p2 - A neural network potential package
nnp-checkf.cpp File Reference
#include "Dataset.h"
#include "mpi-extra.h"
#include "utility.h"
#include <mpi.h>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
Include dependency graph for nnp-checkf.cpp:

Go to the source code of this file.

Functions

int main (int argc, char *argv[])
 

Function Documentation

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 33 of file nnp-checkf.cpp.

34{
35 int numProcs = 0;
36 int myRank = 0;
37 size_t numOriginals = 0;
38 double delta = 0.0;
39 ifstream dataFile;
40 ofstream myLog;
41 ofstream outFileForces;
42 ofstream outFileSummary;
43
44 if (argc > 2)
45 {
46 cout << "USAGE: " << argv[0] << " <<delta>>\n"
47 << " <<delta>> ... (optional) Displacement for central "
48 "difference (default: 1.0e-4).\n"
49 << " Execute in directory with these NNP files present:\n"
50 << " - input.data (structure file)\n"
51 << " - input.nn (NNP settings)\n"
52 << " - scaling.data (symmetry function scaling data)\n"
53 << " - \"weights.%%03d.data\" (weights files)\n";
54 return 1;
55 }
56
57 MPI_Init(&argc, &argv);
58 MPI_Comm_size(MPI_COMM_WORLD, &numProcs);
59 MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
60
61 if (argc == 2) delta = atof(argv[1]);
62 else delta = 1.0E-4;
63
64 Dataset dataset;
65 if (myRank != 0) dataset.log.writeToStdout = false;
66 myLog.open(strpr("nnp-checkf.log.%04d", myRank).c_str());
67 dataset.log.registerStreamPointer(&myLog);
68 dataset.setupMPI();
69 dataset.initialize();
70 dataset.loadSettingsFile();
71 dataset.setupGeneric();
72 bool normalize = dataset.useNormalization();
74 dataset.setupSymmetryFunctionStatistics(false, false, false, false);
76
77 dataset.log << "\n";
78 dataset.log << "*** ANALYTIC/NUMERIC FORCES CHECK *******"
79 "**************************************\n";
80 dataset.log << "\n";
81 dataset.log << strpr("Delta for symmetric difference quotient: %11.3E\n",
82 delta);
83 if (myRank == 0)
84 {
85 string fileName = "checkf-forces.out";
86 dataset.log << strpr("Individual analytic/numeric forces will be "
87 "written to \"%s\"\n", fileName.c_str());
88 outFileForces.open(fileName.c_str());
89 // File header.
90 vector<string> title;
91 vector<string> colName;
92 vector<string> colInfo;
93 vector<size_t> colSize;
94 title.push_back(strpr("Comparison of analytic and numeric forces "
95 "(delta = %11.3E).", delta));
96 colSize.push_back(10);
97 colName.push_back("struct");
98 colInfo.push_back("Structure index (starting with 1).");
99 colSize.push_back(10);
100 colName.push_back("atom");
101 colInfo.push_back("Atom index (starting with 1).");
102 colSize.push_back(3);
103 colName.push_back("xyz");
104 colInfo.push_back("Force component (0 = x, 1 = y, 2 = z).");
105 colSize.push_back(24);
106 colName.push_back("F_analytic");
107 colInfo.push_back("Force computed from analytic derivative of NNP "
108 "energy.");
109 colSize.push_back(24);
110 colName.push_back("F_numeric");
111 colInfo.push_back("Force computed numerically from symmetric "
112 "difference quotient.");
113 appendLinesToFile(outFileForces,
114 createFileHeader(title, colSize, colName, colInfo));
115
116 fileName = "checkf-summary.out";
117 dataset.log << strpr("Per-structure summary of analytic/numeric force "
118 "comparison will be \n"
119 "written to \"%s\"\n",
120 fileName.c_str());
121 outFileSummary.open(fileName.c_str());
122 // File header.
123 title.clear();
124 colName.clear();
125 colInfo.clear();
126 colSize.clear();
127 title.push_back(strpr("Per-structure summary of analytic vs. numeric "
128 "force comparison (delta = %11.3E).", delta));
129 colSize.push_back(10);
130 colName.push_back("struct");
131 colInfo.push_back("Structure index (starting with 1).");
132 colSize.push_back(16);
133 colName.push_back("numForces");
134 colInfo.push_back("Number of forces in this structure");
135 colSize.push_back(16);
136 colName.push_back("meanAbsError");
137 colInfo.push_back("Mean over all absolute differences between "
138 "analytic and numeric forces in this structure.");
139 colSize.push_back(16);
140 colName.push_back("maxAbsError");
141 colInfo.push_back("Maximum over all absolute differences between "
142 "analytic and numeric forces in this structure.");
143 appendLinesToFile(outFileSummary,
144 createFileHeader(title, colSize, colName, colInfo));
145 }
146
147 // First, check how many originals are in "input.data".
148 if (myRank == 0)
149 {
150 string fileName = "input.data";
151 dataFile.open(fileName.c_str());
152 numOriginals = dataset.getNumStructures(dataFile);
153 dataset.log << strpr("Found %zu configurations in data file: %s.\n",
154 numOriginals,
155 fileName.c_str());
156 dataFile.clear();
157 dataFile.seekg(0);
158 }
159 MPI_Bcast(&numOriginals, 1, MPI_SIZE_T, 0, MPI_COMM_WORLD);
160 dataset.log << strpr("Starting loop over %zu configurations...\n",
161 numOriginals);
162
163 if (myRank == 0)
164 {
165 dataset.log << "\n";
166 dataset.log << strpr(" %10s %12s %12s verdict\n",
167 "numForces", "meanAbsError", "maxAbsError");
168 dataset.log << "-----------------------------------"
169 "--------------------------------\n";
170 }
171
172 bool warning = false;
173 for (size_t is = 0; is < numOriginals; ++is)
174 {
175 // Read original, prepare and distribute modified copies.
176 Structure original;
177 original.setElementMap(dataset.elementMap);
178 if (myRank == 0) original.readFromFile(dataFile);
179 size_t numStructures = dataset.prepareNumericForces(original, delta);
180 if (normalize) dataset.toNormalizedUnits();
181
182 // Prepare arrays to collect central difference values and identifiers.
183 vector<double> allEnergies(numStructures - 1, 0.0);
184 vector<size_t> allAtoms(numStructures - 1, 0);
185 vector<size_t> allXYZ(numStructures - 1, 0);
186 vector<int> allSigns(numStructures - 1, 0);
187
188 // Loop over all structures and compute symmetry functions and energies.
189 for (auto& s : dataset.structures)
190 {
191 bool needForces = false;
192 if (s.comment == "original") needForces = true;
193 s.calculateNeighborList(dataset.getMaxCutoffRadius());
194#ifdef N2P2_NO_SF_GROUPS
195 dataset.calculateSymmetryFunctions(s, needForces);
196#else
197 dataset.calculateSymmetryFunctionGroups(s, needForces);
198#endif
199 dataset.calculateAtomicNeuralNetworks(s, needForces);
200 dataset.calculateEnergy(s);
201 if (needForces) dataset.calculateForces(s);
202 s.freeAtoms(true);
203 }
204 if (normalize) dataset.toPhysicalUnits();
205
206 // Loop over all structures collect energies and identifiers.
207 for (auto& s : dataset.structures)
208 {
209 // Skip for original structure.
210 if (s.comment == "original") continue;
211 // Store central difference values in arrays (locally on each
212 // processor, communicate later).
213 vector<string> lsplit = split(s.comment);
214 size_t count = atoi(lsplit.at(0).c_str());
215 size_t iAtom = atoi(lsplit.at(1).c_str());
216 size_t ixyz = atoi(lsplit.at(2).c_str());
217 int sign = atoi(lsplit.at(3).c_str());
218 allEnergies.at(count) = s.energy;
219 allAtoms.at(count) = iAtom;
220 allXYZ.at(count) = ixyz;
221 allSigns.at(count) = sign;
222 }
223
224 // Collect data on rank 0.
225 if (myRank == 0)
226 {
227 MPI_Reduce(MPI_IN_PLACE , allEnergies.data(), numStructures - 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
228 MPI_Reduce(MPI_IN_PLACE , allAtoms.data() , numStructures - 1, MPI_SIZE_T, MPI_SUM, 0, MPI_COMM_WORLD);
229 MPI_Reduce(MPI_IN_PLACE , allXYZ.data() , numStructures - 1, MPI_SIZE_T, MPI_SUM, 0, MPI_COMM_WORLD);
230 MPI_Reduce(MPI_IN_PLACE , allSigns.data() , numStructures - 1, MPI_INT , MPI_SUM, 0, MPI_COMM_WORLD);
231 }
232 else
233 {
234 MPI_Reduce(allEnergies.data(), allEnergies.data(), numStructures - 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
235 MPI_Reduce(allAtoms.data() , allAtoms.data() , numStructures - 1, MPI_SIZE_T, MPI_SUM, 0, MPI_COMM_WORLD);
236 MPI_Reduce(allXYZ.data() , allXYZ.data() , numStructures - 1, MPI_SIZE_T, MPI_SUM, 0, MPI_COMM_WORLD);
237 MPI_Reduce(allSigns.data() , allSigns.data() , numStructures - 1, MPI_INT , MPI_SUM, 0, MPI_COMM_WORLD);
238 }
239
240 if (myRank == 0)
241 {
242 // Now temporarily store central difference energies in
243 // fRef(+delta) and f(-delta) of original structure.
244 for (size_t i = 0; i < numStructures - 1; ++i)
245 {
246 Atom& a = original.atoms.at(allAtoms.at(i));
247 if (allSigns.at(i) == 1)
248 {
249 a.fRef[allXYZ.at(i)] = allEnergies.at(i);
250 }
251 else if (allSigns.at(i) == -1)
252 {
253 a.f[allXYZ.at(i)] = allEnergies.at(i);
254 }
255 }
256
257 double maxAbsError = 0.0;
258 double meanAbsError = 0.0;
259 for (size_t i = 0; i < original.atoms.size(); ++i)
260 {
261 Atom const& ref = dataset.structures.at(0).atoms.at(i);
262 Atom& a = original.atoms.at(i);
263 for (size_t j = 0; j < 3; ++j)
264 {
265 a.f[j] = - (a.fRef[j] - a.f[j]) / (2.0 * delta);
266 a.fRef[j] = ref.f[j];
267 double const error = fabs(a.f[j] - a.fRef[j]);
268 meanAbsError += error;
269 maxAbsError = max(error, maxAbsError);
270 outFileForces << strpr("%10zu %10zu %3zu %24.16E "
271 "%24.16E\n",
272 is + 1,
273 i + 1,
274 j,
275 a.fRef[j],
276 a.f[j]);
277 }
278 }
279 size_t const numForces = 3 * original.atoms.size();
280 meanAbsError /= numForces;
281 dataset.log << strpr("Configuration %6zu: %10zu %12.3E %12.3E",
282 is + 1, numForces, meanAbsError, maxAbsError);
283 outFileSummary << strpr("%10zu %16zu %16.8E %16.8E\n",
284 is + 1,
285 numForces,
286 meanAbsError,
287 maxAbsError);
288 if (maxAbsError > 10 * delta * delta)
289 {
290 dataset.log << " WARNING!\n";
291 warning = true;
292 }
293 else dataset.log << " OK.\n";
294 }
295 }
296
297 if (myRank == 0)
298 {
299 if (warning)
300 {
301 dataset.log << "\n";
302 dataset.log << "IMPORTANT: Some warnings were issued. By default, this happens if the maximum\n"
303 " absolute error (\"maxAbsError\") is higher than 10 * delta².\n"
304 " However, this does NOT mean that analytic forces are incorrect!\n"
305 " Repeat this analysis with a different delta and check whether\n"
306 " the error scales with O(delta²). The prefactor for your system\n"
307 " could be higher than 10, hence, as long as there is a O(delta²)\n"
308 " scaling the analytic forces are probably correct.\n";
309 }
310 outFileForces.close();
311 outFileSummary.close();
312 dataFile.close();
313 }
314
315 dataset.log << "*****************************************"
316 "**************************************\n";
317
318 myLog.close();
319
320 MPI_Finalize();
321
322 return 0;
323}
Collect and process large data sets.
Definition: Dataset.h:35
void setupMPI()
Initialize MPI with MPI_COMM_WORLD.
Definition: Dataset.cpp:52
std::vector< Structure > structures
All structures in this dataset.
Definition: Dataset.h:195
std::size_t prepareNumericForces(Structure &original, double delta)
Prepare numeric force check for a single structure.
Definition: Dataset.cpp:886
void toPhysicalUnits()
Switch all structures to physical units.
Definition: Dataset.cpp:961
std::size_t getNumStructures(std::ifstream &dataFile)
Get number of structures in data file.
Definition: Dataset.cpp:707
void toNormalizedUnits()
Switch all structures to normalized units.
Definition: Dataset.cpp:950
void registerStreamPointer(std::ofstream *const &streamPointer)
Register new C++ ofstream pointer.
Definition: Log.cpp:91
bool writeToStdout
Turn on/off output to stdout.
Definition: Log.h:85
ElementMap elementMap
Global element map, populated by setupElementMap().
Definition: Mode.h:508
void initialize()
Write welcome message with version information.
Definition: Mode.cpp:50
double getMaxCutoffRadius() const
Getter for Mode::maxCutoffRadius.
Definition: Mode.h:563
void calculateAtomicNeuralNetworks(Structure &structure, bool const derivatives)
Calculate a single atomic neural network for a given atom and nn type.
Definition: Mode.cpp:1310
bool useNormalization() const
Check if normalization is enabled.
Definition: Mode.h:573
virtual void setupNeuralNetworkWeights(std::string const &fileNameFormatShort="weights.%03zu.data", std::string const &fileNameFormatCharge="weightse.%03zu.data")
Set up neural network weights from files.
Definition: Mode.cpp:1126
void calculateEnergy(Structure &structure) const
Calculate potential energy for a given structure.
Definition: Mode.cpp:1366
void calculateSymmetryFunctionGroups(Structure &structure, bool const derivatives)
Calculate all symmetry function groups for all atoms in given structure.
Definition: Mode.cpp:1230
virtual void setupSymmetryFunctionScaling(std::string const &fileName="scaling.data")
Set up symmetry function scaling from file.
Definition: Mode.cpp:532
Log log
Global log file.
Definition: Mode.h:510
void setupGeneric(bool skipNormalize=false)
Combine multiple setup routines and provide a basic NNP setup.
Definition: Mode.cpp:197
void calculateSymmetryFunctions(Structure &structure, bool const derivatives)
Calculate all symmetry functions for all atoms in given structure.
Definition: Mode.cpp:1150
void calculateForces(Structure &structure) const
Calculate forces for all atoms in given structure.
Definition: Mode.cpp:1392
void setupSymmetryFunctionStatistics(bool collectStatistics, bool collectExtrapolationWarnings, bool writeExtrapolationWarnings, bool stopOnExtrapolationWarnings)
Set up symmetry function statistics collection.
Definition: Mode.cpp:923
void loadSettingsFile(std::string const &fileName="input.nn")
Open settings file and load all keywords into memory.
Definition: Mode.cpp:156
#define MPI_SIZE_T
Definition: mpi-extra.h:22
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
vector< string > createFileHeader(vector< string > const &title, vector< size_t > const &colSize, vector< string > const &colName, vector< string > const &colInfo, char const &commentChar)
Definition: utility.cpp:104
vector< string > split(string const &input, char delimiter)
Split string at each delimiter.
Definition: utility.cpp:33
void appendLinesToFile(ofstream &file, vector< string > const lines)
Append multiple lines of strings to open file stream.
Definition: utility.cpp:218
Storage for a single atom.
Definition: Atom.h:32
Vec3D f
Force vector calculated by neural network.
Definition: Atom.h:120
Vec3D fRef
Reference force vector from data set.
Definition: Atom.h:122
Storage for one atomic configuration.
Definition: Structure.h:34
void setElementMap(ElementMap const &elementMap)
Set element map of structure.
Definition: Structure.cpp:60
void readFromFile(std::string const fileName="input.data")
Read configuration from file.
Definition: Structure.cpp:86
std::vector< Atom > atoms
Vector of all atoms in this structure.
Definition: Structure.h:96

References nnp::appendLinesToFile(), nnp::Structure::atoms, nnp::Mode::calculateAtomicNeuralNetworks(), nnp::Mode::calculateEnergy(), nnp::Mode::calculateForces(), nnp::Mode::calculateSymmetryFunctionGroups(), nnp::Mode::calculateSymmetryFunctions(), nnp::createFileHeader(), nnp::Mode::elementMap, nnp::Atom::f, nnp::Atom::fRef, nnp::Mode::getMaxCutoffRadius(), nnp::Dataset::getNumStructures(), nnp::Mode::initialize(), nnp::Mode::loadSettingsFile(), nnp::Mode::log, MPI_SIZE_T, nnp::Dataset::prepareNumericForces(), nnp::Structure::readFromFile(), nnp::Log::registerStreamPointer(), nnp::Structure::setElementMap(), nnp::Mode::setupGeneric(), nnp::Dataset::setupMPI(), nnp::Mode::setupNeuralNetworkWeights(), nnp::Mode::setupSymmetryFunctionScaling(), nnp::Mode::setupSymmetryFunctionStatistics(), nnp::split(), nnp::strpr(), nnp::Dataset::structures, nnp::Dataset::toNormalizedUnits(), nnp::Dataset::toPhysicalUnits(), nnp::Mode::useNormalization(), and nnp::Log::writeToStdout.

Here is the call graph for this function: