CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
CompositionConverter.cc
Go to the documentation of this file.
2 
4 
5 // currently still relies on this for getting standard composition axes
8 #include "casm/misc/algorithm.hh"
9 
10 namespace CASM {
11 
19  return m_to_x.rows();
20  }
21 
24  return std::string(1, (char)(i + (int) 'a'));
25  }
26 
27 
29  std::vector<std::string> CompositionConverter::components() const {
30  return m_components;
31  }
32 
38  return m_origin;
39  }
40 
46  return m_end_members.col(i);
47  }
48 
51  return m_to_x;
52  }
53 
56  return m_to_n;
57  }
58 
64  return m_to_x * (n - m_origin);
65  }
66 
72  return m_to_x * dn;
73  }
74 
80  return m_origin + m_to_n * x;
81  }
82 
88  return m_to_n * dx;
89  }
90 
93  return m_to_n.transpose() * chem_pot;
94  }
95 
97  std::string CompositionConverter::mol_formula() const {
98 
99  // n = origin + m_to_n * x
100 
101  std::stringstream tstr;
102 
103  // for each molecule:
104  for(int i = 0; i < m_components.size(); i++) {
105 
106  bool first_char = true;
107 
108  // print mol name 'A('
109  tstr << m_components[i] << "(";
110 
111  // constant term from origin
112  // print 'x' if x != 0
113  if(!almost_zero(m_origin(i))) {
114  first_char = false;
115  tstr << m_origin(i);
116  }
117 
118  // terms from m_to_n columns
119  for(int j = 0; j < m_to_n.cols(); j++) {
120 
121  // print nothing if x == 0
122  if(almost_zero(m_to_n(i, j))) {
123  continue;
124  }
125 
126  // print '+' if x>0 mid-expression
127  if(!first_char && m_to_n(i, j) > 0)
128  tstr << '+';
129  // print '-' if x<0
130  else if(m_to_n(i, j) < 0)
131  tstr << '-';
132  // print absolute value of x if |x|!=1
133  if(!almost_equal(std::abs(m_to_n(i, j)), 1.0))
134  tstr << std::abs(m_to_n(i, j));
135  //print variable ('a','b',etc...)
136  tstr << comp_var(j);
137 
138  first_char = false;
139  }
140 
141  // close ')'
142  tstr << ")";
143  }
144 
145  return tstr.str();
146 
147  }
148 
151 
152  // x_i = m_to_x*(n - origin) = m_to_x*n - m_to_x*origin
153 
154  std::stringstream tstr;
155 
157 
158  // for each independent composition:
159  for(int i = 0; i < independent_compositions(); i++) {
160 
161  bool first_char = true;
162 
163  // print mol name 'a('
164  tstr << comp_var(i) << "(";
165 
166  // constant term from origin
167  // print 'x' if x != 0
168  if(!almost_zero(v(i))) {
169  first_char = false;
170  tstr << v(i);
171  }
172 
173  // terms from m_to_x columns
174  for(int j = 0; j < m_to_x.cols(); j++) {
175 
176  double coeff = m_to_x(i, j);
177 
178  // print nothing if n == 0
179  if(almost_zero(coeff)) {
180  continue;
181  }
182 
183  // print 'A' or '+A' if n == 1
184  if(almost_zero(coeff - 1)) {
185  if(!first_char) {
186  tstr << '+';
187  }
188  tstr << m_components[j];
189  }
190 
191  // print '-A' if n == -1
192  else if(almost_zero(coeff + 1)) {
193  tstr << '-' << m_components[j];
194  }
195 
196  // print 'nA' or '+nA' if n > 0
197  else if(coeff > 0) {
198  if(!first_char) {
199  tstr << '+';
200  }
201  tstr << coeff << m_components[j];
202  }
203 
204  // print '-nA' if n < 0
205  else {
206  tstr << coeff << m_components[j];
207  }
208 
209  first_char = false;
210  }
211 
212  // close ')'
213  tstr << ")";
214  }
215 
216  return tstr.str();
217 
218  }
219 
222 
223  // comp(i) = m_to_x(i,j)*(comp_n(j) - m_origin(j)) + ...
224 
225  std::stringstream ss;
226 
227  auto comp_x_str = [&]() {
228  return "comp(" + comp_var(i) + ")";
229  };
230 
231  auto comp_n_str = [&](int j) {
232  return "comp_n(" + m_components[j] + ")";
233  };
234 
235  auto delta_str = [&](int j) {
236  std::stringstream tss;
237  // print '(comp_n(J) - m_origin(j))' if m_origin(j) != 0
238  if(!almost_zero(m_origin(j))) {
239  tss << "(" << comp_n_str(j) << " - " << m_origin(j) << ")";
240  }
241  // print 'comp_n(J)'
242  else {
243  tss << comp_n_str(j);
244  }
245  return tss.str();
246  };
247 
248  ss << comp_x_str() << " = ";
249  bool first_term = true;
250  for(int j = 0; j < m_to_x.cols(); ++j) {
251 
252  double coeff = m_to_x(i, j);
253 
254  // print nothing if coeff == 0
255  if(almost_zero(coeff)) {
256  continue;
257  }
258 
259  // if coeff < 0
260  if(coeff < 0) {
261  if(!first_term) {
262  ss << " - " << -coeff << "*" << delta_str(j);
263  }
264  else {
265  ss << coeff << "*" << delta_str(j);
266  }
267  }
268 
269  // if coeff > 0
270  else {
271  if(!first_term) {
272  ss << " + " << coeff << "*" << delta_str(j);
273  }
274  else {
275  ss << coeff << "*" << delta_str(j);
276  }
277  }
278  ss << " ";
279 
280  first_term = false;
281 
282  }
283 
284  return ss.str();
285  }
286 
289 
290  // comp_n(i) = m_origin(j) + m_to_n(i,j)*comp(j) + ...
291 
292  std::stringstream ss;
293 
294  auto comp_x_str = [&](int j) {
295  return "comp(" + comp_var(j) + ")";
296  };
297 
298  auto comp_n_str = [&](int j) {
299  return "comp_n(" + m_components[j] + ")";
300  };
301 
302  ss << comp_n_str(i) << " = ";
303  bool first_term = true;
304  // print nothing if coeff == 0
305  if(!almost_zero(m_origin(i))) {
306  ss << m_origin(i);
307  first_term = false;
308  }
309 
310  for(int j = 0; j < m_to_n.cols(); ++j) {
311 
312  double coeff = m_to_n(i, j);
313 
314  // print nothing if coeff == 0
315  if(almost_zero(coeff)) {
316  continue;
317  }
318 
319  // if coeff < 0
320  if(coeff < 0) {
321  if(!first_term) {
322  ss << " - " << -coeff << "*" << comp_x_str(j);
323  }
324  else {
325  ss << coeff << "*" << comp_x_str(j);
326  }
327  }
328 
329  // if coeff > 0
330  else {
331  if(!first_term) {
332  ss << " + " << coeff << "*" << comp_x_str(j);
333  }
334  else {
335  ss << coeff << "*" << comp_x_str(j);
336  }
337  }
338  ss << " ";
339 
340  first_term = false;
341 
342  }
343 
344  return ss.str();
345  }
346 
353  // param_chem_pot = m_to_n.transpose() * chem_pot;
354 
355  std::stringstream ss;
356 
357  auto print_chem_pot = [&](int j) {
358  return "chem_pot(" + m_components[j] + ") ";
359  };
360 
361  ss << "param_chem_pot(" << comp_var(i) << ") = ";
362  Eigen::MatrixXd Mt = m_to_n.transpose();
363  bool first_term = true;
364  for(int j = 0; j < Mt.cols(); ++j) {
365 
366  double coeff = Mt(i, j);
367 
368  // print nothing if n == 0
369  if(almost_zero(coeff) || is_vacancy(m_components[j])) {
370  continue;
371  }
372 
373  // print 'A' or '+A' if n == 1
374  if(almost_zero(coeff - 1)) {
375  if(!first_term) {
376  ss << "+ ";
377  }
378  ss << print_chem_pot(j);
379  }
380 
381  // print '-A' if n == -1
382  else if(almost_zero(coeff + 1)) {
383  ss << "- " << print_chem_pot(j);
384  }
385 
386  // print 'nA' or '+nA' if n > 0
387  else if(coeff > 0) {
388  if(!first_term) {
389  ss << "+ ";
390  }
391  ss << coeff << "*" << print_chem_pot(j);
392  }
393 
394  // print '-nA' if n < 0
395  else {
396  ss << coeff << "*" << print_chem_pot(j);
397  }
398 
399  first_term = false;
400  }
401 
402  return ss.str();
403  }
404 
405 
408  return _n_formula(m_origin);
409  }
410 
413  return _n_formula(m_end_members.col(i));
414  }
415 
416 
419 
420  _check_size(_end_member);
421 
422  int r = m_components.size();
423  int c = m_end_members.cols();
424 
425  Eigen::MatrixXd tmp(r, c + 1);
426  tmp.leftCols(c) = m_end_members;
427  tmp.col(c) = _end_member;
428  m_end_members = tmp;
429  }
430 
433  if(m_components.size() != vec.size()) {
434  throw std::runtime_error(
435  std::string("Error in CompositionConverter: origin or end member vector size does not match components size."));
436  }
437  }
438 
441 
442  // calculate m_to_n and m_to_x:
443  //
444  // n = origin + m_to_n*x
445  // x = m_to_x*(n - origin)
446 
447  // end_members.col(i) corresponds to x such that x[i] = 1, x[j!=i] = 0,
448  // -> end_members.col(i) = origin + m_to_n.col(i)
449 
450  // r > c
451  int r = m_components.size();
452  int c = m_end_members.cols();
453 
454  m_to_n = Eigen::MatrixXd(r, c);
455  for(int i = 0; i < c; i++) {
456  m_to_n.col(i) = m_end_members.col(i) - m_origin;
457  }
458 
459  // x = m_to_x*(n - origin)
460  // -> x = m_to_x*(origin + m_to_n*x - origin)
461  // -> x = m_to_x*m_to_n*x
462 
463  // m_to_x is left pseudoinverse of m_to_n, which must have full column rank,
464  // because it describes the composition space:
465  //
466  // I = A+ * A, (A+ is the left pseudoinverse)
467  // if A has full column rank, (A.t * A) is invertible, so
468  // A+ = (A.t * A).inv * A.t
469  m_to_x = (m_to_n.transpose() * m_to_n).inverse() * m_to_n.transpose();
470 
471  }
472 
474  std::string CompositionConverter::_n_formula(const Eigen::VectorXd &vec) const {
475 
476  std::stringstream tstr;
477 
478  // for each molecule:
479  for(int i = 0; i < vec.size(); i++) {
480 
481  // print 'A' if x == 1
482  if(almost_zero(vec(i) - 1)) {
483  tstr << m_components[i];
484  }
485  // print 'A(x)' if x != 0
486  else if(!almost_zero(vec(i))) {
487  tstr << m_components[i] << "(" << vec(i) << ")";
488  }
489 
490  }
491 
492  return tstr.str();
493  }
494 
501  void display_composition_axes(std::ostream &stream, const std::map<std::string, CompositionConverter> &map) {
502 
503  if(map.size() == 0) {
504  return;
505  }
506 
507  auto comp_var = CompositionConverter::comp_var;
508 
509  stream << std::setw(10) << "KEY" << " ";
510  stream << std::setw(10) << "ORIGIN" << " ";
511  for(int i = 0; i < map.begin()->second.independent_compositions(); i++) {
512  stream << std::setw(10) << comp_var(i) << " ";
513  }
514  stream << " ";
515  stream << "GENERAL FORMULA";
516  stream << std::endl;
517 
518  stream << std::setw(10) << " ---" << " ";
519  stream << std::setw(10) << " ---" << " ";
520  for(int i = 0; i < map.begin()->second.independent_compositions(); i++) {
521  stream << std::setw(10) << " ---" << " ";
522  }
523  stream << " ";
524  stream << "---" << std::endl;
525 
526  for(auto it = map.cbegin(); it != map.cend(); ++it) {
527  stream << std::setw(10) << it->first << " ";
528  stream << std::setw(10) << it->second.origin_formula() << " ";
529  for(int i = 0; i < it->second.independent_compositions(); ++i) {
530  stream << std::setw(10) << it->second.end_member_formula(i) << " ";
531  }
532  stream << " ";
533  stream << std::setw(10) << it->second.mol_formula() << "\n";
534  }
535  }
536 
545  void display_comp(std::ostream &stream, const CompositionConverter &f, int indent) {
546 
547  for(int i = 0; i < f.independent_compositions(); ++i) {
548  stream << std::string(indent, ' ') << f.comp_formula(i) << "\n";
549  }
550 
551  }
552 
561  void display_comp_n(std::ostream &stream, const CompositionConverter &f, int indent) {
562 
563  for(int i = 0; i < f.components().size(); ++i) {
564  stream << std::string(indent, ' ') << f.comp_n_formula(i) << "\n";
565  }
566 
567  }
568 
577  void display_param_chem_pot(std::ostream &stream, const CompositionConverter &f, int indent) {
578 
579  for(int i = 0; i < f.independent_compositions(); ++i) {
580  stream << std::string(indent, ' ') << f.param_chem_pot_formula(i) << "\n";
581  }
582 
583  }
584 
587 
588  json = jsonParser::object();
589  json["components"] = f.components();
590  json["independent_compositions"] = f.independent_compositions();
591  json["origin"] = f.origin();
592  for(int i = 0; i < f.independent_compositions(); i++) {
594  }
595  json["mol_formula"] = f.mol_formula();
596  json["param_formula"] = f.param_formula();
597 
598  return json;
599  }
600 
602  void from_json(CompositionConverter &f, const jsonParser &json) {
603 
604  try {
605 
606  std::vector<std::string> components;
607  Eigen::VectorXd origin;
608 
609  int independent_compositions;
610 
611  from_json(components, json["components"]);
612  from_json(origin, json["origin"]);
613 
614  from_json(independent_compositions, json["independent_compositions"]);
615  Eigen::MatrixXd end_members(components.size(), independent_compositions);
616  Eigen::VectorXd tvec;
617  for(int i = 0; i < independent_compositions; i++) {
619  end_members.col(i) = tvec;
620  }
621 
622  f = CompositionConverter(components.begin(), components.end(), origin, end_members);
623  return;
624  }
625  catch(...) {
627  throw;
628  }
629  }
630 
641  ParamComposition param_comp(prim);
642  param_comp.generate_components();
643  param_comp.generate_sublattice_map();
644  param_comp.generate_prim_end_members();
645  return param_comp.get_prim_end_members().transpose();
646  }
647 
650  // Get Va index if it exists, and store 0 or 1 in N_Va
651  std::vector<std::string> struc_mol_name = prim.get_struc_molecule_name();
652  Index Va_index = find_index_if(struc_mol_name, [ = ](const std::string & str) {
653  return is_vacancy(str);
654  });
655  bool has_Va = (Va_index != struc_mol_name.size());
656 
657  // convert to atom frac
658  Eigen::MatrixXd E = end_members(prim);
659  if(has_Va) {
660  E.row(Va_index) = Eigen::VectorXd::Zero(E.cols());
661  }
662  for(int i = 0; i < E.cols(); i++) {
663  E.col(i) /= E.col(i).sum();
664  }
665 
666  // convert to atom frac space
667  Eigen::MatrixXd M(E.rows(), E.cols() - 1);
668  for(int i = 0; i < M.cols(); ++i) {
669  M.col(i) = E.col(i + 1) - E.col(0);
670  }
671  return M;
672  }
673 
683  auto Qr = _composition_space(prim, tol).fullPivHouseholderQr();
684  Qr.setThreshold(tol);
685  auto Q = Qr.matrixQ();
686  return Q.leftCols(Qr.rank());
687  }
688 
698  auto Qr = _composition_space(prim, tol).fullPivHouseholderQr();
699  Qr.setThreshold(tol);
700  auto Q = Qr.matrixQ();
701  return Q.rightCols(Q.cols() - Qr.rank());
702  }
703 
704 }
Eigen::MatrixXd MatrixXd
Eigen::VectorXd mol_composition(const Eigen::VectorXd &x) const
Convert parametric composition, 'x', to number of mol per prim, 'n'.
Eigen::MatrixXd dparam_dmol() const
Return the matrix Mij = dx_i/dn_j.
void display_comp_n(std::ostream &stream, const CompositionConverter &f, int indent=0)
Pretty-print comp_n in terms of comp.
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:41
void display_composition_axes(std::ostream &stream, const std::map< std::string, CompositionConverter > &map)
Pretty-print map of name/CompositionConverter pairs.
Eigen::MatrixXd composition_space(const Structure &prim, double tol=1e-14)
Return the composition space of a Structure.
void from_json(ClexDescription &desc, const jsonParser &json)
Eigen::MatrixXd dmol_dparam() const
Return the matrix Mij = dn_i/dx_j.
Eigen::VectorXd origin() const
The mol composition of the parameteric composition axes origin.
Eigen::VectorXd param_composition(const Eigen::VectorXd &n) const
Convert number of mol per prim, 'n' to parametric composition 'x'.
bool is_vacancy(const std::string &name)
A vacancy is any Specie/Molecule with (name == "VA" || name == "va" || name == "Va") ...
Definition: Molecule.hh:27
std::string param_chem_pot_formula(size_type i) const
Return formula for param_chem_pot(i) in terms of chem_pot(A), chem_pot(B), ...
Structure specifies the lattice and atomic basis of a crystal.
Definition: Structure.hh:29
jsonParser & to_json(const ClexDescription &desc, jsonParser &json)
std::vector< std::string > m_components
List of all allowed components names in the prim, position in vector is reference for origin and end_...
void _calc_conversion_matrices()
Calculate conversion matrices m_to_n and m_to_x.
void display_param_chem_pot(std::ostream &stream, const CompositionConverter &f, int indent=0)
Pretty-print param_chem_pot in terms of chem_pot.
std::string end_member_formula(size_type i) const
Return formula for end member.
Main CASM namespace.
Definition: complete.cpp:8
std::string mol_formula() const
Return formula for x->n.
Eigen::VectorXd dmol_composition(const Eigen::VectorXd &dx) const
Convert change in parametric composition, 'dx', to change in number of mol per prim, 'dn'.
Index find_index_if(Iterator begin, Iterator end, UnaryPredicate p)
Equivalent to std::distance(begin, std::find_if(begin, end, p))
Definition: algorithm.hh:28
Eigen::VectorXd chem_pot(const Eigen::VectorXd param_chem_pot) const
Convert dG/dx to dG/dn.
void _add_end_member(Eigen::VectorXd _end_member)
Helps make variadic template constructor possible.
std::vector< std::string > get_struc_molecule_name() const
Returns an Array of each possible Molecule in this Structure.
Definition: Structure.cc:166
std::vector< std::string > components() const
The order of components in mol composition vectors.
double tol
static std::string comp_var(size_type i)
Composition variable names: "a", "b", ...
Eigen::VectorXd param_chem_pot(const Eigen::VectorXd chem_pot) const
Convert dG/dn to dG/dx.
std::string comp_formula(size_type i) const
Return formula for comp(i) in terms of comp_n(A), comp_n(B), ...
EigenIndex Index
For long integer indexing:
Eigen::MatrixXd m_to_n
Conversion matrix: n = origin + m_to_n*x.
Eigen::Matrix< typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime > inverse(const Eigen::MatrixBase< Derived > &M)
Return the integer inverse matrix of an invertible integer matrix.
void _check_size(const Eigen::VectorXd &vec) const
Check that origin and end member vectors have same size as the number of components.
Eigen::MatrixXd m_end_members
Column vector matrix, rows == m_components.size(), cols == rank of parametric composition space...
Eigen::VectorXd VectorXd
size_type independent_compositions() const
The dimensionality of the composition space.
Eigen::VectorXd m_origin
Vector, size == m_components.size(), specifying the num_mols_per_prim of each component at the origin...
std::string origin_formula() const
Return formula for origin.
Eigen::MatrixXd _composition_space(const Structure &prim, double tol)
Non-orthogonal composition space.
Eigen::MatrixXd m_to_x
Conversion matrix: x = m_to_x*(n - origin)
std::string comp_n_formula(size_type i) const
Return formula for comp_n(components()[i]) in terms of comp(a), comp(b), ...
Eigen::MatrixXd end_members(const Structure &prim)
Generate a column matrix containing all the possible molecular end members.
Convert between number of species per unit cell and parametric composition.
std::string param_formula() const
Return formula for n->x.
void display_comp(std::ostream &stream, const CompositionConverter &f, int indent=0)
Pretty-print comp in terms of comp_n.
Eigen::VectorXd dparam_composition(const Eigen::VectorXd &dn) const
Convert change in number of atoms per prim, 'dn' to change in parametric composition 'dx'...
Eigen::MatrixXd get_prim_end_members() const
Return all possible end members as row matrix.
std::string _n_formula(const Eigen::VectorXd &vec) const
Return formula for 'n'.
Eigen::VectorXd end_member(size_type i) const
The mol composition of the parameteric composition axes end members.
static jsonParser object()
Returns an empty json object.
Definition: jsonParser.hh:329
Eigen::MatrixXd null_composition_space(const Structure &prim, double tol=1e-14)
Return the null composition space of a Structure.
bool almost_equal(const GenericCluster< CoordType > &LHS, const GenericCluster< CoordType > &RHS, double tol)