CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
MonteDriver.hh
Go to the documentation of this file.
1 #ifndef CASM_MonteDriver_HH
2 #define CASM_MonteDriver_HH
3 
4 #include <string>
5 #include "casm/external/boost.hh"
6 
11 
12 namespace CASM {
13 
25  template<typename RunType>
26  class MonteDriver {
27 
28  public:
29  typedef typename RunType::CondType CondType;
30  typedef typename RunType::SettingsType SettingsType;
31 
33  MonteDriver(PrimClex &primclex, const SettingsType &settings, Log &_log, Log &_err_log);
34 
36  void run();
37 
38  private:
39 
41  bool debug() const {
42  return m_debug;
43  }
44 
46  std::vector<CondType> make_conditions_list(const PrimClex &primclex, const SettingsType &settings);
47 
49  void single_run(Index cond_index);
50 
53 
54 
57 
60 
62  SettingsType m_settings;
63 
66 
69 
71  RunType m_mc;
72 
74  const std::vector<CondType> m_conditions_list;
75 
77  bool m_debug;
78 
81  };
82 
83 
85  template<typename RunType>
86  bool monte_carlo_step(RunType &monte_run);
87 
88 
89  template<typename RunType>
91  m_log(_log),
92  m_err_log(_err_log),
93  m_settings(settings),
94  m_dir(m_settings.output_directory()),
95  m_drive_mode(m_settings.drive_mode()),
96  m_mc(primclex, m_settings, _log),
97  m_conditions_list(make_conditions_list(primclex, m_settings)),
98  m_debug(m_settings.debug()),
99  m_enum(m_settings.is_enumeration() ? new MonteCarloEnum(primclex, settings, _log, m_mc) : nullptr) {
100  }
101 
109  template<typename RunType>
111 
112  m_log.check("For existing calculations");
113 
114  if(!m_settings.write_json() && !m_settings.write_csv()) {
115  throw std::runtime_error(
116  std::string("No valid monte carlo output format.\n") +
117  " Expected [\"data\"][\"storage\"][\"output_format\"] to contain a string or array of strings.\n" +
118  " Valid options are 'csv' or 'json'.");
119  }
120 
121  // Skip any conditions that have already been calculated and saved
122  Index start_i = _find_starting_conditions();
123 
124  // check if we'll be repeating any calculations that already have files written
125  std::vector<Index> repeats;
126  for(Index i = start_i; i < m_conditions_list.size(); ++i) {
127  if(fs::exists(m_dir.conditions_dir(i))) {
128  repeats.push_back(i);
129  }
130  }
131 
132  if(start_i == m_conditions_list.size()) {
133  m_log << "calculations already complete." << std::endl;
134  return;
135  }
136 
137  // if existing calculations
138  if(start_i > 0 || repeats.size() > 0) {
139 
140  m_log << "found existing calculations\n";
141  m_log << "will begin with condition " << start_i << "\n";
142 
143  if(repeats.size()) {
144  jsonParser json;
145  to_json(repeats, json);
146  m_log << "will overwrite existing results for condition(s): " << json << "\n";
147  }
148  }
149  else {
150  m_log << "did not find existing calculations\n";
151  }
152  m_log << std::endl;
153 
154  if(m_settings.dependent_runs()) {
155 
156  // if starting from initial condition
157  if(start_i == 0) {
158 
159  // set intial state
160  m_mc.set_state(m_conditions_list[0], m_settings);
161 
162  // perform any requested explicit equilibration passes
163  if(m_settings.dependent_runs() && m_settings.is_equilibration_passes_first_run()) {
164 
165  auto equil_passes = m_settings.equilibration_passes_first_run();
166 
167  m_log.write("DoF");
168  m_log << "write: " << m_dir.initial_state_firstruneq_json(0) << "\n" << std::endl;
169 
170  jsonParser json;
171  fs::create_directories(m_dir.conditions_dir(0));
172  to_json(m_mc.configdof(), json).write(m_dir.initial_state_firstruneq_json(0));
173 
174  m_log.begin("Equilibration passes");
175  m_log << equil_passes << " equilibration passes\n" << std::endl;
176 
177  MonteCounter equil_counter(m_settings, m_mc.steps_per_pass());
178  while(equil_counter.pass() != equil_passes) {
179  monte_carlo_step(m_mc);
180  equil_counter++;
181  }
182  }
183  }
184  else {
185 
186  // read end state of previous condition
187  ConfigDoF configdof = m_mc.configdof();
188  from_json(configdof, jsonParser(m_dir.final_state_json(start_i - 1)));
189 
190  m_mc.set_state(
191  m_conditions_list[start_i],
192  configdof,
193  std::string("Using: ") + m_dir.final_state_json(start_i - 1).string());
194  }
195  }
196 
197  // Run for all conditions, outputting data as you finish each one
198  for(Index i = start_i; i < m_conditions_list.size(); i++) {
199  if(!m_settings.dependent_runs()) {
200  m_mc.set_state(m_conditions_list[i], m_settings);
201  }
202  else if(i != start_i) {
203 
204  m_mc.set_conditions(m_conditions_list[i]);
205 
206  m_log.custom("Continue with existing DoF");
207  m_log << std::endl;
208  }
209 
210  single_run(i);
211 
212  m_log << std::endl;
213  }
214 
215  return;
216  }
217 
223  template<typename RunType>
225 
226  Index start_i = 0;
227  Index start_max = m_conditions_list.size();
228  Index start_json = m_settings.write_json() ? 0 : start_max;
229  Index start_csv = m_settings.write_csv() ? 0 : start_max;
230  jsonParser json_results;
231  fs::ifstream csv_results;
232  std::string str;
233  std::stringstream ss;
234 
235  // can start with condition i+1 if results (i) and final_state.json (i) exist
236 
237  // check JSON files
238  if(m_settings.write_json() && fs::exists(m_dir.results_json())) {
239 
240  json_results.read(m_dir.results_json());
241 
242  // can start with i+1 if results[i] and final_state.json (i) exist
243  while(json_results.begin()->size() > start_json && fs::exists(m_dir.final_state_json(start_i)) && start_i < start_max) {
244  ++start_json;
245  }
246 
247  start_max = start_json;
248  }
249 
250  // check CSV files
251  if(m_settings.write_csv() && fs::exists(m_dir.results_csv())) {
252 
253  csv_results.open(m_dir.results_csv());
254 
255  // read header
256  std::getline(csv_results, str);
257  ss << str << "\n";
258 
259  // can start with i+1 if results[i] and final_state.json (i) exist
260  while(!csv_results.eof() && fs::exists(m_dir.final_state_json(start_csv)) && start_i < start_max) {
261  ++start_csv;
262  std::getline(csv_results, str);
263  ss << str << "\n";
264  }
265 
266  start_max = start_csv;
267  }
268  csv_results.close();
269 
270  // use minimum of allowed starting conditions, in case a difference is found
271  start_i = std::min(start_json, start_csv);
272 
273 
274  // update results summary files to remove any conditions that must be re-calculated
275 
276  // for JSON
277  if(m_settings.write_json() && fs::exists(m_dir.results_json())) {
278 
279  // if results size > start_i, must fix results by removing results that will be re-run
280  jsonParser finished_results;
281  for(auto it = json_results.begin(); it != json_results.end(); ++it) {
282  jsonParser &ref = finished_results[it.name()].put_array();
283  for(Index i = 0; i < start_i; ++i) {
284  ref.push_back((*it)[i]);
285  }
286  }
287  m_log << "update: " << m_dir.results_json() << "\n";
288  finished_results.write(m_dir.results_json());
289  }
290 
291  // for csv
292  if(m_settings.write_csv() && fs::exists(m_dir.results_csv())) {
293  m_log << "update: " << m_dir.results_csv() << "\n";
294  fs::ofstream out(m_dir.results_csv());
295  out << ss.rdbuf();
296  out.close();
297  }
298 
299  return start_i;
300  }
301 
302  template<typename RunType>
304 
305  fs::create_directories(m_dir.conditions_dir(cond_index));
306 
307  // perform any requested explicit equilibration passes
308  if(m_settings.is_equilibration_passes_each_run()) {
309 
310  m_log.write("DoF");
311  m_log << "write: " << m_dir.initial_state_runeq_json(cond_index) << "\n" << std::endl;
312 
313  jsonParser json;
314  to_json(m_mc.configdof(), json).write(m_dir.initial_state_runeq_json(cond_index));
315  auto equil_passes = m_settings.equilibration_passes_each_run();
316 
317  m_log.begin("Equilibration passes");
318  m_log << equil_passes << " equilibration passes\n" << std::endl;
319 
320  MonteCounter equil_counter(m_settings, m_mc.steps_per_pass());
321  while(equil_counter.pass() != equil_passes) {
322  monte_carlo_step(m_mc);
323  equil_counter++;
324  }
325  }
326 
327  // initial state (after any equilibriation passes)
328  m_log.write("DoF");
329  m_log << "write: " << m_dir.initial_state_json(cond_index) << "\n" << std::endl;
330  jsonParser json;
331  to_json(m_mc.configdof(), json).write(m_dir.initial_state_json(cond_index));
332 
333  std::stringstream ss;
334  ss << "Conditions " << cond_index;
335  m_log.begin(ss.str());
336  m_log << std::endl;
337  m_log.begin_lap();
338 
339  MonteCounter run_counter(m_settings, m_mc.steps_per_pass());
340  if(m_enum) {
341  m_enum->reset();
342  };
343 
344  while(true) {
345 
346  if(debug()) {
347  m_log.custom<Log::debug>("Counter info");
348  m_log << "pass: " << run_counter.pass() << " "
349  << "step: " << run_counter.step() << " "
350  << "samples: " << run_counter.samples() << "\n" << std::endl;
351  }
352 
353  if(m_mc.must_converge()) {
354 
355  if(!run_counter.minimums_met()) {
356 
357  // keep going, but check for conflicts with maximums
358  if(run_counter.maximums_met()) {
359  throw std::runtime_error(
360  std::string("Error in 'MonteDriver<RunType>::single_run()'\n") +
361  " Conflicting input: Minimum number of passes, steps, or samples not met,\n" +
362  " but maximum number of passes, steps, or samples are met.");
363  }
364  }
365  else {
366 
367  if(m_mc.check_convergence_time()) {
368 
369  m_log.require<Log::verbose>() << "\n";
370  m_log.custom<Log::verbose>("Begin convergence checks");
371  m_log << "samples: " << m_mc.sample_times().size() << std::endl;
372  m_log << std::endl;
373 
374  if(m_mc.is_converged()) {
375  break;
376  }
377  }
378 
379  if(run_counter.maximums_met()) {
380  break;
381  }
382  }
383  }
384  else if(run_counter.is_complete()) {
385  // stop
386  break;
387  }
388 
389  bool res = monte_carlo_step(m_mc);
390 
391  if(res && m_enum && m_enum->on_accept()) {
392  m_enum->insert(m_mc.config());
393  }
394 
395  run_counter++;
396 
397  if(run_counter.sample_time()) {
398  if(debug()) {
399  m_log.custom<Log::debug>("Sample data");
400  m_log << "pass: " << run_counter.pass() << " "
401  << "step: " << run_counter.step() << " "
402  << "take sample " << m_mc.sample_times().size() << "\n" << std::endl;
403  }
404 
405  m_mc.sample_data(run_counter);
406  run_counter.increment_samples();
407  if(m_enum && m_enum->on_sample()) {
408  m_enum->insert(m_mc.config());
409  }
410  }
411  }
412  m_log << std::endl;
413 
414 
415  // timing info:
416  double s = m_log.lap_time();
417  m_log.end(ss.str());
418  m_log << "run time: " << s << " (s), " << s / run_counter.pass() << " (s/pass), " << s / (run_counter.pass()*run_counter.steps_per_pass() + run_counter.step()) << "(s/step)\n" << std::endl;
419 
420  m_log.write("DoF");
421  m_log << "write: " << m_dir.final_state_json(cond_index) << "\n" << std::endl;
422  to_json(m_mc.configdof(), json).write(m_dir.final_state_json(cond_index));
423 
424  m_log.write("Output files");
425  m_mc.write_results(cond_index);
426  m_log << std::endl;
427 
428  if(m_enum) {
429  m_enum->save_configs();
430  }
431 
432  return;
433  }
434 
446  template<typename RunType>
447  std::vector<typename MonteDriver<RunType>::CondType>
449 
450  std::vector<CondType> conditions_list;
451 
452  switch(m_drive_mode) {
453 
455 
456  // read existing conditions, and check for agreement
457  std::vector<CondType> custom_cond(settings.custom_conditions());
458  int i = 0;
459  while(fs::exists(m_dir.conditions_json(i))) {
460 
461  CondType existing;
462  jsonParser json(m_dir.conditions_json(i));
463  from_json(existing, primclex, json);
464  if(existing != custom_cond[i]) {
465  m_err_log.error("Conditions mismatch");
466  m_err_log << "existing conditions: " << m_dir.conditions_json(i) << "\n";
467  m_err_log << existing << "\n\n";
468  m_err_log << "specified custom conditions " << i << ":\n";
469  m_err_log << custom_cond[i] << "\n" << std::endl;
470  throw std::runtime_error("ERROR: custom_conditions list has changed.");
471  }
472  ++i;
473 
474  }
475  return custom_cond;
476  }
477 
479 
480  CondType init_cond(settings.initial_conditions());
481  CondType final_cond(settings.final_conditions());
482  CondType cond_increment(settings.incremental_conditions());
483 
484  CondType incrementing_cond = init_cond;
485 
486  int num_increments = 1 + (final_cond - init_cond) / cond_increment;
487 
488  for(int i = 0; i < num_increments; i++) {
489  conditions_list.push_back(incrementing_cond);
490  incrementing_cond += cond_increment;
491  }
492 
493  int i = 0;
494  while(fs::exists(m_dir.conditions_json(i)) && i < conditions_list.size()) {
495 
496  CondType existing;
497  jsonParser json(m_dir.conditions_json(i));
498  from_json(existing, primclex, json);
499  if(existing != conditions_list[i]) {
500  m_err_log.error("Conditions mismatch");
501  m_err_log << "existing conditions: " << m_dir.conditions_json(i) << "\n";
502  m_err_log << existing << "\n";
503  m_err_log << "incremental conditions " << i << ":\n";
504  m_err_log << conditions_list[i] << "\n" << std::endl;
505  throw std::runtime_error("ERROR: initial_conditions or incremental_conditions has changed.");
506  }
507  ++i;
508 
509  }
510 
511  return conditions_list;
512  }
513 
514  default: {
515  throw std::runtime_error("ERROR: An invalid drive mode was given.");
516  }
517  }
518  }
519 
520  template<typename RunType>
521  bool monte_carlo_step(RunType &monte_run) {
522 
523  typedef typename RunType::EventType EventType;
524 
525  const EventType &event = monte_run.propose();
526 
527  if(monte_run.check(event)) {
528  monte_run.accept(event);
529  return true;
530  }
531 
532  else {
533  monte_run.reject(event);
534  return false;
535  }
536 
537  }
538 
539 }
540 
541 #endif
void run()
Run everything requested by the MonteSettings.
Definition: MonteDriver.hh:110
bool m_debug
run in debug mode?
Definition: MonteDriver.hh:77
void single_run(Index cond_index)
Converge the MonteCarlo for conditions 'cond_index'.
Definition: MonteDriver.hh:303
void from_json(ClexDescription &desc, const jsonParser &json)
iterator end()
Returns iterator to end of JSON object or JSON array.
Definition: jsonParser.cc:465
MonteCarloDirectoryStructure m_dir
describes where to write output
Definition: MonteDriver.hh:65
static const int debug
Definition: Log.hh:17
SettingsType m_settings
Copy of initial settings given at construction. Will expand to have MonteCarlo states dumped into it...
Definition: MonteDriver.hh:62
void write(const std::string &file_name, unsigned int indent=2, unsigned int prec=12) const
Write json to file.
Definition: jsonParser.cc:191
jsonParser & to_json(const ClexDescription &desc, jsonParser &json)
std::vector< CondType > make_conditions_list(const PrimClex &primclex, const SettingsType &settings)
Return the appropriate std::vector of conditions to visit based from settings. Use for construction...
Definition: MonteDriver.hh:448
PrimClex * primclex
Definition: settings.cc:101
bool read(std::istream &stream)
Reads json from the stream.
Definition: jsonParser.cc:165
MonteDriver(PrimClex &primclex, const SettingsType &settings, Log &_log, Log &_err_log)
Constructor via MonteSettings.
Definition: MonteDriver.hh:90
Main CASM namespace.
Definition: complete.cpp:8
RunType::CondType CondType
Definition: MonteDriver.hh:29
iterator begin()
Returns const_iterator to beginning of JSON object or JSON array.
Definition: jsonParser.cc:440
pair_type ref
Definition: settings.cc:110
Track the number of passes, steps and samples taken in a Monte Carlo calculation. ...
Definition: MonteCounter.hh:20
static const int verbose
Definition: Log.hh:16
DRIVE_MODE
How to change conditions.
void reset()
Set all counter variables back to 0.
const std::vector< CondType > m_conditions_list
List of specialized conditions to visit in sequential order. Does not include initial conditions...
Definition: MonteDriver.hh:74
EigenIndex Index
For long integer indexing:
A container class for the different degrees of freedom a Configuration might have.
Definition: ConfigDoF.hh:27
RunType m_mc
Specialized Monte Carlo object to use throughout.
Definition: MonteDriver.hh:71
PrimClex is the top-level data structure for a CASM project.
Definition: PrimClex.hh:52
T min(const T &A, const T &B)
Definition: CASM_math.hh:25
bool debug() const
run in debug mode?
Definition: MonteDriver.hh:41
bool monte_carlo_step(RunType &monte_run)
Perform a single monte carlo step, return true if accepted.
Definition: MonteDriver.hh:521
const Monte::DRIVE_MODE m_drive_mode
Specifies how to build the conditions list from the settings.
Definition: MonteDriver.hh:68
notstd::cloneable_ptr< MonteCarloEnum > m_enum
Enumerated configurations encountered during Monte Carlo calculations.
Definition: MonteDriver.hh:80
Index _find_starting_conditions() const
Check for existing calculations to find starting conditions.
Definition: MonteDriver.hh:224
jsonParser & push_back(const T &value)
Puts new valued element at end of array of any type T for which 'jsonParser& to_json( const T &value...
Definition: jsonParser.hh:696
Log & m_err_log
target for error messages
Definition: MonteDriver.hh:59
A 'cloneable_ptr' can be used in place of 'unique_ptr'.
Definition: Log.hh:9
RunType::SettingsType SettingsType
Definition: MonteDriver.hh:30
jsonParser & put_array()
Puts new empty JSON array.
Definition: jsonParser.hh:285
Log & m_log
target for log messages
Definition: MonteDriver.hh:56