|
k-dot-p 0.1
|
00001 /* 00002 ** hamiltonain_effective_mass.c++ 00003 ** 00004 ** Made by Johnny Q. Hacker 00005 ** Login <solarion@nathan> 00006 ** 00007 00008 Copyright (C) 2009 Joseph Pingenot 00009 00010 This program is free software: you can redistribute it and/or modify 00011 it under the terms of the GNU Affero General Public License as published by 00012 the Free Software Foundation, either version 3 of the License, or 00013 (at your option) any later version. 00014 00015 This program is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 GNU Affero General Public License for more details. 00019 00020 You should have received a copy of the GNU Affero General Public License 00021 along with this program. If not, see <http://www.gnu.org/licenses/>. 00022 ** Started on Fri Jul 3 06:18:08 2009 Johnny Q. Hacker 00023 // Last update Thu Oct 1 21:44:48 2009 Johnny Q. Hacker 00024 */ 00025 00026 #include <string.h> 00027 #include <liblapackwrap/blaswrap_real_double.h> 00028 #include <liblapackwrap/lapackwrap_real_double.h> 00029 #include <libkdotp/hamiltonian_cartesian_effective_mass.h++> 00030 #include <libmodelxx/grid.h++> 00031 #include <libkdotp/physical_constants.h++> 00032 #include <libmodelxx/cartesian_grid.h++> 00033 #include <libkdotp/physical_constants.h++> 00034 #include <libmodelxx/material.h++> 00035 00036 using namespace kdp::constants; 00037 00038 namespace kdotp { 00039 namespace libkdotp { 00040 namespace hamiltonian { 00041 00042 typedef libmodelxx::grid::cartesian_grid<libmodelxx::structure::material*,1,1> matgrid_t; 00043 typedef libmodelxx::grid::cartesian_grid<double,1,1> potgrid_t; 00044 00045 cartesian_effective_mass::~cartesian_effective_mass() { 00046 /*We do a bunch of these frees. Cluster them. If they're not NULL, free them. 00047 *Note that these are pointers to pointers; we keep going until the array entry is NULL (if it's a real member of this class, it won't be NULL 00048 */ 00049 void** things_to_free[] = { 00050 (void**)&upper_diagonal, 00051 (void**)&diagonal, 00052 (void**)&matrix, 00053 (void**)&Q, 00054 (void**)&internal_potential, 00055 /*don't free matgrid or potgrid; they are still in use!*/ 00056 NULL 00057 }; 00058 for(int i=0; things_to_free[i] != NULL; i++) { 00059 if(*things_to_free[i] != NULL) { 00060 free(*things_to_free[i]); 00061 *things_to_free[i] = NULL; 00062 } 00063 } 00064 } 00065 00066 cartesian_effective_mass::cartesian_effective_mass(struct libmodelxx::structure::generic_structure *gs, boundary_condition bc_, bool second_neighbor_) 00067 : upper_diagonal(NULL), 00068 diagonal(NULL), 00069 matrix(NULL), 00070 Q(NULL), 00071 bc(bc_), 00072 second_neighbor(second_neighbor_), 00073 internal_potential(NULL), 00074 matgrid(NULL), 00075 potgrid(NULL) 00076 { 00077 n_neighbors=second_neighbor_?2:1; 00078 matgrid = (matgrid_t*)(gs->material_grid); 00079 potgrid = (potgrid_t*)(gs->potential_grid); 00080 L = matgrid->L[0]; 00081 /*Allocate and set the internal potential when we're constructed.*/ 00082 internal_potential = (double*)malloc(sizeof(double)*L); 00083 /*The internal potential is initially set to 0*/ 00084 for(unsigned int i=0; i<L; i++) internal_potential[i] = 0; 00085 if(bc == HARDWALL) { 00086 if(second_neighbor) { 00087 matrix = (double*)malloc(sizeof(double)*(n_neighbors+1)*L); 00088 if(matrix == NULL) { 00089 fprintf(stderr, "Error allocating matrix.\n"); 00090 return; 00091 } 00092 }else{ 00093 upper_diagonal = (double*)malloc(sizeof(double)*(L-1)); 00094 if(upper_diagonal == NULL) { 00095 fprintf(stderr, "Error allocating upper diagonal.\n"); 00096 return; 00097 } 00098 diagonal = (double*)malloc(sizeof(double)*L); 00099 if(diagonal == NULL) { 00100 fprintf(stderr, "Error allocating diagonal.\n"); 00101 return; 00102 } 00103 } 00104 }else{ 00105 matrix = (double*)malloc(sizeof(double)*L*L); 00106 if(matrix == NULL) { 00107 fprintf(stderr, "Error allocating matrix.\n"); 00108 return; 00109 } 00110 } 00111 dx = matgrid->a[0]; 00112 properties = (struct effective_mass_properties*)malloc(sizeof(struct effective_mass_properties)*L); 00113 if(properties == NULL) { 00114 fprintf(stderr, "Error allocating properties.\n"); 00115 return; 00116 } 00117 int e2=0; 00118 double Eg, Ev; 00119 Eg = Ev = 0; 00120 for(unsigned int i=0; i<L; i++) { 00121 properties[i].ooms = 1.0/matgrid->data[i]->get_property("m_e_Gamma", &e2); 00122 if(e2 != 0) { 00123 fprintf(stderr, "hamiltonians::cartesian_effective_mass: ERROR getting m_e_Gamma (err=%d).\n", e2); 00124 e2=0; 00125 } 00126 properties[i].epsilon = matgrid->data[i]->get_property("epsilon_0", &e2); 00127 if(e2 != 0) { 00128 fprintf(stderr, "hamiltonians::cartesian_effective_mass: ERROR getting epsilon_0 (err=%d).\n", e2); 00129 e2=0; 00130 } 00131 Eg = matgrid->data[i]->get_property("E_g_Gamma", &e2); 00132 if(e2 != 0) { 00133 fprintf(stderr, "hamiltonians::cartesian_effective_mass: ERROR getting E_g_Gamma (err=%d).\n", e2); 00134 e2=0; 00135 } 00136 Ev = matgrid->data[i]->get_property("E_v", &e2); 00137 if(e2 != 0) { 00138 fprintf(stderr, "hamiltonians::cartesian_effective_mass: ERROR getting E_v (err=%d).\n", e2); 00139 e2=0; 00140 } 00141 properties[i].Ec = Ev + Eg; 00142 properties[i].potential = potgrid->data[i]; 00143 } 00144 set_up_matrix(); 00145 } 00146 00147 void cartesian_effective_mass::update_matrix_with_internal_potential(double* old_potential) { 00148 /*Remember when updating that the pot for an electron is -1 V[i]. 00149 *Therefore, when updating, we *add* the old one to the previous diagonal terms to remove it, 00150 * and the *subtract* the new one (or, subtract the difference, as below; saves an op) 00151 */ 00152 for(unsigned int i=0; i<L; i++) { 00153 if(bc == HARDWALL) { 00154 if(!second_neighbor) { 00155 diagonal[i] -= internal_potential[i] - old_potential[i]; 00156 }else{ 00157 matrix[get_array_index(i, i)] -= internal_potential[i] - old_potential[i]; 00158 } 00159 }else{ 00160 matrix[get_matrix_index(i,i)] -= internal_potential[i] - old_potential[i]; 00161 } 00162 } 00163 } 00164 00165 void cartesian_effective_mass::replace_internal_potential(double* new_potential) { 00166 double *old_potential = internal_potential; 00167 internal_potential = new_potential; 00168 update_matrix_with_internal_potential(old_potential); 00169 free(old_potential); 00170 } 00171 00172 void cartesian_effective_mass::set_up_matrix() { 00173 /*Set the values of our matrix members to 0 to ensure they're properly initialized before we go setting specific numbers*/ 00174 if(bc == HARDWALL) { 00175 if(second_neighbor) { 00176 for(long unsigned i = 0; i<(n_neighbors+1)*L; i++) matrix[i] = 0; 00177 }else{ 00178 for(unsigned int i=0; i<(L-1); i++) { 00179 upper_diagonal[i] = diagonal[i] = 0; 00180 } 00181 diagonal[L-1]=0; 00182 } 00183 }else{ 00184 for(long unsigned int i=0; i<(L*L); i++) matrix[i] = 0; 00185 } 00186 int e2=0; 00187 /*hbar^2/d^2 in the notebook on April 29*/ 00188 double dconsts = -hbar*hbar/(m0*dx*dx)*1e18; 00189 //double dconsts = -1/(nm_to_bohr(matgrid->a[0])*nm_to_bohr(matgrid->a[0])); 00190 /*Set up the constants for the diagonal, superdiagonal, and second superdiagonal for first nearest neighbor*/ 00191 //double diag_const=-dconsts; 00192 double diag_const=-dconsts/4.0; 00193 double sd1_const=dconsts/4.0; 00194 double sd2_const=0; 00195 if(second_neighbor) { 00196 /*Modify our consts for second-nearest-neighbor*/ 00197 dconsts /= 48; 00198 diag_const=-60*dconsts; 00199 sd1_const=16*dconsts; 00200 sd2_const=-dconsts; 00201 } 00202 00203 //fprintf(stderr, "dconsts: hbar=%g m0=%g a[0]=%g dconsts=%g\n", hbar, m0, dx, dconsts); 00204 double ooms=0, ooms_prev=0, ooms_next=0, ooms_prev2=0, ooms_next2=0; 00205 /*Initialize the current ooms.*/ 00206 ooms = properties[0].ooms; 00207 ooms_next = properties[1].ooms; 00208 for(unsigned int i=0; i<L; i++, ooms_prev2 = ooms_prev, ooms_prev=ooms, ooms=ooms_next, ooms_next = ooms_next2) { 00209 if(i < L-2) { 00210 ooms_next2 = properties[i+2].ooms; 00211 } 00212 char *s; 00213 //fprintf(stderr, "%05u(%s): ooms_prev2=%g ooms_prev=%g ooms=%g ooms_next=%g ooms_next2=%g Ev+Eg=%g\n", i, "(?)", ooms_prev2, ooms_prev, ooms, ooms_next, ooms_next2, properties[i].Ec); 00214 if(bc == HARDWALL) { 00215 if(!second_neighbor) { 00216 /*Note the minus sign, because the potential is acting on an electron.*/ 00217 //diagonal[i] = Eg + Ev - potgrid->data[i] + diag_const*ooms; 00218 diagonal[i] = properties[i].Ec - properties[i].potential - internal_potential[i] + diag_const*(2*ooms + ooms_next + ooms_prev); 00219 //fprintf(stderr, "%05d: Eg+Ev=%g ooms_prev=%g ooms=%g d=%g ", i, Eg+Ev, ooms_prev, ooms, diagonal[i]); 00220 if(i != L-1) { 00221 /*note that ooms_next will not get updated on the last site (address would be out of the array)*/ 00222 upper_diagonal[i] = sd1_const*(ooms + ooms_next); 00223 //fprintf(stderr, "ooms_next=%g ud=%g\n", ooms_next, upper_diagonal[i]); 00224 //}else{ 00225 //fprintf(stderr, "(last; no ud)\n"); 00226 } 00227 }else{ 00228 ///////////////////////////////////////////////////////////////// 00229 //TODO: Need to fill in the appropriate constants. 00230 ///////////////////////////////////////////////////////////////// 00231 /*First, the diagonal*/ 00232 matrix[get_array_index(i, i)] = properties[i].Ec - properties[i].potential - internal_potential[i] + diag_const*ooms; 00233 if(i != L-1) { 00234 if(i != L-2) { 00235 /*Second superdiagonal*/ 00236 matrix[get_array_index(i, i+2)] = sd2_const*(ooms + ooms_next2); 00237 } 00238 /*Superdiagonal*/ 00239 matrix[get_array_index(i, i+1)] = sd1_const*(ooms + ooms_next); 00240 } 00241 //fprintf(stderr, "%05u: d=%g u=%g u2=%g\n", i, matrix[get_array_index(i, i)], matrix[get_array_index(i, i+1)], matrix[get_array_index(i, i+2)]); 00242 } 00243 }else{ 00244 ///////////////////////////////////////////////////////////////// 00245 //TODO: Need to fill in the appropriate constants. 00246 ///////////////////////////////////////////////////////////////// 00247 /*Mostly same as for hardwall, but now we have an explicit matrix*/ 00248 matrix[get_matrix_index(i,i)] = properties[i].Ec - properties[i].potential - internal_potential[i] + diag_const*ooms; 00249 if(i == 0) { 00250 /*Previous ooms is the final gridsite, since we have periodic BCs*/ 00251 ooms_prev = properties[L-1].ooms; 00252 matrix[get_matrix_index(i, i+1)] = sd1_const*(ooms + ooms_next); 00253 matrix[get_matrix_index(i, L-1)] = sd1_const*(ooms + ooms_prev); 00254 if(second_neighbor) { 00255 ooms_prev2 = properties[L-2].ooms; 00256 matrix[get_matrix_index(i, i+2)] = sd2_const*(ooms + ooms_next2); 00257 matrix[get_matrix_index(i, L-2)] = sd2_const*(ooms + ooms_prev2); 00258 } 00259 }else if(i == L-1) { 00260 /*Next ooms is the *first* gridsite 00261 *We've already gotten the previous value (in the for loop)*/ 00262 ooms_next = properties[0].ooms; 00263 /*Previous value is the final gridsite, since we have periodic BCs*/ 00264 matrix[get_matrix_index(i, 0)] = sd1_const*(ooms + ooms_next); 00265 matrix[get_matrix_index(i, i-1)] = sd1_const*(ooms + ooms_prev); 00266 if(second_neighbor) { 00267 ooms_next2 = properties[1].ooms; 00268 matrix[get_matrix_index(i, 1)] = sd2_const*(ooms + ooms_next); 00269 matrix[get_matrix_index(i, i-2)] = sd2_const*(ooms + ooms_prev); 00270 } 00271 }else{ 00272 /*We already have the prev ooms, but we need to fetch the next one.*/ 00273 matrix[get_matrix_index(i, i+1)] = sd1_const*(ooms + ooms_next); 00274 matrix[get_matrix_index(i, i-1)] = sd1_const*(ooms + ooms_prev); 00275 if(second_neighbor) { 00276 if(i == L-2) { 00277 ooms_next2 = properties[0].ooms; 00278 matrix[get_matrix_index(i, 0)] = sd2_const*(ooms + ooms_next2); 00279 matrix[get_matrix_index(i, i-2)] = sd2_const*(ooms + ooms_prev2); 00280 } else if(i == 1) { 00281 ooms_prev2 = properties[L-1].ooms; 00282 matrix[get_matrix_index(i, i+2)] = sd2_const*(ooms + ooms_next2); 00283 matrix[get_matrix_index(i, L-1)] = sd2_const*(ooms + ooms_prev2); 00284 } else { 00285 matrix[get_matrix_index(i, i+2)] = sd2_const*(ooms + ooms_next2); 00286 matrix[get_matrix_index(i, i-2)] = sd2_const*(ooms + ooms_prev2); 00287 } 00288 } 00289 } 00290 } 00291 } 00292 } 00293 00294 double* cartesian_effective_mass::get_eigenvectors(char VoI, double eVmin, double eVmax, int imin, int imax, double **evals, int *n_eigs, GError **err) { 00295 int info; 00296 if ((bc == PERIODIC) && (diagonal == NULL) && (upper_diagonal == NULL) && (Q == NULL)) { 00297 /*If we're not hardwall, we first have to reduce to tridiagonal.*/ 00298 /*We're a symmetric matrix, but not tridiag. 00299 *Call DSYTRD to get the tri-diag matrix*/ 00300 /*Call DORGTR to get the matrix Q to convert the eigenfunctions back to the position basis.*/ 00301 } else if (second_neighbor && (diagonal == NULL) && (upper_diagonal == NULL) && (Q == NULL)) { 00302 /*We're a banded matrix.*/ 00303 /*Call DSBTRD to get the tri-diagonal matrix and Q*/ 00304 info = llw_real_double_sbtrd_a('V', 'U', L, n_neighbors, matrix, n_neighbors+1, &diagonal, &upper_diagonal, &Q); 00305 if(info != 0) { 00306 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 1, "Got error from llw_real_double_sbtrd_a: %d\n", info); 00307 if(diagonal != NULL) { 00308 free(diagonal); 00309 diagonal = NULL; 00310 } 00311 if(upper_diagonal != NULL) { 00312 free(upper_diagonal); 00313 upper_diagonal = NULL; 00314 } 00315 if(Q != NULL) { 00316 free(Q); 00317 Q = NULL; 00318 } 00319 return NULL; 00320 } 00321 } 00322 double *eigvecs; 00323 int n_eigvecs; 00324 int* isuppz; 00325 info = llw_real_double_stegr_a('V', VoI, L, diagonal, upper_diagonal, eVmin, eVmax, imin, imax, n_eigs, evals, &eigvecs, &n_eigvecs, &isuppz); 00326 if((n_eigvecs <= *n_eigs) || (info != 0)) { 00327 if(n_eigvecs <= *n_eigs) { 00328 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 6, "in return from llw_real_double_stegr_a: failed to get same number or more eigenvec storage than there were eigenvals (%d and %d, respectively)", n_eigvecs, *n_eigs); 00329 }else{ 00330 if(info < 0) { 00331 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 1, "Bad argument to LAPACK call DSTEGR: %d\n", -info); 00332 }else if(info < 10) { 00333 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 2, "Error in BLAS call DLARRE called by LAPACK call DSGEGR: %d\n", info); 00334 }else if(info < 20) { 00335 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 3, "Error in BLAS call DLARRV called by LAPACK call DSGEGR: %d\n", info/2); 00336 }else if(info == 100) { 00337 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 5, "Error in liblapackwrap llw_real_double_stegr_a: malloc failed (%d)\n", info/100); 00338 }else{ 00339 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 4, "Got error (info=%d) from DSTEGR, but don't know what it means.\n", info); 00340 } 00341 } 00342 fprintf(stderr, "Got error; freeing evals\n"); 00343 free(*evals); 00344 free(eigvecs); 00345 *evals=NULL; 00346 eigvecs=NULL; 00347 }else{ 00348 free(isuppz); 00349 if(Q != NULL) { 00350 fprintf(stderr, "Transforming eigenvectors by Q.\n"); 00351 double* final_eigvecs = NULL; 00352 double one = 1.0; 00353 info = llw_real_double_gemm_a('N', 'N', L, n_eigvecs, L, &one, Q, L, eigvecs, L, &final_eigvecs); 00354 if(info != 0) { 00355 *err = g_error_new(HAMILTONIAN_CARTESIAN_EFFECTIVE_MASS_GERROR_DOMAIN, 4, "Got error (info=%d) from llw_real_double_gemmm_a\n", info); 00356 if(final_eigvecs != NULL) free(final_eigvecs); 00357 return eigvecs; 00358 } 00359 free(eigvecs); 00360 return final_eigvecs; 00361 } 00362 } 00363 return eigvecs; 00364 } 00365 } 00366 } 00367 }