void removeUnitaryProductions(GrammarADT grammar) { ProductionsADT productions = getProductions(grammar); int i,j,k, productionquant = getQuant(productions), unitaryquant = 0, lastunitaryquant = 0; /*auxiliar array for unitary productions*/ char * unitaries = NULL; /*iterate over productions and determine first unitaries: * the productions that have only one non terminal symbol * on the right side */ for (i=0; i< productionquant; i++) { char first = getProductionComponent(getProduction(productions,i),0); char sec = getProductionComponent(getProduction(productions,i),1); char third = getProductionComponent(getProduction(productions,i),2); if ( isNonTerminal(sec) && third == LAMDA ) { addPair(&unitaries,&unitaryquant,first, sec); } else if( isNonTerminal(third) && sec == LAMDA) { addPair(&unitaries,&unitaryquant,first, third); } } /*iterate over unitaries, adding the closure*/ while(unitaryquant != lastunitaryquant) { lastunitaryquant = unitaryquant; for (i=0; i<unitaryquant ; i+=2) { char first1 = unitaries[i]; char sec1 = unitaries[i+1]; for (j=0; j<unitaryquant ; j+=2) { char first2 = unitaries[j]; char sec2 = unitaries[j+1]; /*(A,B)(B,C)-> (A,C)*/ if (sec1 == first2 ) { if (!containsPair(unitaries,unitaryquant,first1,sec2) && first1 != sec2 ) { /*no sense in adding (A,A) unitaries*/ addPair(&unitaries,&unitaryquant,first1,sec2); } } } } } /*Debug*/ //printByPairs(unitaries,unitaryquant); //printf("unitaries quant: %d\n\n", unitaryquant/2); /*create the new productions and remove the unitaries*/ for(i=0; i<productionquant; i++) { ProductionADT p1 = getProduction(productions,i); if ( isUnitary(p1) ) { char first1 = getProductionComponent(p1,0); char sec1 = getProductionComponent(p1,1); char third1 = getProductionComponent(p1,2); for(j=0; j<unitaryquant; j+=2) { char uni1 = unitaries[j]; char uni2 = unitaries[j+1]; //A->B and (A,B) (unitary production is localized) if ((first1 == uni1) && (sec1 == uni2 || third1 == uni2 )) { for(k=0; k<productionquant; k++ ) { ProductionADT p2 = getProduction(productions,k); char first2 = getProductionComponent(p2,0); char sec2 = getProductionComponent(p2,1); char third2 = getProductionComponent(p2,2); if(!isUnitary(p2)) { if(first2 == uni2 ) { addProduction(productions,newProduction(first1,sec2,third2)); } } } } } removeParticularProduction(productions,p1); free(p1); } } /*remove non terminals and terminals that are no longer there */ actualizeTerminals(grammar); actualizeNonTerminals(grammar); actualizeProductions(grammar); }
void Phonon::setup(bool printDefaults) { //Parse input to initialize unit cell: parse(input, e, printDefaults); logSuspend(); parse(input, eSupTemplate); //silently create a copy by re-parsing input (Everything is not trivially copyable) logResume(); //Ensure phonon command specified: if(!sup.length()) die("phonon supercell must be specified using the phonon command.\n"); if(!e.gInfo.S.length_squared()) die("Manual fftbox setting required for phonon. If supercell grid\n" "initialization fails, specify slightly larger manual fftbox.\n"); //Check kpoint and supercell compatibility: if(e.eInfo.qnums.size()>1 || e.eInfo.qnums[0].k.length_squared()) die("phonon requires a Gamma-centered uniform kpoint mesh.\n"); for(int j=0; j<3; j++) { if(!sup[j] || e.eInfo.kfold[j] % sup[j]) { die("kpoint folding %d is not a multiple of supercell count %d for lattice direction %d.\n", e.eInfo.kfold[j], sup[j], j); } eSupTemplate.eInfo.kfold[j] = e.eInfo.kfold[j] / sup[j]; } logPrintf("########### Unit cell calculation #############\n"); SpeciesInfo::Constraint constraintFull; constraintFull.moveScale = 0; constraintFull.type = SpeciesInfo::Constraint::None; for(size_t sp=0; sp<e.iInfo.species.size(); sp++) e.iInfo.species[sp]->constraints.assign(e.iInfo.species[sp]->atpos.size(), constraintFull); e.setup(); if(!e.coulombParams.supercell) e.updateSupercell(true); //force supercell generation nSpins = e.eInfo.spinType==SpinZ ? 2 : 1; nSpinor = e.eInfo.spinorLength(); //Initialize state of unit cell: if(e.cntrl.dumpOnly) { //Single energy calculation so that all dependent quantities have been initialized: logPrintf("\n----------- Energy evaluation at fixed state -------------\n"); logFlush(); e.eVars.elecEnergyAndGrad(e.ener, 0, 0, true); } else elecFluidMinimize(e); logPrintf("# Energy components:\n"); e.ener.print(); logPrintf("\n"); //Determine optimum number of bands for supercell calculation: nBandsOpt = 0; for(int q=e.eInfo.qStart; q<e.eInfo.qStop; q++) { int nBands_q = std::upper_bound(e.eVars.F[q].begin(), e.eVars.F[q].end(), Fcut, std::greater<double>()) - e.eVars.F[q].begin(); nBandsOpt = std::max(nBandsOpt, nBands_q); } mpiUtil->allReduce(nBandsOpt, MPIUtil::ReduceMax); logPrintf("Fcut=%lg reduced nBands from %d to %d per unit cell.\n", Fcut, e.eInfo.nBands, nBandsOpt); //Make unit cell state available on all processes //(since MPI division of qSup and q are different and independent of the map) for(int q=0; q<e.eInfo.nStates; q++) { //Allocate: if(!e.eInfo.isMine(q)) { e.eVars.C[q].init(e.eInfo.nBands, e.basis[q].nbasis * e.eInfo.spinorLength(), &e.basis[q], &e.eInfo.qnums[q]); e.eVars.F[q].resize(e.eInfo.nBands); e.eVars.Hsub_eigs[q].resize(e.eInfo.nBands); if(e.eInfo.fillingsUpdate==ElecInfo::FermiFillingsAux) e.eVars.B[q].init(e.eInfo.nBands, e.eInfo.nBands); } //Broadcast from owner: int qSrc = e.eInfo.whose(q); e.eVars.C[q].bcast(qSrc); e.eVars.F[q].bcast(qSrc); e.eVars.Hsub_eigs[q].bcast(qSrc); if(e.eInfo.fillingsUpdate==ElecInfo::FermiFillingsAux) e.eVars.B[q].bcast(qSrc); } logPrintf("\n------- Configuring supercell and perturbation modes -------\n"); //Grid: eSupTemplate.gInfo.S = Diag(sup) * e.gInfo.S; //ensure exact supercell eSupTemplate.gInfo.R = e.gInfo.R * Diag(sup); prodSup = sup[0] * sup[1] * sup[2]; //Replicate atoms (and related properties): for(size_t sp=0; sp<e.iInfo.species.size(); sp++) { const SpeciesInfo& spIn = *(e.iInfo.species[sp]); SpeciesInfo& spOut = *(eSupTemplate.iInfo.species[sp]); spOut.atpos.clear(); spOut.initialMagneticMoments.clear(); matrix3<> invSup = inv(Diag(vector3<>(sup))); vector3<int> iR; for(iR[0]=0; iR[0]<sup[0]; iR[0]++) for(iR[1]=0; iR[1]<sup[1]; iR[1]++) for(iR[2]=0; iR[2]<sup[2]; iR[2]++) { for(vector3<> pos: spIn.atpos) spOut.atpos.push_back(invSup * (pos + iR)); for(vector3<> M: spIn.initialMagneticMoments) spOut.initialMagneticMoments.push_back(M); //needed only to determine supercell symmetries } spOut.constraints.assign(spOut.atpos.size(), constraintFull); } //Supercell symmetries: eSupTemplate.symm.setup(eSupTemplate); const std::vector< matrix3<int> >& symSup = eSupTemplate.symm.getMatrices(); symSupCart.clear(); eSupTemplate.gInfo.invR = inv(eSupTemplate.gInfo.R); for(const matrix3<int>& m: symSup) symSupCart.push_back(eSupTemplate.gInfo.R * m * eSupTemplate.gInfo.invR); //Pick maximally symmetric orthogonal basis: logPrintf("\nFinding maximally-symmetric orthogonal basis for displacements:\n"); std::vector< vector3<> > dirBasis; { std::multimap<int, vector3<> > dirList; //directions indexed by their stabilizer group cardinality vector3<int> iR; for(iR[0]=0; iR[0]<=+1; iR[0]++) for(iR[1]=-1; iR[1]<=+1; iR[1]++) for(iR[2]=-1; iR[2]<=+1; iR[2]++) if(iR.length_squared()) { //Try low-order lattice vector linear combination: vector3<> n = eSupTemplate.gInfo.R * iR; n *= (1./n.length()); dirList.insert(std::make_pair(nStabilizer(n, symSupCart), n)); //Try low-order reciprocal lattice vector linear combination: n = iR * eSupTemplate.gInfo.invR; n *= (1./n.length()); dirList.insert(std::make_pair(nStabilizer(n, symSupCart), n)); } dirBasis.push_back(dirList.rbegin()->second); //Pick second driection orthogonal to first: std::multimap<int, vector3<> > dirList2; for(auto entry: dirList) { vector3<> n = entry.second; n -= dot(n, dirBasis[0]) * dirBasis[0]; if(n.length_squared() < symmThresholdSq) continue; n *= (1./n.length()); dirList2.insert(std::make_pair(nStabilizer(n, symSupCart), n)); } dirBasis.push_back(dirList2.rbegin()->second); dirBasis.push_back(cross(dirBasis[0], dirBasis[1])); //third direction constrained by orthogonality } for(const vector3<>& n: dirBasis) logPrintf(" [ %+lf %+lf %+lf ] |Stabilizer|: %d\n", n[0], n[1], n[2], nStabilizer(n,symSupCart)); //List all modes: modes.clear(); for(size_t sp=0; sp<e.iInfo.species.size(); sp++) for(size_t at=0; at<e.iInfo.species[sp]->atpos.size(); at++) //only need to move atoms in first unit cell for(int iDir=0; iDir<3; iDir++) { Mode mode; mode.sp = sp; mode.at = at; mode.dir[iDir] = 1.; modes.push_back(mode); } //Find irreducible modes: perturbations.clear(); for(unsigned sp=0; sp<e.iInfo.species.size(); sp++) { int nAtoms = e.iInfo.species[sp]->atpos.size(); int nPert = nAtoms * dirBasis.size(); //generate all perturbations first: std::vector<Perturbation> pertSp(nPert); //perturbations of this species std::vector<matrix> proj(nPert); //projection operator into subspace spanned by star of current perturbation matrix projTot; const auto& atomMap = eSupTemplate.symm.getAtomMap()[sp]; for(int iPert=0; iPert<nPert; iPert++) { pertSp[iPert].sp = sp; pertSp[iPert].at = iPert / dirBasis.size(); pertSp[iPert].dir = dirBasis[iPert % dirBasis.size()]; pertSp[iPert].weight = 1./symSupCart.size(); for(unsigned iSym=0; iSym<symSupCart.size(); iSym++) { int at = atomMap[pertSp[iPert].at][iSym] % nAtoms; //map back to first cell vector3<> dir = symSupCart[iSym] * pertSp[iPert].dir; matrix nHat = zeroes(nPert,1); for(int iDir=0; iDir<3; iDir++) nHat.set(at*3+iDir,0, dir[iDir]); proj[iPert] += pertSp[iPert].weight * nHat * dagger(nHat); } projTot += proj[iPert]; } myassert(nrm2(projTot - eye(nPert)) < symmThreshold); //only select perturbations with distinct subspace projections: std::vector<bool> irred(nPert, true); //whether each perturbation is in irreducible set for(int iPert=0; iPert<nPert; iPert++) { for(int jPert=0; jPert<iPert; jPert++) if(irred[jPert] && nrm2(proj[iPert]-proj[jPert])<symmThreshold) { pertSp[jPert].weight += pertSp[iPert].weight; //send weight of current mode to its image in irreducible set irred[iPert] = false; //this mode will be accounted for upon symmetrization break; } } for(int iPert=0; iPert<nPert; iPert++) if(irred[iPert]) perturbations.push_back(pertSp[iPert]); } logPrintf("\n%d perturbations of the unit cell reduced to %d under symmetries:\n", int(modes.size()), int(perturbations.size())); for(const Perturbation& pert: perturbations) logPrintf("%s %d [ %+lf %+lf %+lf ] %lf\n", e.iInfo.species[pert.sp]->name.c_str(), pert.at, pert.dir[0], pert.dir[1], pert.dir[2], pert.weight*symSupCart.size()); //Determine wavefunction unitary rotations: logPrintf("\nCalculating unitary rotations of unit cell states under symmetries:\n"); stateRot.resize(nSpins); double unitarityErr = 0.; for(int iSpin=0; iSpin<nSpins; iSpin++) { //Find states involved in the supercell Gamma-point: struct Kpoint : public Supercell::KmeshTransform { vector3<> k; //also store k-point for convenience (KmeshTransform doesn't have it) }; std::vector<Kpoint> kpoints; kpoints.reserve(prodSup); const Supercell& supercell = *(e.coulombParams.supercell); for(unsigned ik=0; ik<supercell.kmesh.size(); ik++) { double kSupErr; round(matrix3<>(Diag(sup)) * supercell.kmesh[ik], &kSupErr); if(kSupErr < symmThreshold) //maps to Gamma point { Kpoint kpoint; (Supercell::KmeshTransform&)kpoint = supercell.kmeshTransform[ik]; //copy base class kpoint.k = supercell.kmesh[ik]; kpoint.iReduced += iSpin*(e.eInfo.nStates/nSpins); //point to source k-point with appropriate spin kpoints.push_back(kpoint); } } myassert(int(kpoints.size()) == prodSup); //Initialize basis and qnum for these states: std::vector<QuantumNumber> qnums(prodSup); std::vector<Basis> basis(prodSup); logSuspend(); for(int ik=0; ik<prodSup; ik++) { qnums[ik].k = kpoints[ik].k; qnums[ik].spin = (nSpins==1 ? 0 : (iSpin ? +1 : -1)); qnums[ik].weight = 1./prodSup; basis[ik].setup(e.gInfo, e.iInfo, e.cntrl.Ecut, kpoints[ik].k); } logResume(); //Get wavefunctions for all these k-points: #define whose_ik(ik) (((ik) * mpiUtil->nProcesses())/prodSup) //local MPI division std::vector<ColumnBundle> C(prodSup); std::vector<std::shared_ptr<ColumnBundleTransform::BasisWrapper> > basisWrapper(prodSup); auto sym = e.symm.getMatrices(); //unit cell symmetries for(int ik=0; ik<prodSup; ik++) { C[ik].init(e.eInfo.nBands, basis[ik].nbasis*nSpinor, &basis[ik], &qnums[ik], isGpuEnabled()); if(whose_ik(ik) == mpiUtil->iProcess()) { int q = kpoints[ik].iReduced; C[ik].zero(); basisWrapper[ik] = std::make_shared<ColumnBundleTransform::BasisWrapper>(basis[ik]); ColumnBundleTransform(e.eInfo.qnums[q].k, e.basis[q], qnums[ik].k, *(basisWrapper[ik]), nSpinor, sym[kpoints[ik].iSym], kpoints[ik].invert).scatterAxpy(1., e.eVars.C[q], C[ik],0,1); } } for(int ik=0; ik<prodSup; ik++) C[ik].bcast(whose_ik(ik)); //make available on all processes //Determine max eigenvalue: int nBands = e.eInfo.nBands; double Emax = -INFINITY; for(int q=e.eInfo.qStart; q<e.eInfo.qStop; q++) Emax = std::max(Emax, e.eVars.Hsub_eigs[q].back()); mpiUtil->allReduce(Emax, MPIUtil::MPIUtil::ReduceMax); double EmaxValid = +INFINITY; //Loop over supercell symmetry operations: PeriodicLookup<QuantumNumber> plook(qnums, e.gInfo.GGT); stateRot[iSpin].resize(symSupCart.size()); for(size_t iSym=0; iSym<symSupCart.size(); iSym++) { matrix3<> symUnitTmp = e.gInfo.invR * symSupCart[iSym] * e.gInfo.R; //in unit cell lattice coordinates #define SymmErrMsg \ "Supercell symmetries do not map unit cell k-point mesh onto itself.\n" \ "This implies that the supercell is more symmetric than the unit cell!\n" \ "Please check to make sure that you have used the minimal unit cell.\n\n" matrix3<int> symUnit; for(int j1=0; j1<3; j1++) for(int j2=0; j2<3; j2++) { symUnit(j1,j2) = round(symUnitTmp(j1,j2)); if(fabs(symUnit(j1,j2) - symUnitTmp(j1,j2)) > symmThreshold) die(SymmErrMsg) } //Find image kpoints under rotation: (do this for all k-points so that all processes exit together if necessary) std::vector<int> ikRot(prodSup); for(int ik=0; ik<prodSup; ik++) { size_t ikRotCur = plook.find(qnums[ik].k * symUnit); if(ikRotCur==string::npos) die(SymmErrMsg) ikRot[ik] = ikRotCur; } #undef SymmErrMsg //Calculate unitary transformation matrix: stateRot[iSpin][iSym].init(prodSup, nBands); for(int ik=0; ik<prodSup; ik++) if(whose_ik(ikRot[ik]) == mpiUtil->iProcess()) //MPI division by target k-point { ColumnBundle Crot = C[ikRot[ik]].similar(); Crot.zero(); ColumnBundleTransform(qnums[ik].k, basis[ik], qnums[ikRot[ik]].k, *(basisWrapper[ikRot[ik]]), nSpinor, symUnit, +1).scatterAxpy(1., C[ik], Crot,0,1); matrix Urot = Crot ^ O(C[ikRot[ik]]); //will be unitary if Crot is a strict unitary rotation of C[ikRot[ik]] //Check maximal subspace that is unitary: (remiander must be incomplete degenerate subspace) int nBandsValid = nBands; while(nBandsValid && !isUnitary(Urot(0,nBandsValid, 0,nBandsValid))) nBandsValid--; if(nBandsValid<nBands) { //Update energy range of validity: EmaxValid = std::min(EmaxValid, e.eVars.Hsub_eigs[kpoints[ik].iReduced][nBandsValid]); //Make valid subspace exactly unitary: matrix UrotSub = Urot(0,nBandsValid, 0,nBandsValid); matrix UrotOverlap = dagger(UrotSub) * UrotSub; UrotSub = UrotSub * invsqrt(UrotOverlap); //make exactly unitary unitarityErr += std::pow(nrm2(UrotOverlap - eye(nBandsValid)), 2); //Zero out invalid subspace: Urot.zero(); Urot.set(0,nBandsValid, 0,nBandsValid, UrotSub); } stateRot[iSpin][iSym].set(ik, ikRot[ik], Urot); } stateRot[iSpin][iSym].allReduce(); } #undef whose_ik mpiUtil->allReduce(EmaxValid, MPIUtil::ReduceMin); if(nSpins>1) logPrintf("\tSpin %+d: ", iSpin ? +1 : -1); else logPrintf("\t"); logPrintf("Matrix elements valid for "); if(std::isfinite(EmaxValid)) logPrintf("E < %+.6lf (Emax = %+.6lf) due to incomplete degenerate subspaces.\n", EmaxValid, Emax); else logPrintf("all available states (all degenerate subspaces are complete).\n"); } mpiUtil->allReduce(unitarityErr, MPIUtil::ReduceSum); unitarityErr = sqrt(unitarityErr / (nSpins * prodSup * symSupCart.size())); logPrintf("\tRMS unitarity error in valid subspaces: %le\n", unitarityErr); }