Vector GetFiniteDifferenceGradient(Vector Original_coords) { int Natoms = Cluster::cluster().GetTotalNumberOfAtoms(); Vector Eplus(3*Natoms), Eminus(3*Natoms), Grad(3*Natoms); // Do the finite difference double delta = 0.001; // step size for (int i=0;i<3*Natoms;i++) { printf("Shifting coord %d by -%f\n",i,delta); Vector Coords = Original_coords; Coords[i] -= delta; // Update the coordinates & get the energy Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); Eplus[i] = Cluster::cluster().GetHMBIEnergy(); printf("Shifting coord %d by +%f\n",i,delta); Coords[i] += 2.0*delta; // Update the coordinates & get the energy Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); Eminus[i] = Cluster::cluster().GetHMBIEnergy(); } // Form the actual gradient in hartrees/bohr Grad.Set(); Grad = Eminus; Grad -= Eplus; Grad.Scale(1.0/(2*delta*AngToBohr)); //Grad.Print("Finite Difference HMBI Gradient"); return Grad; }
void MatterFreeE::Sound(double *S,double *Dencity,double *Energy,int Num) { VecCl T(Num),Eplus(Num),Tplus(Num),Dplus(Num),Pplus(Num),P(Num); Temperature(T.Ptr,Dencity,Energy,Num); FreeEPtr->Pressure(P.Ptr,Dencity,T.Ptr,Num); Tplus= T * (1 + MinimT2EstpCoef) + MinimT2EstpMin; FreeEPtr->Energy(Eplus.Ptr,Dencity,Tplus.Ptr,Num); for (int k=1;k<=Num;k++) { Dplus[k]=Dencity[k]; if (Dencity[k]<MathZer) { cout<<"MatterFreeEIO::Sound; Dencity<MathZer :" <<Dencity<<"\n";Tplus[k]=0;continue;} double dE=max<double>(Eplus[k]-Energy[k],MathZer); double dr=Dencity[k]+dE*sqr(Dencity[k])/max<double>(1e-4,P.Ptr[k]); if (fabs(dr)<MathZer) {Tplus[k]=0;continue;} Dplus[k]=Dplus[k]+dr; } FreeEPtr->Pressure(Pplus.Ptr,Dplus.Ptr,Tplus.Ptr,Num); for (int k=1;k<=Num;k++) S[k]=sqrt( max<double>(0,Pplus[k]-P[k])/(Dplus[k]-Dencity[k]) ); };
/* HMBI code. Invoke as: hmbi <input file> [# of processors] The number of processors is optional, and the default is 1. To run in code in parallel, the source code must be compiled with -DPARALLEL, and MPI is required. */ main(int argc, char ** argv) { if (argc < 2) { printf("HMBI syntax:: hmbi <input file> [ # of processors (optional)]\n"); exit(1); } int nproc = 1; if (argc > 2) { string tmp = argv[2]; nproc = atoi(tmp.c_str()); } #ifdef PARALLEL int mynode=0,totalnodes; if (nproc > 1) { MPI_Init(&argc, &argv); MPI_Comm io_comm; MPI_File *fh, *fg; MPI_Info info; MPI_Comm_size(MPI_COMM_WORLD, &totalnodes); // get totalnodes MPI_Comm_rank(MPI_COMM_WORLD, &mynode); // get mynode if (mynode==0) printf("Running MPI parallel version with %d processors\n",nproc); } if (mynode == 0) { #endif /* PARALLEL */ // Set the number of processors Params::Parameters().SetNumberOfProcessors(nproc); // Store the primary process ID of the job. Used to create unique // scratch files int pid = getpid(); Params::Parameters().SetPID(pid); // Start wall clock timer time_t start_time, stop_time; start_time = time(NULL); // Open input file const int maxlength = 50; char filename[maxlength]; strcpy(filename, argv[1]); ifstream infile; infile.open(filename); assert(infile.is_open()); // Initialize Cluster object Cluster::cluster().Initialize(infile,nproc); printf("Cluster initialization complete\n"); if (! Params::Parameters().DoForces()) Params::Parameters().SetUseDLFind(false); // Compute the Energy (and forces, if requested) if (!Params::Parameters().UseDLFind()) { Cluster::cluster().RunJobsAndComputeEnergy(); } if ( Params::Parameters().PrintLevel() > 0) Cluster::cluster().ComputeDistanceMatrix(); // For debugging: Compute finite difference stress tensor bool do_finite_difference_stress_tensor = false; if (do_finite_difference_stress_tensor) { double delta = 0.001; // step size, in Angstroms Vector a1(3), a2(3), a3(3); // original unit cell vectors Vector new_a1(3),new_a2(3), new_a3(3); // deformed unit cell vectors Matrix Strain(3,3), Stress(3,3); Stress.Set(); // Grab original unit cell vectors a1 = Cluster::cluster().GetUnitCellVector(0); a2 = Cluster::cluster().GetUnitCellVector(1); a3 = Cluster::cluster().GetUnitCellVector(2); printf("Original lattice vectors (Ang):\n"); printf("a1 = (%f, %f, %f)\n",a1[0],a1[1],a1[2]); printf("a2 = (%f, %f, %f)\n",a2[0],a2[1],a2[2]); printf("a3 = (%f, %f, %f)\n",a3[0],a3[1],a3[2]); // Get cell volume, convert to Bohr^3 double V = Cluster::cluster().GetCellvolume() * AngToBohr * AngToBohr * AngToBohr; // Get the atomic coords. We use these when triggering the // reset of the dimer images, even though we don't change the atomic // positions at all. Vector Coords = Cluster::cluster().GetCurrentCoordinates(); // Loop over elements of the strain tensor e_ij for (int i=0;i<3;i++) { for (int j=0;j<3;j++) { // Set one element of the strain tensor to +delta printf("Setting Strain(%d,%d) to +%f\n",i,j,delta); Strain.Set(); new_a1.Set(); new_a2.Set(); new_a3.Set(); Strain(i,j) = delta; Strain.Print("Strain tensor"); // Deform the lattice vectors: new_ai = ai + Strain * ai new_a1 = Strain.MatrixTimesVector(a1); new_a1 += a1; new_a2 = Strain.MatrixTimesVector(a2); new_a2 += a2; new_a3 = Strain.MatrixTimesVector(a3); new_a3 += a3; printf("Updated lattice vectors (Ang):\n"); printf("a1 = (%f, %f, %f)\n",new_a1[0],new_a1[1],new_a1[2]); printf("a2 = (%f, %f, %f)\n",new_a2[0],new_a2[1],new_a2[2]); printf("a3 = (%f, %f, %f)\n",new_a3[0],new_a3[1],new_a3[2]); // Set the new lattice vectors and get the energy Cluster::cluster().SetUnitCellVectors(new_a1, new_a2, new_a3); Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); double E1 = Cluster::cluster().GetHMBIEnergy(); // Repeat for strain tensor element -delta printf("Setting Strain(%d,%d) to -%f\n",i,j,delta); Strain.Set(); new_a1.Set(); new_a2.Set(); new_a3.Set(); Strain(i,j) = -delta; Strain.Print("Strain tensor"); // Deform the lattice vectors: new_ai = ai + Strain * ai new_a1 = Strain.MatrixTimesVector(a1); new_a1 += a1; new_a2 = Strain.MatrixTimesVector(a2); new_a2 += a2; new_a3 = Strain.MatrixTimesVector(a3); new_a3 += a3; printf("Updated lattice vectors (Ang):\n"); printf("a1 = (%f, %f, %f)\n",new_a1[0],new_a1[1],new_a1[2]); printf("a2 = (%f, %f, %f)\n",new_a2[0],new_a2[1],new_a2[2]); printf("a3 = (%f, %f, %f)\n",new_a3[0],new_a3[1],new_a3[2]); // Set the new lattice vectors and get the energy Cluster::cluster().SetUnitCellVectors(new_a1, new_a2, new_a3); Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); double E2 = Cluster::cluster().GetHMBIEnergy(); // Final Stress tensor element stress(i,j) = 1/V dE/dstrain(i,j) Stress(i,j) = (1.0/V)*(E1 - E2)/(2*delta); } } printf("Finite Difference Stress Tensor:\n"); for (int i=0;i<3;i++) { printf("%15.9f %15.9f %15.9f\n",Stress(i,0), Stress(i,1), Stress(i,2)); } } // For debugging: Compute finite difference gradients bool do_finite_difference_grad = Params::Parameters().UseFiniteDifferenceGradients(); if (do_finite_difference_grad && Params::Parameters().DoForces()) { Vector Grad = Cluster::cluster().GetHMBIGradient(); Grad.Print("Analytical HMBI Gradient"); int Natoms = Cluster::cluster().GetTotalNumberOfAtoms(); Vector Eplus(3*Natoms), Eminus(3*Natoms); Vector Original_coords = Cluster::cluster().GetCurrentCoordinates(); Vector LatticeGradient(6); LatticeGradient.Set(); // Do the finite difference double delta = 0.001; // step size, in Angstroms /* for (int i=0;i<3*Natoms;i++) { printf("Shifting coord %d by -%f\n",i,delta); Vector Coords = Original_coords; Coords[i] -= delta; // Update the coordinates & get the energy Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); Eplus[i] = Cluster::cluster().GetHMBIEnergy(); printf("Shifting coord %d by +%f\n",i,delta); Coords[i] += 2.0*delta; // Update the coordinates & get the energy Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); Eminus[i] = Cluster::cluster().GetHMBIEnergy(); } */ // Now do finite difference over the lattice parameters, if appropriate double deltaTheta = 0.01; if (Params::Parameters().IsPeriodic() ) { string *params; params = new string[6]; params[0] = "a"; params[1] = "b"; params[2] = "c"; params[3] = "alpha", params[4] = "beta"; params[5] = "gamma"; Vector Coords = Original_coords; // we need these to trigger the reset of the // dimer images, even if we don't change the atomic positions at all. for (int i=0;i<6;i++) { double current = Cluster::cluster().GetUnitCellParameter(params[i]); double new1,new2; if (i < 3) { printf("Shifting coord %s by -%f\n",params[i].c_str(),delta); new1 = current - delta; } else { printf("Shifting coord %s by -%f\n",params[i].c_str(),deltaTheta); new1 = current - deltaTheta; } Cluster::cluster().SetUnitCellParameter(params[i],new1); Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); double E1 = Cluster::cluster().GetHMBIEnergy(); if (i < 3) { printf("Shifting coord %s by +%f\n",params[i].c_str(),delta); new2 = current + delta; } else { printf("Shifting coord %s by +%f\n",params[i].c_str(),deltaTheta); new2 = current + deltaTheta; } // Update the coordinates & get the energy Cluster::cluster().SetUnitCellParameter(params[i],new2); Cluster::cluster().SetNewCoordinates(Coords); Cluster::cluster().RunJobsAndComputeEnergy(); double E2 = Cluster::cluster().GetHMBIEnergy(); if (i < 3) { LatticeGradient[i] = (E2 - E1) / ((new2 - new1)*AngToBohr); } else{ LatticeGradient[i] = (E2 - E1) / ((new2 - new1)*DegreesToRadians); } // Reset the lattice parameter to its original value Cluster::cluster().SetUnitCellParameter(params[i],current); } } // Form the actual gradient, G = (Eminus - Eplus)/(2*delta) Grad.Set(); Grad = Eminus; Grad -= Eplus; Grad.Scale(1.0/(2*delta*AngToBohr)); Grad.Print("Finite Difference HMBI Gradient"); LatticeGradient.Print("Finite Difference Lattice Parameter Gradient"); printf("*** Finished with finite difference."); exit(1); } // End finite difference debug code // Optimize geometry, if requested if ( Params::Parameters().DoForces() ) { if (Params::Parameters().UseDLFind()) { // Use DL-FIND for geometry optimization int Natoms = Cluster::cluster().GetTotalNumberOfAtoms(); int nvarin = 3*Natoms; int nvarin2 = 5*Natoms;// nframe*nat*3 + nweight + nmass + n_po_scaling int nspec = 2*Natoms; // nspec= nat + nz + 5*ncons + 2*nconn int master = 1; // work is done on the master node printf("Using DL-FIND for optimization\n"); dl_find_(&nvarin, &nvarin2, &nspec, &master); // Final energy printing doesn't work, because final //dlf_put_coords erases energies... double energy = Cluster::cluster().GetHMBIEnergy(); printf("HMBI Final Energy = %15.9f\n",energy); printf("\n"); Cluster::cluster().ComputeDistanceMatrix(); // Save a copy of the new geometry in a new input file. FILE *input; string input_file = "new_geom.in"; if ((input = fopen(input_file.c_str(),"w"))==NULL) { printf("OptimizeGeometry() : Cannot open file '%s'\n",input_file.c_str()); exit(1); } Cluster::cluster().PrintInputFile(input); printf("\nNew input file written to '%s'\n",input_file.c_str()); fclose(input); } else{ string type = "SteepestDescent"; //string type = "ConjugateGradients"; OptimizeGeometry(type ); } } if ( Params::Parameters().GetJobTypeStr() == "hessian" ) { printf("Computing finite difference Hessian\n"); fflush(stdout); Vector Original_coords = Cluster::cluster().GetCurrentCoordinates(); int imon = Params::Parameters().GetFrequencyForMonomer(); printf("Computing the vibrational frequencies for Monomer %d\n",imon); Matrix Hessian = GetFiniteDifferenceHessian(Original_coords,imon); Cluster::cluster().ComputeHarmonicFrequencies(Hessian); } infile.close(); if (Params::Parameters().CheckWarnings() > 0) { printf("\nHMBI job completed, but with %d warning(s).\n",Params::Parameters().CheckWarnings()); } else { printf("\nHMBI job succesful!!!\n"); } #ifdef PARALLEL // Tell all nodes to shut down if (nproc > 1) { MPI_Comm_size(MPI_COMM_WORLD, &totalnodes); // get totalnodes for (int rank = 1; rank < totalnodes; rank++) { MPI_Send(0, 0, MPI_INT, rank, DIETAG, MPI_COMM_WORLD); } } #endif /* PARALLEL */ // Stop the timer and print out the time stop_time = time(NULL); double elapsed_time = difftime(stop_time,start_time); printf("Total job wall time = %.0f seconds\n",elapsed_time); #ifdef PARALLEL } else { /* Slave node controller: just accept & run the jobs */ int MPI_PrintLevel = 0; MPI_Comm_size(MPI_COMM_WORLD, &totalnodes); // get totalnodes MPI_Comm_rank(MPI_COMM_WORLD, &mynode); // get mynode while (1) { char job[BUFFSIZE]; int success; MPI_Status status; while (1) { success = 0; // run the job MPI_Recv(&job,BUFFSIZE,MPI_CHAR,0,MPI_ANY_TAG,MPI_COMM_WORLD,&status); /* Check the tag of the received message. */ if (status.MPI_TAG == DIETAG) { //printf("%d: Received DIETAG\n",mynode); break; } else if (status.MPI_TAG == QCHEM_TAG) { if (MPI_PrintLevel > 0 ) printf("%d: Q-chem job: %s\n",mynode,job); } else if (status.MPI_TAG == TINKER_TAG) { if (MPI_PrintLevel > 0 ) printf("%d: Tinker job: %s\n",mynode,job); } else if (status.MPI_TAG == CAMCASP_TAG) { if (MPI_PrintLevel > 0 ) printf("%d: CamCasp job: %s\n",mynode,job); } else { printf("Unknown MPI tag\n"); } // Go ahead and run the job system(job); string jobstr = job; success = CheckIfJobSuccessful(jobstr,status.MPI_TAG); //MPI_Send(&success,1,MPI_INT,0,0,MPI_COMM_WORLD); MPI_Send(&success,1,MPI_INT,0,status.MPI_TAG,MPI_COMM_WORLD); } if (MPI_PrintLevel > 0 ) printf("MPI: Node %d exiting\n",mynode); break; } } // Finalize MPI and exit fflush(stdout); MPI_Finalize(); #endif /* PARALLEL */ }