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
66 myLog.open(
strpr(
"nnp-checkf.log.%04d", myRank).c_str());
76
78 dataset.
log <<
"*** ANALYTIC/NUMERIC FORCES CHECK *******"
79 "**************************************\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
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.");
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
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.");
145 }
146
147
148 if (myRank == 0)
149 {
150 string fileName = "input.data";
151 dataFile.open(fileName.c_str());
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 {
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
181
182
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
190 {
191 bool needForces = false;
192 if (s.comment == "original") needForces = true;
194#ifdef N2P2_NO_SF_GROUPS
196#else
198#endif
202 s.freeAtoms(true);
203 }
205
206
208 {
209
210 if (s.comment == "original") continue;
211
212
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
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
243
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 {
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,
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 {
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.
void setupMPI()
Initialize MPI with MPI_COMM_WORLD.
std::vector< Structure > structures
All structures in this dataset.
std::size_t prepareNumericForces(Structure &original, double delta)
Prepare numeric force check for a single structure.
void toPhysicalUnits()
Switch all structures to physical units.
std::size_t getNumStructures(std::ifstream &dataFile)
Get number of structures in data file.
void toNormalizedUnits()
Switch all structures to normalized units.
void registerStreamPointer(std::ofstream *const &streamPointer)
Register new C++ ofstream pointer.
bool writeToStdout
Turn on/off output to stdout.
ElementMap elementMap
Global element map, populated by setupElementMap().
void initialize()
Write welcome message with version information.
void setupGeneric(std::string const &nnpDir="", bool skipNormalize=false, bool initialHardness=false)
Combine multiple setup routines and provide a basic NNP setup.
virtual void setupNeuralNetworkWeights(std::map< std::string, std::string > fileNameFormats=std::map< std::string, std::string >())
Set up neural network weights from files with given name format.
void calculateAtomicNeuralNetworks(Structure &structure, bool const derivatives, std::string id="")
Calculate atomic neural networks for all atoms in given structure.
double getMaxCutoffRadius() const
Getter for Mode::maxCutoffRadius.
bool useNormalization() const
Check if normalization is enabled.
void calculateEnergy(Structure &structure) const
Calculate potential energy for a given structure.
void calculateSymmetryFunctionGroups(Structure &structure, bool const derivatives)
Calculate all symmetry function groups for all atoms in given structure.
virtual void setupSymmetryFunctionScaling(std::string const &fileName="scaling.data")
Set up symmetry function scaling from file.
void calculateSymmetryFunctions(Structure &structure, bool const derivatives)
Calculate all symmetry functions for all atoms in given structure.
void calculateForces(Structure &structure) const
Calculate forces for all atoms in given structure.
void setupSymmetryFunctionStatistics(bool collectStatistics, bool collectExtrapolationWarnings, bool writeExtrapolationWarnings, bool stopOnExtrapolationWarnings)
Set up symmetry function statistics collection.
void loadSettingsFile(std::string const &fileName="input.nn")
Open settings file and load all keywords into memory.
string strpr(const char *format,...)
String version of printf function.
vector< string > createFileHeader(vector< string > const &title, vector< size_t > const &colSize, vector< string > const &colName, vector< string > const &colInfo, char const &commentChar)
vector< string > split(string const &input, char delimiter)
Split string at each delimiter.
void appendLinesToFile(ofstream &file, vector< string > const lines)
Append multiple lines of strings to open file stream.
Storage for a single atom.
Vec3D f
Force vector calculated by neural network.
Vec3D fRef
Reference force vector from data set.
Storage for one atomic configuration.
void setElementMap(ElementMap const &elementMap)
Set element map of structure.
void readFromFile(std::string const fileName="input.data")
Read configuration from file.
std::vector< Atom > atoms
Vector of all atoms in this structure.