CASM  1.1.0
A Clusters Approach to Statistical Mechanics
ClusterSpecs_json_io.cc
Go to the documentation of this file.
2 
10 
11 namespace CASM {
12 
13 namespace ClusterSpecs_json_io_impl {
14 
29 template <typename SpecsType>
30 std::vector<double> parse_orbit_branch_specs_attr(
31  InputParser<SpecsType> &parser, const std::string &attrname) {
32  auto specs_it = parser.self.find("orbit_branch_specs");
33  if (specs_it == parser.self.end() || specs_it->is_null()) {
34  return std::vector<double>({0.});
35  }
36  std::vector<double> result({0.});
37 
38  if (!specs_it->is_obj()) {
39  fs::path path{"orbit_branch_specs"};
40  std::stringstream msg;
41  msg << "Expected a JSON object";
42  parser.insert_error("orbit_branch_specs", msg.str());
43  return result;
44  }
45 
46  auto update_result = [&](int branch, double value) {
47  while (branch >= result.size()) {
48  result.push_back(0.0);
49  }
50  result[branch] = value;
51  };
52 
53  for (auto it = specs_it->begin(); it != specs_it->end(); ++it) {
54  double value = 0.; // default is 0. if no attrname found
55  if (!it->is_obj()) {
56  fs::path path{"orbit_branch_specs"};
57  std::stringstream msg;
58  msg << "Expected JSON object containing '" << attrname
59  << "' (double, default=0.).";
60  parser.insert_error(path / it.name(), msg.str());
61  continue;
62  }
63  auto attr_it = it->find(attrname);
64  if (attr_it != it->end()) {
65  value = attr_it->template get<double>();
66  }
67  update_result(std::stoi(it.name()), value);
68  }
69  return result;
70 }
71 
72 void write_group_indices(const SymGroup &grp, jsonParser &json) {
73  json.put_array();
74  for (const auto &op : grp) {
75  json.push_back(op.index());
76  }
77 }
78 
79 void write_group_indices(std::vector<PermuteIterator> const &grp,
80  jsonParser &json) {
81  std::set<Index> prim_factor_group_indices;
82  for (auto const &permute_it : grp) {
83  prim_factor_group_indices.insert(permute_it.prim_factor_group_index());
84  }
85 
86  json = prim_factor_group_indices;
87 }
88 
89 std::vector<SymOp>::const_iterator find_by_master_group_index(
90  const SymOp &op, const SymGroup &super_group) {
91  auto it = super_group.begin();
92  auto end = super_group.end();
93  for (; it != end; ++it) {
94  if (it->index() == op.index()) {
95  return it;
96  }
97  }
98  return end;
99 }
100 
101 std::unique_ptr<SymGroup> group_from_indices(const SymGroup &super_group,
102  const std::set<Index> &indices) {
103  auto result = notstd::make_unique<SymGroup>();
104  result->set_lattice(super_group.lattice());
105  for (SymOp const &op : super_group) {
106  if (indices.count(op.index())) {
107  result->push_back(op);
108  }
109  }
110  result->sort();
111  if ((*result)[0].index() != 0) {
112  throw std::runtime_error(
113  "Error in group_from_indices: First element is not identity.");
114  }
115  if (!result->is_group(super_group.lattice().tol())) {
116  throw std::runtime_error("Error in local_group_from_indices: Not a group.");
117  }
118  return result;
119 }
120 
121 template <typename SymCompareType>
122 std::unique_ptr<SymGroup> local_group_from_indices(
123  const SymGroup &super_group, const std::set<Index> &indices,
124  const IntegralCluster &phenomenal,
125  const SymCompareType &sym_compare) { // TODO: check this?
126 
127  IntegralCluster e{sym_compare.prepare(phenomenal)};
128  auto result = notstd::make_unique<SymGroup>();
129  result->set_lattice(super_group.lattice());
130  for (SymOp const &op : super_group) {
131  if (indices.count(op.index())) {
132  // get translation & check that phenomenal cluster sites remain invariant
133  // - note: this is a minimum requirement check that provides the
134  // translation, it does not
135  // guarantee the user input indices are correct for a particular context
136  // (i.e. DiffTrans)
137  if (sym_compare.equal(
138  e, sym_compare.prepare(sym_compare.copy_apply(op, e)))) {
139  result->push_back(sym_compare.spatial_transform() * op);
140  } else {
141  throw std::runtime_error(
142  "Error in local_group_from_indices: Phenomenal cluster sites are "
143  "not invariant.");
144  }
145  }
146  }
147  result->sort();
148  if ((*result)[0].index() != 0) {
149  throw std::runtime_error(
150  "Error in local_group_from_indices: First element is not identity.");
151  }
152  if (!result->is_group(sym_compare.tol())) {
153  throw std::runtime_error("Error in local_group_from_indices: Not a group.");
154  }
155  return result;
156 }
157 
158 std::unique_ptr<SymGroup> parse_generating_group(
160  const std::shared_ptr<const Structure> &shared_prim,
161  const SymGroup &super_group) {
162  auto generating_group_indices =
163  parser.optional<std::set<Index>>("generating_group");
164  if (generating_group_indices) {
165  try {
166  return group_from_indices(super_group, *generating_group_indices);
167  } catch (std::exception &e) {
168  parser.error.insert(std::string("Error parsing generating_group:") +
169  e.what());
170  return std::unique_ptr<SymGroup>{};
171  }
172  } else {
173  return notstd::clone(super_group);
174  }
175 }
176 
177 template <typename SymCompareType>
178 std::unique_ptr<SymGroup> parse_local_generating_group(
180  const std::shared_ptr<const Structure> &shared_prim,
181  const IntegralCluster &phenomenal, const SymGroup &super_group,
182  const SymCompareType &sym_compare) {
183  std::set<Index> generating_group_indices;
184  parser.require(generating_group_indices, "generating_group");
185  if (!parser.valid()) {
186  return std::unique_ptr<SymGroup>();
187  }
188  try {
189  return local_group_from_indices(super_group, generating_group_indices,
190  phenomenal, sym_compare);
191  } catch (std::exception &e) {
192  parser.error.insert(std::string("Error parsing generating_group:") +
193  e.what());
194  }
195  return std::unique_ptr<SymGroup>();
196 }
197 } // namespace ClusterSpecs_json_io_impl
198 
222  const std::shared_ptr<const Structure> &shared_prim,
223  const SymGroup &super_group) {
224  using namespace ClusterSpecs_json_io_impl;
225 
226  // parse generating group
227  auto generating_group_ptr =
228  parse_generating_group(parser, shared_prim, super_group);
229  if (!generating_group_ptr) {
230  generating_group_ptr = notstd::clone(super_group);
231  }
232 
233  // parse max length
234  auto max_length = parse_orbit_branch_specs_attr(parser, "max_length");
235 
236  // parse custom generators ("orbit_specs")
237  std::vector<IntegralClusterOrbitGenerator> default_custom_generators{};
238  auto custom_generators_parser =
239  parser.subparse_else<std::vector<IntegralClusterOrbitGenerator>>(
240  "orbit_specs", default_custom_generators, *shared_prim);
241 
242  if (!parser.valid()) {
243  return;
244  }
245  parser.value = notstd::make_unique<PeriodicMaxLengthClusterSpecs>(
246  shared_prim, *generating_group_ptr, dof_sites_filter(), max_length,
247  *custom_generators_parser->value);
248 }
249 
281  const std::shared_ptr<const Structure> &shared_prim,
282  const SymGroup &super_group) {
283  using namespace ClusterSpecs_json_io_impl;
284 
285  // parse phenomenal
286  auto phenomenal_subparser_ptr =
287  parser.subparse<IntegralCluster>("phenomenal", *shared_prim);
288  if (!phenomenal_subparser_ptr->valid()) {
289  return;
290  }
291  IntegralCluster phenomenal = *phenomenal_subparser_ptr->value;
292 
293  // parse generating group
294  typedef PrimPeriodicSymCompare<IntegralCluster> SymCompareType;
295  SymCompareType sym_compare{shared_prim, shared_prim->lattice().tol()};
296  auto generating_group_ptr = parse_local_generating_group(
297  parser, shared_prim, phenomenal, super_group, sym_compare);
298 
299  // parse max_length and cutoff_radius
300  auto max_length = parse_orbit_branch_specs_attr(parser, "max_length");
301  auto cutoff_radius = parse_orbit_branch_specs_attr(parser, "cutoff_radius");
302 
303  // parse custom generators ("orbit_specs")
304  std::vector<IntegralClusterOrbitGenerator> default_custom_generators{};
305  auto custom_generators_parser =
306  parser.subparse_else<std::vector<IntegralClusterOrbitGenerator>>(
307  "orbit_specs", default_custom_generators, *shared_prim);
308 
309  // TODO: include option in JSON?
310  bool include_phenomenal_sites = false;
311 
312  if (!parser.valid()) {
313  return;
314  }
315  parser.value = notstd::make_unique<LocalMaxLengthClusterSpecs>(
316  shared_prim, *generating_group_ptr, phenomenal, dof_sites_filter(),
317  max_length, cutoff_radius, include_phenomenal_sites,
318  *custom_generators_parser->value);
319 }
320 
328  const std::shared_ptr<const Structure> &shared_prim) {
329  parse(parser, shared_prim, shared_prim->factor_group());
330 }
331 
514  const std::shared_ptr<const Structure> &shared_prim,
515  const SymGroup &super_group) {
516  std::string method;
517  parser.require(method, "method");
518  if (method.empty()) {
519  return;
520  }
521 
522  // ** this could be a dictionary lookup **
523  if (method == "periodic_max_length") {
524  auto subparser = parser.subparse<PeriodicMaxLengthClusterSpecs>(
525  "params", shared_prim, super_group);
526  if (subparser->value) {
527  parser.value = std::move(subparser->value);
528  }
529  } else if (method == "local_max_length") {
530  auto subparser = parser.subparse<LocalMaxLengthClusterSpecs>(
531  "params", shared_prim, super_group);
532  if (subparser->value) {
533  parser.value = std::move(subparser->value);
534  }
535  } else {
536  parser.error.insert("Error: unknown cluster_specs method '" + method +
537  "'.");
538  }
539 }
540 
541 namespace ClusterSpecs_json_io_impl {
542 
543 jsonParser &orbit_branch_specs_attr_to_json(std::vector<double> attr,
544  const std::string &attrname,
545  jsonParser &json) {
546  jsonParser &j = json["orbit_branch_specs"];
547  for (int b = 0; b < attr.size(); ++b) {
548  j[std::to_string(b)][attrname] = attr[b];
549  }
550  return json;
551 }
552 } // namespace ClusterSpecs_json_io_impl
553 
555  jsonParser &json) {
556  json["method"] = cspecs.name();
557  jsonParser &json_params = json["params"];
558 
559  using namespace ClusterSpecs_json_io_impl;
560  orbit_branch_specs_attr_to_json(cspecs.max_length, "max_length", json_params);
561  write_group_indices(cspecs.generating_group, json_params["generating_group"]);
562  if (cspecs.custom_generators.size()) {
563  json_params["orbit_specs"] = cspecs.custom_generators;
564  }
565  // currently fixed: site_filter=dof_sites_filter()
566  return json;
567 }
568 
571  jsonParser &json) {
572  json["method"] = cspecs.name();
573  jsonParser &json_params = json["params"];
574 
575  // select based on method
576  using namespace ClusterSpecs_json_io_impl;
577  orbit_branch_specs_attr_to_json(cspecs.max_length, "max_length", json_params);
578  orbit_branch_specs_attr_to_json(cspecs.cutoff_radius, "cutoff_radius",
579  json_params);
580  write_group_indices(cspecs.generating_group, json_params["generating_group"]);
581  to_json(cspecs.phenomenal, json_params["phenomenal"]);
582  if (cspecs.custom_generators.size()) {
583  json_params["orbit_specs"] = cspecs.custom_generators;
584  }
585  // currently fixed: site_filter=dof_sites_filter()
586  return json;
587 }
588 
591 jsonParser &to_json(const ClusterSpecs &cspecs, jsonParser &json) {
593  return to_json(static_cast<const PeriodicMaxLengthClusterSpecs &>(cspecs),
594  json);
595  } else if (cspecs.name() == LocalMaxLengthClusterSpecs::method_name) {
596  return to_json(static_cast<const LocalMaxLengthClusterSpecs &>(cspecs),
597  json);
598  } else {
599  throw std::runtime_error(
600  std::string("Error converting ClusterSpecs to JSON:") +
601  " cannot convert method: '" + cspecs.name() + "'");
602  }
603 }
604 
605 } // namespace CASM
std::shared_ptr< Structure const > shared_prim
std::string name() const
This is the orbit generation method name.
Definition: ClusterSpecs.cc:14
std::unique_ptr< RequiredType > optional(fs::path option, Args &&... args)
std::shared_ptr< InputParser< RequiredType > > subparse_else(fs::path option, const RequiredType &_default, Args &&... args)
std::unique_ptr< RequiredType > require(fs::path option, Args &&... args)
std::shared_ptr< InputParser< RequiredType > > subparse(fs::path option, Args &&... args)
std::unique_ptr< T > value
Definition: InputParser.hh:234
Parameters most commonly used for local orbit generation.
static std::string const method_name
std::vector< double > max_length
std::vector< IntegralClusterOrbitGenerator > custom_generators
Specifies particular clusters that should be used to generate orbits.
IntegralCluster phenomenal
Phenomenal cluster, used to find local neighborhood.
std::vector< double > cutoff_radius
static std::string const method_name
std::vector< double > max_length
std::vector< IntegralClusterOrbitGenerator > custom_generators
Specifies particular clusters that should be used to generate orbits.
SymGroup is a collection of symmetry operations that satisfy the group property The symmetry operatio...
Definition: SymGroup.hh:42
const Lattice & lattice() const
Lattice used for periodic comparisons (for instance, to generate multiplcation table)
Definition: SymGroup.cc:950
SymOp is the Coordinate representation of a symmetry operation it keeps fraction (FRAC) and Cartesian...
Definition: SymOp.hh:28
iterator end()
Returns iterator to end of JSON object or JSON array.
Definition: jsonParser.cc:520
iterator find(const std::string &name)
Return iterator to JSON object value with 'name'.
Definition: jsonParser.cc:543
jsonParser & put_array()
Puts new empty JSON array.
Definition: jsonParser.hh:362
double tol() const
Definition: Lattice.hh:195
std::string to_string(ENUM val)
Return string representation of enum class.
Definition: io_traits.hh:172
jsonParser & push_back(const T &value, Args &&... args)
Definition: jsonParser.hh:684
std::unique_ptr< SymGroup > local_group_from_indices(const SymGroup &super_group, const std::set< Index > &indices, const IntegralCluster &phenomenal, const SymCompareType &sym_compare)
std::unique_ptr< SymGroup > group_from_indices(const SymGroup &super_group, const std::set< Index > &indices)
void write_group_indices(const SymGroup &grp, jsonParser &json)
std::vector< SymOp >::const_iterator find_by_master_group_index(const SymOp &op, const SymGroup &super_group)
std::vector< double > parse_orbit_branch_specs_attr(InputParser< SpecsType > &parser, const std::string &attrname)
std::unique_ptr< SymGroup > parse_generating_group(InputParser< PeriodicMaxLengthClusterSpecs > &parser, const std::shared_ptr< const Structure > &shared_prim, const SymGroup &super_group)
jsonParser & orbit_branch_specs_attr_to_json(std::vector< double > attr, const std::string &attrname, jsonParser &json)
std::unique_ptr< SymGroup > parse_local_generating_group(InputParser< LocalMaxLengthClusterSpecs > &parser, const std::shared_ptr< const Structure > &shared_prim, const IntegralCluster &phenomenal, const SymGroup &super_group, const SymCompareType &sym_compare)
Main CASM namespace.
Definition: APICommand.hh:8
jsonParser & to_json(const ClexDescription &desc, jsonParser &json)
SiteFilterFunction dof_sites_filter(const std::vector< DoFKey > &dofs={})
Generate clusters using Site with specified DoF.
void parse(InputParser< ConfigEnumOptions > &parser, std::string method_name, PrimClex const &primclex, DataFormatterDictionary< Configuration > const &dict)
std::unique_ptr< T > clone(const T &obj)
bool valid() const
Return true if this and and all subparsers are valid.
Definition: InputParser.cc:56
jsonParser const & self
Definition: InputParser.hh:81
void insert_error(fs::path option, std::string message)
Insert a subparser at location option with a single error message
Definition: InputParser.cc:97
std::set< std::string > error
Definition: Validator.hh:11