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