CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
update.cc
Go to the documentation of this file.
1 #include "casm/clex/PrimClex.hh"
8 
10 
11 namespace CASM {
12  namespace Update_impl {
13  // record some pieces of data from each import. These are used to resolve conflicts at the end
14  // the 'relaxjson' datarecord stores relaxation properties that will be merged into Configuration::calc_properties() during the final step
15  enum DataType {relaxjson = 0, self_map = 1, new_config = 2};
16  using Data = std::tuple<jsonParser, bool, bool>;
17  }
18 
19  namespace Completer {
21 
23  return m_lattice_weight;
24  }
25 
26  double UpdateOption::vol_tolerance() const {
27  return m_vol_tolerance;
28  }
29 
30  double UpdateOption::min_va_frac() const {
31  return m_min_va_frac;
32  }
33 
34  double UpdateOption::max_va_frac() const {
35  return m_max_va_frac;
36  }
37 
41 
42  m_desc.add_options()
43  ("cost-weight,w", po::value<double>(&m_lattice_weight)->default_value(0.5),
44  "Adjusts cost function for mapping optimization (cost=w*lattice_deformation+(1-w)*basis_deformation)")
45  ("max-vol-change", po::value<double>(&m_vol_tolerance)->default_value(0.3),
46  "Adjusts range of SCEL volumes searched while mapping imported structure onto ideal crystal (only necessary if the presence of vacancies makes the volume ambiguous). Default is +/- 25% of relaxed_vol/prim_vol. Smaller values yield faster execution, larger values may yield more accurate mapping.")
47  ("max-va-frac", po::value<double>(&m_max_va_frac)->default_value(0.5),
48  "Places upper bound on the fraction of sites that are allowed to be vacant after relaxed structure is mapped onto the ideal crystal. Smaller values yield faster execution, larger values may yield more accurate mapping. Has no effect if supercell volume can be inferred from the number of atoms in the structure. Default value allows up to 50% of sites to be vacant.")
49  ("min-va-frac", po::value<double>(&m_min_va_frac)->default_value(0.),
50  "Places lower bound on the fraction of sites that are allowed to be vacant after relaxed structure is mapped onto the ideal crystal. Nonzero values may yield faster execution if updating configurations that are known to have a large number of vacancies, at potential sacrifice of mapping accuracy. Has no effect if supercell volume can be inferred from the number of atoms in the structure. Default value allows as few as 0% of sites to be vacant.")
51  ("force,f", "Force all configurations to update (otherwise, use timestamps to determine which configurations to update)")
52  ("strict,s", "Attempt to import exact configuration.");
53 
54  return;
55  }
56  }
57 
58  // ///////////////////////////////////////
59  // 'update' function for casm
60  // (add an 'if-else' statement in casm.cpp to call this)
61 
62  int update_command(const CommandArgs &args) {
63 
64  po::variables_map vm;
65  double tol(TOL);
66  double vol_tol;
67  double lattice_weight;
68 
69  Completer::UpdateOption update_opt;
70 
71  try {
72  po::store(po::parse_command_line(args.argc, args.argv, update_opt.desc()), vm);
73 
74 
77  if(vm.count("help")) {
78  args.log << std::endl;
79  args.log << update_opt.desc() << std::endl;
80 
81  return 0;
82  }
83 
84  if(vm.count("desc")) {
85  args.log << "\n";
86  args.log << update_opt.desc() << std::endl;
87  args.log << "DESCRIPTION" << std::endl;
88  args.log << " Updates all values and files after manual changes or configuration \n";
89  args.log << " calculations.\n";
90  args.log << "\n";
91 
92  return 0;
93  }
94 
95  po::notify(vm);
96 
97  vol_tol = update_opt.vol_tolerance();
98  lattice_weight = update_opt.lattice_weight();
99  }
100  catch(po::error &e) {
101  args.err_log << update_opt.desc() << std::endl;
102  args.err_log << "ERROR: " << e.what() << std::endl << std::endl;
103  return 1;
104  }
105 
106  catch(std::exception &e) {
107  args.err_log << update_opt.desc() << std::endl;
108  args.err_log << "ERROR: " << e.what() << std::endl << std::endl;
109  return 1;
110  }
111 
112  const fs::path &root = args.root;
113  if(root.empty()) {
114  args.err_log.error("No casm project found");
115  args.err_log << std::endl;
116  return ERR_NO_PROJ;
117  }
118 
119  // If 'args.primclex', use that, else construct PrimClex in 'uniq_primclex'
120  // Then whichever exists, store reference in 'primclex'
121  std::unique_ptr<PrimClex> uniq_primclex;
122  PrimClex &primclex = make_primclex_if_not(args, uniq_primclex);
123 
124  ConfigMapper configmapper(primclex, lattice_weight, vol_tol, ConfigMapper::rotate | ConfigMapper::robust | (vm.count("strict") ? ConfigMapper::strict : 0), tol);
125  configmapper.set_min_va_frac(update_opt.min_va_frac());
126  configmapper.set_max_va_frac(update_opt.max_va_frac());
127  args.log << "Reading calculation data... " << std::endl << std::endl;
128  std::vector<std::string> bad_config_report;
129  std::vector<std::string> prop_names = primclex.settings().properties();
130 
131  Index num_updated(0);
132 
133  std::map<Configuration *, std::map<Configuration *, Update_impl::Data> > update_map;
134 
135  // Get configuration selection
136  ConfigSelection<false> selection(primclex, update_opt.selection_path());
137 
138  auto it = selection.selected_config_begin();
139  auto end = selection.selected_config_end();
140 
141  for(; it != end; ++it) {
146 
147 
148  //args.log << "begin Configuration::read_calculated()" << std::endl;
149 
152  fs::path filepath = it->calc_properties_path();
153  // determine if there is fresh data to read and put it in 'calc_properties'
154  jsonParser parsed_props;
155  if(!fs::exists(filepath)) {
156  if(it->calc_properties().size() > 0 && !(it->calc_properties().is_null())) {
157  //clear the calculated properties if the data file is missing -- this probably means that the user has deleted it
158  it->set_calc_properties(jsonParser());
159  }
160  }
161  else {
162  time_t datatime(0), filetime(fs::last_write_time(filepath));
163  // Compare 'datatime', from config_list database to 'filetime', from filesystem timestamp
164  it->calc_properties().get_if(datatime, "data_timestamp");
165 
166  if(!vm.count("force") && filetime == datatime) {
167  continue;
168  }
169  it->set_calc_properties(jsonParser());
170  num_updated++;
171  args.log << std::endl << "***************************" << std::endl << std::endl;
172  args.log << "Working on " << filepath.string() << "\n";
173 
174  bool new_config_flag;
175  std::string imported_name;
176  try {
177  //json relax_data;
178  it->read_calc_properties(parsed_props);
179  }
180  catch(std::exception &e) {
181  args.err_log << "\nError: Unable to read properties.calc.json" << std::endl;
182  args.err_log << e.what() << std::endl;
183  return ERR_INVALID_INPUT_FILE;
184  }
185 
186  {
187  //Convert relaxed structure into a configuration, merge calculation data
188  BasicStructure<Site> relaxed_struc;
189  jsonParser json(filepath);
190  from_json(simple_json(relaxed_struc, "relaxed_"), json);
191  std::vector<Index> best_assignment;
192  Eigen::Matrix3d cart_op;
193  //NOTE: reusing jsonParser to collect relaxation data
194  json.put_obj();
195  try {
196  if(vm.count("strict")) {
197  new_config_flag = configmapper.import_structure(relaxed_struc,
198  imported_name,
199  json,
200  best_assignment,
201  cart_op);
202  }
203  else {
204  new_config_flag = configmapper.import_structure_occupation(relaxed_struc,
205  &(*it),
206  imported_name,
207  json,
208  best_assignment,
209  cart_op);
210  }
211  }
212  catch(std::exception &e) {
213  args.err_log << "\nError: Unable to map relaxed structure data contained in " << filepath << " onto PRIM.\n"
214  << " " << e.what() << std::endl;
215  //throw std::runtime_error(std::string("Unable to map relaxed structure data contained in ") + filepath.string() + " onto PRIM.\n");
216  return 1;
217  }
218 
219  //copy data over
220  if(imported_name != it->name() && json.contains("suggested_mapping")) {
221  auto j_it(json["suggested_mapping"].cbegin()), j_end(json["suggested_mapping"].cend());
222  for(; j_it != j_end; ++j_it) {
223  parsed_props[j_it.name()] = *j_it;
224  }
225  update_map[&(*it)][&(*it)] = Update_impl::Data(parsed_props, false, new_config_flag);
226  }
227 
228  Configuration &imported_config = primclex.configuration(imported_name);
229  auto j_it(json["best_mapping"].cbegin()), j_end(json["best_mapping"].cend());
230  for(; j_it != j_end; ++j_it) {
231  parsed_props[j_it.name()] = *j_it;
232  }
233 
234  update_map[&imported_config][&(*it)] = Update_impl::Data(parsed_props, it->name() == imported_name, new_config_flag);
235  }
236 
237 
238  }
239 
240  }
241 
242 
243  // Now loop over update_map to construct a relaxation report and record calculation data appropriately
244  double w = lattice_weight;
245  std::stringstream relax_log;
246  for(auto it = update_map.begin(); it != update_map.end(); ++it) {
247  Configuration &imported_config = *(it->first);
248  std::map<Configuration *, Update_impl::Data> &datamap(it->second);
249  bool self_mapped(false);
250 
251  double lowest_energy(std::numeric_limits<double>::max());
252  std::map<Configuration *, Update_impl::Data>::iterator best_it;
253  double best_cost(1e20);
254 
255  //enum DataType {name = 0, relaxjson = 1, self_map = 2};
256  std::vector<std::string> report;
257  for(auto it2 = datamap.begin(); it2 != datamap.end(); ++it2) {
258  std::stringstream t_ss;
259  //if(imported_config.name() == it->name()) {
260  //it->set_calc_properties(parsed_props);
261  //continue;
262  //}
263  Configuration &source_config(*(it2->first));
264  const jsonParser &source_data(std::get<Update_impl::relaxjson>(it2->second));
265  if(&source_config == &imported_config) {
266  self_mapped = std::get<Update_impl::self_map>(it2->second);
267  }
268  else {
269  // Note the instability:
270  source_config.push_back_source(jsonParser("mechanically_unstable"));
271  source_config.push_back_source(jsonParser(std::make_pair("relaxed_to", imported_config.name())));
272  imported_config.push_back_source(jsonParser(std::make_pair("relaxation_of", source_config.name())));
273  }
274  double bd = source_data["basis_deformation"].get<double>();
275  double ld = source_data["lattice_deformation"].get<double>();
276  double tcost = w * ld + (1.0 - w) * bd;
277 
278  if(tcost < best_cost) {
279  best_cost = tcost;
280  best_it = it2;
281  }
282 
283  t_ss << "Starting configuration " << source_config.name() << ":" << std::endl;
284  t_ss << " Result of mapping relaxation onto " << imported_config.name() << std::endl;
285  t_ss << " -- lattice_deformation = " << ld << "; basis_deformation = " << bd << "; weighted_avg = " << tcost << std::endl;
286 
287  if(&source_config != &imported_config) {
288  jsonParser const &alt_data(std::get<Update_impl::relaxjson>(update_map[&source_config][&source_config]));
289  bd = alt_data["basis_deformation"].get<double>();
290  ld = alt_data["lattice_deformation"].get<double>();
291  tcost = w * ld + (1.0 - w) * bd;
292  t_ss << " Result of mapping relaxation onto " << source_config.name() << std::endl;
293  t_ss << " -- lattice_deformation = " << ld << "; basis_deformation = " << bd << "; weighted_avg = " << tcost << std::endl;
294  }
295  if(source_data.contains("relaxed_energy")) {
296  double energy = source_data["relaxed_energy"].get<double>();
297  if(energy < lowest_energy)
298  lowest_energy = energy;
299  t_ss << " -- relaxed_energy = " << source_data["relaxed_energy"].get<double>() << std::endl;
300  }
301  else
302  t_ss << " -- relaxed_energy = unknown" << std::endl;
303  report.push_back(t_ss.str());
304  }
305  Index best_ind(std::distance(datamap.begin(), best_it));
306  if(datamap.size() > 1 || (!self_mapped && datamap.find(&imported_config) == datamap.end())) {
307  relax_log << " " << datamap.size() << " structure" << (datamap.size() > 1 ? "s have" : " has") << " mapped onto Configuration " << imported_config.name()
308  << ", which " << (std::get<Update_impl::new_config>(datamap.cbegin()->second)
309  ? "has been automatically added to your project"
310  : "already existed in your project")
311  << std::endl << std::endl;
312 
313  for(Index i = 0; i < report.size(); i++) {
314  if(i == best_ind) {
315  relax_log << " best ==> " << i + 1 << ") " << report[i] << std::endl;
316  }
317  else {
318  relax_log << " " << i + 1 << ") " << report[i] << std::endl;
319  }
320  }
321 
322  // if imported_config has no properties, copy properties from best config
323  fs::path filepath((best_it->first)->calc_properties_path());
324  jsonParser &parsed_props(std::get<Update_impl::relaxjson>(best_it->second));
325 
326  if(!fs::exists(imported_config.calc_properties_path())
327  && (imported_config.calc_properties().is_null() || imported_config.calc_properties().size() == 0)) {
328  relax_log << " Because no calculation data exists for configuration " << imported_config.name() << ",\n"
329  << " it will acquire the data from " << filepath << "\n";
330 
331  if(!fs::exists(imported_config.get_pos_path()))
332  primclex.configuration(imported_config.name()).write_pos();
333 
334  fs::path import_target = imported_config.calc_properties_path();
335  import_target.remove_filename();
336  if(!fs::exists(import_target))
337  fs::create_directories(import_target);
338 
339  //relax_log << "New file path is " << import_target << "\n";
340  //fs::copy_file(filepath, imported_config.calc_properties_path());
341 
342  //parsed_props["data_timestamp"] = fs::last_write_time(imported_config.calc_properties_path());
343 
344  imported_config.set_calc_properties(parsed_props);
345  imported_config.push_back_source(jsonParser(std::make_pair("data_inferred_from_mapping", (best_it->first)->name())));
346  }
347 
348  // prior data exist -- we won't copy data over, but do some validation to see if data are compatible
349  else {
350  relax_log << " There is already data associated with " << imported_config.name() << " -- it will not be overwritten.\n\n";
351  const jsonParser &extant_props = imported_config.calc_properties();
352  bool data_mismatch = false;
353 
354  auto prop_it = extant_props.cbegin(), prop_end = extant_props.cend();
355  for(; prop_it != prop_end; ++prop_it) {
356  if(!parsed_props.contains(prop_it.name())) {
357  data_mismatch = true;
358  continue;
359  }
360 
361  // Try to ensure that the property is some sort of energy and convertible to scalar
362  if(!prop_it->is_number() || (prop_it.name()).find("energy") == std::string::npos)
363  continue;
364 
365  if(!almost_equal(prop_it->get<double>(), parsed_props[prop_it.name()].get<double>(), 1e-4)) {
366  if(parsed_props[prop_it.name()].get<double>() < prop_it->get<double>()) {
367  relax_log << "\n WARNING: Mapped configuration " << imported_config.name() << " has \n"
368  << " " << prop_it.name() << "=" << prop_it->get<double>() << "\n"
369  << " Which is higher than the relaxed value for mechanically unstable configuration " << (best_it->first)->name() << " which is\n"
370  << " " << prop_it.name() << "=" << parsed_props[prop_it.name()].get<double>() << "\n"
371  << " This suggests that " << imported_config.name() << " may be a metastable minimum. Please investigate further.\n";
372  continue;
373  }
374  data_mismatch = true;
375  }
376  }
377  if(data_mismatch)
378  relax_log << " WARNING: There are calculated properties from \n"
379  << " " << filepath << "\n"
380  << " that do not match existing calculations for configuration " << imported_config.name() << " (they differ by more than 1E-04)\n"
381  << " even though " << (best_it->first)->name() << " was found to relax to " << imported_config.name() << "\n";
382  }
383  relax_log << "\n ----------------------------------------------\n" << std::endl;
384  if(self_mapped && datamap.find(&imported_config) != datamap.end())
385  imported_config.set_calc_properties(std::get<Update_impl::relaxjson>(datamap[&imported_config]));
386  }
387  else if(self_mapped) { //don't report anything, just record the data
388  imported_config.set_calc_properties(std::get<Update_impl::relaxjson>(best_it->second));
389  }
390  else {
391  //relax_log << "Starting configuration " << source_config.name() << " is mechanically unstable.\n";
392  //relax_log << "\n ----------------------------------------------\n" << std::endl;
393  }
394  }
395 
396  args.log << std::endl << "***************************" << std::endl << std::endl;
397  args.log << " DONE: ";
398  if(num_updated == 0)
399  args.log << "No new data were detected." << std::endl << std::endl;
400  else {
401  args.log << "Analyzed new data for " << num_updated << " configurations." << std::endl << std::endl;
402 
403  if(relax_log.str().size() > 0) {
404  args.log << "WARNING: Abnormal relaxations were detected:\n" << std::endl
405  << " *** Final Relaxation Report ***" << std::endl
406  << relax_log.str()
407  << std::endl << std::endl;
408  args.log << "\nIt is recommended that you review these configurations more carefully.\n" << std::endl;
409  }
410 
411  args.log << "Writing to SCEL database..." << std::endl << std::endl;
412  primclex.print_supercells();
413 
414  args.log << "Writing to configuration database..." << std::endl << std::endl;
415  primclex.write_config_list();
416  }
417 
418  return 0;
419  }
420 }
Data structure holding basic CASM command info.
size_type size() const
Returns array size if *this is a JSON array, object size if *this is a JSON object, 1 otherwise.
Definition: jsonParser.cc:430
int update_command(const CommandArgs &args)
Definition: update.cc:62
void write_config_list(std::set< std::string > scel_to_delete={})
Definition: PrimClex.cc:450
void from_json(ClexDescription &desc, const jsonParser &json)
PrimClex * primclex
Definition: settings.cc:101
fs::path calc_properties_path() const
void add_help_suboption()
Add a plain –help suboption.
Definition: Handlers.cc:276
iterator selected_config_begin()
void push_back_source(const jsonParser &source)
bool import_structure(const fs::path &pos_path, std::string &imported_name, jsonParser &relaxation_properties, std::vector< Index > &best_assignment, Eigen::Matrix3d &cart_op) const
imports structure specified by 'pos_path' into primclex() by finding optimal mapping unlike import_st...
void add_configlist_suboption(const fs::path &_default="MASTER")
Add –config suboption (defaults to MASTER)
Definition: Handlers.cc:238
Main CASM namespace.
Definition: complete.cpp:8
const double TOL
const_iterator cend() const
Returns const_iterator to end of JSON object or JSON array.
Definition: jsonParser.cc:480
const fs::path & selection_path() const
Returns the string corresponding to add_config_suboption()
Definition: Handlers.cc:168
T get(Args...args) const
Get data from json, using one of several alternatives.
Definition: jsonParser.hh:729
double tol
const std::vector< std::string > & properties() const
Get current properties.
const Properties & calc_properties() const
bool is_null() const
Check if null type.
Definition: jsonParser.cc:251
const po::options_description & desc()
Get the program options, filled with the initialized values.
Definition: Handlers.cc:160
fs::path get_pos_path() const
Path to various files.
double lattice_weight() const
Definition: update.cc:22
void set_min_va_frac(double _min_va)
EigenIndex Index
For long integer indexing:
po::options_description m_desc
Boost program options. All the derived classes have them, but will fill them up themselves.
Definition: Handlers.hh:126
ProjectSettings & settings()
Definition: PrimClex.hh:116
Iterator find(Iterator begin, Iterator end, const T &value, BinaryCompare q)
Equivalent to std::find(begin, end, value), but with custom comparison.
Definition: algorithm.hh:10
PrimClex is the top-level data structure for a CASM project.
Definition: PrimClex.hh:52
std::tuple< jsonParser, bool, bool > Data
Definition: update.cc:16
T max(const T &A, const T &B)
Definition: CASM_math.hh:32
double max_va_frac() const
Definition: update.cc:34
SimpleJSonSiteStructure< true > simple_json(const BasicStructure< Site > &struc, const std::string &prefix)
Definition: jsonStruc.hh:110
std::string name() const
SCELV_A_B_C_D_E_F/i.
const Configuration & configuration(const std::string &configname) const
access configuration by name (of the form "scellname/[NUMBER]", e.g., ("SCEL1_1_1_1_0_0_0/0") ...
Definition: PrimClex.cc:347
void print_supercells(std::set< std::string > scel_to_delete={}) const
Definition: PrimClex.cc:557
void set_calc_properties(const jsonParser &json)
Read calculation results into the configuration.
void set_max_va_frac(double _max_va)
Eigen::Matrix3d Matrix3d
jsonParser & put_obj()
Puts new empty JSON object.
Definition: jsonParser.hh:276
PrimClex & make_primclex_if_not(const CommandArgs &args, std::unique_ptr< PrimClex > &uniq_primclex)
If !_primclex, construct new PrimClex stored in uniq_primclex, then return reference to existing or c...
void initialize() override
Fill in the options descriptions accordingly.
Definition: update.cc:38
double vol_tolerance() const
Definition: update.cc:26
bool contains(const std::string &name) const
Return true if JSON object contains 'name'.
Definition: jsonParser.cc:500
void error(const std::string &what)
Definition: Log.hh:86
double min_va_frac() const
Definition: update.cc:30
const_iterator cbegin() const
Returns const_iterator to beginning of JSON object or JSON array.
Definition: jsonParser.cc:455
bool almost_equal(const GenericCluster< CoordType > &LHS, const GenericCluster< CoordType > &RHS, double tol)
A Configuration represents the values of all degrees of freedom in a Supercell.
bool import_structure_occupation(const fs::path &pos_path, std::string &imported_name, jsonParser &relaxation_properties, std::vector< Index > &best_assignment, Eigen::Matrix3d &cart_op) const
imports structure specified by 'pos_path' into primclex() by finding optimal mapping and then setting...
#define ERR_NO_PROJ
#define ERR_INVALID_INPUT_FILE
const std::string & name() const