CASM  1.1.0
A Clusters Approach to Statistical Mechanics
MonteDriver_impl.hh
Go to the documentation of this file.
1 #ifndef CASM_MonteDriver_impl
2 #define CASM_MonteDriver_impl
3 
4 #include <boost/filesystem.hpp>
5 #include <boost/filesystem/fstream.hpp>
6 #include <string>
7 
15 
16 namespace CASM {
17 namespace Monte {
18 
19 template <typename RunType>
21  const SettingsType &settings, Log &_log,
22  Log &_err_log)
23  : m_log(_log),
24  m_err_log(_err_log),
25  m_settings(settings),
26  m_dir(m_settings.output_directory()),
27  m_drive_mode(m_settings.drive_mode()),
28  m_mc(primclex, m_settings, _log),
29  m_conditions_list(make_conditions_list(primclex, m_settings)),
30  m_debug(m_settings.debug()),
31  m_enum(m_settings.is_enumeration()
32  ? new MonteCarloEnum(primclex, settings, _log, m_mc)
33  : nullptr) {}
34 
46 template <typename RunType>
48  m_log.check("For existing calculations");
49 
50  if (!m_settings.write_json() && !m_settings.write_csv()) {
51  throw std::runtime_error(
52  std::string("No valid monte carlo output format.\n") +
53  " Expected [\"data\"][\"storage\"][\"output_format\"] to contain a "
54  "string or array of strings.\n" +
55  " Valid options are 'csv' or 'json'.");
56  }
57 
58  // Skip any conditions that have already been calculated and saved
59  Index start_i = _find_starting_conditions();
60 
61  // check if we'll be repeating any calculations that already have files
62  // written
63  std::vector<Index> repeats;
64  for (Index i = start_i; i < m_conditions_list.size(); ++i) {
65  if (fs::exists(m_dir.conditions_dir(i))) {
66  repeats.push_back(i);
67  }
68  }
69 
70  if (start_i == m_conditions_list.size()) {
71  m_log << "calculations already complete." << std::endl;
72  return;
73  }
74 
75  // if existing calculations
76  if (start_i > 0 || repeats.size() > 0) {
77  m_log << "found existing calculations\n";
78  m_log << "will begin with condition " << start_i << "\n";
79 
80  if (repeats.size()) {
81  jsonParser json;
82  to_json(repeats, json);
83  m_log << "will overwrite existing results for condition(s): " << json
84  << "\n";
85  }
86  } else {
87  m_log << "did not find existing calculations\n";
88  }
89  m_log << std::endl;
90 
91  if (m_settings.dependent_runs()) {
92  // if starting from initial condition
93  if (start_i == 0) {
94  // set intial state
95  m_mc.set_state(m_conditions_list[0], m_settings);
96 
97  // perform any requested explicit equilibration passes
98  if (m_settings.dependent_runs() &&
99  m_settings.is_equilibration_passes_first_run()) {
100  auto equil_passes = m_settings.equilibration_passes_first_run();
101 
102  m_log.write("DoF");
103  m_log << "write: " << m_dir.initial_state_firstruneq_json(0) << "\n"
104  << std::endl;
105 
106  jsonParser json;
107  fs::create_directories(m_dir.conditions_dir(0));
108  to_json(m_mc.configdof(), json)
109  .write(m_dir.initial_state_firstruneq_json(0));
110 
111  m_log.begin("Equilibration passes");
112  m_log << equil_passes << " equilibration passes\n" << std::endl;
113 
114  MonteCounter equil_counter(m_settings, m_mc.steps_per_pass());
115  while (equil_counter.pass() != equil_passes) {
116  monte_carlo_step(m_mc);
117  equil_counter++;
118  }
119  }
120  } else {
121  // read end state of previous condition
122  ConfigDoF configdof = m_mc.configdof();
123  from_json(configdof, jsonParser(m_dir.final_state_json(
124  start_i - 1))); //, m_mc.primclex().n_basis());
125 
126  m_mc.set_configdof(configdof,
127  std::string("Using: ") +
128  m_dir.final_state_json(start_i - 1).string());
129  }
130  }
131 
132  // Run for all conditions, outputting data as you finish each one
133  for (Index i = start_i; i < m_conditions_list.size(); i++) {
134  if (!m_settings.dependent_runs()) {
135  m_mc.set_state(m_conditions_list[i], m_settings);
136  } else {
137  m_mc.set_conditions(m_conditions_list[i]);
138 
139  m_log.custom("Continue with existing DoF");
140  m_log << std::endl;
141  }
142  single_run(i);
143  m_log << std::endl;
144  }
145 
146  return;
147 }
148 
154 template <typename RunType>
156  Index start_i = 0;
157  Index start_max = m_conditions_list.size();
158  Index start_json = m_settings.write_json() ? 0 : start_max;
159  Index start_csv = m_settings.write_csv() ? 0 : start_max;
160  jsonParser json_results;
161  fs::ifstream csv_results;
162  std::string str;
163  std::stringstream ss;
164 
165  // can start with condition i+1 if results (i) and final_state.json (i) exist
166 
167  // check JSON files
168  if (m_settings.write_json() && fs::exists(m_dir.results_json())) {
169  json_results.read(m_dir.results_json());
170 
171  // can start with i+1 if results[i] and final_state.json (i) exist
172  while (json_results.begin()->size() > start_json &&
173  fs::exists(m_dir.final_state_json(start_i)) && start_i < start_max) {
174  ++start_json;
175  }
176 
177  start_max = start_json;
178  }
179 
180  // check CSV files
181  if (m_settings.write_csv() && fs::exists(m_dir.results_csv())) {
182  csv_results.open(m_dir.results_csv());
183 
184  // read header
185  std::getline(csv_results, str);
186  ss << str << "\n";
187 
188  // can start with i+1 if results[i] and final_state.json (i) exist
189  while (!csv_results.eof() &&
190  fs::exists(m_dir.final_state_json(start_csv)) &&
191  start_i < start_max) {
192  ++start_csv;
193  std::getline(csv_results, str);
194  ss << str << "\n";
195  }
196 
197  start_max = start_csv;
198  }
199  csv_results.close();
200 
201  // use minimum of allowed starting conditions, in case a difference is found
202  start_i = std::min(start_json, start_csv);
203 
204  // update results summary files to remove any conditions that must be
205  // re-calculated
206 
207  // for JSON
208  if (m_settings.write_json() && fs::exists(m_dir.results_json())) {
209  // if results size > start_i, must fix results by removing results that will
210  // be re-run
211  jsonParser finished_results;
212  for (auto it = json_results.begin(); it != json_results.end(); ++it) {
213  jsonParser &ref = finished_results[it.name()].put_array();
214  for (Index i = 0; i < start_i; ++i) {
215  ref.push_back((*it)[i]);
216  }
217  }
218  m_log << "update: " << m_dir.results_json() << "\n";
219  finished_results.write(m_dir.results_json());
220  }
221 
222  // for csv
223  if (m_settings.write_csv() && fs::exists(m_dir.results_csv())) {
224  m_log << "update: " << m_dir.results_csv() << "\n";
225  fs::ofstream out(m_dir.results_csv());
226  out << ss.rdbuf();
227  out.close();
228  }
229 
230  return start_i;
231 }
232 
233 template <typename RunType>
235  fs::create_directories(m_dir.conditions_dir(cond_index));
236 
237  // perform any requested explicit equilibration passes
238  if (m_settings.is_equilibration_passes_each_run()) {
239  m_log.write("DoF");
240  m_log << "write: " << m_dir.initial_state_runeq_json(cond_index) << "\n"
241  << std::endl;
242 
243  jsonParser json;
244  to_json(m_mc.configdof(), json)
245  .write(m_dir.initial_state_runeq_json(cond_index));
246  auto equil_passes = m_settings.equilibration_passes_each_run();
247 
248  m_log.begin("Equilibration passes");
249  m_log << equil_passes << " equilibration passes\n" << std::endl;
250 
251  MonteCounter equil_counter(m_settings, m_mc.steps_per_pass());
252  while (equil_counter.pass() != equil_passes) {
253  monte_carlo_step(m_mc);
254  equil_counter++;
255  }
256  }
257 
258  // initial state (after any equilibriation passes)
259  m_log.write("DoF");
260  m_log << "write: " << m_dir.initial_state_json(cond_index) << "\n"
261  << std::endl;
262  jsonParser json;
263  to_json(m_mc.configdof(), json).write(m_dir.initial_state_json(cond_index));
264 
265  std::stringstream ss;
266  ss << "Conditions " << cond_index;
267  m_log.begin(ss.str());
268  m_log << std::endl;
269  m_log.begin_lap();
270 
271  MonteCounter run_counter(m_settings, m_mc.steps_per_pass());
272  if (m_enum) {
273  m_enum->reset();
274  };
275 
276  while (true) {
277  if (debug()) {
278  m_log.custom<Log::debug>("Counter info");
279  m_log << "pass: " << run_counter.pass() << " "
280  << "step: " << run_counter.step() << " "
281  << "samples: " << run_counter.samples() << "\n"
282  << std::endl;
283  }
284 
285  if (m_mc.must_converge()) {
286  if (!run_counter.minimums_met()) {
287  // keep going, but check for conflicts with maximums
288  if (run_counter.maximums_met()) {
289  throw std::runtime_error(
290  std::string("Error in 'MonteDriver<RunType>::single_run()'\n") +
291  " Conflicting input: Minimum number of passes, steps, or "
292  "samples not met,\n" +
293  " but maximum number of passes, steps, or samples are met.");
294  }
295  } else {
296  if (m_mc.check_convergence_time()) {
297  m_log.require<Log::verbose>() << "\n";
298  m_log.custom<Log::verbose>("Begin convergence checks");
299  m_log << "samples: " << m_mc.sample_times().size() << std::endl;
300  m_log << std::endl;
301 
302  if (m_mc.is_converged()) {
303  break;
304  }
305  }
306 
307  if (run_counter.maximums_met()) {
308  break;
309  }
310  }
311  } else if (run_counter.is_complete()) {
312  // stop
313  break;
314  }
315 
316  bool res = monte_carlo_step(m_mc);
317 
318  if (res && m_enum && m_enum->on_accept()) {
319  m_enum->insert(m_mc.config());
320  }
321 
322  run_counter++;
323 
324  if (run_counter.sample_time()) {
325  m_log.custom<Log::debug>("Sample data");
326  m_log << "pass: " << run_counter.pass() << " "
327  << "step: " << run_counter.step() << " "
328  << "take sample " << m_mc.sample_times().size() << "\n"
329  << std::endl;
330 
331  m_mc.sample_data(run_counter);
332  run_counter.increment_samples();
333  if (m_enum && m_enum->on_sample()) {
334  m_enum->insert(m_mc.config());
335  }
336  }
337  }
338  m_log << std::endl;
339 
340  // timing info:
341  double s = m_log.lap_time();
342  m_log.end(ss.str());
343  m_log << "run time: " << s << " (s), " << s / run_counter.pass()
344  << " (s/pass), "
345  << s / (run_counter.pass() * run_counter.steps_per_pass() +
346  run_counter.step())
347  << "(s/step)\n"
348  << std::endl;
349 
350  m_log.write("DoF");
351  m_log << "write: " << m_dir.final_state_json(cond_index) << "\n" << std::endl;
352  to_json(m_mc.configdof(), json).write(m_dir.final_state_json(cond_index));
353 
354  m_log.write("Output files");
355  m_mc.write_results(cond_index);
356  m_log << std::endl;
357 
358  if (m_enum) {
359  m_enum->save_configs();
360  }
361 
362  return;
363 }
364 
376 template <typename RunType>
377 std::vector<typename MonteDriver<RunType>::CondType>
379  const SettingsType &settings) {
380  std::vector<CondType> conditions_list;
381 
382  switch (m_drive_mode) {
384  // read existing conditions, and check for agreement
385  std::vector<CondType> custom_cond(settings.custom_conditions());
386  int i = 0;
387  while (fs::exists(m_dir.conditions_json(i))) {
388  CondType existing;
389  jsonParser json(m_dir.conditions_json(i));
390  from_json(existing, primclex, json);
391  if (existing != custom_cond[i]) {
392  m_err_log.error("Conditions mismatch");
393  m_err_log << "existing conditions: " << m_dir.conditions_json(i)
394  << "\n";
395  m_err_log << existing << "\n\n";
396  m_err_log << "specified custom conditions " << i << ":\n";
397  m_err_log << custom_cond[i] << "\n" << std::endl;
398  throw std::runtime_error(
399  "ERROR: custom_conditions list has changed.");
400  }
401  ++i;
402  }
403  return custom_cond;
404  }
405 
407  CondType init_cond(settings.initial_conditions());
408  CondType final_cond(settings.final_conditions());
409  CondType cond_increment(settings.incremental_conditions());
410 
411  CondType incrementing_cond = init_cond;
412 
413  int num_increments = 1 + (final_cond - init_cond) / cond_increment;
414 
415  for (int i = 0; i < num_increments; i++) {
416  conditions_list.push_back(incrementing_cond);
417  incrementing_cond += cond_increment;
418  }
419 
420  int i = 0;
421  while (fs::exists(m_dir.conditions_json(i)) &&
422  i < conditions_list.size()) {
423  CondType existing;
424  jsonParser json(m_dir.conditions_json(i));
425  from_json(existing, primclex, json);
426  if (existing != conditions_list[i]) {
427  m_err_log.error("Conditions mismatch");
428  m_err_log << "existing conditions: " << m_dir.conditions_json(i)
429  << "\n";
430  m_err_log << existing << "\n";
431  m_err_log << "incremental conditions " << i << ":\n";
432  m_err_log << conditions_list[i] << "\n" << std::endl;
433  throw std::runtime_error(
434  "ERROR: initial_conditions or incremental_conditions has "
435  "changed.");
436  }
437  ++i;
438  }
439 
440  return conditions_list;
441  }
442 
443  default: {
444  throw std::runtime_error("ERROR: An invalid drive mode was given.");
445  }
446  }
447 }
448 
449 template <typename RunType>
450 bool monte_carlo_step(RunType &monte_run) {
451  typedef typename RunType::EventType EventType;
452 
453  const EventType &event = monte_run.propose();
454 
455  if (monte_run.check(event)) {
456  monte_run.accept(event);
457  return true;
458  }
459 
460  else {
461  monte_run.reject(event);
462  return false;
463  }
464 }
465 
466 } // namespace Monte
467 } // namespace CASM
468 
469 #endif
std::set< std::string > & s
Definition: Log.hh:48
static const int debug
Definition: Log.hh:54
static const int verbose
Definition: Log.hh:53
Track the number of passes, steps and samples taken in a Monte Carlo calculation.
Definition: MonteCounter.hh:23
bool sample_time() const
Returns true if based on period and current number of steps it is time to take a sample.
bool maximums_met() const
Check if maximum number of pass, step, and samples has been met.
size_type samples() const
Number of samples taken.
bool is_complete() const
Check if requested number of pass, step, or samples has been met.
bool minimums_met() const
Check if minimum number of pass, step, and samples has been met.
size_type steps_per_pass() const
Number of steps per pass.
Definition: MonteCounter.cc:98
size_type step() const
Number of steps into the current pass.
Definition: MonteCounter.cc:95
void increment_samples()
Increments the number of samples taken and resets counter until the next sample should be taken.
size_type pass() const
Number of complete passes performed.
Definition: MonteCounter.cc:93
void run()
Run everything requested by the MonteSettings.
void single_run(Index cond_index)
Converge the MonteCarlo for conditions 'cond_index'.
RunType::SettingsType SettingsType
Definition: MonteDriver.hh:33
MonteDriver(const PrimClex &primclex, const SettingsType &settings, Log &_log, Log &_err_log)
Constructor via MonteSettings.
RunType::CondType CondType
Definition: MonteDriver.hh:32
std::vector< CondType > make_conditions_list(const PrimClex &primclex, const SettingsType &settings)
Index _find_starting_conditions() const
Check for existing calculations to find starting conditions.
PrimClex is the top-level data structure for a CASM project.
Definition: PrimClex.hh:55
iterator begin()
Returns const_iterator to beginning of JSON object or JSON array.
Definition: jsonParser.cc:497
iterator end()
Returns iterator to end of JSON object or JSON array.
Definition: jsonParser.cc:520
bool read(std::istream &stream)
Reads json from the stream.
Definition: jsonParser.cc:168
void write(const std::string &file_name, unsigned int indent=2, unsigned int prec=12) const
Write json to file.
Definition: jsonParser.cc:196
jsonParser & put_array()
Puts new empty JSON array.
Definition: jsonParser.hh:362
void from_json(CanonicalConditions &conditions, const PrimClex &primclex, const jsonParser &json)
Read CanonicalConditions from JSON format.
Definition: CanonicalIO.cc:137
bool monte_carlo_step(RunType &monte_run)
Perform a single monte carlo step, return true if accepted.
jsonParser & to_json(const CanonicalConditions &conditions, jsonParser &json)
Store CanonicalConditions in JSON format.
Definition: CanonicalIO.cc:102
Main CASM namespace.
Definition: APICommand.hh:8
T min(const T &A, const T &B)
Definition: CASM_math.hh:88
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
pair_type ref
Definition: settings.cc:144
PrimClex * primclex
Definition: settings.cc:135