// Variational Monte Carlo for atoms and quantum dots with importance sampling // Test case for 2-electron quantum dot, no classes using Mersenne-Twister RNG // Compile as c++ -O3 -std=c++11 -Rpass=loop-vectorize -o Vmcqdot.x vmcqdot.cpp -larmadillo #include #include #include #include #include #include #include using namespace std; using namespace arma; // output file as global variable ofstream ofile; // the step length and its squared inverse for the second derivative // Here we define global variables used in various functions // These can be changed by using classes int Dimension = 2; int NumberParticles = 2; // we fix also the number of electrons to be 2 // declaration of functions // The Mc sampling for the variational Monte Carlo void MonteCarloSampling(int, double &, double &, vec &); // The variational wave function double WaveFunction(mat &, vec &); // The local energy double LocalEnergy(mat &, vec &); // The quantum force void QuantumForce(mat &, mat &, vec &); // inline function for single-particle wave function inline double SPwavefunction(double r, double alpha) { return exp(-alpha*r*0.5); } // inline function for derivative of single-particle wave function inline double DerivativeSPwavefunction(double r, double alpha) { return -r*alpha; } // function for absolute value of relative distance double RelativeDistance(mat &r, int i, int j) { double r_ij = 0; for (int k = 0; k < Dimension; k++) { r_ij += (r(i,k)-r(j,k))*(r(i,k)-r(j,k)); } return sqrt(r_ij); } // inline function for derivative of Jastrow factor inline double JastrowDerivative(mat &r, double beta, int i, int j, int k){ return (r(i,k)-r(j,k))/(RelativeDistance(r, i, j)*pow(1.0+beta*RelativeDistance(r, i, j),2)); } // function for square of position of single particle double singleparticle_pos2(mat &r, int i) { double r_single_particle = 0; for (int j = 0; j < Dimension; j++) { r_single_particle += r(i,j)*r(i,j); } return r_single_particle; } void lnsrch(int n, vec &xold, double fold, vec &g, vec &p, vec &x, double *f, double stpmax, int *check, double (*func)(vec &p)); void dfpmin(vec &p, int n, double gtol, int *iter, double *fret, double(*func)(vec &p), void (*dfunc)(vec &p, vec &g)); static double sqrarg; #define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg) static double maxarg1,maxarg2; #define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\ (maxarg1) : (maxarg2)) // Begin of main program int main(int argc, char* argv[]) { int TotalNumberMCsamples; if (argc <= 1) { cout << "Bad Usage: " << argv[0] << " Read also output file on same line and number of Monte Carlo cycles" << endl; } // Read filename and number of Monte Carlo cycles from the command line if (argc > 2) { string filename = argv[1]; // first command line argument after name of program TotalNumberMCsamples = atoi(argv[2]); string fileout = filename; string argument = to_string(TotalNumberMCsamples); // Final filename as filename+NumberMCsamples fileout.append(argument); ofile.open(fileout); } // Two variational parameters only vec VariationalParameters(2); // Loop over variational parameters for (double alpha = 0.5; alpha <= 1.5; alpha +=0.1){ for (double beta = 0.1; beta <= 0.5; beta +=0.05){ VariationalParameters(0) = alpha; // value of alpha VariationalParameters(1) = beta; // value of beta // Do the mc sampling and accumulate data with MPI_Reduce double Energy, EnergySquared; Energy = EnergySquared = 0.0; MonteCarloSampling(TotalNumberMCsamples, Energy, EnergySquared, VariationalParameters); double Variance = EnergySquared-Energy*Energy; double StandardDeviation = sqrt(Variance/((double)TotalNumberMCsamples)); // over optimistic error ofile << setiosflags(ios::showpoint | ios::uppercase); ofile << setw(15) << setprecision(8) << VariationalParameters(0); ofile << setw(15) << setprecision(8) << VariationalParameters(1); ofile << setw(15) << setprecision(8) << Energy; ofile << setw(15) << setprecision(8) << Variance; ofile << setw(15) << setprecision(8) << StandardDeviation << endl; } } ofile.close(); // close output file return 0; } // end of main function // Monte Carlo sampling with the Metropolis algorithm void MonteCarloSampling(int NumberMCsamples, double &cumulative_e, double &cumulative_e2, vec &VariationalParameters) { // Initialize the seed and call the Mersienne algo std::random_device rd; std::mt19937_64 gen(rd()); // Set up the uniform distribution for x \in [[0, 1] std::uniform_real_distribution UniformNumberGenerator(0.0,1.0); std::normal_distribution Normaldistribution(0.0,1.0); // diffusion constant from Schroedinger equation double D = 0.5; double timestep = 0.05; // we fix the time step for the gaussian deviate // allocate matrices which contain the position of the particles mat OldPosition( NumberParticles, Dimension), NewPosition( NumberParticles, Dimension); mat OldQuantumForce(NumberParticles, Dimension), NewQuantumForce(NumberParticles, Dimension); double Energy = 0.0; double EnergySquared = 0.0; double DeltaE = 0.0; // initial trial positions for (int i = 0; i < NumberParticles; i++) { for (int j = 0; j < Dimension; j++) { OldPosition(i,j) = Normaldistribution(gen)*sqrt(timestep); } } double OldWaveFunction = WaveFunction(OldPosition, VariationalParameters); QuantumForce(OldPosition, OldQuantumForce, VariationalParameters); // loop over monte carlo cycles for (int cycles = 1; cycles <= NumberMCsamples; cycles++){ // new position for (int i = 0; i < NumberParticles; i++) { for (int j = 0; j < Dimension; j++) { // gaussian deviate to compute new positions using a given timestep NewPosition(i,j) = OldPosition(i,j) + Normaldistribution(gen)*sqrt(timestep)+OldQuantumForce(i,j)*timestep*D; } // for the other particles we need to set the position to the old position since // we move only one particle at the time for (int k = 0; k < NumberParticles; k++) { if ( k != i) { for (int j = 0; j < Dimension; j++) { NewPosition(k,j) = OldPosition(k,j); } } } double NewWaveFunction = WaveFunction(NewPosition, VariationalParameters); QuantumForce(NewPosition, NewQuantumForce, VariationalParameters); // we compute the log of the ratio of the greens functions to be used in the // Metropolis-Hastings algorithm double GreensFunction = 0.0; for (int j = 0; j < Dimension; j++) { GreensFunction += 0.5*(OldQuantumForce(i,j)+NewQuantumForce(i,j))* (D*timestep*0.5*(OldQuantumForce(i,j)-NewQuantumForce(i,j))-NewPosition(i,j)+OldPosition(i,j)); } GreensFunction = exp(GreensFunction); // The Metropolis test is performed by moving one particle at the time if(UniformNumberGenerator(gen) <= GreensFunction*NewWaveFunction*NewWaveFunction/OldWaveFunction/OldWaveFunction ) { for (int j = 0; j < Dimension; j++) { OldPosition(i,j) = NewPosition(i,j); OldQuantumForce(i,j) = NewQuantumForce(i,j); } OldWaveFunction = NewWaveFunction; } } // end of loop over particles // compute local energy double DeltaE = LocalEnergy(OldPosition, VariationalParameters); // update energies Energy += DeltaE; EnergySquared += DeltaE*DeltaE; } // end of loop over MC trials // update the energy average and its squared cumulative_e = Energy/NumberMCsamples; cumulative_e2 = EnergySquared/NumberMCsamples; } // end MonteCarloSampling function // Function to compute the squared wave function and the quantum force double WaveFunction(mat &r, vec &VariationalParameters) { double wf = 0.0; // full Slater determinant for two particles, replace with Slater det for more particles wf = SPwavefunction(singleparticle_pos2(r, 0), VariationalParameters(0))*SPwavefunction(singleparticle_pos2(r, 1),VariationalParameters(0)); // contribution from Jastrow factor for (int i = 0; i < NumberParticles-1; i++) { for (int j = i+1; j < NumberParticles; j++) { wf *= exp(RelativeDistance(r, i, j)/((1.0+VariationalParameters(1)*RelativeDistance(r, i, j)))); } } return wf; } // Function to calculate the local energy without numerical derivation of kinetic energy double LocalEnergy(mat &r, vec &VariationalParameters) { // compute the kinetic and potential energy from the single-particle part // for a many-electron system this has to be replaced by a Slater determinant // The absolute value of the interparticle length mat length( NumberParticles, NumberParticles); // Set up interparticle distance for (int i = 0; i < NumberParticles-1; i++) { for(int j = i+1; j < NumberParticles; j++){ length(i,j) = RelativeDistance(r, i, j); length(j,i) = length(i,j); } } double KineticEnergy = 0.0; // Set up kinetic energy from Slater and Jastrow terms for (int i = 0; i < NumberParticles; i++) { for (int k = 0; k < Dimension; k++) { double sum1 = 0.0; for(int j = 0; j < NumberParticles; j++){ if ( j != i) { sum1 += JastrowDerivative(r, VariationalParameters(1), i, j, k); } } KineticEnergy += (sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0)))*(sum1+DerivativeSPwavefunction(r(i,k),VariationalParameters(0))); } } KineticEnergy += -2*VariationalParameters(0)*NumberParticles; for (int i = 0; i < NumberParticles-1; i++) { for (int j = i+1; j < NumberParticles; j++) { KineticEnergy += 2.0/(pow(1.0 + VariationalParameters(1)*length(i,j),2))*(1.0/length(i,j)-2*VariationalParameters(1)/(1+VariationalParameters(1)*length(i,j)) ); } } KineticEnergy *= -0.5; // Set up potential energy, external potential + eventual electron-electron repulsion double PotentialEnergy = 0; for (int i = 0; i < NumberParticles; i++) { double DistanceSquared = singleparticle_pos2(r, i); PotentialEnergy += 0.5*DistanceSquared; // sp energy HO part, note it has the oscillator frequency set to 1! } // Add the electron-electron repulsion for (int i = 0; i < NumberParticles-1; i++) { for (int j = i+1; j < NumberParticles; j++) { PotentialEnergy += 1.0/length(i,j); } } double LocalE = KineticEnergy+PotentialEnergy; return LocalE; } // Compute the analytical expression for the quantum force void QuantumForce(mat &r, mat &qforce, vec &VariationalParameters) { // compute the first derivative for (int i = 0; i < NumberParticles; i++) { for (int k = 0; k < Dimension; k++) { // single-particle part, replace with Slater det for larger systems double sppart = DerivativeSPwavefunction(r(i,k),VariationalParameters(0)); // Jastrow factor contribution double Jsum = 0.0; for (int j = 0; j < NumberParticles; j++) { if ( j != i) { Jsum += JastrowDerivative(r, VariationalParameters(1), i, j, k); } } qforce(i,k) = 2.0*(Jsum+sppart); } } } // end of QuantumForce function #define ITMAX 200 #define EPS 3.0e-8 #define TOLX (4*EPS) #define STPMX 100.0 void dfpmin(vec &p, int n, double gtol, int *iter, double *fret, double(*func)(vec &p), void (*dfunc)(vec &p, vec &g)) { int check,i,its,j; double den,fac,fad,fae,fp,stpmax,sum=0.0,sumdg,sumxi,temp,test; vec dg(n), g(n), hdg(n), pnew(n), xi(n); mat hessian(n,n); fp=(*func)(p); (*dfunc)(p,g); for (i = 0;i < n;i++) { for (j = 0; j< n;j++) hessian(i,j)=0.0; hessian(i,i)=1.0; xi(i) = -g(i); sum += p(i)*p(i); } stpmax=STPMX*FMAX(sqrt(sum),(double)n); for (its=1;its<=ITMAX;its++) { *iter=its; lnsrch(n,p,fp,g,xi,pnew,fret,stpmax,&check,func); fp = *fret; for (i = 0; i< n;i++) { xi(i)=pnew(i)-p(i); p(i)=pnew(i); } test=0.0; for (i = 0;i< n;i++) { temp=fabs(xi(i))/FMAX(fabs(p(i)),1.0); if (temp > test) test=temp; } if (test < TOLX) { return; } for (i=0;i test) test=temp; } if (test < gtol) { return; } for (i=0;i EPS*sumdg*sumxi) { fac=1.0/fac; fad=1.0/fae; for (i=0;i stpmax) for (i=0;i test) test=temp; } alamin=TOLX/test; alam=1.0; for (;;) { for (i=0;i0.5*alam) tmplam=0.5*alam; } } alam2=alam; f2 = *f; fold2=fold; alam=FMAX(tmplam,0.1*alam); } } #undef ALF #undef TOLX