CASM  1.1.0
A Clusters Approach to Statistical Mechanics
CompositionConverter.cc
Go to the documentation of this file.
2 
4 
5 // currently still relies on this for getting standard composition axes
7 #include "casm/misc/algorithm.hh"
8 
9 namespace CASM {
10 namespace Local {
13 static bool _is_vacancy(const std::string &name) {
14  return (name == "VA" || name == "va" || name == "Va");
15 }
16 } // namespace Local
24  const {
25  return m_to_x.rows();
26 }
27 
30  return std::string(1, (char)(i + (int)'a'));
31 }
32 
34 std::vector<std::string> CompositionConverter::components() const {
35  return m_components;
36 }
37 
43 
49  return m_end_members.col(i);
50 }
51 
54 
57 
63  const Eigen::VectorXd &n) const {
64  return m_to_x * (n - m_origin);
65 }
66 
73  const Eigen::VectorXd &dn) const {
74  return m_to_x * dn;
75 }
76 
82  const Eigen::VectorXd &x) const {
83  return m_origin + m_to_n * x;
84 }
85 
92  const Eigen::VectorXd &dx) const {
93  return m_to_n * dx;
94 }
95 
99  const Eigen::VectorXd chem_pot) const {
100  return m_to_n.transpose() * chem_pot;
101 }
102 
105  // n = origin + m_to_n * x
106 
107  std::stringstream tstr;
108 
109  // for each molecule:
110  for (int i = 0; i < m_components.size(); i++) {
111  bool first_char = true;
112 
113  // print mol name 'A('
114  tstr << m_components[i] << "(";
115 
116  // constant term from origin
117  // print 'x' if x != 0
118  if (!almost_zero(m_origin(i))) {
119  first_char = false;
120  tstr << m_origin(i);
121  }
122 
123  // terms from m_to_n columns
124  for (int j = 0; j < m_to_n.cols(); j++) {
125  // print nothing if x == 0
126  if (almost_zero(m_to_n(i, j))) {
127  continue;
128  }
129 
130  // print '+' if x>0 mid-expression
131  if (!first_char && m_to_n(i, j) > 0) tstr << '+';
132  // print '-' if x<0
133  else if (m_to_n(i, j) < 0)
134  tstr << '-';
135  // print absolute value of x if |x|!=1
136  if (!almost_equal(std::abs(m_to_n(i, j)), 1.0))
137  tstr << std::abs(m_to_n(i, j));
138  // print variable ('a','b',etc...)
139  tstr << comp_var(j);
140 
141  first_char = false;
142  }
143 
144  // close ')'
145  tstr << ")";
146  }
147 
148  return tstr.str();
149 }
150 
153  // x_i = m_to_x*(n - origin) = m_to_x*n - m_to_x*origin
154 
155  std::stringstream tstr;
156 
158 
159  // for each independent composition:
160  for (int i = 0; i < independent_compositions(); i++) {
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  double coeff = m_to_x(i, j);
176 
177  // print nothing if n == 0
178  if (almost_zero(coeff)) {
179  continue;
180  }
181 
182  // print 'A' or '+A' if n == 1
183  if (almost_zero(coeff - 1)) {
184  if (!first_char) {
185  tstr << '+';
186  }
187  tstr << m_components[j];
188  }
189 
190  // print '-A' if n == -1
191  else if (almost_zero(coeff + 1)) {
192  tstr << '-' << m_components[j];
193  }
194 
195  // print 'nA' or '+nA' if n > 0
196  else if (coeff > 0) {
197  if (!first_char) {
198  tstr << '+';
199  }
200  tstr << coeff << m_components[j];
201  }
202 
203  // print '-nA' if n < 0
204  else {
205  tstr << coeff << m_components[j];
206  }
207 
208  first_char = false;
209  }
210 
211  // close ')'
212  tstr << ")";
213  }
214 
215  return tstr.str();
216 }
217 
220  // comp(i) = m_to_x(i,j)*(comp_n(j) - m_origin(j)) + ...
221 
222  std::stringstream ss;
223 
224  auto comp_x_str = [&]() { return "comp(" + comp_var(i) + ")"; };
225 
226  auto comp_n_str = [&](int j) { return "comp_n(" + m_components[j] + ")"; };
227 
228  auto delta_str = [&](int j) {
229  std::stringstream tss;
230  // print '(comp_n(J) - m_origin(j))' if m_origin(j) != 0
231  if (!almost_zero(m_origin(j))) {
232  tss << "(" << comp_n_str(j) << " - " << m_origin(j) << ")";
233  }
234  // print 'comp_n(J)'
235  else {
236  tss << comp_n_str(j);
237  }
238  return tss.str();
239  };
240 
241  ss << comp_x_str() << " = ";
242  bool first_term = true;
243  for (int j = 0; j < m_to_x.cols(); ++j) {
244  double coeff = m_to_x(i, j);
245 
246  // print nothing if coeff == 0
247  if (almost_zero(coeff)) {
248  continue;
249  }
250 
251  // if coeff < 0
252  if (coeff < 0) {
253  if (!first_term) {
254  ss << " - " << -coeff << "*" << delta_str(j);
255  } else {
256  ss << coeff << "*" << delta_str(j);
257  }
258  }
259 
260  // if coeff > 0
261  else {
262  if (!first_term) {
263  ss << " + " << coeff << "*" << delta_str(j);
264  } else {
265  ss << coeff << "*" << delta_str(j);
266  }
267  }
268  ss << " ";
269 
270  first_term = false;
271  }
272 
273  return ss.str();
274 }
275 
279  // comp_n(i) = m_origin(j) + m_to_n(i,j)*comp(j) + ...
280 
281  std::stringstream ss;
282 
283  auto comp_x_str = [&](int j) { return "comp(" + comp_var(j) + ")"; };
284 
285  auto comp_n_str = [&](int j) { return "comp_n(" + m_components[j] + ")"; };
286 
287  ss << comp_n_str(i) << " = ";
288  bool first_term = true;
289  // print nothing if coeff == 0
290  if (!almost_zero(m_origin(i))) {
291  ss << m_origin(i);
292  first_term = false;
293  }
294 
295  for (int j = 0; j < m_to_n.cols(); ++j) {
296  double coeff = m_to_n(i, j);
297 
298  // print nothing if coeff == 0
299  if (almost_zero(coeff)) {
300  continue;
301  }
302 
303  // if coeff < 0
304  if (coeff < 0) {
305  if (!first_term) {
306  ss << " - " << -coeff << "*" << comp_x_str(j);
307  } else {
308  ss << coeff << "*" << comp_x_str(j);
309  }
310  }
311 
312  // if coeff > 0
313  else {
314  if (!first_term) {
315  ss << " + " << coeff << "*" << comp_x_str(j);
316  } else {
317  ss << coeff << "*" << comp_x_str(j);
318  }
319  }
320  ss << " ";
321 
322  first_term = false;
323  }
324 
325  return ss.str();
326 }
327 
335  // param_chem_pot = m_to_n.transpose() * chem_pot;
336 
337  std::stringstream ss;
338 
339  auto print_chem_pot = [&](int j) {
340  return "chem_pot(" + m_components[j] + ") ";
341  };
342 
343  ss << "param_chem_pot(" << comp_var(i) << ") = ";
344  Eigen::MatrixXd Mt = m_to_n.transpose();
345  bool first_term = true;
346  for (int j = 0; j < Mt.cols(); ++j) {
347  double coeff = Mt(i, j);
348 
349  // print nothing if n == 0
350 
351  if (almost_zero(coeff) || Local::_is_vacancy(m_components[j])) {
352  continue;
353  }
354 
355  // print 'A' or '+A' if n == 1
356  if (almost_zero(coeff - 1)) {
357  if (!first_term) {
358  ss << "+ ";
359  }
360  ss << print_chem_pot(j);
361  }
362 
363  // print '-A' if n == -1
364  else if (almost_zero(coeff + 1)) {
365  ss << "- " << print_chem_pot(j);
366  }
367 
368  // print 'nA' or '+nA' if n > 0
369  else if (coeff > 0) {
370  if (!first_term) {
371  ss << "+ ";
372  }
373  ss << coeff << "*" << print_chem_pot(j);
374  }
375 
376  // print '-nA' if n < 0
377  else {
378  ss << coeff << "*" << print_chem_pot(j);
379  }
380 
381  first_term = false;
382  }
383 
384  return ss.str();
385 }
386 
389  return _n_formula(m_origin);
390 }
391 
394  return _n_formula(m_end_members.col(i));
395 }
396 
399  _check_size(_end_member);
400 
401  int r = m_components.size();
402  int c = m_end_members.cols();
403 
404  Eigen::MatrixXd tmp(r, c + 1);
405  tmp.leftCols(c) = m_end_members;
406  tmp.col(c) = _end_member;
407  m_end_members = tmp;
408 }
409 
413  if (m_components.size() != mat.rows()) {
414  throw std::runtime_error(
415  std::string("Error in CompositionConverter: origin or end member "
416  "vector size does not match components size."));
417  }
418 }
419 
422  // calculate m_to_n and m_to_x:
423  //
424  // n = origin + m_to_n*x
425  // x = m_to_x*(n - origin)
426 
427  // end_members.col(i) corresponds to x such that x[i] = 1, x[j!=i] = 0,
428  // -> end_members.col(i) = origin + m_to_n.col(i)
429 
430  int c = m_end_members.cols();
431 
432  m_to_n = m_end_members.leftCols(c).colwise() - m_origin;
433 
434  // x = m_to_x*(n - origin)
435  // -> x = m_to_x*(origin + m_to_n*x - origin)
436  // -> x = m_to_x*m_to_n*x
437 
438  // m_to_x is left pseudoinverse of m_to_n, which must have full column rank,
439  // because it describes the composition space:
440  //
441  // I = A+ * A, (A+ is the left pseudoinverse)
442  // if A has full column rank, (A.t * A) is invertible, so
443  // A+ = (A.t * A).inv * A.t
444  m_to_x = (m_to_n.transpose() * m_to_n).inverse() * m_to_n.transpose();
445 }
446 
448 std::string CompositionConverter::_n_formula(const Eigen::VectorXd &vec) const {
449  std::stringstream tstr;
450 
451  // for each molecule:
452  for (int i = 0; i < vec.size(); i++) {
453  // print 'A' if x == 1
454  if (almost_zero(vec(i) - 1)) {
455  tstr << m_components[i];
456  }
457  // print 'A(x)' if x != 0
458  else if (!almost_zero(vec(i))) {
459  tstr << m_components[i] << "(" << vec(i) << ")";
460  }
461  }
462 
463  return tstr.str();
464 }
465 
473  std::ostream &stream,
474  const std::map<std::string, CompositionConverter> &map) {
475  if (map.size() == 0) {
476  return;
477  }
478 
479  auto comp_var = CompositionConverter::comp_var;
480 
481  stream << std::setw(10) << "KEY"
482  << " ";
483  stream << std::setw(10) << "ORIGIN"
484  << " ";
485  for (int i = 0; i < map.begin()->second.independent_compositions(); i++) {
486  stream << std::setw(10) << comp_var(i) << " ";
487  }
488  stream << " ";
489  stream << "GENERAL FORMULA";
490  stream << std::endl;
491 
492  stream << std::setw(10) << " ---"
493  << " ";
494  stream << std::setw(10) << " ---"
495  << " ";
496  for (int i = 0; i < map.begin()->second.independent_compositions(); i++) {
497  stream << std::setw(10) << " ---"
498  << " ";
499  }
500  stream << " ";
501  stream << "---" << std::endl;
502 
503  for (auto it = map.cbegin(); it != map.cend(); ++it) {
504  stream << std::setw(10) << it->first << " ";
505  stream << std::setw(10) << it->second.origin_formula() << " ";
506  for (int i = 0; i < it->second.independent_compositions(); ++i) {
507  stream << std::setw(10) << it->second.end_member_formula(i) << " ";
508  }
509  stream << " ";
510  stream << std::setw(10) << it->second.mol_formula() << "\n";
511  }
512 }
513 
522 void display_comp(std::ostream &stream, const CompositionConverter &f,
523  int indent) {
524  for (int i = 0; i < f.independent_compositions(); ++i) {
525  stream << std::string(indent, ' ') << f.comp_formula(i) << "\n";
526  }
527 }
528 
537 void display_comp_n(std::ostream &stream, const CompositionConverter &f,
538  int indent) {
539  for (int i = 0; i < f.components().size(); ++i) {
540  stream << std::string(indent, ' ') << f.comp_n_formula(i) << "\n";
541  }
542 }
543 
552 void display_param_chem_pot(std::ostream &stream, const CompositionConverter &f,
553  int indent) {
554  for (int i = 0; i < f.independent_compositions(); ++i) {
555  stream << std::string(indent, ' ') << f.param_chem_pot_formula(i) << "\n";
556  }
557 }
558 
571  const ParamComposition::AllowedOccupants &_allowed_occs) {
572  ParamComposition param_comp(_allowed_occs);
573  param_comp.generate_prim_end_members();
574  return param_comp.prim_end_members().transpose();
575 }
576 
579  const ParamComposition::AllowedOccupants &_allowed_occs, double tol) {
580  // Get Va index if it exists, and store 0 or 1 in N_Va
581  std::vector<std::string> struc_mol_name =
582  ParamComposition(_allowed_occs).components();
583  Index Va_index = find_index_if(struc_mol_name, [=](const std::string &str) {
584  return Local::_is_vacancy(str);
585  });
586  bool has_Va = (Va_index != struc_mol_name.size());
587 
588  // convert to atom frac
589  Eigen::MatrixXd E = end_members(_allowed_occs);
590  if (has_Va) {
591  E.row(Va_index) = Eigen::VectorXd::Zero(E.cols());
592  }
593  for (int i = 0; i < E.cols(); i++) {
594  E.col(i) /= E.col(i).sum();
595  }
596 
597  // convert to atom frac space
598  Eigen::MatrixXd M(E.rows(), E.cols() - 1);
599  for (int i = 0; i < M.cols(); ++i) {
600  M.col(i) = E.col(i + 1) - E.col(0);
601  }
602  return M;
603 }
604 
616  const ParamComposition::AllowedOccupants &_allowed_occs, double tol) {
617  auto Qr = _composition_space(_allowed_occs, tol).fullPivHouseholderQr();
618  Qr.setThreshold(tol);
619  auto Q = Qr.matrixQ();
620  return Q.leftCols(Qr.rank());
621 }
622 
634  const ParamComposition::AllowedOccupants &_allowed_occs, double tol) {
635  auto Qr = _composition_space(_allowed_occs, tol).fullPivHouseholderQr();
636  Qr.setThreshold(tol);
637  auto Q = Qr.matrixQ();
638  return Q.rightCols(Q.cols() - Qr.rank());
639 }
640 
641 } // namespace CASM
Convert between number of species per unit cell and parametric composition.
Eigen::VectorXd mol_composition(const Eigen::VectorXd &x) const
Convert parametric composition, 'x', to number of mol per prim, 'n'.
Eigen::MatrixXd m_end_members
Column vector matrix, rows == m_components.size(), cols == rank of parametric composition space.
std::string comp_formula(size_type i) const
Return formula for comp(i) in terms of comp_n(A), comp_n(B), ...
Eigen::VectorXd chem_pot(const Eigen::VectorXd param_chem_pot) const
Convert dG/dx to dG/dn.
std::string _n_formula(const Eigen::VectorXd &vec) const
Return formula for 'n'.
void _calc_conversion_matrices()
Calculate conversion matrices m_to_n and m_to_x.
Eigen::VectorXd m_origin
Vector, size == m_components.size(), specifying the num_mols_per_prim of each component at the origin...
Eigen::MatrixXd m_to_x
Conversion matrix: x = m_to_x*(n - origin)
Eigen::VectorXd end_member(size_type i) const
The mol composition of the parameteric composition axes end members.
std::string origin_formula() const
Return formula for origin.
std::string param_formula() const
Return formula for n->x.
void _check_size(const Eigen::MatrixXd &vec) const
Check that origin and end member vectors have same size as the number of components.
Eigen::VectorXd param_composition(const Eigen::VectorXd &n) const
Convert number of mol per prim, 'n' to parametric composition 'x'.
std::string comp_n_formula(size_type i) const
Return formula for comp_n(components()[i]) in terms of comp(a), comp(b), ...
static std::string comp_var(size_type i)
Composition variable names: "a", "b", ...
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::VectorXd origin() const
The mol composition of the parameteric composition axes origin.
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), ...
size_type independent_compositions() const
The dimensionality of the composition space.
Eigen::VectorXd param_chem_pot(const Eigen::VectorXd chem_pot) const
Convert dG/dn to dG/dx.
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,...
void _add_end_member(Eigen::VectorXd _end_member)
Helps make variadic template constructor possible.
Eigen::MatrixXd dparam_dmol() const
Return the matrix Mij = dx_i/dn_j.
Eigen::MatrixXd dmol_dparam() const
Return the matrix Mij = dn_i/dx_j.
Eigen::MatrixXd m_to_n
Conversion matrix: n = origin + m_to_n*x.
std::vector< std::string > components() const
The order of components in mol composition vectors.
std::vector< std::string > m_components
List of all allowed components names in the prim, position in vector is reference for origin and end_...
std::string end_member_formula(size_type i) const
Return formula for end member.
Eigen::MatrixXd prim_end_members() const
Return all possible end members as row matrix.
std::vector< std::vector< std::string > > AllowedOccupants
const std::vector< std::string > & components() const
Components are in order of appearance precedence in allowed_occs()
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.
static bool _is_vacancy(const std::string &name)
A vacancy is any Specie/Molecule with (name == "VA" || name == "va" || name == "Va")
Main CASM namespace.
Definition: APICommand.hh:8
bool almost_equal(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Check if ClusterInvariants are equal.
void display_comp_n(std::ostream &stream, const CompositionConverter &f, int indent=0)
Pretty-print comp_n in terms of comp.
Eigen::MatrixXd MatrixXd
Eigen::MatrixXd composition_space(const ParamComposition::AllowedOccupants &_allowed_occs, double tol=1e-14)
Return the composition space of a ParamComposition::AllowedOccupants.
void display_param_chem_pot(std::ostream &stream, const CompositionConverter &f, int indent=0)
Pretty-print param_chem_pot in terms of chem_pot.
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 ParamComposition::AllowedOccupants &_allowed_occs, double tol)
Non-orthogonal composition space.
GenericDatumFormatter< std::string, DataObject > name()
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:104
Index find_index_if(Iterator begin, Iterator end, UnaryPredicate p)
Equivalent to std::distance(begin, std::find_if(begin, end, p))
Definition: algorithm.hh:53
Eigen::MatrixXd end_members(const ParamComposition::AllowedOccupants &_allowed_occs)
Serialize CompositionConverter to JSON.
Eigen::MatrixXd null_composition_space(const ParamComposition::AllowedOccupants &_allowed_occs, double tol=1e-14)
Return the null composition space of a ParamComposition::AllowedOccupants.
void display_comp(std::ostream &stream, const CompositionConverter &f, int indent=0)
Pretty-print comp in terms of comp_n.
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
Eigen::VectorXd VectorXd