Kindfield
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Friends Pages
main.cpp
Go to the documentation of this file.
1 #include <iostream>
2 #include <iomanip>
3 #include <fstream>
4 #include <libconfig.h++>
5 #include <string.h>
6 
11 #include <boost/mpi.hpp>
12 #include <yaml-cpp/yaml.h>
13 
14 #include <H5Cpp.h>
15 
16 using namespace std;
17 using namespace H5;
18 namespace mpi = boost::mpi;
19 
20 void operator >> (const YAML::Node& node, Vector3& v)
21 {
22  double x;
23  double y;
24  double z;
25  node[0] >> x;
26  node[1] >> y;
27  node[2] >> z;
28  v = Vector3(x,y,z);
29 }
30 
31 int blockLow(int id, int np, int n) {
32  return (id * n) / np;
33 }
34 
35 int blockHigh(int id, int np, int n) {
36  return blockLow(id + 1, np, n) - 1;
37 }
38 
39 int blockSize(int id, int p, int n) {
40  return blockLow(id + 1,p,n) - blockLow(id,p,n);
41 }
42 
43 int main(int argc, char* argv[])
44 {
45  mpi::environment env;
46  mpi::communicator world;
47  mpi::timer timer;
48  timer.restart();
49  if(argc < 2) {
50  cout << "Error: No file provided." << endl;
51  cout << "Usage: programname <infile> [outfile]" << endl;
52  exit(0);
53  }
54 
55  string inFileName = argv[1];
56 
57  stringstream outFileName;
58  if(argc < 3) {
59  outFileName << "states";
60  } else {
61  outFileName << argv[2];
62  }
63 
64  struct AtomData {
65  double x;
66  double y;
67  double z;
68  double partialCharge;
69  };
70 
71  CompType atomCompound( sizeof(AtomData) );
72  atomCompound.insertMember("x", HOFFSET(AtomData, x), PredType::NATIVE_DOUBLE);
73  atomCompound.insertMember("y", HOFFSET(AtomData, y), PredType::NATIVE_DOUBLE);
74  atomCompound.insertMember("z", HOFFSET(AtomData, z), PredType::NATIVE_DOUBLE);
75 
76  struct AtomMetaData {
77  int type;
78  char basisName[64];
79  };
80 
81  H5::StrType string_type(H5::PredType::C_S1, 64);
82  CompType atomMetaCompound( sizeof(AtomMetaData) );
83  atomMetaCompound.insertMember("type", HOFFSET(AtomMetaData, type), PredType::NATIVE_INT);
84  atomMetaCompound.insertMember("basisName", HOFFSET(AtomMetaData, basisName), string_type);
85 
86  vector<int> stateIDs;
87  if(world.rank() == 0) {
88  H5File inFile(inFileName, H5F_ACC_RDONLY );
89  H5::Group statesGroup(inFile.openGroup("/states"));
90  int nStates = statesGroup.getNumObjs();
91  vector<int> allStates;
92  allStates.reserve(nStates);
93  for(int i = 0; i < nStates; i++) {
94  allStates.push_back(i);
95  }
96  random_shuffle(allStates.begin(), allStates.end());
97  for(int p = 0; p < world.size(); p++) {
98  vector<int> states;
99  for(int i = blockLow(p, world.size(), allStates.size());
100  i <= blockHigh(p, world.size(), allStates.size());
101  i++) {
102 
103  states.push_back(allStates.at(i));
104 
105  }
106  if(p == 0) {
107  stateIDs = states;
108  } else {
109  world.send(p, 0, states);
110  }
111  }
112  inFile.close();
113  } else {
114  world.recv(0, 0, stateIDs);
115  }
116  cout << "Rank " << world.rank() << ": I have " << stateIDs.size() << " states to dig!" << endl;
117  world.barrier();
118 
119  outFileName << "." << setfill('0') << setw(4) << world.rank() << ".h5";
120 
121  // Open outFile for writing
122  H5File outFile(outFileName.str(), H5F_ACC_TRUNC);
123  for(int p = 0; p < world.size(); p++) {
124  world.barrier();
125  if(p != world.rank()) {
126  continue;
127  }
128  // Open inFile for reading
129  H5File inFile(inFileName, H5F_ACC_RDONLY);
130 
131  // Copy states to outFile
132 
133  for(int i = 0; i < int(inFile.getNumObjs()); i++) {
134  string objectName = inFile.getObjnameByIdx(i);
135  if(objectName == string("states")) {
136  H5::Group statesGroup(inFile.openGroup("/states"));
137  H5::Group statesGroupOut(outFile.createGroup("/states"));
138  for(int stateID : stateIDs) {
139  string stateName = statesGroup.getObjnameByIdx(stateID);
140  H5Ocopy(statesGroup.getId(), stateName.c_str(), statesGroupOut.getId(), stateName.c_str(), H5Pcreate(H5P_OBJECT_COPY), H5P_DEFAULT);
141  }
142  } else {
143  H5Ocopy(inFile.getId(), objectName.c_str(), outFile.getId(), objectName.c_str(), H5Pcreate(H5P_OBJECT_COPY), H5P_DEFAULT);
144  }
145  }
146  inFile.close();
147  }
148 
149  H5::Group rootGroup(outFile.openGroup("/"));
150  string metaInformationName = "atomMeta";
151  DataSet atomMeta(rootGroup.openDataSet(metaInformationName));
152  hsize_t dims[1];
153  atomMeta.getSpace().getSimpleExtentDims(dims);
154  int nAtoms = dims[0];
155 
156  AtomMetaData atomMetaData[nAtoms];
157  atomMeta.read(atomMetaData, atomMetaCompound);
158 
159  string method("");
160  Attribute hartreeFockMethodAttribute = atomMeta.openAttribute("method");
161  hartreeFockMethodAttribute.read(hartreeFockMethodAttribute.getDataType(), method);
162 
163  mat coefficientMatrixUp;
164  mat coefficientMatrixDown;
165 
166  // Precalculate energy offset only if using unrestricted.
167  cout << "Calculating energy offset..." << endl;
168  Attribute energyOffsetAttribute = atomMeta.createAttribute("energyOffset", PredType::NATIVE_DOUBLE, H5S_SCALAR);
169  double energyOffset = 0.0;
170  for(int i = 0; i < nAtoms; i++) {
171  GaussianSystem system;
172  system.addCore(GaussianCore({0,0,0}, atomMetaData[i].type, atomMetaData[i].basisName));
173  // Restricted HF cannot be done on one atom currently.
174  // Restricted tends to have convergence problems with high distances anyway,
175  // so just use the energy offset from UHF in case we want to plot the two together with offset
176  UnrestrictedHartreeFockSolver solver(&system);
177  solver.setDiisEnabled(false);
178  solver.setNIterationsMax(1e3);
179  solver.setDensityMixFactor(0.95);
180  solver.setConvergenceTreshold(1e-9);
181  solver.solve();
182  energyOffset += solver.energy();
183  }
184  energyOffsetAttribute.write(PredType::NATIVE_DOUBLE, &energyOffset);
185  cout << "Energy offset: " << energyOffset << endl;
186  // Done precalculating energy offset
187 
188  // Precalculate ground state
189  cout << "Calculating ground state to get coefficients..." << endl;
190 
191  bool foundGroundState = false;
192  DataSet groundStateDataSet;
193  try {
194  groundStateDataSet = rootGroup.openDataSet("groundState");
195  foundGroundState = true;
196  } catch (GroupIException exception) {
197  cout << "Did not find ground state data set, skipping initialization of coefficient matrices." << endl;
198  }
199 
200  if(foundGroundState) {
201  hsize_t dims2[1];
202  groundStateDataSet.getSpace().getSimpleExtentDims(dims2);
203  int groundStateNAtoms2 = dims2[0];
204 
205  if(nAtoms != groundStateNAtoms2) {
206  cerr << "Error! The number of atoms in the ground state (nAtoms = " << groundStateNAtoms2 << ") does not match "
207  << "the number of atoms in the metadata (= " << nAtoms << ")" << endl;
208  cerr << "Cannot continue" << endl;
209  throw std::logic_error("Mismatching number of atoms");
210  }
211 
212  AtomData *groundStateAtoms = new AtomData[groundStateNAtoms2];
213  groundStateDataSet.read(groundStateAtoms, atomCompound);
214 
215  GaussianSystem groundStateSystem;
216  for(int i = 0; i < groundStateNAtoms2; i++) {
217  stringstream basisFile;
218  basisFile << "atom_" << atomMetaData[i].type << "_basis_" << atomMetaData[i].basisName << ".tm";
219  string fileName = basisFile.str();
220  groundStateSystem.addCore(GaussianCore({ groundStateAtoms[i].x, groundStateAtoms[i].y, groundStateAtoms[i].z}, fileName));
221  }
222  if(method == "unrestricted") {
223  UnrestrictedHartreeFockSolver groundStateSolver(&groundStateSystem);
224  groundStateSolver.setDiisEnabled(false);
225  groundStateSolver.setNIterationsMax(1e3);
226  groundStateSolver.setDensityMixFactor(0.95);
227  groundStateSolver.setConvergenceTreshold(1e-9);
228  groundStateSolver.solve();
229 
230  coefficientMatrixUp = groundStateSolver.coeffcientMatrixUp();
231  coefficientMatrixDown = groundStateSolver.coeffcientMatrixDown();
232  cout << "Ground state energy: " << groundStateSolver.energy() << endl;
233  } else {
234  RestrictedHartreeFockSolver groundStateSolver(&groundStateSystem);
235  groundStateSolver.setNIterationsMax(1e3);
236  groundStateSolver.setDensityMixFactor(0.95);
237  groundStateSolver.setConvergenceTreshold(1e-9);
238  groundStateSolver.solve();
239 
240  coefficientMatrixUp = groundStateSolver.coefficientMatrix();
241  cout << "Ground state energy: " << groundStateSolver.energy() << endl;
242  }
243 
244  delete groundStateAtoms;
245  }
246  // Done precalculate ground state
247 
248  H5::Group statesGroup(outFile.openGroup("/states"));
249 
250  int nTotal = statesGroup.getNumObjs();
251  int currentState = 0;
252  for(int stateID = 0; stateID < nTotal; stateID++) {
253  if(world.rank() == 0) {
254  cout << "Progress: " << fixed << setprecision(2) << double(currentState) / nTotal * 100 << " %"
255  << ", time: " << timer.elapsed() << " s"
256  << endl;
257  }
258  string stateName = statesGroup.getObjnameByIdx(stateID);
259  DataSet stateDataSet(statesGroup.openDataSet(stateName));
260  hsize_t dims2[1];
261  stateDataSet.getSpace().getSimpleExtentDims(dims2);
262  int nAtoms2 = dims2[0];
263 
264  if(nAtoms != nAtoms2) {
265  cerr << "Error! The number of atoms in " << stateName << " (nAtoms = " << nAtoms2 << ") does not match "
266  << "the number of atoms in the metadata (= " << nAtoms << ")" << endl;
267  cerr << "Cannot continue" << endl;
268  throw std::logic_error("Mismatching number of atoms");
269  }
270 
271  AtomData *atoms = new AtomData[nAtoms2];
272  stateDataSet.read(atoms, atomCompound);
273 
274  GaussianSystem system;
275  for(int i = 0; i < nAtoms2; i++) {
276  stringstream basisFile;
277  basisFile << "atom_" << atomMetaData[i].type << "_basis_" << atomMetaData[i].basisName << ".tm";
278  string fileName = basisFile.str();
279  system.addCore(GaussianCore({ atoms[i].x, atoms[i].y, atoms[i].z}, fileName));
280  }
281  double energy;
282  if(method == "unrestricted") {
283  UnrestrictedHartreeFockSolver solver(&system);
284  solver.setInitialCoefficientMatrices(coefficientMatrixUp, coefficientMatrixDown);
285  solver.setNIterationsMax(1e3);
286  solver.setDensityMixFactor(0.95);
287  solver.setConvergenceTreshold(1e-9);
288  solver.solve();
289  energy = solver.energy();
290  } else {
291  RestrictedHartreeFockSolver solver(&system);
292  solver.setInitialCoefficientMatrix(coefficientMatrixUp);
293  solver.setNIterationsMax(1e3);
294  solver.setDensityMixFactor(0.95);
295  solver.setConvergenceTreshold(1e-9);
296  solver.solve();
297  energy = solver.energy();
298  }
299 
300  Attribute energyAttribute(stateDataSet.createAttribute("energy", PredType::NATIVE_DOUBLE, H5S_SCALAR));
301  energyAttribute.write(PredType::NATIVE_DOUBLE, &energy);
302  outFile.flush(H5F_SCOPE_GLOBAL);
303 
304  currentState++;
305 
306  delete atoms;
307  }
308  world.barrier();
309  if(world.rank() == 0) {
310  cout << "Progress: 100 %, time " << fixed << setprecision(2) << timer.elapsed() << " s" << endl;
311  cout << "Done!" << endl;
312  }
313  outFile.close();
314  return 0;
315 }
316