1 /*
2 * This source code is part of
3 *
4 * E R K A L E
5 * -
6 * DFT from Hel
7 *
8 * Written by Susi Lehtola, 2010-2012
9 * Copyright (c) 2010-2012, Susi Lehtola
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 */
16
17 #include "emd_gto.h"
18 #include "../mathf.h"
19 #include <algorithm>
20
RadialGaussian(int lambdav,int lv)21 RadialGaussian::RadialGaussian(int lambdav, int lv) : RadialFourier(lv) {
22 lambda=lambdav;
23 }
24
~RadialGaussian()25 RadialGaussian::~RadialGaussian() {
26 }
27
add_term(const contr_t & t)28 void RadialGaussian::add_term(const contr_t & t) {
29 if(c.size()==0) {
30 c.push_back(t);
31 } else {
32 // Get upper bound
33 std::vector<contr_t>::iterator high;
34 high=std::upper_bound(c.begin(),c.end(),t);
35
36 // Corresponding index is
37 size_t ind=high-c.begin();
38
39 if(ind>0 && c[ind-1].z==t.z)
40 // Found it.
41 c[ind-1].c+=t.c;
42 else {
43 // Term does not exist, add it
44 c.insert(high,t);
45 }
46 }
47 }
48
print() const49 void RadialGaussian::print() const {
50 printf("l=%i, lambda=%i:",l,lambda);
51 for(size_t i=0;i<c.size();i++)
52 printf(" %+e (%e)\n",c[i].c,c[i].z);
53 }
54
get(double p) const55 std::complex<double> RadialGaussian::get(double p) const {
56 std::complex<double> ret=0.0;
57
58 if(lambda==l) {
59 // Pure spherical harmonics.
60 for(size_t i=0;i<c.size();i++)
61 ret+=c[i].c*exp(-p*p/(4.0*c[i].z));
62 } else {
63 // Mixed set.
64 for(size_t i=0;i<c.size();i++)
65 ret+=c[i].c*hyperg_1F1((l+lambda)/2.0+1.5,l+1.5,-p*p/(4.0*c[i].z));
66
67 ret*=pow(sqrt(2.0),lambda-l)*doublefact(l+lambda+1)/doublefact(2*l+1);
68 }
69
70 ret*=pow(std::complex<double>(0.0,-p),l)*pow(M_2_PI,1.0/4.0)/sqrt(doublefact(2*lambda+1));
71
72 return ret;
73 }
74
find_identical_functions(const BasisSet & bas)75 std::vector< std::vector<size_t> > find_identical_functions(const BasisSet & bas) {
76 // Get shells in basis set
77 std::vector<GaussianShell> sh=bas.get_shells();
78 // and the list of "identical" shells
79 std::vector< std::vector<size_t> > idsh=bas.find_identical_shells();
80
81 // The returned list is
82 std::vector< std::vector<size_t> > ret;
83
84 // Now we just add all of the functions to their separate lists.
85 for(size_t iidsh=0;iidsh<idsh.size();iidsh++) {
86 // The index of the first function in the return array is
87 size_t first=ret.size();
88
89 // Increase the size of the return array
90 ret.resize(ret.size()+bas.get_Nbf(idsh[iidsh][0]));
91
92 // Add the functions on all equivalent shells
93 for(size_t ifunc=0;ifunc<bas.get_Nbf(idsh[iidsh][0]);ifunc++)
94 for(size_t ish=0;ish<idsh[iidsh].size();ish++)
95 ret[first+ifunc].push_back(bas.get_first_ind(idsh[iidsh][ish])+ifunc);
96 }
97
98 /*
99 printf("Identical functions:\n");
100 for(size_t ig=0;ig<ret.size();ig++) {
101 printf("Group %i:",(int) ig);
102 for(size_t i=0;i<ret[ig].size();i++)
103 printf(" %i",(int) ret[ig][i]);
104 printf("\n");
105 }
106 */
107
108 return ret;
109 }
110
form_clm(const BasisSet & bas)111 std::vector< std::vector<ylmcoeff_t> > form_clm(const BasisSet & bas) {
112 // Get shells in basis set
113 std::vector<GaussianShell> sh=bas.get_shells();
114 // and the list of "identical" shells
115 std::vector< std::vector<size_t> > idsh=bas.find_identical_shells();
116
117 // Returned decompositions
118 std::vector< std::vector<ylmcoeff_t> > ret;
119
120 // Form cartesian expansions
121 CartesianExpansion cart(bas.get_max_am());
122
123 // Loop over shells
124 for(size_t iid=0;iid<idsh.size();iid++) {
125 // Angular momentum
126 int l=bas.get_am(idsh[iid][0]);
127
128 // Are spherical harmonics used?
129 if(bas.lm_in_use(idsh[iid][0])) {
130 // Easy job.
131 for(int m=-l;m<=l;m++) {
132 // The coefficients for current m
133 std::vector<ylmcoeff_t> c;
134
135 ylmcoeff_t tmp;
136 tmp.l=l;
137
138 if(m==0) {
139 tmp.m=0;
140 tmp.c=1.0;
141 c.push_back(tmp);
142 } else if(m>0) {
143 // Y_{lm} = ( (-1)^m Y_l^m + Y_l^{-m} ) / sqrt(2)
144 tmp.m=m;
145 tmp.c=M_SQRT1_2*pow(-1,m);
146 c.push_back(tmp);
147
148 tmp.m=-m;
149 tmp.c=M_SQRT1_2;
150 c.push_back(tmp);
151 } else {
152 // Y_{lm} = ( (-1)^m Y_l^{-m} - Y_l^{m} ) / [i sqrt(2)]
153
154 tmp.m=-m;
155 tmp.c=std::complex<double>(0.0,-M_SQRT1_2*pow(-1,m));
156 c.push_back(tmp);
157
158 tmp.m=m;
159 tmp.c=std::complex<double>(0.0,M_SQRT1_2);
160 c.push_back(tmp);
161 }
162
163 // Add function to stack
164 ret.push_back(c);
165 }
166 } else {
167 // Need to do cartesian decomposition. Loop over functions on shell.
168 for(int i=0; i<=l; i++) {
169 int nx = l - i;
170 for(int j=0; j<=i; j++) {
171 int ny = i-j;
172 int nz = j;
173
174 // Get transform
175 SphericalExpansion expn=cart.get(nx,ny,nz);
176
177 // Get coefficients
178 std::vector<ylmcoeff_t> c=expn.getcoeffs();
179 // and normalize them
180 double n=0.0;
181 for(size_t ic=0;ic<c.size();ic++)
182 n+=norm(c[ic].c);
183 n=sqrt(n);
184 for(size_t ic=0;ic<c.size();ic++)
185 c[ic].c/=n;
186
187 // and add them to the stack
188 ret.push_back(c);
189 }
190 }
191 }
192 }
193
194 /*
195 for(size_t i=0;i<ret.size();i++) {
196 printf("*** Function %3i ***\n",(int) i +1);
197 for(size_t j=0;j<ret[i].size();j++)
198 printf(" (% e,% e) Y_%i^%+i",ret[i][j].c.real(),ret[i][j].c.imag(),ret[i][j].l,ret[i][j].m);
199 printf("\n");
200 }
201 */
202
203 return ret;
204 }
205
form_radial(const BasisSet & bas)206 std::vector< std::vector<RadialGaussian> > form_radial(const BasisSet & bas) {
207 // Get shells in basis set
208 std::vector<GaussianShell> sh=bas.get_shells();
209 // and the list of "identical" shells
210 std::vector< std::vector<size_t> > idsh=bas.find_identical_shells();
211
212 // Returned functions
213 std::vector< std::vector<RadialGaussian> > ret;
214
215 // Form cartesian expansions
216 CartesianExpansion cart(bas.get_max_am());
217
218 // Loop over shells
219 for(size_t iid=0;iid<idsh.size();iid++) {
220 // Angular momentum
221 int am=bas.get_am(idsh[iid][0]);
222
223 // Normalized contraction for shell
224 std::vector<contr_t> c=bas.get_contr_normalized(idsh[iid][0]);
225
226 // The radial part for this shell
227 std::vector<RadialGaussian> rad;
228
229 // Are spherical harmonics used?
230 if(bas.lm_in_use(idsh[iid][0])) {
231 // Yes. We only get a single l value.
232 RadialGaussian help(am,am);
233
234 // Add the contractions.
235 for(size_t i=0;i<c.size();i++) {
236 contr_t term;
237 term.z=c[i].z;
238 term.c=c[i].c*pow(c[i].z,-am/2.0-3.0/4.0);
239 help.add_term(term);
240 }
241 rad.push_back(help);
242
243 // All functions on shell have the same radial part
244 for(size_t ind=0;ind<bas.get_Nbf(idsh[iid][0]);ind++)
245 ret.push_back(rad);
246
247 } else {
248 // No, we get multiple values of l.
249
250 // Loop over possible l values
251 for(int l=am;l>=0;l-=2) {
252 // Construct the radial gaussian
253 RadialGaussian help(am,l);
254
255 // Add the contractions.
256 for(size_t i=0;i<c.size();i++) {
257 contr_t term;
258 term.z=c[i].z;
259 term.c=c[i].c*pow(c[i].z,-l/2.0-3.0/4.0);
260 help.add_term(term);
261 }
262 // and add the term
263 rad.push_back(help);
264 }
265
266 // All functions on shell have the same radial part
267 for(size_t ind=0;ind<bas.get_Nbf(idsh[iid][0]);ind++)
268 ret.push_back(rad);
269 }
270 }
271
272 return ret;
273 }
274
GaussianEMDEvaluator()275 GaussianEMDEvaluator::GaussianEMDEvaluator() {
276 }
277
GaussianEMDEvaluator(const BasisSet & bas,const arma::cx_mat & Pv,int lp,int mp)278 GaussianEMDEvaluator::GaussianEMDEvaluator(const BasisSet & bas, const arma::cx_mat & Pv, int lp, int mp) {
279 // Check size of P
280 if(Pv.n_cols!=Pv.n_rows) {
281 ERROR_INFO();
282 throw std::runtime_error("P is not square matrix!\n");
283 }
284 if(Pv.n_cols!=bas.get_Nbf()) {
285 ERROR_INFO();
286 throw std::runtime_error("Density matrix does not correspond to basis!\n");
287 }
288
289 // Form radial functions
290 radf=form_radial(bas);
291
292 // Form identical functions
293 std::vector< std::vector<size_t> > idf=find_identical_functions(bas);
294
295 // Form Ylm expansion of functions
296 std::vector< std::vector<ylmcoeff_t> > clm=form_clm(bas);
297
298 // Form the index list of the centers of the functions
299 std::vector<size_t> locv;
300 for(size_t ish=0;ish<bas.get_Nshells();ish++)
301 for(size_t ifunc=0;ifunc<bas.get_Nbf(ish);ifunc++)
302 locv.push_back(bas.get_shell_center_ind(ish));
303
304 /*
305 printf("Functions centered on atoms:\n");
306 for(size_t i=0;i<loc.size();i++)
307 printf("%i: %i\n",(int) i+1, (int) loc[i]+1);
308 */
309
310 // Form the list of atomic coordinates
311 std::vector<coords_t> coord;
312 for(size_t inuc=0;inuc<bas.get_Nnuc();inuc++)
313 coord.push_back(bas.get_nuclear_coords(inuc));
314
315 /*
316 printf("Coordinates of atoms:\n");
317 for(size_t i=0;i<bas.get_Nnuc();i++)
318 printf("%3i % f % f % f\n",(int) i+1, coord[i].x, coord[i].y, coord[i].z);
319 */
320
321 *this=GaussianEMDEvaluator(radf,idf,clm,locv,coord,Pv,lp,mp);
322
323 // Check norm of radial functions
324 // check_norm();
325 }
326
GaussianEMDEvaluator(const std::vector<std::vector<RadialGaussian>> & radfv,const std::vector<std::vector<size_t>> & idfuncsv,const std::vector<std::vector<ylmcoeff_t>> & clm,const std::vector<size_t> & locv,const std::vector<coords_t> & coord,const arma::cx_mat & Pv,int lp,int mp)327 GaussianEMDEvaluator::GaussianEMDEvaluator(const std::vector< std::vector<RadialGaussian> > & radfv, const std::vector< std::vector<size_t> > & idfuncsv, const std::vector< std::vector<ylmcoeff_t> > & clm, const std::vector<size_t> & locv, const std::vector<coords_t> & coord, const arma::cx_mat & Pv, int lp, int mp) : EMDEvaluator(idfuncsv,clm,locv,coord,Pv,lp,mp) {
328 // Set the radial functions
329 radf=radfv;
330 // and assign the necessary pointers
331 update_pointers();
332 }
333
~GaussianEMDEvaluator()334 GaussianEMDEvaluator::~GaussianEMDEvaluator() {
335 }
336
337
operator =(const GaussianEMDEvaluator & rhs)338 GaussianEMDEvaluator & GaussianEMDEvaluator::operator=(const GaussianEMDEvaluator & rhs) {
339 // Assign superclass part
340 EMDEvaluator::operator=(rhs);
341 // Copy radial functions
342 radf=rhs.radf;
343 // Update the pointers
344 update_pointers();
345
346 return *this;
347 }
348
update_pointers()349 void GaussianEMDEvaluator::update_pointers() {
350 rad.resize(radf.size());
351 for(size_t i=0;i<radf.size();i++) {
352 rad[i].resize(radf[i].size());
353 for(size_t j=0;j<radf[i].size();j++)
354 rad[i][j]=&radf[i][j];
355 }
356 }
357