MPQC  3.0.0-alpha
shellorder.hpp
1 //
2 // shellorder.hpp
3 //
4 // Copyright (C) 2013 Drew Lewis
5 //
6 // Authors: Drew Lewis
7 // Maintainer: Drew Lewis and Edward Valeev
8 //
9 // This file is part of the MPQC Toolkit.
10 //
11 // The MPQC Toolkit is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU Library General Public License as published by
13 // the Free Software Foundation; either version 2, or (at your option)
14 // any later version.
15 //
16 // The MPQC Toolkit is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Library General Public License for more details.
20 //
21 // You should have received a copy of the GNU Library General Public License
22 // along with the MPQC Toolkit; see the file COPYING.LIB. If not, write to
23 // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // The U.S. Government is granted a limited license as per AL 91-7.
26 //
27 
28 #ifndef CHEMISTRY_QC_BASIS_SHELLORDER_HPP
29 #define CHEMISTRY_QC_BASIS_SHELLORDER_HPP
30 
31 #include<vector>
32 #include<string>
33 #include<cassert>
34 
35 #include <Eigen/Dense>
36 #include <chemistry/molecule/molecule.h>
37 #include <chemistry/qc/basis/basis.h>
38 
39 #include "kcluster.hpp"
40 
41 namespace mpqc{
42 namespace TA{
43 
47  class ShellOrder {
48 
49  public:
51  using Atom = KCluster::Atom;
55  using ShellRange = std::vector<std::size_t>;
56  using Vector3 = KCluster::Vector3;
57 
63  clusters_(),
64  atoms_(),
65  basis_(basis),
66  com_(Vector3::Zero(3))
67  {
68  // Get the molecule.
69  sc::Ref<sc::Molecule> mol = basis->molecule();
70  com_[0] = mol->center_of_mass()[0];
71  com_[1] = mol->center_of_mass()[1];
72  com_[2] = mol->center_of_mass()[2];
73 
74  // Get the Atoms and their index out of the molecule for the clusters.
75  for(auto i = 0; i < mol->natom(); ++i){
76  atoms_.push_back(Atom(mol->atom(i), i));
77  }
78  }
79 
84  std::vector<Shell>
85  ordered_shells(std::size_t nclusters,
86  const sc::GaussianBasisSet *new_parent_basis){
87  // Number of clusters desired.
88  nclusters_ = nclusters;
89 
90  // Compute clusters using Lloyd's Algorithm
91  compute_clusters(nclusters_);
92 
93  std::vector<Shell> shells = cluster_shells(new_parent_basis);
94 
95  return shells;
96  }
97 
98  std::vector<KCluster>& get_clusters(std::size_t nclusters) {
99  nclusters_ = nclusters;
100  compute_clusters(nclusters_);
101  return clusters_;
102  }
103 
108  return compute_shell_ranges();
109  }
110 
111  private:
112  /*
113  * Find clusters using Lloyd's algorithm.
114  */
115  void compute_clusters(std::size_t nclusters){
116  // Make initial guess at clusters
117  init_clusters();
118  // Search for local minimium in terms of clustering.
119  k_means_search();
120  }
121 
122  /*
123  * Determines initial guess for clusters.
124  */
125  void init_clusters(){
126  // Sort atoms in molecule by distance from COM such that 1. Closest
127  // . . . N. Farthest. If two atoms are equidistant then sort based
128  // on x, then y, and finally z.
129  auto ordering = [&](const Atom &a, const Atom &b){
130  // Get position vector for each atom.
131  const Vector3 va = Eigen::Map<const Vector3>(a.r(),3);
132  const Vector3 vb = Eigen::Map<const Vector3>(b.r(),3);
133 
134  // Find distance from center of mass for each atom
135  double da = Vector3(va - com_).norm();
136  double db = Vector3(vb - com_).norm();
137 
138  // Begin to check coordinates
139  if(da != db) { // Check not equidistant
140  return (da < db);
141  }
142  else if(va[0] != vb[0]){ // Check x isn't equidistant
143  return (va[0] < vb[0]);
144  }
145  else if(va[1] != vb[1]){ // Check y isn't equidistant
146  return (va[1] < vb[1]);
147  }
148  else if(va[2] != vb[2]){ // Check z isn't equidistant
149  return (va[2] < vb[2]);
150  }
151  else {
152  return false;
153  }
154  };
155 
156  std::sort(atoms_.begin(), atoms_.end(), ordering);
157 
158  // Initialize the kcluster guess at the position of atoms closest to
159  // COM.
160  for(auto i = 0; i < nclusters_; ++i){
161  clusters_.push_back(
162  Vector3(atoms_[i].r(0), atoms_[i].r(1), atoms_[i].r(2))
163  );
164  }
165 
166  // Attach atoms to the cluster which they are closest too.
167  attach_to_closest_cluster();
168  }
169 
170  /*
171  * Attaches each atom to its closest cluster.
172  */
173  void attach_to_closest_cluster(){
174  // Loop over all the atoms.
175  for(const auto atom : atoms_){
176  // Guess that first cluster is closest
177  double smallest = clusters_[0].distance(atom);
178 
179  // To which cluster the atom belongs.
180  std::size_t kindex = 0;
181 
182  // Loop over kclusters using i = 1 because we already computed 0
183  for(auto i = 1; i < nclusters_; ++i){
184  // Compute distance from atom to next kcluster
185  double dist = clusters_[i].distance(atom);
186 
187  // if closer update index info
188  if(dist < smallest){
189  kindex = i;
190  smallest = dist;
191  }
192  }
193 
194  // Add atom to the closest kcluster
195  clusters_[kindex].add_atom(atom);
196  }
197  // Put atoms in correct order inside clusters so that the
198  // Shell ordering becomes deterministic.
199  for(auto& cluster : clusters_){
200  cluster.sort_atoms();
201  }
202  }
203 
204  // Computes Lloyd's algorith to find a local minimium for the clusters.
205  void k_means_search(std::size_t niter = 100){
206 
207  // Lloyd's algorithm iterations.
208  for(auto i = 0; i < niter; ++i){
209 
210  // Recompute the center of the cluster using the centroid of
211  // the atoms. Will lose information about which atoms
212  // go with which center.
213  for(auto &cluster : clusters_){ cluster.guess_center(); }
214  sort_clusters();
215 
216  attach_to_closest_cluster();
217  }
218  }
219 
220  /*
221  * Returns a vector of shells in the order they appear in the clusters.
222  * Need to pass in a basis which will become the parent of the new Shells
223  */
224  std::vector<Shell>
225  cluster_shells(const sc::GaussianBasisSet *new_parent_basis) const {
226 
227  std::vector<Shell> shells;
228  // Loop over clusters
229  for(const auto& cluster : clusters_){
230  // Loop over atoms in cluster
231  for(const auto& atom : cluster.atoms()){
232  // Figure out where in the molecule the atom is located.
233  std::size_t center_ = atom.mol_index();
234  // Figure out how many shells are on the atom.
235  std::size_t nshells_on_atom =
236  basis_->nshell_on_center(center_);
237  // Loop over the shells on the atom and pack them into
238  // the shell contatiner.
239  for(auto i = 0; i < nshells_on_atom; ++i){
240  shells.push_back(Shell(
241  new_parent_basis, center_,
242  basis_->operator()(center_, i)));
243  }
244  }
245  }
246 
247  return shells;
248  }
249 
250  /*
251  * Returns a ShellRange that contains the shells included on each cluster.
252  */
253  ShellRange compute_shell_ranges() const {
254  ShellRange range;
255  range.reserve(nclusters_);
256 
257  // First range is easy
258  range.push_back(0);
259 
260  // Loop over clusters
261  for(auto i = 0; i < nclusters_; ++i){
262  // Holds the number of shells on the cluster.
263  std::size_t shells_in_cluster = 0;
264  // Loop over atoms
265  for(const auto atom : clusters_[i].atoms()){
266  // position of atom in molecule
267  std::size_t atom_index = atom.mol_index();
268  // Get number of shells on the atom.
269  shells_in_cluster += basis_->nshell_on_center(atom_index);
270  }
271  // Compute the Starting Shell of the next tile.
272  range.push_back(range.at(i) + shells_in_cluster);
273  }
274 
275  return range;
276  }
277 
278  /*
279  * Sort clusters on distance from com.
280  */
281  void sort_clusters(){
282  std::sort(clusters_.begin(), clusters_.end(),
283  [&](const KCluster &a, const KCluster &b){
284  return ((a.center()-com_).norm() < (b.center()-com_).norm());}
285  );
286  }
287 
288  private:
289  std::size_t nclusters_ = 0;
290  std::vector<Atom> atoms_;
291  Vector3 com_;
292  std::vector<KCluster> clusters_; // Note that KCluster contains fixed
293  // Eigen objects, but these are not vectorizable
294  // Thus we don't have to worry about Alignment issues.
296  };
297 
298 } // namespace TA
299 } // namespace mpqc
300 
301 #endif /* CHEMISTRY_QC_BASIS_SHELLORDER_HPP */
mpqc::TA::ShellOrder
Determines the clustering of shells based on k-means clustering.
Definition: shellorder.hpp:47
mpqc::TA::ShellOrder::ShellOrder
ShellOrder(const sc::Ref< sc::GaussianBasisSet > &basis)
Initializes ShellOrder with the atoms from the molecule and the shells from the basis.
Definition: shellorder.hpp:62
mpqc
Contains new MPQC code since version 3.
Definition: integralenginepool.hpp:37
mpqc::TA::cluster::ClusterAtom
Definition: kcluster.hpp:41
sc::Ref< sc::GaussianBasisSet >
sc::Molecule::center_of_mass
SCVector3 center_of_mass() const
Returns a SCVector3 containing the cartesian coordinates of the center of mass for the molecule.
mpqc::TA::ShellOrder::ShellRange
std::vector< std::size_t > ShellRange
Each element represents the shell a new tile starts on.
Definition: shellorder.hpp:55
sc::GaussianBasisSet::Shell
Shell is a GaussianShell that is part of GaussianBasisSet, i.e. has a center on which it's centered.
Definition: gaussbas.h:149
mpqc::norm
T norm(const matrix< T > &a)
Matrix norm.
Definition: matrix.hpp:209
mpqc::TA::ShellOrder::ordered_shells
std::vector< Shell > ordered_shells(std::size_t nclusters, const sc::GaussianBasisSet *new_parent_basis)
Returns a list of shells ordered by which cluster they belong to.
Definition: shellorder.hpp:85
sc::GaussianBasisSet::molecule
Ref< Molecule > molecule() const
Return the Molecule object.
Definition: gaussbas.h:489
sc::GaussianBasisSet
The GaussianBasisSet class is used describe a basis set composed of atomic gaussian orbitals.
Definition: gaussbas.h:141
sc::Molecule::natom
size_t natom() const
Returns the number of atoms in the molecule.
Definition: molecule.h:327
sc::GaussianBasisSet::nshell_on_center
int nshell_on_center(int icenter) const
Return the number of shells on the given center.
mpqc::TA::ShellOrder::shell_ranges
ShellRange shell_ranges() const
Returns a a ShellRange which specifies what shell each tile starts on.
Definition: shellorder.hpp:107

Generated at Sun Jan 26 2020 23:23:57 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.8.16.