mirror of
https://github.com/paboyle/Grid.git
synced 2026-08-20 17:39:37 +01:00
241 lines
11 KiB
C++
241 lines
11 KiB
C++
/*************************************************************************************
|
|
|
|
Grid physics library, www.github.com/paboyle/Grid
|
|
|
|
Source file: ./examples/Example_mdagm.cc
|
|
|
|
Copyright (C) 2023
|
|
|
|
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it 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.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
See the full license in the file "LICENSE" in the top level distribution directory
|
|
*************************************************************************************/
|
|
/* END LEGAL */
|
|
#include <Grid/Grid.h>
|
|
#include <Grid/lattice/PaddedCell.h>
|
|
#include <Grid/stencil/GeneralLocalStencil.h>
|
|
|
|
using namespace std;
|
|
using namespace Grid;
|
|
|
|
// Routes Op/AdjOp -> HermOp so that CoarsenOperator and CreateSubspace
|
|
// both see the HPD operator M†M rather than bare M.
|
|
template<class Field>
|
|
class HermOpAdaptor : public LinearOperatorBase<Field>
|
|
{
|
|
LinearOperatorBase<Field> &wrapped;
|
|
public:
|
|
HermOpAdaptor(LinearOperatorBase<Field> &wrapme) : wrapped(wrapme) {};
|
|
void Op (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
void HermOp (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
void AdjOp (const Field &in, Field &out) { wrapped.HermOp(in,out); }
|
|
void OpDiag (const Field &in, Field &out) { GRID_ASSERT(0); }
|
|
void OpDir (const Field &in, Field &out,int dir,int disp) { GRID_ASSERT(0); }
|
|
void OpDirAll(const Field &in, std::vector<Field> &out) { GRID_ASSERT(0); }
|
|
void HermOpAndNorm(const Field &in, Field &out, RealD &n1, RealD &n2) {
|
|
wrapped.HermOp(in, out);
|
|
ComplexD dot = innerProduct(in, out);
|
|
n1 = real(dot);
|
|
n2 = norm2(out);
|
|
}
|
|
};
|
|
|
|
// Fixed-iteration CG smoother: runs exactly `iters` steps of CG on the
|
|
// shifted operator. tolerance=0 so CG never exits early.
|
|
template<class Field>
|
|
class CGSmoother : public LinearFunction<Field>
|
|
{
|
|
public:
|
|
using LinearFunction<Field>::operator();
|
|
typedef LinearOperatorBase<Field> FineOperator;
|
|
FineOperator &_SmootherOperator;
|
|
int iters;
|
|
CGSmoother(int _iters, FineOperator &SmootherOperator)
|
|
: _SmootherOperator(SmootherOperator), iters(_iters)
|
|
{
|
|
std::cout << GridLogMessage << " CGSmoother order " << iters << std::endl;
|
|
}
|
|
void operator()(const Field &in, Field &out)
|
|
{
|
|
ConjugateGradient<Field> CG(0.0, iters, false);
|
|
out = Zero();
|
|
CG(_SmootherOperator, in, out);
|
|
}
|
|
};
|
|
|
|
int main (int argc, char ** argv)
|
|
{
|
|
Grid_init(&argc,&argv);
|
|
|
|
const int Ls = 24;
|
|
const int nbasis = 60;
|
|
const int cb = 0;
|
|
RealD M5 = 1.8;
|
|
RealD b = 1.5;
|
|
RealD c = 0.5;
|
|
|
|
RealD mass = 0.00078;
|
|
{ const char *e = getenv("MASS"); if (e && *e) mass = atof(e); }
|
|
|
|
std::cout << GridLogMessage << "Mass: " << mass
|
|
<< " Ls: " << Ls << " b=" << b << " c=" << c << std::endl;
|
|
std::cout << GridLogMessage << "nbasis: " << nbasis << std::endl;
|
|
|
|
// ── Grids ──────────────────────────────────────────────────────────────
|
|
std::vector<int> lat_size{48,48,48,96};
|
|
|
|
GridCartesian *UGrid = SpaceTimeGrid::makeFourDimGrid(lat_size,
|
|
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
|
GridDefaultMpi());
|
|
GridRedBlackCartesian *UrbGrid = SpaceTimeGrid::makeFourDimRedBlackGrid(UGrid);
|
|
GridCartesian *FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,UGrid);
|
|
GridRedBlackCartesian *FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,UGrid);
|
|
|
|
Coordinate Block({4,4,3,4});
|
|
Coordinate clatt = lat_size;
|
|
for (int d = 0; d < (int)clatt.size(); d++) clatt[d] /= Block[d];
|
|
|
|
GridCartesian *Coarse4d = SpaceTimeGrid::makeFourDimGrid(clatt,
|
|
GridDefaultSimd(Nd,vComplex::Nsimd()),
|
|
GridDefaultMpi());
|
|
GridCartesian *Coarse5d = SpaceTimeGrid::makeFiveDimGrid(1,Coarse4d);
|
|
|
|
// ── RNGs ───────────────────────────────────────────────────────────────
|
|
GridParallelRNG RNG5(FGrid); RNG5.SeedFixedIntegers({5,6,7,8});
|
|
GridParallelRNG RNG4(UGrid); RNG4.SeedFixedIntegers({1,2,3,4});
|
|
|
|
// ── Gauge field ────────────────────────────────────────────────────────
|
|
LatticeGaugeField Umu(UGrid);
|
|
FieldMetaData header;
|
|
NerscIO::readConfiguration(Umu, header, std::string("/ccs/home/poare/ckpoint_lat.1000"));
|
|
|
|
// ── Fermion operator ───────────────────────────────────────────────────
|
|
MobiusFermionD Ddwf(Umu,*FGrid,*FrbGrid,*UGrid,*UrbGrid,mass,M5,b,c);
|
|
|
|
MdagMLinearOperator<MobiusFermionD,LatticeFermionD> MdagMOp(Ddwf);
|
|
HermOpAdaptor<LatticeFermionD> HermFineOp(MdagMOp);
|
|
|
|
// ── Coarse geometry ────────────────────────────────────────────────────
|
|
typedef GeneralCoarsenedMatrix<vSpinColourVector,vTComplex,nbasis> LittleDiracOperator;
|
|
typedef LittleDiracOperator::CoarseVector CoarseVector;
|
|
typedef Aggregation<vSpinColourVector,vTComplex,nbasis> Subspace;
|
|
|
|
NextToNearestStencilGeometry5D geom(Coarse5d);
|
|
|
|
// ── Power method: estimate upper end of M†M spectrum ───────────────────
|
|
LatticeFermionD pm_src(FGrid); random(RNG5, pm_src);
|
|
PowerMethod<LatticeFermionD> PM;
|
|
RealD hi = PM(HermFineOp, pm_src);
|
|
std::cout << GridLogMessage << "Power method: hi = " << hi << std::endl;
|
|
|
|
// ── Smoother: fixed-iteration CG on (M†M + lo) ─────────────────────────
|
|
// lo/hi ~ 2/95 matches the HDCG ratio; tune empirically.
|
|
RealD lo = hi / 40.0;
|
|
int ord = 12;
|
|
std::cout << GridLogMessage << "Smoother shift lo = " << lo
|
|
<< " order = " << ord << std::endl;
|
|
ShiftedHermOpLinearOperator<LatticeFermionD> ShiftedFineOp(HermFineOp, lo);
|
|
CGSmoother<LatticeFermionD> Smoother(ord, ShiftedFineOp);
|
|
|
|
// ── Subspace via CG inverse iteration ──────────────────────────────────
|
|
Subspace Aggregates(Coarse5d, FGrid, cb);
|
|
Aggregates.CreateSubspace(RNG5, HermFineOp, nbasis);
|
|
|
|
// ── Cheap coarse deflation: diagonalise W BEFORE block-GS ──────────────
|
|
// Orthogonalise() applies block-local Gram-Schmidt which rotates subspace[i]
|
|
// into orthonormal block-local combinations, destroying the near-null
|
|
// property of individual vectors. We must extract the coarse zero-mode
|
|
// combinations χₖ = Σᵢ V[i,k] ψᵢ from the pre-GS near-null vectors first.
|
|
std::vector<LatticeFermionD> chi(nbasis, FGrid);
|
|
{
|
|
std::vector<LatticeFermionD> &psi = Aggregates.subspace;
|
|
LatticeFermionD tmp(FGrid);
|
|
Eigen::MatrixXcd W = Eigen::MatrixXcd::Zero(nbasis, nbasis);
|
|
for (int i = 0; i < nbasis; i++) {
|
|
HermFineOp.Op(psi[i], tmp);
|
|
for (int j = 0; j < nbasis; j++)
|
|
W(j, i) = TensorRemove(innerProduct(psi[j], tmp));
|
|
}
|
|
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXcd> esolver(W);
|
|
for (int k = 0; k < nbasis; k++) {
|
|
chi[k] = Zero();
|
|
for (int i = 0; i < nbasis; i++)
|
|
chi[k] += ComplexD(esolver.eigenvectors()(i, k)) * psi[i];
|
|
}
|
|
}
|
|
|
|
Aggregates.Orthogonalise();
|
|
|
|
// ── Coarse operator ────────────────────────────────────────────────────
|
|
LittleDiracOperator LittleDiracOp(geom, FGrid, Coarse5d);
|
|
LittleDiracOp.CoarsenOperator(HermFineOp, Aggregates);
|
|
|
|
// ── Coarse linear operator ─────────────────────────────────────────────
|
|
HermitianLinearOperator<LittleDiracOperator,CoarseVector> LinOpCoarse(LittleDiracOp);
|
|
|
|
// ── Project χₖ to coarse grid; Rayleigh quotients give deflation evals ─
|
|
// ProjectToSubspace uses the post-GS basis, correctly mapping the pre-GS
|
|
// near-null combinations to coarse vectors via the block-local U†(x_c).
|
|
std::vector<CoarseVector> coarse_deflation_vecs(nbasis, Coarse5d);
|
|
std::vector<RealD> coarse_deflation_evals(nbasis);
|
|
{
|
|
CoarseVector Ac(Coarse5d);
|
|
for (int k = 0; k < nbasis; k++) {
|
|
Aggregates.ProjectToSubspace(coarse_deflation_vecs[k], chi[k]);
|
|
RealD n = norm2(coarse_deflation_vecs[k]);
|
|
coarse_deflation_vecs[k] *= 1.0 / std::sqrt(n);
|
|
LinOpCoarse.HermOp(coarse_deflation_vecs[k], Ac);
|
|
coarse_deflation_evals[k] = real(TensorRemove(innerProduct(coarse_deflation_vecs[k], Ac)));
|
|
std::cout << GridLogMessage << "Coarse deflation eval[" << k << "] = "
|
|
<< coarse_deflation_evals[k] << std::endl;
|
|
}
|
|
}
|
|
|
|
// ── Coarse solve: CG + deflation guesser ──────────────────────────────
|
|
ConjugateGradient<CoarseVector> coarseCG(5.0e-2, 10000, false);
|
|
DeflatedGuesser<CoarseVector> coarseGuess(coarse_deflation_vecs, coarse_deflation_evals);
|
|
HPDSolver<CoarseVector> CoarseSolve(LinOpCoarse, coarseCG, coarseGuess);
|
|
|
|
// ── ADEF2 outer solve ──────────────────────────────────────────────────
|
|
LatticeFermionD src(FGrid); random(RNG5, src);
|
|
LatticeFermionD result(FGrid); result = Zero();
|
|
|
|
TwoLevelADEF2<LatticeFermionD, CoarseVector, Subspace>
|
|
HDCG(1.0e-8, 1000,
|
|
HermFineOp,
|
|
Smoother,
|
|
CoarseSolve, // used in PcgM1
|
|
CoarseSolve, // used in Vstart
|
|
Aggregates);
|
|
|
|
HDCG(src, result);
|
|
|
|
// ── Reference RBCG ─────────────────────────────────────────────────────
|
|
#if 0
|
|
{
|
|
SchurDiagMooeeOperator<MobiusFermionD,LatticeFermion> HermOpEO(Ddwf);
|
|
LatticeFermionD rb_src(FrbGrid); random(RNG5, rb_src);
|
|
LatticeFermionD rb_res(FrbGrid); rb_res = Zero();
|
|
ConjugateGradient<LatticeFermionD> CG(1.0e-8, 30000, false);
|
|
CG(HermOpEO, rb_src, rb_res);
|
|
}
|
|
#endif
|
|
|
|
Grid_finalize();
|
|
return 0;
|
|
}
|