//////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2011 The Regents of the University of California // // This file is part of Qbox // // Qbox is distributed under the terms of the GNU General Public License // as published by the Free Software Foundation, either version 2 of // the License, or (at your option) any later version. // See the file COPYING in the root directory of this distribution // or . // //////////////////////////////////////////////////////////////////////////////// // // CGCellStepper.C // //////////////////////////////////////////////////////////////////////////////// #include "CGCellStepper.h" #include "CGOptimizer.h" using namespace std; //////////////////////////////////////////////////////////////////////////////// CGCellStepper::CGCellStepper(Sample& s) : CellStepper(s), cgopt_(CGOptimizer(3*s.atoms.size()+9)), cell0(s_.atoms.cell()) { nat_ = atoms_.size(); cgopt_.set_alpha_start(0.002); cgopt_.set_alpha_max(0.5); cgopt_.set_beta_max(10.0); #ifdef DEBUG if ( s.ctxt_.onpe0() ) cgopt_.set_debug_print(); #endif rp_.resize(s.atoms.nsp()); for ( int is = 0; is < rp_.size(); is++ ) rp_[is].resize(3*atoms_.na(is)); // store full strain tensor u for consistency of dot products u_.resize(9); up_.resize(9); for ( int i = 0; i < u_.size(); i++ ) { u_[i] = 0.0; up_[i] = 0.0; } } //////////////////////////////////////////////////////////////////////////////// void CGCellStepper::compute_new_cell(double e, const valarray& sigma, const std::vector >& fion) { // compute new cell and ionic positions using the stress tensor sigma // and the forces on ions fion const UnitCell cell = s_.wf.cell(); // total number of dofs: 3* natoms + cell parameters valarray x(3*nat_+9), xp(3*nat_+9), g(3*nat_+9); // copy current positions into x vector > r0, gvec; atoms_.get_positions(r0); double tmp3[3]; // convert position from r0 to tau (relative) coordinates: tau = A^-1 R for ( int is = 0, i = 0; is < r0.size(); is++ ) { for ( int ia = 0; ia < r0[is].size()/3; ia++ ) { cell.vecmult3x3(cell.amat_inv(),&r0[is][3*ia],tmp3); x[i++]=tmp3[0]; x[i++]=tmp3[1]; x[i++]=tmp3[2]; } } // copy current strain tensor into x for ( int i = 0; i < 9; i++ ) x[3*nat_+i] = u_[i]; // convert forces on positions to forces on tau coordinates, store -f in gvec // f = A^-1 * fion gvec.resize(r0.size()); for ( int is = 0; is < r0.size(); is++ ) { gvec[is].resize(r0[is].size()); for ( int ia = 0; ia < r0[is].size()/3; ia++ ) { cell.vecmult3x3(cell.amat_inv(),&fion[is][3*ia],tmp3); gvec[is][3*ia+0]=-tmp3[0]; gvec[is][3*ia+1]=-tmp3[1]; gvec[is][3*ia+2]=-tmp3[2]; } } // project the gradient gvec in a direction compatible with constraints s_.constraints.enforce_v(r0,gvec); for ( int j = 0, is = 0; is < r0.size(); is++ ) for ( int i = 0; i < r0[is].size(); i++ ) g[j++] = gvec[is][i]; // compute descent direction in strain space // gradient g = - sigma * volume g[3*nat_+0] = -sigma[0] * cell.volume(); g[3*nat_+1] = -sigma[3] * cell.volume(); g[3*nat_+2] = -sigma[5] * cell.volume(); g[3*nat_+3] = -sigma[3] * cell.volume(); g[3*nat_+4] = -sigma[1] * cell.volume(); g[3*nat_+5] = -sigma[4] * cell.volume(); g[3*nat_+6] = -sigma[5] * cell.volume(); g[3*nat_+7] = -sigma[4] * cell.volume(); g[3*nat_+8] = -sigma[2] * cell.volume(); // the vector g now contains the gradient of the energy in tau+strain space // enforce constraints on the unit cell // Next line only affects the cell components of g enforce_constraints(&g[3*nat_]); // the vector g now contains a gradient of the energy in tau+strain space // compatible with atoms and cell constraints // CG algorithm cgopt_.compute_xp(x,e,g,xp); if ( s_.ctxt_.onpe0() ) { cout << " CGCellStepper: alpha = " << cgopt_.alpha() << endl; } for ( int i = 0; i < 9; i++ ) up_[i] = xp[3*nat_+i]; // enforce cell_lock constraints enforce_constraints(&up_[0]); // compute cellp = ( I + Up ) * A0 // symmetric matrix I+U stored in iumat: xx, yy, zz, xy, yz, xz double iupmat[6]; iupmat[0] = 1.0 + up_[0]; iupmat[1] = 1.0 + up_[4]; iupmat[2] = 1.0 + up_[8]; iupmat[3] = 0.5 * ( up_[1] + up_[3] ); iupmat[4] = 0.5 * ( up_[5] + up_[7] ); iupmat[5] = 0.5 * ( up_[2] + up_[6] ); const double *a0mat = cell0.amat(); double apmat[9]; cell0.smatmult3x3(&iupmat[0],a0mat,apmat); D3vector a0p(apmat[0],apmat[1],apmat[2]); D3vector a1p(apmat[3],apmat[4],apmat[5]); D3vector a2p(apmat[6],apmat[7],apmat[8]); cellp.set(a0p,a1p,a2p); // compute new atomic positions rp_[is][3*ia+j] from xp and cellp for ( int is = 0, i = 0; is < fion.size(); is++ ) { for ( int ia = 0; ia < fion[is].size()/3; ia++ ) { tmp3[0] = xp[i+0]; tmp3[1] = xp[i+1]; tmp3[2] = xp[i+2]; cellp.vecmult3x3(cellp.amat(),tmp3,&rp_[is][3*ia]); i+=3; } } // enforce constraints on atomic positions s_.constraints.enforce_r(r0,rp_); } //////////////////////////////////////////////////////////////////////////////// void CGCellStepper::update_cell(void) { s_.atoms.set_positions(rp_); s_.atoms.set_cell(cellp); u_ = up_; // resize wavefunction and basis sets s_.wf.resize(cellp,s_.wf.refcell(),s_.wf.ecut()); if ( s_.wfv != 0 ) s_.wfv->resize(cellp,s_.wf.refcell(),s_.wf.ecut()); }