CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
select.cc
Go to the documentation of this file.
7 
8 namespace CASM {
9 
10  template<typename ConfigIterType>
11  void set_selection(const DataFormatterDictionary<Configuration> &dict, ConfigIterType begin, ConfigIterType end, const std::string &criteria, bool mk, Log &err_log) {
12  //boost::trim(criteria);
13 
14  try {
15  if(criteria.size()) {
16  DataFormatter<Configuration> tformat(dict.parse(criteria));
17  for(; begin != end; ++begin) {
18  if(begin.selected() == mk)
19  continue;
20  ValueDataStream<bool> select_stream;
21  if(select_stream.fail()) {
22  err_log << "Warning: Unable to apply criteria \"" << criteria << "\" to configuration " << begin.name() << "\n";
23  continue;
24  }
25 
26  select_stream << tformat(*begin);
27  if(select_stream.value()) {
28  begin.set_selected(mk);
29  }
30  }
31  }
32  else {
33  for(; begin != end; ++begin) {
34  begin.set_selected(mk);
35  }
36  }
37  }
38  catch(std::exception &e) {
39  throw std::runtime_error(std::string("Failure to select using criteria \"") + criteria + "\" for configuration " + begin.name() + "\n Reason: " + e.what());
40  }
41  return;
42  }
43 
44  template<typename ConfigIterType>
45  void set_selection(const DataFormatterDictionary<Configuration> &dict, ConfigIterType begin, ConfigIterType end, const std::string &criteria, Log &err_log) {
46  //boost::trim(criteria);
47  try {
48  if(criteria.size()) {
49  DataFormatter<Configuration> tformat(dict.parse(criteria));
50  for(; begin != end; ++begin) {
51  ValueDataStream<bool> select_stream;
52  if(select_stream.fail()) {
53  err_log << "Warning: Unable to apply criteria \"" << criteria << "\" to configuration " << begin.name() << "\n";
54  continue;
55  }
56  select_stream << tformat(*begin);
57  begin.set_selected(select_stream.value());
58  }
59  }
60  }
61  catch(std::exception &e) {
62  throw std::runtime_error(std::string("Failure to select using criteria \"") + criteria + "\" for configuration " + begin.name() + "\n Reason: " + e.what());
63  }
64 
65  return;
66  }
67 
68  template<bool IsConst>
69  bool write_selection(const DataFormatterDictionary<Configuration> &dict, const ConfigSelection<IsConst> &config_select, bool force, const fs::path &out_path, bool write_json, bool only_selected, Log &err_log) {
70  if(fs::exists(out_path) && !force) {
71  err_log << "File " << out_path << " already exists. Use --force to force overwrite." << std::endl;
72  return ERR_EXISTING_FILE;
73  }
74 
75  if(write_json || out_path.extension() == ".json" || out_path.extension() == ".JSON") {
76  jsonParser json;
77  config_select.to_json(dict, json, only_selected);
78  SafeOfstream sout;
79  sout.open(out_path);
80  json.print(sout.ofstream());
81  sout.close();
82  }
83  else {
84  SafeOfstream sout;
85  sout.open(out_path);
86  config_select.print(dict, sout.ofstream(), only_selected);
87  sout.close();
88  }
89  return 0;
90  }
91 
92  void select_help(const DataFormatterDictionary<Configuration> &_dict, std::ostream &_stream, std::vector<std::string> help_opt) {
93  _stream << "DESCRIPTION" << std::endl
94  << "\n"
95  << " Use '[--set | --set-on | --set-off] [criteria]' for specifying or editing a selection.\n";
96 
97  for(const std::string &str : help_opt) {
98  if(str.empty()) {
99  continue;
100  }
101 
102  if(str[0] == 'o') {
103  _stream << "Available operators for use within selection criteria:" << std::endl;
105  }
106  else if(str[0] == 'p') {
107  _stream << "Available property tags are currently:" << std::endl;
109  }
110  _stream << std::endl;
111  }
112  _stream << std::endl;
113  }
114 
115  namespace Completer {
117 
118  const std::vector<std::string> &SelectOption::criteria_vec() const {
119  return m_criteria_vec;
120  }
121 
126 
127  m_desc.add_options()
128  ("json", "Write JSON output (otherwise CSV, unless output extension is '.json' or '.JSON')")
129  ("subset", "Only write selected configurations to output. Can be used by itself or in conjunction with other options")
130  ("xor", "Performs logical XOR on two configuration selections")
131  ("not", "Performs logical NOT on configuration selection")
132  ("or", "Write configurations selected in any of the input lists. Equivalent to logical OR")
133  ("and", "Write configurations selected in all of the input lists. Equivalent to logical AND")
134  ("set-on", po::value<std::vector<std::string> >(&m_criteria_vec)->multitoken()->zero_tokens(), "Add configurations to selection if they meet specified criteria. Call using 'casm select --set-on [\"criteria\"]'")
135  ("set-off", po::value<std::vector<std::string> >(&m_criteria_vec)->multitoken()->zero_tokens(), "Remove configurations from selection if they meet specified criteria. Call using 'casm select --set-off [\"criteria\"]'")
136  ("set", po::value<std::vector<std::string> >(&m_criteria_vec)->multitoken(), "Create a selection of Configurations that meet specified criteria. Call using 'casm select --set [\"criteria\"]'")
137  ("force,f", "Overwrite output file");
138 
139  return;
140  }
141 
142  }
143 
144  template<bool IsConst>
145  void write_selection_stats(Index Ntot, const ConfigSelection<IsConst> &config_select, Log &log, bool only_selected) {
146 
147  auto Nselected = std::distance(config_select.selected_config_begin(), config_select.selected_config_end());
148  auto Ninclude = only_selected ? Nselected : std::distance(config_select.config_begin(), config_select.config_end());
149 
150  log << "# configurations in this project: " << Ntot << "\n";
151  log << "# configurations included in this list: " << Ninclude << "\n";
152  log << "# configurations selected in this list: " << Nselected << "\n";
153  }
154 
155  template<bool IsConst>
157 
158  auto Nselected = std::distance(config_select.selected_config_begin(), config_select.selected_config_end());
159 
160  log << "# configurations in this project: " << Ntot << "\n";
161  log << "# configurations selected in this list: " << Nselected << "\n";
162  }
163 
164  // ///////////////////////////////////////
165  // 'select' function for casm
166  // (add an 'if-else' statement in casm.cpp to call this)
167 
168  int select_command(const CommandArgs &args) {
169 
170  //casm enum [—supercell min max] [—config supercell ] [—hopconfigs hop.background]
171  //- enumerate supercells and configs and hop local configurations
172 
173  std::vector<std::string> criteria_vec, help_opt_vec;
174  std::vector<fs::path> selection;
175 
176  fs::path out_path;
177  //COORD_TYPE coordtype;
178  po::variables_map vm;
179 
181  // NOTE: multitoken() is used instead of implicit_value() because implicit_value() is broken on some systems -- i.e., braid.cnsi.ucsb.edu
182  // (not sure if it's an issue with a particular shell, or boost version, or something else)
183  Completer::SelectOption select_opt;
184 
185  std::string cmd;
186  std::vector<std::string> allowed_cmd = {"and", "or", "xor", "not", "set-on", "set-off", "set"};
187 
188  try {
189  po::store(po::parse_command_line(args.argc, args.argv, select_opt.desc()), vm); // can throw
190 
191  Index num_cmd(0);
192  for(const std::string &cmd_str : allowed_cmd) {
193  if(vm.count(cmd_str)) {
194  num_cmd++;
195  cmd = cmd_str;
196  }
197  }
198 
199  if(!vm.count("help")) {
200  if(num_cmd > 1) {
201  args.err_log << "Error in 'casm select'. Must use exactly one of --set-on, --set-off, --set, --and, --or, --xor, or --not." << std::endl;
202  return ERR_INVALID_ARG;
203  }
204  else if(vm.count("subset") && vm.count("config") && selection.size() != 1) {
205  args.err_log << "ERROR: 'casm select --subset' expects zero or one list as argument." << std::endl;
206  return ERR_INVALID_ARG;
207  }
208 
209 
210 
211  if(!vm.count("output") && (cmd == "or" || cmd == "and" || cmd == "xor" || cmd == "not")) {
212  args.err_log << "ERROR: 'casm select --" << cmd << "' expects an --output file." << std::endl;
213  return ERR_INVALID_ARG;
214  }
215 
216  }
217 
218  // Start --help option
219  if(vm.count("help")) {
220  args.log << std::endl << select_opt.desc() << std::endl;
221  }
222 
223  po::notify(vm); // throws on error, so do after help in case of problems
224 
225  criteria_vec = select_opt.criteria_vec();
226  help_opt_vec = select_opt.help_opt_vec();
227  selection = select_opt.selection_paths();
228  out_path = select_opt.output_path();
229 
230  // Finish --help option
231  if(vm.count("help")) {
232  const fs::path &root = args.root;
233  if(root.empty()) {
234  auto dict = make_dictionary<Configuration>();
235  select_help(dict, args.log, help_opt_vec);
236  }
237  else {
238  // set status_stream: where query settings and PrimClex initialization messages are sent
239  Log &status_log = (out_path.string() == "STDOUT") ? args.err_log : args.log;
240 
241  // If '_primclex', use that, else construct PrimClex in 'uniq_primclex'
242  // Then whichever exists, store reference in 'primclex'
243  std::unique_ptr<PrimClex> uniq_primclex;
244  if(out_path.string() == "STDOUT") {
245  args.log.set_verbosity(0);
246  }
247  PrimClex &primclex = make_primclex_if_not(args, uniq_primclex, status_log);
248 
249  select_help(primclex.settings().query_handler<Configuration>().dict(), args.log, help_opt_vec);
250  }
251  return 0;
252  }
253 
254  if((vm.count("set-on") || vm.count("set-off") || vm.count("set")) && vm.count("config") && selection.size() != 1) {
255  std::string cmd = "--set-on";
256  if(vm.count("set-off")) {
257  cmd = "--set-off";
258  }
259  if(vm.count("set")) {
260  cmd = "--set";
261  }
262 
263  args.err_log << "Error in 'casm select " << cmd << "'. " << selection.size() << " config selections were specified, but no more than one selection is allowed (MASTER list is used if no other is specified)." << std::endl;
264  return ERR_INVALID_ARG;
265  }
266 
267  }
268  catch(po::error &e) {
269  args.err_log << select_opt.desc() << std::endl;
270  args.err_log << "ERROR: " << e.what() << std::endl << std::endl;
271  return ERR_INVALID_ARG;
272  }
273  catch(std::exception &e) {
274  args.err_log << select_opt.desc() << std::endl;
275  args.err_log << "ERROR: " << e.what() << std::endl << std::endl;
276  return ERR_UNKNOWN;
277  }
278 
279 
280  if(vm.count("output") && out_path != "MASTER") {
281 
282  //check now so we can exit early with an obvious error
283  if(fs::exists(out_path) && !vm.count("force")) {
284  args.err_log << "ERROR: File " << out_path << " already exists. Use --force to force overwrite." << std::endl;
285  return ERR_EXISTING_FILE;
286  }
287  }
288 
289  bool only_selected(false);
290  if(selection.empty()) {
291  only_selected = true;
292  selection.push_back("MASTER");
293  }
294 
295  const fs::path &root = args.root;
296  if(root.empty()) {
297  args.err_log.error("No casm project found");
298  args.err_log << std::endl;
299  return ERR_NO_PROJ;
300  }
301 
302  // If 'args.primclex', use that, else construct PrimClex in 'uniq_primclex'
303  // Then whichever exists, store reference in 'primclex'
304  std::unique_ptr<PrimClex> uniq_primclex;
305  PrimClex &primclex = make_primclex_if_not(args, uniq_primclex);
306  ProjectSettings &set = primclex.settings();
307 
308  // count total number of configurations in this project one time
309  Index Ntot = std::distance(primclex.config_begin(), primclex.config_end());
310 
311 
312  // load initial selection into config_select -- this is also the selection that will be printed at end
313  ConfigSelection<false> config_select(primclex, selection[0]);
314 
315  std::stringstream ss;
316  ss << selection[0];
317 
318  std::vector<ConstConfigSelection> tselect(selection.size() - 1);
319  for(int i = 1; i < selection.size(); ++i) {
320  ss << ", " << selection[i];
321  tselect.push_back(ConstConfigSelection(primclex, selection[i]));
322  }
323 
324  set.query_handler<Configuration>().set_selected(config_select);
325 
326  args.log.custom("Input config list", selection[0].string());
327  write_selection_stats(Ntot, config_select, args.log, false);
328  args.log << std::endl;
329 
330  for(int i = 1; i < selection.size(); ++i) {
331  args.log.custom("Input config list", selection[i].string());
332  write_selection_stats(Ntot, tselect[i], args.log, false);
333  args.log << std::endl;
334  }
335 
336  if(vm.count("set-on") || vm.count("set-off") || vm.count("set")) {
337  bool select_switch = vm.count("set-on");
338  std::string criteria;
339  if(criteria_vec.size() == 1) {
340  criteria = criteria_vec[0];
341  }
342  else if(criteria_vec.size() > 1) {
343  args.err_log << "ERROR: Selection criteria must be a single string. You provided " << criteria_vec.size() << " strings:\n";
344  for(const std::string &str : criteria_vec)
345  args.err_log << " - " << str << "\n";
346  return ERR_INVALID_ARG;
347  }
348 
349  if(vm.count("set-on")) {
350  args.log.custom("set-on", criteria);
351  }
352  if(vm.count("set-off")) {
353  args.log.custom("set-off", criteria);
354  }
355  if(vm.count("set")) {
356  args.log.custom("set", criteria);
357  }
358  args.log.begin_lap();
359 
360  try {
361  if(vm.count("set"))
362  set_selection(set.query_handler<Configuration>().dict(), config_select.config_begin(), config_select.config_end(), criteria, args.err_log);
363  else
364  set_selection(set.query_handler<Configuration>().dict(), config_select.config_begin(), config_select.config_end(), criteria, select_switch, args.err_log);
365  }
366  catch(std::exception &e) {
367  args.err_log << "ERROR: " << e.what() << "\n";
368  return ERR_INVALID_ARG;
369  }
370 
371  args.log << "selection time: " << args.log.lap_time() << " (s)\n" << std::endl;
372  }
373 
374  if(vm.count("subset")) {
375  args.log.custom("subset");
376  args.log.begin_lap();
377  args.log << "selection time: " << args.log.lap_time() << " (s)\n" << std::endl;
378  only_selected = true;
379  }
380 
381  if(vm.count("not")) {
382  if(selection.size() != 1) {
383  args.err_log << "ERROR: Option --not requires exactly 1 selection as argument\n";
384  return ERR_INVALID_ARG;
385  }
386 
387  args.log.custom(std::string("not ") + selection[0].string());
388  args.log.begin_lap();
389 
390  // loop through other lists, keeping only configurations selected in the other lists
391  auto it = config_select.config_begin();
392  for(; it != config_select.config_end(); ++it) {
393  it.set_selected(!it.selected());
394  }
395  args.log << "selection time: " << args.log.lap_time() << " (s)\n" << std::endl;
396  }
397 
398  if(vm.count("or")) {
399 
400  args.log.custom(std::string("or(") + ss.str() + ")");
401  args.log.begin_lap();
402 
403  // loop through other lists, inserting all selected configurations
404  for(int i = 1; i < selection.size(); i++) {
405  for(auto it = tselect[i].selected_config_cbegin(); it != tselect[i].selected_config_cend(); ++it) {
406  config_select.set_selected(it.name(), true);
407  }
408  }
409  args.log << "selection time: " << args.log.lap_time() << " (s)\n" << std::endl;
410  only_selected = true;
411  }
412 
413  if(vm.count("and")) {
414  args.log.custom(std::string("and(") + ss.str() + ")");
415  args.log.begin_lap();
416 
417  // loop through other lists, keeping only configurations selected in the other lists
418  for(int i = 1; i < selection.size(); i++) {
419  auto it = config_select.selected_config_begin();
420  for(; it != config_select.selected_config_end(); ++it) {
421  it.set_selected(tselect[i].selected(it.name()));
422  }
423  }
424 
425  args.log << "selection time: " << args.log.lap_time() << " (s)\n" << std::endl;
426  only_selected = true;
427  }
428 
429  if(vm.count("xor")) {
430  if(selection.size() != 2) {
431  args.err_log << "ERROR: Option --xor requires exactly 2 selections as argument\n";
432  return 1;
433  }
434 
435  args.log.custom(selection[0].string() + " xor " + selection[1].string());
436  args.log.begin_lap();
437 
438  for(auto it = tselect[1].selected_config_begin(); it != tselect[1].selected_config_end(); ++it) {
439  //If selected in both lists, deselect it
440  if(config_select.selected(it.name())) {
441  config_select.set_selected(it.name(), false);
442  }
443  else { // If only selected in tselect, add it to config_select
444  config_select.set_selected(it.name(), true);
445  }
446 
447  }
448  args.log << "selection time: " << args.log.lap_time() << " (s)\n" << std::endl;
449  only_selected = true;
450  }
451 
453  if(!vm.count("output") || out_path == "MASTER") {
454  auto pc_it = primclex.config_begin(), pc_end = primclex.config_end();
455  for(; pc_it != pc_end; ++pc_it) {
456  pc_it->set_selected(false);
457  }
458 
459  auto it = config_select.selected_config_begin(), it_end = config_select.selected_config_end();
460  for(; it != it_end; ++it) {
461  it->set_selected(true);
462  }
463 
464  args.log.write("Master config_list");
465  primclex.write_config_list();
466  args.log << "wrote: MASTER\n" << std::endl;
467 
468  args.log.custom("Master config list");
469  write_master_selection_stats(Ntot, config_select, args.log);
470 
471  args.log << std::endl;
472  return 0;
473  }
474  else {
475 
476  args.log.write("Selection");
477  int ret_code = write_selection(set.query_handler<Configuration>().dict(), config_select, vm.count("force"), out_path, vm.count("json"), only_selected, args.err_log);
478  args.log << "write: " << out_path << "\n" << std::endl;
479 
480  args.log.custom("Output config list", out_path.string());
481  write_selection_stats(Ntot, config_select, args.log, only_selected);
482 
483  args.log << std::endl;
484  return ret_code;
485  }
486 
487  return 0;
488  };
489 
490 }
Data structure holding basic CASM command info.
void close()
Closes stream, and if not a failed write, removes "file" and renames "file.tmp" to "file"...
Definition: SafeOfstream.hh:81
void add_output_suboption()
Add a –output suboption. Expects to allow "STDOUT" to print to screen.
Definition: Handlers.cc:326
void write_config_list(std::set< std::string > scel_to_delete={})
Definition: PrimClex.cc:450
#define ERR_UNKNOWN
#define ERR_INVALID_ARG
void write(const std::string &what)
Definition: Log.hh:66
jsonParser & to_json(const DataFormatterDictionary< Configuration > &_dict, jsonParser &_json, bool only_selected=false) const
void print(const DataFormatterDictionary< Configuration > &_dict, std::ostream &_out, bool only_selected=false) const
PrimClex * primclex
Definition: settings.cc:101
int select_command(const CommandArgs &args)
Definition: select.cc:168
iterator selected_config_begin()
void begin_lap()
Definition: Log.cc:28
void add_general_help_suboption()
Add a smart –help suboption that takes "properties" or "operators".
Definition: Handlers.cc:283
Main CASM namespace.
Definition: complete.cpp:8
Write to a temporary file to ensure a good write, then rename.
Definition: SafeOfstream.hh:28
Log & log
Definition: settings.cc:105
fs::ofstream & ofstream()
Access underlying stream.
Definition: SafeOfstream.hh:76
void set_selected(bool _select)
double lap_time() const
Definition: Log.cc:32
config_iterator config_begin()
Configuration iterator: begin.
Definition: PrimClex.cc:371
config_iterator config_end()
Configuration iterator: end.
Definition: PrimClex.cc:379
ProjectSettings & set
Definition: settings.cc:103
void print(std::ostream &stream, unsigned int indent=2, unsigned int prec=12) const
Print json to stream.
Definition: jsonParser.cc:185
void open(fs::path name, std::string tmp_ext="tmp")
Opens "file.tmp" for writing, with intended final target "file".
Definition: SafeOfstream.hh:62
void add_configlists_suboption(const fs::path &_default="MASTER")
Add –configs suboption (defaults to MASTER)
Definition: Handlers.cc:248
void custom(const std::string &what)
Definition: Log.hh:96
void print_help(std::ostream &_stream, typename BaseDatumFormatter< DataObject >::FormatterType ftype, int width=60, int separation=8) const
Generates formatted help using the 'name' and 'description' of all contained BaseDatumFormatter.
const po::options_description & desc()
Get the program options, filled with the initialized values.
Definition: Handlers.cc:160
const std::vector< std::string > & criteria_vec() const
Definition: select.cc:118
bool write_selection(const DataFormatterDictionary< Configuration > &dict, const ConfigSelection< IsConst > &config_select, bool force, const fs::path &out_path, bool write_json, bool only_selected, Log &err_log)
Definition: select.cc:69
void select_help(const DataFormatterDictionary< Configuration > &_dict, std::ostream &_stream, std::vector< std::string > help_opt)
Definition: select.cc:92
Read/modify settings of an already existing CASM project.
const std::vector< std::string > & help_opt_vec() const
Returns the list of strings corresponding to add_general_help_suboption()
Definition: Handlers.cc:196
EigenIndex Index
For long integer indexing:
QueryHandler< DataObject > & query_handler()
std::vector< std::string > m_criteria_vec
Definition: Handlers.hh:608
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
void write_selection_stats(Index Ntot, const ConfigSelection< IsConst > &config_select, Log &log, bool only_selected)
Definition: select.cc:145
PrimClex is the top-level data structure for a CASM project.
Definition: PrimClex.hh:52
const T & value() const
Definition: DataStream.hh:296
Abstract base class from which all other DatumFormatter classes inherit.
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...
Extract data from objects of 'DataObject' class.
void set_verbosity(int _verbosity)
Definition: Log.cc:42
void write_master_selection_stats(Index Ntot, const ConfigSelection< IsConst > &config_select, Log &log)
Definition: select.cc:156
bool fail() const
Definition: DataStream.hh:73
Log & err_log
Definition: settings.cc:106
ConfigSelection< true > ConstConfigSelection
void error(const std::string &what)
Definition: Log.hh:86
void set_selection(const DataFormatterDictionary< Configuration > &dict, ConfigIterType begin, ConfigIterType end, const std::string &criteria, bool mk, Log &err_log)
Definition: select.cc:11
const fs::path output_path() const
Returns the path corresponding to add_output_suboption()
Definition: Handlers.cc:188
Definition: Log.hh:9
#define ERR_EXISTING_FILE
void initialize() override
Fill in the options descriptions accordingly.
Definition: select.cc:122
DataFormatter< DataObject > parse(const std::string &input) const
Use the vector of strings to build a DataFormatter
Parsing dictionary for constructing a DataFormatter object.
const std::vector< fs::path > & selection_paths() const
Returns the string corresponding to add_config_suboption()
Definition: Handlers.cc:172
A Configuration represents the values of all degrees of freedom in a Supercell.
#define ERR_NO_PROJ