MPQC 3.0.0-alpha
Loading...
Searching...
No Matches
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
41namespace mpqc{
42namespace 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 */
Determines the clustering of shells based on k-means clustering.
Definition shellorder.hpp:47
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
ShellRange shell_ranges() const
Returns a a ShellRange which specifies what shell each tile starts on.
Definition shellorder.hpp:107
std::vector< std::size_t > ShellRange
Each element represents the shell a new tile starts on.
Definition shellorder.hpp:55
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
Definition kcluster.hpp:41
Shell is a GaussianShell that is part of GaussianBasisSet, i.e. has a center on which it's centered.
Definition gaussbas.h:149
The GaussianBasisSet class is used describe a basis set composed of atomic gaussian orbitals.
Definition gaussbas.h:145
int nshell_on_center(int icenter) const
Return the number of shells on the given center.
A template class that maintains references counts.
Definition ref.h:361
Contains new MPQC code since version 3.
Definition integralenginepool.hpp:37

Generated at Wed Sep 25 2024 02:45:29 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.12.0.