1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: moment_compute.cc
4 // Copyright (C) 2014 Toru Shiozaki
5 //
6 // Author: Ryan D. Reynolds <RyanDReynolds@u.northwestern.edu>
7 // Maintainer: Shiozaki group
8 //
9 // This file is part of the BAGEL package.
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program 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 General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 //
24 
25 #include <src/molecule/moment_compute.h>
26 #include <src/molecule/carsph_shell.h>
27 #include <src/util/math/matop.h>
28 
29 using namespace std;
30 using namespace bagel;
31 
32 
33 // for each primitive basis function
mblock(const Shell & shell,const double exponent)34 array<shared_ptr<const Matrix>,6> MomentCompute::mblock(const Shell& shell, const double exponent) {
35 
36   const int angular_number = shell.angular_number();
37 
38   const int norig = (angular_number+1) * (angular_number+2) / 2;
39   const int ninc = norig + angular_number + 2;
40   const int ndec = norig - angular_number - 1;
41 
42   array<shared_ptr<Matrix>,6> mcart;
43   for (int i = 0; i != 3; ++i) {
44     mcart[i]   = make_shared<Matrix>(ninc, norig, true);
45     mcart[3+i] = shell.aux_decrement() ? make_shared<Matrix>(ndec, norig, true) : nullptr;
46   }
47 
48   for (int z = 0, column = 0; z <= angular_number; ++z) {
49     for (int y = 0; y <= angular_number-z; ++y, ++column) {
50       const int x = angular_number - y - z;
51 
52       // three components of the angular momentum
53       const array<int,3> index = {{x, y, z}};
54 
55       const double talph = 2.0 * exponent;
56 
57       // k tells us which dimension of the momentum operator we're using
58       for (int k = 0; k != 3; ++k) {
59 
60         // -i a_x phi^(x-1)
61         if (index[k] != 0) {
62           array<int,3> newindex = index;
63           --newindex[k];
64           const size_t row = newindex[2]*(angular_number) - newindex[2]*(newindex[2]-1)/2 + newindex[1];
65           mcart[3+k]->element(row, column) = -index[k];
66         }
67 
68         // +i 2alpha phi^(x+1)
69         {
70           array<int,3> newindex = index;
71           ++newindex[k];
72           const size_t row = newindex[2]*(angular_number+2) - newindex[2]*(newindex[2]-1)/2 + newindex[1];
73           mcart[k]->element(row, column) = talph;
74         }
75       }
76     }
77   }
78 
79   array<shared_ptr<const Matrix>,6> out;
80 
81   // convert this block from cartesian to shell.spherical
82   if (shell.spherical()) {
83     shared_ptr<Matrix> carsphmatrix = carsph_matrix(angular_number);
84     for (int i = 0; i != 6; ++i)
85       out[i] = mcart[i] ? make_shared<Matrix>(*mcart[i] * *carsphmatrix) : nullptr;
86   } else {
87     copy(mcart.begin(), mcart.end(), out.begin());
88   }
89 
90   return out;
91 }
92 
93 
94 // for each primitive basis function
mblock(const Shell & shell,const double exponent,const array<double,3> magnetic_field,const bool london)95 array<shared_ptr<const ZMatrix>,9> MomentCompute::mblock(const Shell& shell, const double exponent, const array<double,3> magnetic_field, const bool london) {
96 
97   const int angular_number = shell.angular_number();
98 
99   const int norig = (angular_number+1) * (angular_number+2) / 2;
100   const int ninc = norig + angular_number + 2;
101   const int ndec = norig - angular_number - 1;
102   const complex<double> imag(0.0, 1.0);
103 
104   array<shared_ptr<ZMatrix>,9> mcart;
105   for (int i = 0; i != 3; ++i) {
106     mcart[i]   = make_shared<ZMatrix>(ninc, norig, true);
107     mcart[3+i] = shell.aux_decrement() ? make_shared<ZMatrix>(ndec, norig, true) : nullptr;
108     mcart[6+i] = shell.aux_same() ? make_shared<ZMatrix>(norig, norig, true) : nullptr;
109   }
110 
111   for (int z = 0, column = 0; z <= angular_number; ++z) {
112     for (int y = 0; y <= angular_number-z; ++y, ++column) {
113       const int x = angular_number - y - z;
114 
115       // three components of the angular momentum
116       const array<int,3> index = {{x, y, z}};
117       const array<int,3> fwd  = {{1, 2, 0}};
118       const array<int,3> back = {{2, 0, 1}};
119 
120       const complex<double> tialph = imag * 2.0 * exponent;
121       const array<complex<double>,3> halfb = {{0.5*magnetic_field[0], 0.5*magnetic_field[1], 0.5*magnetic_field[2]}};
122 
123       // k tells us which dimension of the momentum operator we're using
124       for (int k = 0; k != 3; ++k) {
125 
126         // -i a_x phi^(x-1)
127         if (index[k] != 0) {
128           array<int,3> newindex = index;
129           --newindex[k];
130           const size_t row = newindex[2]*(angular_number) - newindex[2]*(newindex[2]-1)/2 + newindex[1];
131           mcart[3+k]->element(row, column) = -static_cast<double>(index[k])*imag;
132         }
133 
134         // +i 2alpha phi^(x+1)
135         {
136           array<int,3> newindex = index;
137           ++newindex[k];
138           const size_t row = newindex[2]*(angular_number+2) - newindex[2]*(newindex[2]-1)/2 + newindex[1];
139           mcart[k]->element(row, column) = tialph;
140         }
141 
142         // + 1/2 B_y phi^(z+1)
143         {
144           array<int,3> newindex = index;
145           ++newindex[back[k]];
146           const size_t row = newindex[2]*(angular_number+2) - newindex[2]*(newindex[2]-1)/2 + newindex[1];
147           mcart[k]->element(row, column) = halfb[fwd[k]];
148         }
149 
150         // - 1/2 B_z phi^(y+1)
151         {
152           array<int,3> newindex = index;
153           ++newindex[fwd[k]];
154           const size_t row = newindex[2]*(angular_number+2) - newindex[2]*(newindex[2]-1)/2 + newindex[1];
155           mcart[k]->element(row, column) = -halfb[back[k]];
156         }
157 
158         // + 1/2 (B_y R_z - B_z R_y) phi
159         if (!london) {
160           array<int,3> newindex = index;
161           const size_t row = newindex[2]*(angular_number+1) - newindex[2]*(newindex[2]-1)/2 + newindex[1];
162           mcart[6+k]->element(row, column) = halfb[fwd[k]]*shell.position(back[k]) - halfb[back[k]]*shell.position(fwd[k]);
163         }
164 
165       }
166     }
167   }
168 
169   array<shared_ptr<const ZMatrix>,9> out;
170   for (int i = 0; i != 9; ++i)
171     if (mcart[i])
172       mcart[i]->scale(-imag);
173 
174   // convert this block from cartesian to shell.spherical
175   if (shell.spherical()) {
176     auto carsphmatrix = make_shared<ZMatrix>(*carsph_matrix(angular_number), 1.0);
177     for (int i = 0; i != 9; ++i)
178       out[i] = mcart[i] ? make_shared<ZMatrix>(*mcart[i] * *carsphmatrix) : nullptr;
179   } else {
180     copy(mcart.begin(), mcart.end(), out.begin());
181   }
182 
183   return out;
184 }
185 
186 
call(const Shell & shell)187 array<shared_ptr<const Matrix>,3> MomentCompute::call(const Shell& shell) {
188 
189   const int angular_number = shell.angular_number();
190   const int ncart = (angular_number+1) * (angular_number+2) / 2;
191   const int ninc = ncart + (angular_number + 2);
192   const int ndec = ncart - (angular_number + 1);
193   const int n = ninc + ndec;
194   const int norig = shell.spherical() ? 2*angular_number+1 : ncart;
195 
196   // build the momentum transformation matrix for primitive functions
197   // each exponent gets 2 blocks, one for L+1 & one for L-1
198   array<shared_ptr<Matrix>,3> tmp;
199   for (int i = 0; i != 3; ++i)
200     tmp[i] = make_shared<Matrix>(n*shell.num_primitive(), norig*shell.num_primitive(), true);
201 
202   for (int j = 0; j != shell.num_primitive(); ++j) {
203     array<shared_ptr<const Matrix>,6> thisblock = mblock(shell, shell.exponents(j));
204     for (int i = 0; i != 3; ++i) {
205       tmp[i]->copy_block(j*ninc, j*norig, ninc, norig, thisblock[i]);
206       if (shell.aux_decrement())
207         tmp[i]->copy_block(ninc*shell.num_primitive()+j*ndec, j*norig, ndec, norig, thisblock[3+i]);
208     }
209   }
210 
211   // build the contraction matrix
212   Matrix contract(norig*shell.num_primitive(), norig*shell.num_contracted(), true);
213   for (int j = 0; j != shell.num_contracted(); ++j)
214     for (int k = shell.contraction_ranges(j).first; k != shell.contraction_ranges(j).second; ++k)
215       for (int l = 0; l != norig; ++l)
216         contract(k*norig+l, j*norig+l) = shell.contractions(j)[k];
217 
218   // contract
219   array<shared_ptr<const Matrix>,3> out;
220   for (int i = 0; i != 3; ++i)
221     out[i] = make_shared<Matrix>(*tmp[i] * contract);
222   return out;
223 }
224 
225 
call(const Shell & shell,const array<double,3> magnetic_field,const bool london)226 array<shared_ptr<const ZMatrix>,3> MomentCompute::call(const Shell& shell, const array<double,3> magnetic_field, const bool london) {
227 
228   const int angular_number = shell.angular_number();
229   const int ncart = (angular_number+1) * (angular_number+2) / 2;
230   const int ninc = ncart + (angular_number + 2);
231   const int nsame = london ? 0 : ncart;
232   const int ndec = ncart - (angular_number + 1);
233   const int norig = shell.spherical() ? 2*angular_number+1 : ncart;
234   const int n = ninc + ndec + nsame;
235 
236   // build the momentum transformation matrix for primitive functions
237   // each exponent gets 2-3 blocks, for L+1, L-1, and if needed, L+0 (in that order)
238   array<shared_ptr<ZMatrix>,3> tmp;
239   for (int i = 0; i != 3; ++i)
240     tmp[i] = make_shared<ZMatrix>(n*shell.num_primitive(), norig*shell.num_primitive(), true);
241 
242   for (int j = 0; j != shell.num_primitive(); ++j) {
243     array<shared_ptr<const ZMatrix>,9> thisblock = mblock(shell, shell.exponents(j), magnetic_field, london);
244     for (int i = 0; i != 3; ++i) {
245       tmp[i]->copy_block(j*ninc, j*norig, ninc, norig, thisblock[i]);
246       if (shell.aux_decrement())
247         tmp[i]->copy_block(ninc*shell.num_primitive()+j*ndec, j*norig, ndec, norig, thisblock[3+i]);
248       if (shell.aux_same())
249         tmp[i]->copy_block((ninc+ndec)*shell.num_primitive()+j*nsame, j*norig, nsame, norig, thisblock[6+i]);
250     }
251   }
252 
253   // build the contraction matrix
254   ZMatrix contract(norig*shell.num_primitive(), norig*shell.num_contracted(), true);
255   for (int j = 0; j != shell.num_contracted(); ++j)
256     for (int k = shell.contraction_ranges(j).first; k != shell.contraction_ranges(j).second; ++k)
257       for (int l = 0; l != norig; ++l)
258         contract(k*norig+l, j*norig+l) = shell.contractions(j)[k];
259 
260   // contract
261   array<shared_ptr<const ZMatrix>,3> out;
262   for (int i = 0; i != 3; ++i)
263     out[i] = make_shared<ZMatrix>(*tmp[i] * contract);
264   return out;
265 }
266 
267