k-dot-p 0.1

hamiltonian_cartesian_effective_mass.c++

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines