1 /*
2  *                This source code is part of
3  *
4  *                          HelFEM
5  *                             -
6  * Finite element methods for electronic structure calculations on small systems
7  *
8  * Written by Susi Lehtola, 2018-
9  * Copyright (c) 2018- 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 #ifndef SADATOM_DFTGRID_H
18 #define SADATOM_DFTGRID_H
19 
20 #include "basis.h"
21 
22 namespace helfem {
23   namespace sadatom {
24     namespace dftgrid {
25 
26       /// Worker class
27       class DFTGridWorker {
28       protected:
29         /// Basis set
30         const helfem::sadatom::basis::TwoDBasis *basp;
31 
32         /// Total quadrature weight
33         arma::rowvec wtot;
34 
35         /// List of basis functions in element
36         arma::uvec bf_ind;
37         /// Values of important functions in grid points, Nbf * Ngrid
38         arma::mat bf;
39         /// Radial gradient
40         arma::mat bf_rho;
41 
42         /// Density helper matrices: P_{uv} chi_v, and P_{uv} nabla(chi_v)
43         arma::mat Pv, Pv_rho;
44         /// Same for spin-polarized
45         arma::mat Pav, Pav_rho;
46         arma::mat Pbv, Pbv_rho;
47 
48         /// Is gradient needed?
49         bool do_grad;
50         /// Is kinetic energy density needed?
51         bool do_tau;
52         /// Is laplacian needed?
53         bool do_lapl;
54 
55         /// Spin-polarized calculation?
56         bool polarized;
57 
58         /// GGA functional used? (Set in compute_xc, only affects eval_Fxc)
59         bool do_gga;
60         /// Meta-GGA tau used? (Set in compute_xc, only affects eval_Fxc)
61         bool do_mgga_t;
62         /// Meta-GGA lapl used? (Set in compute_xc, only affects eval_Fxc)
63         bool do_mgga_l;
64 
65         // LDA stuff:
66 
67         /// Density, Nrho x Npts
68         arma::mat rho;
69         /// Energy density, Npts
70         arma::rowvec exc;
71         /// Functional derivative of energy wrt electron density, Nrho x Npts
72         arma::mat vxc;
73 
74         // GGA stuff
75 
76         /// Gradient of electron density, (3 x Nrho) x Npts
77         arma::mat grho;
78         /// Dot products of gradient of electron density, N x Npts; N=1 for closed-shell and 3 for open-shell
79         arma::mat sigma;
80         /// Functional derivative of energy wrt gradient of electron density
81         arma::mat vsigma;
82 
83         // Meta-GGA stuff
84 
85         /// Laplacian of electron density
86         arma::mat lapl;
87         /// Kinetic energy density
88         arma::mat tau;
89 
90         /// Functional derivative of energy wrt laplacian of electron density
91         arma::mat vlapl;
92         /// Functional derivative of energy wrt kinetic energy density
93         arma::mat vtau;
94 
95       public:
96         /// Dummy constructor
97         DFTGridWorker();
98         /// Constructor
99         DFTGridWorker(const helfem::sadatom::basis::TwoDBasis * basp);
100         /// Destructor
101         ~DFTGridWorker();
102 
103         /// Check necessity of computing gradient and laplacians, necessary for compute_bf!
104         void check_grad_tau_lapl(int x_func, int c_func);
105         /// Get necessity of computing gradient and laplacians
106         void get_grad_tau_lapl(bool & grad, bool & tau, bool & lapl) const;
107         /// Set necessity of computing gradient and laplacians, necessary for compute_bf!
108         void set_grad_tau_lapl(bool grad, bool tau, bool lapl);
109 
110         /// Compute basis functions on grid points
111         void compute_bf(size_t iel);
112         /// Free memory
113         void free();
114 
115         /// Update values of density, restricted calculation
116         void update_density(const arma::mat & P);
117         /// Update values of density, unrestricted calculation
118         void update_density(const arma::mat & Pa, const arma::mat & Pb);
119         /// Screen out small densities
120         void screen_density(double thr);
121 
122         /// Compute number of electrons
123         double compute_Nel() const;
124 
125         /// Initialize XC arrays
126         void init_xc();
127         /// Compute XC functional from density and add to total XC
128         /// array. Pot toggles evaluation of potential
129         void compute_xc(int func_id, const arma::vec & params, bool pot=true);
130         /// Evaluate exchange/correlation energy
131         double eval_Exc() const;
132         /// Zero out energy
133         void zero_Exc();
134 
135         /// Evaluate overlap matrix
136         void eval_overlap(arma::mat & S) const;
137 
138         /// Evaluate Fock matrix, restricted calculation
139         void eval_Fxc(arma::mat & H) const;
140         /// Evaluate Fock matrix, unrestricted calculation
141         void eval_Fxc(arma::mat & Ha, arma::mat & Hb, bool beta=true) const;
142       };
143 
144       /// Wrapper routine
145       class DFTGrid {
146       private:
147         /// Pointer to basis set
148         const helfem::sadatom::basis::TwoDBasis * basp;
149 
150       public:
151         /// Dummy constructor
152         DFTGrid();
153         /// Constructor
154         DFTGrid(const helfem::sadatom::basis::TwoDBasis * basp);
155         /// Destructor
156         ~DFTGrid();
157 
158         /// Compute Fock matrix, exchange-correlation energy and integrated electron density, restricted case
159         void eval_Fxc(int x_func, const arma::vec & x_pars, int c_func, const arma::vec & c_pars, const arma::mat & P, arma::mat & H, double & Exc, double & Nel, double thr);
160         /// Compute Fock matrix, exchange-correlation energy and integrated electron density, unrestricted case
161         void eval_Fxc(int x_func, const arma::vec & x_pars, int c_func, const arma::vec & c_pars, const arma::mat & Pa, const arma::mat & Pb, arma::mat & Ha, arma::mat & Hb, double & Exc, double & Nel, bool beta, double thr);
162 
163         /// Evaluate overlap
164         arma::mat eval_overlap();
165       };
166 
167       /// BLAS routine for LDA-type quadrature
increment_lda(arma::mat & H,const arma::rowvec & vxc,const arma::Mat<T> & f)168       template<typename T> void increment_lda(arma::mat & H, const arma::rowvec & vxc, const arma::Mat<T> & f) {
169         if(f.n_cols != vxc.n_elem) {
170           std::ostringstream oss;
171           oss << "Number of functions " << f.n_cols << " and potential values " << vxc.n_elem << " do not match!\n";
172           throw std::runtime_error(oss.str());
173         }
174         if(H.n_rows != f.n_rows || H.n_cols != f.n_rows) {
175           std::ostringstream oss;
176           oss << "Size of basis function (" << f.n_rows << "," << f.n_cols << ") and Fock matrix (" << H.n_rows << "," << H.n_cols << ") doesn't match!\n";
177           throw std::runtime_error(oss.str());
178         }
179 
180         // Form helper matrix
181         arma::Mat<T> fhlp(f);
182         for(size_t i=0;i<fhlp.n_rows;i++)
183           for(size_t j=0;j<fhlp.n_cols;j++)
184             fhlp(i,j)*=vxc(j);
185         H+=arma::real(fhlp*arma::trans(f));
186       }
187 
188       /// BLAS routine for GGA-type quadrature
increment_gga(arma::mat & H,const arma::mat & gn,const arma::Mat<T> & f,arma::Mat<T> f_x)189       template<typename T> void increment_gga(arma::mat & H, const arma::mat & gn, const arma::Mat<T> & f, arma::Mat<T> f_x) {
190         if(gn.n_cols!=3) {
191           throw std::runtime_error("Grad rho must have three columns!\n");
192         }
193         if(f.n_rows != f_x.n_rows || f.n_cols != f_x.n_cols) {
194           throw std::runtime_error("Sizes of basis function and derivative matrices doesn't match!\n");
195         }
196         if(H.n_rows != f.n_rows || H.n_cols != f.n_rows) {
197           throw std::runtime_error("Sizes of basis function and Fock matrices doesn't match!\n");
198         }
199 
200         // Compute helper: gamma_{ip} = \sum_c \chi_{ip;c} gr_{p;c}
201         //                 (N, Np)    =        (N Np; c)    (Np, 3)
202         arma::Mat<T> gamma(f.n_rows,f.n_cols);
203         gamma.zeros();
204         {
205           // Helper
206           arma::rowvec gc;
207 
208           // x gradient
209           gc=arma::strans(gn.col(0));
210           for(size_t j=0;j<f_x.n_cols;j++)
211             for(size_t i=0;i<f_x.n_rows;i++)
212               f_x(i,j)*=gc(j);
213           gamma+=f_x;
214         }
215 
216         // Form Fock matrix
217         H+=arma::real(gamma*arma::trans(f) + f*arma::trans(gamma));
218       }
219     }
220   }
221 }
222 
223 #endif
224