1 /*
2 * This file is written by Arto Sakko and Susi Lehtola, 2011.
3 * Copyright (c) 2011, Arto Sakko and Susi Lehtola
4 *
5 *
6 *
7 * This file is part of
8 *
9 * E R K A L E
10 * -
11 * DFT from Hel
12 *
13 * Erkale is written by Susi Lehtola, 2010-2011
14 * Copyright (c) 2010-2011, Susi Lehtola
15 *
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
21 */
22
23 #include "casida.h"
24 #include "casida_grid.h"
25 #include "../lmgrid.h"
26 #include "../settings.h"
27 #include "../xrs/fourierprod.h"
28 #include "../linalg.h"
29 #include "../eriworker.h"
30 #include "../stringutil.h"
31 #include "../timer.h"
32
33 #include <cstdio>
34 #include <cstdlib>
35 #include <cfloat>
36
37 // \delta parameter in Eichkorn et al
38 #define DELTA 1e-9
39
40 // Screen Coulomb integrals?
41 #define SCREENING
42 // Screening threshold
43 #define SCREENTHR 1e-10
44
45 extern Settings settings;
46
Casida()47 Casida::Casida() {
48 }
49
Casida(const BasisSet & basis,const arma::vec & Ev,const arma::mat & Cv,const arma::mat & Pv,const std::vector<double> & occs)50 Casida::Casida(const BasisSet & basis, const arma::vec & Ev, const arma::mat & Cv, const arma::mat & Pv, const std::vector<double> & occs) {
51 E.push_back(Ev);
52 C.push_back(Cv);
53 P.push_back(Pv);
54
55 // Form pairs
56 std::vector< std::vector<double> > occ;
57 occ.push_back(occs);
58 form_pairs(occ);
59 // Sanity check
60 if(pairs[0].size()==0)
61 throw std::runtime_error("No pairs for Casida calculation! Please check your input.\n");
62 printf("Casida calculation has %u pairs.\n",(unsigned int) pairs[0].size());
63
64 // Parse coupling mode
65 parse_coupling();
66
67 // Calculate K matrix
68 calc_K(basis);
69 // and solve Casida equation
70 solve();
71 }
72
Casida(const BasisSet & basis,const arma::vec & Ea,const arma::vec & Eb,const arma::mat & Ca,const arma::mat & Cb,const arma::mat & Pa,const arma::mat & Pb,const std::vector<double> & occa,const std::vector<double> & occb)73 Casida::Casida(const BasisSet & basis, const arma::vec & Ea, const arma::vec & Eb, const arma::mat & Ca, const arma::mat & Cb, const arma::mat & Pa, const arma::mat & Pb, const std::vector<double> & occa, const std::vector<double> & occb) {
74
75 E.push_back(Ea);
76 E.push_back(Eb);
77 C.push_back(Ca);
78 C.push_back(Cb);
79 P.push_back(Pa);
80 P.push_back(Pb);
81
82 // Form pairs
83 std::vector< std::vector<double> > occ;
84 occ.push_back(occa);
85 occ.push_back(occb);
86 form_pairs(occ);
87 // Sanity check
88 if(pairs[0].size()==0 && pairs[1].size()==0)
89 throw std::runtime_error("No pairs for Casida calculation! Please check your input.\n");
90 printf("Casida calculation has %u spin up and %u spin down pairs.\n",(unsigned int) pairs[0].size(),(unsigned int) pairs[1].size());
91
92 // Parse coupling mode
93 parse_coupling();
94
95 // Calculate K matrix
96 calc_K(basis);
97 // and solve Casida equation
98 solve();
99 }
100
parse_coupling()101 void Casida::parse_coupling() {
102 // Determine coupling
103 switch(settings.get_int("CasidaCoupling")) {
104 case(0):
105 // IPA.
106 coupling=IPA;
107 break;
108
109 case(1):
110 // RPA.
111 coupling=RPA;
112 break;
113
114 case(2):
115 // LDAXC, add xc contribution.
116 coupling=TDLDA;
117 break;
118
119 default:
120 throw std::runtime_error("Unknown coupling!\n");
121 }
122 }
123
calc_K(const BasisSet & basis)124 void Casida::calc_K(const BasisSet & basis) {
125 // Exchange and correlation functionals
126 int x_func=settings.get_int("CasidaXfunc");
127 int c_func=settings.get_int("CasidaCfunc");
128 double tol=settings.get_double("CasidaTol");
129
130 // Allocate memory
131 if(pairs.size()==1)
132 // Restricted case
133 K.zeros(pairs[0].size(),pairs[0].size());
134 else
135 // Unrestricted case
136 K.zeros(pairs[0].size()+pairs[1].size(),pairs[0].size()+pairs[1].size());
137
138 printf("\n");
139
140 // Do we need to form K?
141 if(coupling!=IPA) {
142 // Compute Coulomb coupling
143 Kcoul(basis);
144
145 // Compute XC coupling if necessary
146 if(coupling==TDLDA) {
147 Kxc(basis,tol,x_func,c_func);
148 }
149 }
150 }
151
~Casida()152 Casida::~Casida() {
153 };
154
matrix_transform(bool ispin,const arma::mat & m) const155 arma::mat Casida::matrix_transform(bool ispin, const arma::mat & m) const {
156 return arma::trans(C[ispin])*m*C[ispin];
157 }
158
matrix_transform(bool ispin,const arma::cx_mat & m) const159 arma::cx_mat Casida::matrix_transform(bool ispin, const arma::cx_mat & m) const {
160 return arma::trans(C[ispin])*m*C[ispin];
161 }
162
form_pairs(const std::vector<std::vector<double>> occs)163 void Casida::form_pairs(const std::vector< std::vector<double> > occs) {
164 // First, determine amount of occupied and virtual states.
165
166 nocc.resize(occs.size());
167 nvirt.resize(occs.size());
168
169 for(size_t ispin=0;ispin<nocc.size();ispin++) {
170 // Count number of occupied states.
171 nocc[ispin]=0;
172 while(occs[ispin][nocc[ispin]]>0)
173 nocc[ispin]++;
174
175 // Check that all values are equal.
176 for(size_t i=0;i<nocc[ispin];i++)
177 if(occs[ispin][i]!=occs[ispin][0]) {
178 ERROR_INFO();
179 throw std::runtime_error("Error - occupancies of occupied orbitals differ!\n");
180 }
181
182 // Count number of unoccupied states.
183 nvirt[ispin]=occs[ispin].size()-nocc[ispin];
184 for(size_t i=nocc[ispin];i<occs[ispin].size();i++)
185 if(occs[ispin][i]!=0.0) {
186 ERROR_INFO();
187 throw std::runtime_error("Gaps in occupancy not allowed!\n");
188 }
189 }
190
191 // Resize pairs
192 pairs.resize(nocc.size());
193 // Resize occupation numbers
194 f.resize(nocc.size());
195
196 // What orbitals are included in the calculation?
197 std::vector<std::string> states=splitline(settings.get_string("CasidaStates"));
198 if(states.size()==0) {
199 // Include all pairs in the calculation.
200 for(size_t ispin=0;ispin<nocc.size();ispin++) {
201 for(size_t iocc=0;iocc<nocc[ispin];iocc++)
202 for(size_t ivirt=0;ivirt<nvirt[ispin];ivirt++) {
203 states_pair_t tmp;
204 tmp.i=iocc;
205 tmp.f=nocc[ispin]+ivirt;
206 pairs[ispin].push_back(tmp);
207 }
208 }
209
210 // Form f. Polarized calculation?
211 bool pol=(nocc.size() == 2);
212 for(size_t ispin=0;ispin<nocc.size();ispin++) {
213 f[ispin].zeros(nocc[ispin]+nvirt[ispin]);
214 for(size_t iocc=0;iocc<nocc[ispin];iocc++)
215 f[ispin](iocc)=pol ? 1.0 : 2.0;
216 }
217 } else {
218 // Loop over spins
219 for(size_t ispin=0;ispin<nocc.size();ispin++) {
220
221 // Indices of orbitals to include.
222 std::vector<size_t> idx=parse_range(states[ispin]);
223
224 // Check that we don't run over states
225 if(idx[idx.size()-1]>nocc[ispin]+nvirt[ispin]) {
226 ERROR_INFO();
227 std::ostringstream oss;
228 oss << "Orbital " << idx[idx.size()-1] << " was requested in calculation, but only " << nocc[ispin]+nvirt[ispin] << " orbitals exist!\n";
229 throw std::runtime_error(oss.str());
230 }
231
232 // Convert to C++ indexing
233 for(size_t i=0;i<idx.size();i++)
234 idx[i]--;
235
236 // Get active orbitals and energies
237 arma::mat newC(C[ispin].n_rows,idx.size());
238 for(size_t i=0;i<idx.size();i++)
239 newC.col(i)=C[ispin].col(idx[i]);
240 C[ispin]=newC;
241
242 arma::vec newE(C[ispin].n_elem);
243 for(size_t i=0;i<idx.size();i++)
244 newE(i)=E[ispin](idx[i]);
245 E[ispin]=newE;
246
247 // Form f. Polarized calculation?
248 bool pol=(nocc.size()==2);
249 f[ispin].zeros(idx.size());
250 for(size_t i=0;i<idx.size();i++)
251 if(idx[i]<nocc[ispin])
252 // Occupied orbital
253 f[ispin](i)=pol ? 1.0 : 2.0;
254 else
255 break;
256
257 // Loop over indices
258 for(size_t iocc=0;iocc<idx.size();iocc++) {
259 // Check that it truly is occupied.
260 if(idx[iocc]>=nocc[ispin])
261 continue;
262
263 for(size_t jvirt=iocc+1;jvirt<idx.size();jvirt++) {
264 // Check that it truly is virtual.
265 if(idx[jvirt]<nocc[ispin])
266 continue;
267
268 // Create state pair (no idx needed here since we have
269 // already dropped inactive orbitals from C)
270 states_pair_t tmp;
271 tmp.i=iocc;
272 tmp.f=jvirt;
273
274 pairs[ispin].push_back(tmp);
275 }
276 }
277 }
278 }
279 }
280
esq(states_pair_t ip,bool ispin) const281 double Casida::esq(states_pair_t ip, bool ispin) const {
282 double dE=E[ispin](ip.f)-E[ispin](ip.i);
283 return dE*dE;
284 }
285
fe(states_pair_t ip,bool ispin) const286 double Casida::fe(states_pair_t ip, bool ispin) const {
287 return sqrt((f[ispin](ip.i)-f[ispin](ip.f))*(E[ispin](ip.f)-E[ispin](ip.i)));
288 }
289
solve()290 void Casida::solve() {
291 Timer t;
292
293 w_i.zeros(K.n_rows);
294 F_i.zeros(K.n_rows,K.n_cols);
295
296 // Generate the coupling matrix (eqn 2.11), but use the K array to
297 // save memory.
298
299 if(coupling!=IPA) {
300 // Add relevant factors to Coulomb / exchange-correlation terms
301 for(size_t ispin=0;ispin<pairs.size();ispin++)
302 for(size_t jspin=0;jspin<pairs.size();jspin++) {
303 // Offset in i
304 const size_t ioff=ispin*pairs[0].size();
305 // Offset in j
306 const size_t joff=jspin*pairs[0].size();
307
308 for(size_t ip=0;ip<pairs[ispin].size();ip++)
309 for(size_t jp=0;jp<pairs[jspin].size();jp++)
310 K(ioff+ip,joff+jp)*=2.0*fe(pairs[ispin][ip],ispin)*fe(pairs[jspin][jp],jspin);
311 }
312 }
313
314
315 // Add IPA contribution to diagonal
316 for(size_t ispin=0;ispin<pairs.size();ispin++) {
317 // Offset in i
318 const size_t ioff=ispin*pairs[0].size();
319 for(size_t ip=0;ip<pairs[ispin].size();ip++)
320 K(ioff+ip,ioff+ip)+=esq(pairs[ispin][ip],ispin);
321 }
322
323 // Solve eigenvalues and eigenvectors using direct linear algebraic methods
324 eig_sym_ordered(w_i, F_i, K);
325
326 // The eigenvalues are the squares of the excitation energies
327 for(size_t i=0;i<w_i.n_elem;i++)
328 w_i(i) = sqrt(w_i(i));
329
330 printf("Casida equations solved in %s.\n",t.elapsed().c_str());
331 }
332
transition(const std::vector<arma::mat> & m) const333 arma::mat Casida::transition(const std::vector<arma::mat> & m) const {
334 // Transition rates for every transition
335 arma::mat tr(w_i.n_elem,3);
336 tr.zeros();
337
338 // Loop over transitions
339 for(size_t it=0;it<w_i.n_elem;it++) {
340 // Loop over spins
341 for(size_t jspin=0;jspin<pairs.size();jspin++) {
342 // Offset in F
343 size_t joff=jspin*pairs[0].size();
344 // Loop over pairs
345 for(size_t jp=0;jp<pairs[jspin].size();jp++) {
346 // Compute |x| = x^T S^{-1/2} F_i
347 tr(it)+=m[jspin](pairs[jspin][jp].i,pairs[jspin][jp].f)*F_i(joff+jp,it)*fe(pairs[jspin][jp],jspin);
348 }
349 }
350
351 // Normalize to get \f$ \left\langle \Psi_0 \left| \hat{x}
352 // \right| \right\rangle \f$ , see Eq. 4.40 of Casida (1994),
353 // or compare Eqs. 2.14 and 2.16 in Jamorski et al (1996).
354 tr(it)/=sqrt(w_i(it));
355 }
356
357 // Transition energies and oscillator strengths
358 arma::mat osc(w_i.n_elem,2);
359 for(size_t it=0; it<w_i.n_elem;it++) {
360 osc(it,0) = w_i(it);
361 osc(it,1) = tr(it)*tr(it);
362 }
363
364 return osc;
365 }
366
transition(const std::vector<arma::cx_mat> & m) const367 arma::mat Casida::transition(const std::vector<arma::cx_mat> & m) const {
368 // Transition rates for every transition
369 arma::cx_vec tr(w_i.n_elem,3);
370 tr.zeros();
371
372 // Loop over transitions
373 for(size_t it=0;it<w_i.n_elem;it++) {
374 // Loop over spins
375 for(size_t jspin=0;jspin<pairs.size();jspin++) {
376 // Offset in F
377 size_t joff=jspin*pairs[0].size();
378 // Loop over pairs
379 for(size_t jp=0;jp<pairs[jspin].size();jp++) {
380 // Compute |x| = x^T S^{-1/2} F_i
381 tr(it)+=m[jspin](pairs[jspin][jp].i,pairs[jspin][jp].f)*F_i(joff+jp,it)*fe(pairs[jspin][jp],jspin);
382 }
383 }
384
385 // Normalize to get \f$ \left\langle \Psi_0 \left| \hat{x}
386 // \right| \right\rangle \f$ , see Eq. 4.40 of Casida (1994),
387 // or compare Eqs. 2.14 and 2.16 in Jamorski et al (1996).
388 tr(it)/=sqrt(w_i(it));
389 }
390
391 // Transition energies and oscillator strengths
392 arma::mat osc(w_i.n_elem,2);
393 for(size_t it=0; it<w_i.n_elem;it++) {
394 osc(it,0) = w_i(it);
395 osc(it,1) = std::norm(tr(it));
396 }
397
398 return osc;
399 }
400
dipole_transition(const BasisSet & bas) const401 arma::mat Casida::dipole_transition(const BasisSet & bas) const {
402 // Form dipole matrix
403 std::vector<arma::mat> dm=bas.moment(1);
404
405 // and convert it to the MO basis
406 std::vector< std::vector<arma::mat> > dip(3);
407 for(int ic=0;ic<3;ic++)
408 for(size_t ispin=0;ispin<C.size();ispin++) {
409 dip[ic].resize(C.size());
410 dip[ic][ispin]=matrix_transform(ispin,dm[ic]);
411 }
412
413 // Compute the oscillator strengths.
414 arma::mat osc(w_i.n_elem,2);
415 osc.zeros();
416 for(int ic=0;ic<3;ic++) {
417 // Compute the transitions in the current direction
418 arma::mat hlp=transition(dip[ic]);
419
420 // Store the energies
421 osc.col(0)=hlp.col(0);
422 // and increment the transition speeds
423 osc.col(1)+=2.0/3.0*hlp.col(1);
424 }
425
426 return osc;
427 }
428
transition(const BasisSet & basis,const arma::vec & q) const429 arma::mat Casida::transition(const BasisSet & basis, const arma::vec & q) const {
430 if(q.n_elem!=3) {
431 ERROR_INFO();
432 throw std::runtime_error("Momentum transfer should have 3 coordinates!\n");
433 }
434
435 // Form products of basis functions.
436 const size_t Nbf=basis.get_Nbf();
437 std::vector<prod_gaussian_3d> bfprod=compute_products(basis);
438
439 // and their Fourier transforms
440 std::vector<prod_fourier> bffour=fourier_transform(bfprod);
441
442 // Get the momentum transfer matrix
443 arma::cx_mat momtrans=momentum_transfer(bffour,Nbf,q);
444 // and transform it to the MO basis
445 std::vector< arma::cx_mat > mtrans(C.size());
446 for(size_t ispin=0;ispin<C.size();ispin++)
447 mtrans[ispin]=matrix_transform(ispin,momtrans);
448
449 // Compute the transitions
450 return transition(mtrans);
451 }
452
transition(const BasisSet & basis,double qr) const453 arma::mat Casida::transition(const BasisSet & basis, double qr) const {
454 // Form products of basis functions.
455 const size_t Nbf=basis.get_Nbf();
456 std::vector<prod_gaussian_3d> bfprod=compute_products(basis);
457
458 // and their Fourier transforms
459 std::vector<prod_fourier> bffour=fourier_transform(bfprod);
460
461 // Get the grid for computing the spherical averages.
462 std::vector<angular_grid_t> grid=form_angular_grid(2*basis.get_max_am());
463 // We normalize the weights so that for purely dipolar transitions we
464 // get the same output as with using the dipole matrix.
465 for(size_t i=0;i<grid.size();i++) {
466 // Dipole integral is only wrt theta - divide off phi part.
467 grid[i].w/=2.0*M_PI;
468 }
469
470 // Transition energies and oscillator strengths
471 arma::mat osc(w_i.n_elem,2);
472 osc.zeros();
473
474 // Loop over the angular mesh
475 for(size_t ig=0;ig<grid.size();ig++) {
476 // Current value of q is
477 arma::vec q(3);
478 q(0)=qr*grid[ig].r.x;
479 q(1)=qr*grid[ig].r.y;
480 q(2)=qr*grid[ig].r.z;
481 // and the weight is
482 double w=grid[ig].w;
483
484 // Get the momentum transfer matrix
485 arma::cx_mat momtrans=momentum_transfer(bffour,Nbf,q);
486 // and transform it to the MO basis
487 std::vector< arma::cx_mat > mtrans(C.size());
488 for(size_t ispin=0;ispin<C.size();ispin++)
489 mtrans[ispin]=matrix_transform(ispin,momtrans);
490
491 // Compute the transitions
492 arma::mat hlp=transition(mtrans);
493 // Store the energies
494 osc.col(0)=hlp.col(0);
495 // and increment the transition speeds
496 osc.col(1)+=w*hlp.col(1);
497 }
498
499 return osc;
500 }
501
coulomb_fit(const BasisSet & basis,std::vector<arma::mat> & munu,arma::mat & ab_inv) const502 void Casida::coulomb_fit(const BasisSet & basis, std::vector<arma::mat> & munu, arma::mat & ab_inv) const {
503 // Get density fitting basis
504 BasisSet dfitbas;
505
506 if(stricmp(settings.get_string("FittingBasis"),"Auto")==0)
507 dfitbas=basis.density_fitting();
508 else {
509 // Load basis library
510 BasisSetLibrary fitlib;
511 fitlib.load_basis(settings.get_string("FittingBasis"));
512
513 // Construct fitting basis
514 construct_basis(dfitbas,basis.get_nuclei(),fitlib);
515 }
516
517 // Amount of auxiliary functions
518 const size_t Naux=dfitbas.get_Nbf();
519
520 // Get the shells
521 std::vector<GaussianShell> orbshells=basis.get_shells();
522 std::vector<GaussianShell> auxshells=dfitbas.get_shells();
523
524 // Get list of pairs
525 std::vector<shellpair_t> orbpairs=basis.get_unique_shellpairs();
526 std::vector<shellpair_t> auxpairs=dfitbas.get_unique_shellpairs();
527
528 // Dummy shell, helper for computing ERIs
529 GaussianShell dummy=dummyshell();
530
531 // First, compute the two-center integrals
532 arma::mat ab(Naux,Naux);
533 ab.zeros();
534
535 #ifdef _OPENMP
536 #pragma omp parallel
537 #endif
538 {
539 ERIWorker eri(dfitbas.get_max_am(),dfitbas.get_max_Ncontr());
540 const std::vector<double> * erip;
541
542 #ifdef _OPENMP
543 #pragma omp for schedule(dynamic)
544 #endif
545 for(size_t ip=0;ip<auxpairs.size();ip++) {
546 // Shells in question are
547 size_t is=auxpairs[ip].is;
548 size_t js=auxpairs[ip].js;
549
550 // Compute (a|b)
551 eri.compute(&auxshells[is],&dummy,&auxshells[js],&dummy);
552 erip=eri.getp();
553
554 // Store integrals
555 for(size_t ii=0;ii<auxshells[is].get_Nbf();ii++)
556 for(size_t jj=0;jj<auxshells[js].get_Nbf();jj++) {
557 ab(auxshells[is].get_first_ind()+ii,auxshells[js].get_first_ind()+jj)=(*erip)[ii*auxshells[js].get_Nbf()+jj];
558 ab(auxshells[js].get_first_ind()+jj,auxshells[is].get_first_ind()+ii)=(*erip)[ii*auxshells[js].get_Nbf()+jj];
559 }
560 }
561 }
562
563 // Form ab_inv
564 ab_inv=arma::inv(ab+DELTA);
565
566 // Allocate memory for the three-center integrals.
567 munu.resize(C.size());
568 for(size_t ispin=0;ispin<C.size();ispin++) {
569 munu[ispin].zeros(C[ispin].n_cols*C[ispin].n_cols,Naux);
570 }
571
572 #ifdef SCREENING
573 // Screen the integrals.
574 arma::mat screen(orbshells.size(),orbshells.size());
575 #ifdef _OPENMP
576 #pragma omp parallel
577 #endif
578 {
579 ERIWorker eri(basis.get_max_am(),basis.get_max_Ncontr());
580 const std::vector<double> * erip;
581
582 #ifdef _OPENMP
583 #pragma omp for schedule(dynamic)
584 #endif
585 for(size_t ip=0;ip<orbpairs.size();ip++) {
586 // The shells in question are
587 size_t is=orbpairs[ip].is;
588 size_t js=orbpairs[ip].js;
589
590 // Compute (*Erip)
591 eri.compute(&orbshells[is],&orbshells[js],&orbshells[is],&orbshells[js]);
592 erip=eri.getp();
593
594 // Find out maximum value
595 double max=0.0;
596 for(size_t i=0;i<(*erip).size();i++)
597 if(fabs((*erip)[i])>max)
598 max=(*erip)[i];
599 max=sqrt(max);
600
601 // Store value
602 screen(is,js)=max;
603 screen(js,is)=max;
604 }
605 }
606 #endif
607
608 // Compute the three-center integrals.
609 #ifdef _OPENMP
610 #pragma omp parallel
611 #endif
612 {
613 ERIWorker eri(std::max(basis.get_max_am(),dfitbas.get_max_am()),std::max(basis.get_max_Ncontr(),dfitbas.get_max_Ncontr()));
614 const std::vector<double> * erip;
615
616
617 #ifdef _OPENMP
618 // Worker stack for each thread
619 std::vector<arma::mat> munu_wrk=munu;
620
621 #pragma omp for schedule(dynamic)
622 #endif
623 for(size_t ip=0;ip<orbpairs.size();ip++) {
624 // Shells in question are
625 size_t imu=orbpairs[ip].is;
626 size_t inu=orbpairs[ip].js;
627
628 #ifdef SCREENING
629 // Do we need to compute the integral?
630 if(screen(imu,inu)<SCREENTHR)
631 continue;
632 #endif
633
634 // Amount of functions on shell
635 size_t Nmu=orbshells[imu].get_Nbf();
636 // Index of first function on shell
637 size_t mu0=orbshells[imu].get_first_ind();
638
639 // Amount of functions on shell
640 size_t Nnu=orbshells[inu].get_Nbf();
641 // Index of first function on shell
642 size_t nu0=orbshells[inu].get_first_ind();
643
644 for(size_t ia=0;ia<auxshells.size();ia++) {
645 // Amount of functions on shell
646 size_t Na=auxshells[ia].get_Nbf();
647 // Index of first function on shell
648 size_t a0=auxshells[ia].get_first_ind();
649
650 // Compute the integral over the AOs
651 eri.compute(&auxshells[ia],&dummy,&orbshells[imu],&orbshells[inu]);
652 erip=eri.getp();
653
654 // Transform integrals to spin orbitals.
655 for(size_t ispin=0;ispin<C.size();ispin++) {
656 // Amount of active orbitals with current spin.
657 size_t Norb=C[ispin].n_cols;
658
659 size_t indmu, indnu, inda;
660 // Loop over orbitals
661 for(size_t mu=0;mu<Norb;mu++)
662 for(size_t nu=0;nu<=mu;nu++) {
663 // Loop over functions
664 for(size_t muf=0;muf<Nmu;muf++) {
665 indmu=mu0+muf;
666 for(size_t nuf=0;nuf<Nnu;nuf++) {
667 indnu=nu0+nuf;
668
669 // Coefficient of integral is
670 double c= (imu!=inu) ? C[ispin](indmu,mu)*C[ispin](indnu,nu) + C[ispin](indmu,nu)*C[ispin](indnu,mu) : C[ispin](indmu,mu)*C[ispin](indnu,nu);
671
672 // Loop over auxiliary functions
673 for(size_t af=0;af<Na;af++) {
674 inda=a0+af;
675
676 #ifdef _OPENMP
677 munu_wrk[ispin](mu*Norb+nu,inda)+=c*(*erip)[(af*Nmu+muf)*Nnu+nuf];
678 #else
679 munu[ispin](mu*Norb+nu,inda)+=c*(*erip)[(af*Nmu+muf)*Nnu+nuf];
680 #endif
681 }
682 }
683 }
684 }
685
686 } // end loop over spins
687 }
688 }
689
690 #ifdef _OPENMP
691 #pragma omp critical
692 // Sum the results together
693 for(size_t ispin=0;ispin<C.size();ispin++)
694 munu[ispin]+=munu_wrk[ispin];
695 #endif
696 } // end parallel region
697
698 // Symmetrize munu
699 for(size_t ispin=0;ispin<C.size();ispin++) {
700 size_t Norb=C[ispin].n_cols;
701 for(size_t mu=0;mu<Norb;mu++)
702 for(size_t nu=0;nu<=mu;nu++)
703 munu[ispin].row(nu*Norb+mu)=munu[ispin].row(mu*Norb+nu);
704 }
705 }
706
Kcoul(const BasisSet & basis)707 void Casida::Kcoul(const BasisSet & basis) {
708 Timer t;
709
710 if(!C.size())
711 throw std::runtime_error("Error - no orbitals!\n");
712
713 // Inverse Coulomb overlap matrix of fitting basis
714 arma::mat ab_inv;
715 // The [\mu \nu|I] matrices in Jamorski (4.16).
716 std::vector<arma::mat> munu_I;
717
718 // Get density fitting integrals
719 coulomb_fit(basis,munu_I,ab_inv);
720
721 // Construct K
722 for(size_t ispin=0;ispin<C.size();ispin++)
723 for(size_t jspin=0;jspin<=ispin;jspin++) {
724 // Amount of active orbitals
725 const size_t Norbi=C[ispin].n_cols;
726 const size_t Norbj=C[jspin].n_cols;
727
728 // Offset in i
729 const size_t ioff=ispin*pairs[0].size();
730 // Offset in j
731 const size_t joff=jspin*pairs[0].size();
732
733 if(ispin==jspin) {
734 #ifdef _OPENMP
735 #pragma omp parallel for
736 #endif
737 for(size_t ip=0;ip<pairs[ispin].size();ip++) {
738 // Off-diagonal, symmetrization is done later
739 for(size_t jp=0;jp<ip;jp++) {
740 double tmp=arma::as_scalar(munu_I[ispin].row(pairs[ispin][ip].i*Norbi+pairs[ispin][ip].f)*ab_inv*arma::trans(munu_I[ispin].row(pairs[ispin][jp].i*Norbi+pairs[ispin][jp].f)));
741 K(ioff+ip,joff+jp)+=tmp;
742 K(joff+jp,ioff+ip)+=tmp;
743 }
744 // Diagonal
745 K(ioff+ip,ioff+ip)+=arma::as_scalar(munu_I[ispin].row(pairs[ispin][ip].i*Norbi+pairs[ispin][ip].f)*ab_inv*arma::trans(munu_I[ispin].row(pairs[ispin][ip].i*Norbi+pairs[ispin][ip].f)));
746 }
747 } else {
748 #ifdef _OPENMP
749 #pragma omp parallel for
750 #endif
751 for(size_t ip=0;ip<pairs[ispin].size();ip++)
752 for(size_t jp=0;jp<pairs[jspin].size();jp++) {
753 K(ioff+ip,joff+jp)=arma::as_scalar(munu_I[ispin].row(pairs[ispin][ip].i*Norbi+pairs[ispin][ip].f)*ab_inv*arma::trans(munu_I[jspin].row(pairs[jspin][jp].i*Norbj+pairs[jspin][jp].f)));
754 }
755 }
756
757 if(ispin!=jspin) {
758 // Symmetrize
759 K.submat(joff,ioff,joff+pairs[jspin].size()-1,ioff+pairs[ispin].size()-1)=arma::trans(K.submat(ioff,joff,ioff+pairs[ispin].size()-1,joff+pairs[jspin].size()-1));
760 }
761 }
762
763 printf("Coulomb coupling matrix computed in %s.\n",t.elapsed().c_str());
764 }
765
Kxc(const BasisSet & bas,double tol,int x_func,int c_func)766 void Casida::Kxc(const BasisSet & bas, double tol, int x_func, int c_func) {
767 Timer t;
768
769 // Make grid
770 CasidaGrid grid(&bas);
771 // Evaluate Kxc
772 grid.Kxc(P,tol,x_func,c_func,C,pairs,K);
773
774 printf("XC coupling matrix computed in %s.\n",t.elapsed().c_str());
775 }
776