CASM  1.1.0
A Clusters Approach to Statistical Mechanics
Hull.cc
Go to the documentation of this file.
1 #include "casm/hull/Hull.hh"
2 
3 #include <iterator>
4 
6 #include "casm/clex/PrimClex.hh"
8 #include "casm/external/Eigen/Dense"
9 #include "casm/external/qhull/libqhullcpp/QhullFacetList.h"
10 #include "casm/external/qhull/libqhullcpp/QhullVertexSet.h"
11 #include "casm/misc/PCA.hh"
12 
13 namespace CASM {
14 
15 template class DataFormatter<std::pair<std::string, std::pair<bool, bool>>>;
16 
17 namespace Hull_impl {
18 
21 void _validate_input(const DB::Selection<Configuration> &selection,
22  const Hull::CompCalculator &comp_calculator,
23  const Hull::EnergyCalculator &energy_calculator);
24 } // namespace Hull_impl
25 
28  const CompCalculator &_comp_calculator,
29  const EnergyCalculator &_energy_calculator,
30  double _singular_value_tol, double _bottom_facet_tol)
31  : m_selection(_selection),
32  m_comp_calculator(_comp_calculator.clone()),
33  m_energy_calculator(_energy_calculator.clone()) {
36 
37  // get the number of configurations and compositions (full composition space)
38  Index Nselected = m_selection.selected_size();
39  Index Ncomp = composition(*m_selection.selected().begin()).size();
40 
41  // generate initial set of points (col vector matrix)
42  Eigen::MatrixXd mat(Ncomp + 1, Nselected);
43 
44  Index i = 0;
45  for (const auto &config : m_selection.selected()) {
46  mat.block(0, i, Ncomp, 1) = composition(config);
47  mat(Ncomp, i) = energy(config);
48  ++i;
49  }
50 
51  // principal component analysis to get rotation matrix
52  PCA pca(mat.topRows(Ncomp), _singular_value_tol);
53  m_reduce = pad(pca.reduce(), 1);
54 
55  Eigen::MatrixXd reduced_mat = m_reduce * mat;
56 
57  // Construct Qhull PointCoordiantes, with correct dimension
58  orgQhull::PointCoordinates points(reduced_mat.rows(), "");
59 
60  // use matrix data to set point coordinates
61  points.append(reduced_mat.size(), reduced_mat.data());
62 
63  // calculate hull
64  std::string qh_command = "";
65  m_hull.runQhull(points.comment().c_str(), points.dimension(), points.count(),
66  &*points.coordinates(), qh_command.c_str());
67 
68  // check for errors
69  if (m_hull.hasQhullMessage()) {
70  std::cerr << "\nQhull message:\n" << m_hull.qhullMessage();
71  m_hull.clearQhullMessage();
72  throw std::runtime_error("Qhull Error (see message)");
73  }
74 
75  // want to find minimum distance from point to a bottom facet
76 
77  // V: any vector from point to facet
78  // N: outward unit normal of facet
79  // D: unit 'down' direction (by convention always [0, 0, ..., -1])
80  //
81  // distance to facet along normal: V.dot(N) = a (Qhull reports this as a
82  // negative number) proj of D onto N: D.dot(N) = b (if b is positive, then
83  // this is 'bottom' facet) distance along D to facet: a/b
84 
85  // collect bottom facets (along with 'b')
86  {
87  auto begin = m_hull.facetList().begin();
88  auto end = m_hull.facetList().end();
89  int dim = m_hull.dimension();
90  double b;
91  Eigen::Map<const Eigen::VectorXd> outnorm((*begin).hyperplane().begin(),
92  dim);
93 
94  for (auto facet_it = begin; facet_it != end; ++facet_it) {
95  // reset location of outnorm data
96  new (&outnorm) Eigen::Map<const Eigen::VectorXd>(
97  (*facet_it).hyperplane().begin(), dim);
98  b = -outnorm(dim - 1);
99 
100  if (b > _bottom_facet_tol) {
101  m_bottom_facets.push_back(std::make_pair(*facet_it, b));
102  }
103  }
104  }
105 
106  // find set of bottom vertices using bottom facets
107  // use pointers for the set comparison
108  for (auto facet_it = m_bottom_facets.begin();
109  facet_it != m_bottom_facets.end(); ++facet_it) {
110  orgQhull::QhullVertexSet vertices = (*facet_it).first.vertices();
111  for (auto vertex_it = vertices.begin(); vertex_it != vertices.end();
112  ++vertex_it) {
113  m_bottom_vertices.insert(*vertex_it);
114  }
115  }
116 }
117 
119 const orgQhull::Qhull &Hull::data() const { return m_hull; }
120 
128 const Eigen::MatrixXd &Hull::reduce() const { return m_reduce; }
129 
132 const Eigen::Transpose<const Eigen::MatrixXd> Hull::expand() const {
133  return m_reduce.transpose();
134 }
135 
138  const orgQhull::QhullPoint &point) const {
139  auto it = m_selection.selected().begin();
140  std::advance(it, point.id());
141  return *it;
142 }
143 
146  const orgQhull::QhullVertex &vertex) const {
147  return configuration(vertex.point());
148 }
149 
154 Eigen::VectorXd Hull::mu(const orgQhull::QhullFacet facet) const {
155  // mu_i = dG/dx_i
156  // hyperplane normal = [dx_i, dx_j, ... dG] (normalized)
157 
158  int dim = m_hull.dimension();
159  Eigen::Map<const Eigen::VectorXd> reduced_hyperplane(
160  facet.hyperplane().begin(), dim);
161 
162  Eigen::VectorXd _mu =
163  Eigen::VectorXd::Constant(dim - 1, reduced_hyperplane(dim - 1));
164 
165  return _mu.cwiseQuotient(expand().leftCols(dim - 1) *
166  reduced_hyperplane.head(dim - 1));
167 }
168 
174  // mu_i = dG/dx_i
175  // hyperplane normal = [dx_i, dx_j, ... dG] (normalized)
176 
177  // normal vector to hyperplane
178  Eigen::VectorXd hyperplane(mu.size() + 1);
179  hyperplane.head(mu.size()) = mu.cwiseInverse();
180  hyperplane(mu.size()) = 1.0;
181  hyperplane.normalize();
182 
183  Eigen::VectorXd reduced_hyperplane = reduce() * hyperplane;
184 
185  double max_offset = std::numeric_limits<double>::min();
186  orgQhull::QhullVertex _groundstate = *m_bottom_vertices.begin();
187 
188  // m_bottom_vertices: a std::set<orgQhull::QhullVertex*>
189  for (auto it = m_bottom_vertices.begin(); it != m_bottom_vertices.end();
190  ++it) {
191  Eigen::Map<const Eigen::VectorXd> reduced_vertex_point(it->point().begin(),
192  it->point().size());
193 
194  double offset = reduced_hyperplane.dot(reduced_vertex_point);
195 
196  if (offset > max_offset) {
197  max_offset = offset;
198  _groundstate = *it;
199  }
200  }
201 
202  return configuration(_groundstate);
203 }
204 
206 double Hull::energy(const Configuration &config) const {
207  return (*m_energy_calculator)(config);
208 }
209 
212  return (*m_comp_calculator)(config);
213 }
214 
218  Eigen::VectorXd _point(m_reduce.cols());
219  _point.head(m_reduce.cols() - 1) = composition(config);
220  _point.tail(1)(0) = energy(config);
221  return _point;
222 }
223 
227  return reduce() * point(config);
228 }
229 
233 }
234 
237 double Hull::dist_to_hull(Eigen::VectorXd _reduced_point) const {
238  // want to find minimum distance from point to a bottom facet
239 
240  // V: any vector from point to facet
241  // N: outward unit normal of facet
242  // D: unit 'down' direction (by convention always [0, 0, ..., -1])
243  //
244  // distance to facet along normal: V.dot(N) = a (Qhull reports this as a
245  // negative number) proj of D onto N: D.dot(N) = b (if b is positive, then
246  // this is 'bottom' facet) distance along D to facet: a/b
247 
248  orgQhull::QhullPoint qpoint(_reduced_point.size(), _reduced_point.data());
249 
250  double a, b;
252 
253  for (auto facet_it = m_bottom_facets.begin();
254  facet_it != m_bottom_facets.end(); ++facet_it) {
255  // Qhull distance is negative for internal points
256  a = -(facet_it->first).distance(qpoint);
257  b = facet_it->second;
258  if (a / b < dist_to_hull) {
259  dist_to_hull = a / b;
260  }
261  }
262 
263  return dist_to_hull;
264 }
265 
266 namespace Hull_impl {
267 
271  const Hull::CompCalculator &comp_calculator,
272  const Hull::EnergyCalculator &energy_calculator) {
273  typedef std::map<std::string, std::pair<bool, bool>> CheckMap;
274 
275  CheckMap invalid_data;
276  for (const auto &config : selection.selected()) {
277  if (!comp_calculator.validate(config) ||
278  !energy_calculator.validate(config)) {
279  invalid_data[config.name()] = std::make_pair(
280  comp_calculator.validate(config), energy_calculator.validate(config));
281  }
282  }
283 
284  if (invalid_data.size()) {
285  std::cerr << "Invalid data for hull construction:\n";
287 
289  Formatter("configname", "",
290  [](CheckMap::value_type v) { return v.first; }),
291  Formatter("composition", "",
292  [](CheckMap::value_type v) {
293  return v.second.first ? "OK" : "invalid";
294  }),
295  Formatter("energy", "", [](CheckMap::value_type v) {
296  return v.second.second ? "OK" : "invalid";
297  }));
298 
299  std::cerr << f(invalid_data.begin(), invalid_data.end()) << "\n";
300 
301  std::stringstream ss;
302  ss << "Error in Hull(): Invalid composition or energy data. \n"
303  "Make sure you have set composition axes, all selected "
304  "configurations\n"
305  "have calculation results, and you have set your chemical reference.";
306  throw std::runtime_error(ss.str());
307  }
308 }
309 
310 } // namespace Hull_impl
311 
312 } // namespace CASM
Abstract base class for creating 1D DatumFormatter.
virtual bool validate(const DataObject &_data_obj) const
Returns true if _data_obj has valid values for requested data.
Base class for creating scalar DatumFormatter.
boost::iterator_range< iterator > selected()
Definition: Selection.cc:237
Extract data from objects of 'DataObject' class.
A DatumFormatter that returns a value of specified type, via functions that may be specified at runti...
const Eigen::Transpose< const Eigen::MatrixXd > expand() const
Orthogonal transformation matrix from a point in the dimension-reduced comp/energy space to the full ...
Definition: Hull.cc:132
const Configuration & configuration(const orgQhull::QhullPoint &point) const
Return the configuration corresponding to any point.
Definition: Hull.cc:137
std::set< orgQhull::QhullVertex, CompareVertex > m_bottom_vertices
Definition: Hull.hh:109
Eigen::VectorXd reduced_point(const Configuration &config) const
Return a vector corresponding to the coordinate of a given configuration in the reduced composition/e...
Definition: Hull.cc:226
std::vector< std::pair< orgQhull::QhullFacet, double > > m_bottom_facets
Definition: Hull.hh:106
const Configuration & groundstate(const Eigen::VectorXd &mu) const
Return the 0K ground state corresponding to the input chemical potential.
Definition: Hull.cc:173
orgQhull::Qhull m_hull
Definition: Hull.hh:89
DB::Selection< Configuration > m_selection
Definition: Hull.hh:92
Hull(const DB::Selection< Configuration > &_selection, const CompCalculator &_comp_calculator=ConfigIO::SpeciesFrac(), const EnergyCalculator &_energy_calculator=ConfigIO::formation_energy_per_species(), double _singular_value_tol=1e-14, double _bottom_facet_tol=1e-14)
Constructor for convex hull in composition/energy space.
Definition: Hull.cc:27
const Eigen::MatrixXd & reduce() const
Orthogonal transformation matrix from a point in full comp/energy space to dimension-reduced comp/ene...
Definition: Hull.cc:128
notstd::cloneable_ptr< EnergyCalculator > m_energy_calculator
Definition: Hull.hh:98
Eigen::VectorXd mu(const orgQhull::QhullFacet facet) const
Return the chemical potential corresponding to a facet.
Definition: Hull.cc:154
Eigen::VectorXd composition(const Configuration &config) const
Use the CompCalculator to return the composition of a Configuration.
Definition: Hull.cc:211
double dist_to_hull(const Configuration &config) const
The distance a Configuration is above the hull along the energy axis.
Definition: Hull.cc:231
double energy(const Configuration &config) const
Use the EnergyCalculator to return the energy of a Configuration.
Definition: Hull.cc:206
Eigen::VectorXd point(const Configuration &config) const
Return a vector corresponding to the coordinate of a given configuration in full composition/energy s...
Definition: Hull.cc:217
notstd::cloneable_ptr< CompCalculator > m_comp_calculator
Definition: Hull.hh:95
const orgQhull::Qhull & data() const
const Access the hull object directly
Definition: Hull.cc:119
Eigen::MatrixXd m_reduce
Definition: Hull.hh:102
Principle component analysis.
Definition: PCA.hh:9
Eigen::MatrixXd reduce() const
Orthogonal transformation matrix from a point in full [input...] space to dimension-reduced [range(in...
Definition: PCA.hh:49
ConfigIO::GenericConfigFormatter< jsonParser > config()
Definition: ConfigIO.cc:777
void _validate_input(const DB::Selection< Configuration > &selection, const Hull::CompCalculator &comp_calculator, const Hull::EnergyCalculator &energy_calculator)
Print informational message and throw exception if input data is not valid.
Definition: Hull.cc:270
Main CASM namespace.
Definition: APICommand.hh:8
Eigen::MatrixXd MatrixXd
T min(const T &A, const T &B)
Definition: CASM_math.hh:88
Eigen::MatrixXd pad(const Eigen::MatrixXd &M, int n)
Construct a matrix consisting of blocks M and Identity(n,n)
Definition: PCA.hh:73
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
Eigen::VectorXd VectorXd
T max(const T &A, const T &B)
Definition: CASM_math.hh:95
std::unique_ptr< T > clone(const T &obj)